source: trunk/gtk/tracker-list.c @ 6862

Last change on this file since 6862 was 6862, checked in by charles, 14 years ago

(gtk) fix some format mangling caused by uncrustify

File size: 15.0 KB
Line 
1/*
2 * This file Copyright (C) 2007-2008 Charles Kerr <charles@rebelbase.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: details.c 5987 2008-06-01 01:40:32Z charles $
11 */
12
13#include <errno.h>
14#include <stddef.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <glib/gi18n.h>
18#include <gtk/gtk.h>
19
20#include <libtransmission/transmission.h>
21#include <libtransmission/utils.h> /* tr_httpIsValidURL */
22
23#include "actions.h"
24#include "details.h"
25#include "file-list.h"
26#include "tracker-list.h"
27#include "tr-torrent.h"
28#include "hig.h"
29#include "util.h"
30
31#define UPDATE_INTERVAL_MSEC 2000
32#define PAGE_KEY "page"
33
34struct tracker_page
35{
36    TrTorrent *         gtor;
37
38    GtkTreeView *       view;
39    GtkListStore *      store;
40    GtkTreeSelection *  sel;
41
42    GtkWidget *         add_button;
43    GtkWidget *         remove_button;
44    GtkWidget *         save_button;
45    GtkWidget *         revert_button;
46
47    GtkWidget *         last_scrape_time_lb;
48    GtkWidget *         last_scrape_response_lb;
49    GtkWidget *         next_scrape_countdown_lb;
50
51    GtkWidget *         last_announce_time_lb;
52    GtkWidget *         last_announce_response_lb;
53    GtkWidget *         next_announce_countdown_lb;
54    GtkWidget *         manual_announce_countdown_lb;
55};
56
57enum
58{
59    TR_COL_TIER,
60    TR_COL_ANNOUNCE,
61    TR_N_COLS
62};
63
64static void
65setTrackerChangeState( struct tracker_page * page,
66                       gboolean              changed )
67{
68    if( page->save_button )
69        gtk_widget_set_sensitive( page->save_button, changed );
70
71    if( page->revert_button )
72        gtk_widget_set_sensitive( page->revert_button, changed );
73}
74
75static GtkTreeModel*
76tracker_model_new( tr_torrent * tor )
77{
78    int             i;
79    const tr_info * inf = tr_torrentInfo( tor );
80    GtkListStore *  store = gtk_list_store_new( TR_N_COLS, G_TYPE_INT,
81                                                G_TYPE_STRING );
82
83    for( i = 0; inf && i < inf->trackerCount; ++i )
84    {
85        GtkTreeIter             iter;
86        const tr_tracker_info * tinf = inf->trackers + i;
87        gtk_list_store_append( store, &iter );
88        gtk_list_store_set( store, &iter,
89                            TR_COL_TIER, tinf->tier + 1,
90                            TR_COL_ANNOUNCE, tinf->announce,
91                            -1 );
92    }
93
94    gtk_tree_sortable_set_sort_column_id( GTK_TREE_SORTABLE( store ),
95                                          TR_COL_TIER,
96                                          GTK_SORT_ASCENDING );
97
98    return GTK_TREE_MODEL( store );
99}
100
101static void
102onTrackerSelectionChanged( GtkTreeSelection * sel,
103                           gpointer           gpage )
104{
105    struct tracker_page * page = gpage;
106    const gboolean has_selection =
107        gtk_tree_selection_count_selected_rows( sel ) > 0;
108    GtkTreeModel * model = GTK_TREE_MODEL( page->store );
109    const int trackerCount = gtk_tree_model_iter_n_children( model, NULL );
110    const gboolean ok_to_remove = !page->gtor || ( trackerCount > 1 );
111    gtk_widget_set_sensitive( page->remove_button, has_selection && ok_to_remove );
112}
113
114static void
115onTrackerRemoveClicked( GtkButton * w UNUSED,
116                        gpointer      gpage )
117{
118    struct tracker_page * page = gpage;
119    GtkTreeModel * model;
120    GList * l;
121    GList * list = gtk_tree_selection_get_selected_rows( page->sel, &model );
122
123    /* convert the list to references */
124    for( l=list; l; l=l->next ) {
125        GtkTreePath * path = l->data;
126        l->data = gtk_tree_row_reference_new( model, path );
127        gtk_tree_path_free( path );
128    }
129
130    /* remove the selected rows */
131    for( l=list; l; l=l->next ) {
132        GtkTreePath * path = gtk_tree_row_reference_get_path( l->data );
133        GtkTreeIter iter;
134        gtk_tree_model_get_iter( model, &iter, path );
135        gtk_list_store_remove( page->store, &iter );
136        gtk_tree_path_free( path );
137        gtk_tree_row_reference_free( l->data );
138    }
139
140    setTrackerChangeState( page, TRUE );
141
142    /* cleanup */
143    g_list_free( list );
144}
145
146static void
147onTrackerAddClicked( GtkButton * w UNUSED,
148                     gpointer      gpage )
149{
150    GtkTreeIter           iter;
151    struct tracker_page * page = gpage;
152    GtkTreePath *         path;
153
154    gtk_list_store_append( page->store, &iter );
155    setTrackerChangeState( page, TRUE );
156    gtk_list_store_set( page->store, &iter,
157                        TR_COL_TIER, 1,
158                        TR_COL_ANNOUNCE, "http://",
159                        -1 );
160    path = gtk_tree_model_get_path( GTK_TREE_MODEL( page->store ), &iter );
161    gtk_tree_view_set_cursor( page->view,
162                              path,
163                              gtk_tree_view_get_column( page->view,
164                                                        TR_COL_ANNOUNCE ),
165                              TRUE );
166    gtk_tree_path_free( path );
167}
168
169static void
170onTrackerSaveClicked( GtkButton * w UNUSED,
171                      gpointer      gpage )
172{
173    struct tracker_page * page = gpage;
174    GtkTreeModel *        model = GTK_TREE_MODEL( page->store );
175    const int             n = gtk_tree_model_iter_n_children( model, NULL );
176
177    if( n > 0 ) /* must have at least one tracker */
178    {
179        int               i = 0;
180        GtkTreeIter       iter;
181        tr_tracker_info * trackers;
182
183        /* build the tracker list */
184        trackers = g_new0( tr_tracker_info, n );
185        if( gtk_tree_model_get_iter_first( model, &iter ) ) do
186            {
187                gtk_tree_model_get( model, &iter, TR_COL_TIER,
188                                    &trackers[i].tier,
189                                    TR_COL_ANNOUNCE, &trackers[i].announce,
190                                    -1 );
191                ++i;
192            }
193            while( gtk_tree_model_iter_next( model, &iter ) );
194
195        g_assert( i == n );
196
197        /* set the tracker list */
198        tr_torrentSetAnnounceList( tr_torrent_handle( page->gtor ),
199                                   trackers, n );
200
201
202        setTrackerChangeState( page, FALSE );
203
204        /* cleanup */
205        for( i = 0; i < n; ++i )
206            g_free( trackers[i].announce );
207        g_free( trackers );
208    }
209}
210
211static void
212onTrackerRevertClicked( GtkButton * w UNUSED,
213                        gpointer      gpage )
214{
215    struct tracker_page * page = gpage;
216    GtkTreeModel *        model =
217        tracker_model_new( tr_torrent_handle( page->gtor ) );
218
219    gtk_tree_view_set_model( page->view, model );
220    page->store = GTK_LIST_STORE( model );
221    g_object_unref( G_OBJECT( model ) );
222    setTrackerChangeState( page, FALSE );
223}
224
225static void
226onAnnounceEdited( GtkCellRendererText * renderer UNUSED,
227                  gchar *                        path_string,
228                  gchar *                        new_text,
229                  gpointer                       gpage )
230{
231    struct tracker_page * page = gpage;
232    GtkTreeModel *        model = GTK_TREE_MODEL( page->store );
233    GtkTreeIter           iter;
234    GtkTreePath *         path = gtk_tree_path_new_from_string( path_string );
235
236    if( gtk_tree_model_get_iter( model, &iter, path ) )
237    {
238        char * old_text;
239        gtk_tree_model_get( model, &iter, TR_COL_ANNOUNCE, &old_text, -1 );
240        if( tr_httpIsValidURL( new_text ) )
241        {
242            if( strcmp( old_text, new_text ) )
243            {
244                gtk_list_store_set( page->store, &iter, TR_COL_ANNOUNCE,
245                                    new_text,
246                                    -1 );
247                setTrackerChangeState( page, TRUE );
248            }
249        }
250        else if( !tr_httpIsValidURL( old_text ) )
251        {
252            /* both old and new are invalid...
253               they must've typed in an invalid URL
254               after hitting the "Add" button */
255            onTrackerRemoveClicked( NULL, page );
256            setTrackerChangeState( page, TRUE );
257        }
258        g_free( old_text );
259    }
260    gtk_tree_path_free( path );
261}
262
263static void
264onTierEdited( GtkCellRendererText  * renderer UNUSED,
265              gchar *                         path_string,
266              gchar *                         new_text,
267              gpointer                        gpage )
268{
269    struct tracker_page * page = gpage;
270    GtkTreeModel *        model = GTK_TREE_MODEL( page->store );
271    GtkTreeIter           iter;
272    GtkTreePath *         path;
273    char *                end;
274    int                   new_tier;
275
276    errno = 0;
277    new_tier = strtol( new_text, &end, 10 );
278    if( new_tier < 1 || *end || errno )
279        return;
280
281    path = gtk_tree_path_new_from_string( path_string );
282    if( gtk_tree_model_get_iter( model, &iter, path ) )
283    {
284        int old_tier;
285        gtk_tree_model_get( model, &iter, TR_COL_TIER, &old_tier, -1 );
286        if( old_tier != new_tier )
287        {
288            gtk_list_store_set( page->store, &iter, TR_COL_TIER, new_tier,
289                                -1 );
290            setTrackerChangeState( page, TRUE );
291        }
292    }
293    gtk_tree_path_free( path );
294}
295
296GtkWidget*
297tracker_list_new( TrTorrent * gtor )
298{
299    GtkWidget *           w;
300    GtkWidget *           buttons;
301    GtkWidget *           box;
302    GtkWidget *           top;
303    GtkWidget *           fr;
304    GtkTreeModel *        m;
305    GtkCellRenderer *     r;
306    GtkTreeViewColumn *   c;
307    GtkTreeSelection *    sel;
308    struct tracker_page * page = g_new0( struct tracker_page, 1 );
309
310    page->gtor = gtor;
311
312    top = gtk_hbox_new( FALSE, GUI_PAD );
313    box = gtk_vbox_new( FALSE, GUI_PAD );
314    buttons = gtk_vbox_new( TRUE, GUI_PAD );
315
316    m = tracker_model_new( tr_torrent_handle( gtor ) );
317    page->store = GTK_LIST_STORE( m );
318    w = gtk_tree_view_new_with_model( m );
319    g_signal_connect( w, "button-release-event",
320                      G_CALLBACK( on_tree_view_button_released ), NULL );
321    page->view = GTK_TREE_VIEW( w );
322    gtk_tree_view_set_enable_search( page->view, FALSE );
323    r = gtk_cell_renderer_text_new( );
324    g_object_set( G_OBJECT( r ),
325                  "editable", TRUE,
326                  NULL );
327    g_signal_connect( r, "edited",
328                      G_CALLBACK( onTierEdited ), page );
329    c = gtk_tree_view_column_new_with_attributes( _( "Tier" ), r,
330                                                  "text", TR_COL_TIER,
331                                                  NULL );
332    gtk_tree_view_column_set_sort_column_id( c, TR_COL_TIER );
333    gtk_tree_view_append_column( page->view, c );
334    r = gtk_cell_renderer_text_new( );
335    g_object_set( G_OBJECT( r ),
336                  "editable", TRUE,
337                  "ellipsize", PANGO_ELLIPSIZE_END,
338                  NULL );
339    g_signal_connect( r, "edited",
340                      G_CALLBACK( onAnnounceEdited ), page );
341    c = gtk_tree_view_column_new_with_attributes( _( "Announce URL" ), r,
342                                                  "text", TR_COL_ANNOUNCE,
343                                                  NULL );
344    gtk_tree_view_column_set_sort_column_id( c, TR_COL_ANNOUNCE );
345    gtk_tree_view_append_column( page->view, c );
346    w = gtk_scrolled_window_new( NULL, NULL );
347    gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( w ),
348                                    GTK_POLICY_NEVER,
349                                    GTK_POLICY_AUTOMATIC );
350    sel = gtk_tree_view_get_selection( page->view );
351    page->sel = sel;
352    g_signal_connect( sel, "changed",
353                      G_CALLBACK( onTrackerSelectionChanged ), page );
354    gtk_tree_selection_set_mode( sel, GTK_SELECTION_MULTIPLE );
355    gtk_container_add( GTK_CONTAINER( w ), GTK_WIDGET( page->view ) );
356    gtk_widget_set_size_request( w, -1, 133 );
357    fr = gtk_frame_new( NULL );
358    gtk_frame_set_shadow_type( GTK_FRAME( fr ), GTK_SHADOW_IN );
359    gtk_container_add( GTK_CONTAINER( fr ), w );
360    g_object_unref( G_OBJECT( m ) );
361
362    w = gtk_button_new_from_stock( GTK_STOCK_ADD );
363    g_signal_connect( w, "clicked", G_CALLBACK( onTrackerAddClicked ), page );
364    gtk_box_pack_start_defaults( GTK_BOX( buttons ), w );
365    page->add_button = w;
366    w = gtk_button_new_from_stock( GTK_STOCK_REMOVE );
367    g_signal_connect( w, "clicked", G_CALLBACK(
368                          onTrackerRemoveClicked ), page );
369    gtk_box_pack_start_defaults( GTK_BOX( buttons ), w );
370    page->remove_button = w;
371    if( gtor )
372    {
373        w = gtk_button_new_from_stock( GTK_STOCK_SAVE );
374        g_signal_connect( w, "clicked", G_CALLBACK(
375                              onTrackerSaveClicked ), page );
376        gtk_widget_set_sensitive( w, FALSE );
377        gtk_box_pack_start_defaults( GTK_BOX( buttons ), w );
378        page->save_button = w;
379
380        w = gtk_button_new_from_stock( GTK_STOCK_REVERT_TO_SAVED );
381        g_signal_connect( w, "clicked", G_CALLBACK(
382                              onTrackerRevertClicked ), page );
383        gtk_widget_set_sensitive( w, FALSE );
384        gtk_box_pack_start_defaults( GTK_BOX( buttons ), w );
385        page->revert_button = w;
386    }
387
388    gtk_box_pack_start( GTK_BOX( box ), buttons, FALSE, FALSE, 0 );
389    gtk_box_pack_start_defaults( GTK_BOX( top ), fr );
390    gtk_box_pack_start( GTK_BOX( top ), box, FALSE, FALSE, 0 );
391
392    onTrackerSelectionChanged( sel, page );
393
394    g_object_set_data_full( G_OBJECT( top ), PAGE_KEY, page, g_free );
395    return top;
396}
397
398tr_tracker_info*
399tracker_list_get_trackers( GtkWidget * list,
400                           int *       trackerCount )
401{
402    struct tracker_page * page = g_object_get_data( G_OBJECT(
403                                                        list ), PAGE_KEY );
404    GtkTreeModel *        model = GTK_TREE_MODEL( page->store );
405    const int             n = gtk_tree_model_iter_n_children( model, NULL );
406    tr_tracker_info *     trackers;
407    int                   i = 0;
408    GtkTreeIter           iter;
409
410    /* build the tracker list */
411    trackers = g_new0( tr_tracker_info, n );
412    if( gtk_tree_model_get_iter_first( model, &iter ) ) do
413        {
414            int tier;
415            gtk_tree_model_get( model, &iter,
416                                TR_COL_TIER, &tier,
417                                TR_COL_ANNOUNCE, &trackers[i].announce,
418                                -1 );
419            /* tracker_info.tier is zero-based, but the display is 1-based */
420            trackers[i].tier = tier - 1;
421            ++i;
422        }
423        while( gtk_tree_model_iter_next( model, &iter ) );
424
425    g_assert( i == n );
426
427    *trackerCount = n;
428
429    return trackers;
430}
431
432void
433tracker_list_add_trackers( GtkWidget             * list,
434                           const tr_tracker_info * trackers,
435                           int                     trackerCount )
436{
437    int i;
438    struct tracker_page * page = g_object_get_data( G_OBJECT( list ), PAGE_KEY );
439    GtkListStore * store = page->store;
440
441    for( i=0; i<trackerCount; ++i )
442    {
443        GtkTreeIter             iter;
444        gtk_list_store_append( store, &iter );
445        gtk_list_store_set( store, &iter,
446                            TR_COL_TIER, trackers[i].tier + 1,
447                            TR_COL_ANNOUNCE, trackers[i].announce,
448                            -1 );
449    }
450}
Note: See TracBrowser for help on using the repository browser.