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

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

run libT, cli, daemon, gtk through the source-code formatter "uncrustify" as promised/threatened

File size: 13.5 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
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, TR_COL_TIER, tinf->tier + 1,
89                            TR_COL_ANNOUNCE, tinf->announce,
90                            -1 );
91    }
92
93    gtk_tree_sortable_set_sort_column_id( GTK_TREE_SORTABLE( store ),
94                                          TR_COL_TIER,
95                                          GTK_SORT_ASCENDING );
96
97    return GTK_TREE_MODEL( store );
98}
99
100static void
101onTrackerSelectionChanged( GtkTreeSelection * sel,
102                           gpointer           gpage )
103{
104    struct tracker_page * page = gpage;
105    gboolean              has_selection = gtk_tree_selection_get_selected(
106        sel, NULL, NULL );
107    gboolean              ok_to_remove = !page->gtor
108                                         || gtk_tree_model_iter_n_children(
109        GTK_TREE_MODEL(
110            page->store ),
111        NULL )
112                                         > 1;
113
114    gtk_widget_set_sensitive( page->remove_button,
115                              has_selection && ok_to_remove );
116}
117
118static void
119onTrackerRemoveClicked( GtkButton * w UNUSED,
120                        gpointer      gpage )
121{
122    struct tracker_page * page = gpage;
123    GtkTreeIter           iter;
124
125    if( gtk_tree_selection_get_selected( page->sel, NULL, &iter ) )
126    {
127        gtk_list_store_remove( page->store, &iter );
128        setTrackerChangeState( page, TRUE );
129    }
130}
131
132static void
133onTrackerAddClicked( GtkButton * w UNUSED,
134                     gpointer      gpage )
135{
136    GtkTreeIter           iter;
137    struct tracker_page * page = gpage;
138    GtkTreePath *         path;
139
140    gtk_list_store_append( page->store, &iter );
141    setTrackerChangeState( page, TRUE );
142    gtk_list_store_set( page->store, &iter, TR_COL_TIER, 1,
143                        TR_COL_ANNOUNCE, "http://",
144                        -1 );
145    path = gtk_tree_model_get_path( GTK_TREE_MODEL( page->store ), &iter );
146    gtk_tree_view_set_cursor( page->view,
147                              path,
148                              gtk_tree_view_get_column( page->view,
149                                                        TR_COL_ANNOUNCE ),
150                              TRUE );
151    gtk_tree_path_free( path );
152}
153
154static void
155onTrackerSaveClicked( GtkButton * w UNUSED,
156                      gpointer      gpage )
157{
158    struct tracker_page * page = gpage;
159    GtkTreeModel *        model = GTK_TREE_MODEL( page->store );
160    const int             n = gtk_tree_model_iter_n_children( model, NULL );
161
162    if( n > 0 ) /* must have at least one tracker */
163    {
164        int               i = 0;
165        GtkTreeIter       iter;
166        tr_tracker_info * trackers;
167
168        /* build the tracker list */
169        trackers = g_new0( tr_tracker_info, n );
170        if( gtk_tree_model_get_iter_first( model, &iter ) ) do
171            {
172                gtk_tree_model_get( model, &iter, TR_COL_TIER,
173                                    &trackers[i].tier,
174                                    TR_COL_ANNOUNCE, &trackers[i].announce,
175                                    -1 );
176                ++i;
177            }
178            while( gtk_tree_model_iter_next( model, &iter ) );
179
180        g_assert( i == n );
181
182        /* set the tracker list */
183        tr_torrentSetAnnounceList( tr_torrent_handle( page->gtor ),
184                                   trackers, n );
185
186
187        setTrackerChangeState( page, FALSE );
188
189        /* cleanup */
190        for( i = 0; i < n; ++i )
191            g_free( trackers[i].announce );
192        g_free( trackers );
193    }
194}
195
196static void
197onTrackerRevertClicked( GtkButton * w UNUSED,
198                        gpointer      gpage )
199{
200    struct tracker_page * page = gpage;
201    GtkTreeModel *        model =
202        tracker_model_new( tr_torrent_handle( page->gtor ) );
203
204    gtk_tree_view_set_model( page->view, model );
205    page->store = GTK_LIST_STORE( model );
206    g_object_unref( G_OBJECT( model ) );
207    setTrackerChangeState( page, FALSE );
208}
209
210static void
211onAnnounceEdited( GtkCellRendererText * renderer UNUSED,
212                  gchar *                        path_string,
213                  gchar *                        new_text,
214                  gpointer                       gpage )
215{
216    struct tracker_page * page = gpage;
217    GtkTreeModel *        model = GTK_TREE_MODEL( page->store );
218    GtkTreeIter           iter;
219    GtkTreePath *         path = gtk_tree_path_new_from_string( path_string );
220
221    if( gtk_tree_model_get_iter( model, &iter, path ) )
222    {
223        char * old_text;
224        gtk_tree_model_get( model, &iter, TR_COL_ANNOUNCE, &old_text, -1 );
225        if( tr_httpIsValidURL( new_text ) )
226        {
227            if( strcmp( old_text, new_text ) )
228            {
229                gtk_list_store_set( page->store, &iter, TR_COL_ANNOUNCE,
230                                    new_text,
231                                    -1 );
232                setTrackerChangeState( page, TRUE );
233            }
234        }
235        else if( !tr_httpIsValidURL( old_text ) )
236        {
237            /* both old and new are invalid...
238               they must've typed in an invalid URL
239               after hitting the "Add" button */
240            onTrackerRemoveClicked( NULL, page );
241            setTrackerChangeState( page, TRUE );
242        }
243        g_free( old_text );
244    }
245    gtk_tree_path_free( path );
246}
247
248static void
249onTierEdited( GtkCellRendererText  * renderer UNUSED,
250              gchar *                         path_string,
251              gchar *                         new_text,
252              gpointer                        gpage )
253{
254    struct tracker_page * page = gpage;
255    GtkTreeModel *        model = GTK_TREE_MODEL( page->store );
256    GtkTreeIter           iter;
257    GtkTreePath *         path;
258    char *                end;
259    int                   new_tier;
260
261    errno = 0;
262    new_tier = strtol( new_text, &end, 10 );
263    if( new_tier < 1 || *end || errno )
264        return;
265
266    path = gtk_tree_path_new_from_string( path_string );
267    if( gtk_tree_model_get_iter( model, &iter, path ) )
268    {
269        int old_tier;
270        gtk_tree_model_get( model, &iter, TR_COL_TIER, &old_tier, -1 );
271        if( old_tier != new_tier )
272        {
273            gtk_list_store_set( page->store, &iter, TR_COL_TIER, new_tier,
274                                -1 );
275            setTrackerChangeState( page, TRUE );
276        }
277    }
278    gtk_tree_path_free( path );
279}
280
281GtkWidget*
282tracker_list_new( TrTorrent * gtor )
283{
284    GtkWidget *           w;
285    GtkWidget *           buttons;
286    GtkWidget *           box;
287    GtkWidget *           top;
288    GtkWidget *           fr;
289    GtkTreeModel *        m;
290    GtkCellRenderer *     r;
291    GtkTreeViewColumn *   c;
292    GtkTreeSelection *    sel;
293    struct tracker_page * page = g_new0( struct tracker_page, 1 );
294
295    page->gtor = gtor;
296
297    top = gtk_hbox_new( FALSE, GUI_PAD );
298    box = gtk_vbox_new( FALSE, GUI_PAD );
299    buttons = gtk_vbox_new( TRUE, GUI_PAD );
300
301    m = tracker_model_new( tr_torrent_handle( gtor ) );
302    page->store = GTK_LIST_STORE( m );
303    w = gtk_tree_view_new_with_model( m );
304    page->view = GTK_TREE_VIEW( w );
305    gtk_tree_view_set_enable_search( page->view, FALSE );
306    r = gtk_cell_renderer_text_new( );
307    g_object_set( G_OBJECT( r ),
308                  "editable", TRUE,
309                  NULL );
310    g_signal_connect( r, "edited",
311                      G_CALLBACK( onTierEdited ), page );
312    c = gtk_tree_view_column_new_with_attributes( _( "Tier" ), r,
313                                                  "text", TR_COL_TIER,
314                                                  NULL );
315    gtk_tree_view_column_set_sort_column_id( c, TR_COL_TIER );
316    gtk_tree_view_append_column( page->view, c );
317    r = gtk_cell_renderer_text_new( );
318    g_object_set( G_OBJECT( r ),
319                  "editable", TRUE,
320                  "ellipsize", PANGO_ELLIPSIZE_END,
321                  NULL );
322    g_signal_connect( r, "edited",
323                      G_CALLBACK( onAnnounceEdited ), page );
324    c = gtk_tree_view_column_new_with_attributes( _( "Announce URL" ), r,
325                                                  "text", TR_COL_ANNOUNCE,
326                                                  NULL );
327    gtk_tree_view_column_set_sort_column_id( c, TR_COL_ANNOUNCE );
328    gtk_tree_view_append_column( page->view, c );
329    w = gtk_scrolled_window_new( NULL, NULL );
330    gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( w ),
331                                    GTK_POLICY_NEVER,
332                                    GTK_POLICY_AUTOMATIC );
333    sel = gtk_tree_view_get_selection( page->view );
334    page->sel = sel;
335    g_signal_connect( sel, "changed",
336                      G_CALLBACK( onTrackerSelectionChanged ), page );
337    gtk_container_add( GTK_CONTAINER( w ), GTK_WIDGET( page->view ) );
338    gtk_widget_set_size_request( w, -1, 133 );
339    fr = gtk_frame_new( NULL );
340    gtk_frame_set_shadow_type( GTK_FRAME( fr ), GTK_SHADOW_IN );
341    gtk_container_add( GTK_CONTAINER( fr ), w );
342    g_object_unref( G_OBJECT( m ) );
343
344    w = gtk_button_new_from_stock( GTK_STOCK_ADD );
345    g_signal_connect( w, "clicked", G_CALLBACK( onTrackerAddClicked ), page );
346    gtk_box_pack_start_defaults( GTK_BOX( buttons ), w );
347    page->add_button = w;
348    w = gtk_button_new_from_stock( GTK_STOCK_REMOVE );
349    g_signal_connect( w, "clicked", G_CALLBACK(
350                          onTrackerRemoveClicked ), page );
351    gtk_box_pack_start_defaults( GTK_BOX( buttons ), w );
352    page->remove_button = w;
353    if( gtor )
354    {
355        w = gtk_button_new_from_stock( GTK_STOCK_SAVE );
356        g_signal_connect( w, "clicked", G_CALLBACK(
357                              onTrackerSaveClicked ), page );
358        gtk_widget_set_sensitive( w, FALSE );
359        gtk_box_pack_start_defaults( GTK_BOX( buttons ), w );
360        page->save_button = w;
361
362        w = gtk_button_new_from_stock( GTK_STOCK_REVERT_TO_SAVED );
363        g_signal_connect( w, "clicked", G_CALLBACK(
364                              onTrackerRevertClicked ), page );
365        gtk_widget_set_sensitive( w, FALSE );
366        gtk_box_pack_start_defaults( GTK_BOX( buttons ), w );
367        page->revert_button = w;
368    }
369
370    gtk_box_pack_start( GTK_BOX( box ), buttons, FALSE, FALSE, 0 );
371    gtk_box_pack_start_defaults( GTK_BOX( top ), fr );
372    gtk_box_pack_start( GTK_BOX( top ), box, FALSE, FALSE, 0 );
373
374    onTrackerSelectionChanged( sel, page );
375
376    g_object_set_data_full( G_OBJECT( top ), "page", page, g_free );
377    return top;
378}
379
380tr_tracker_info*
381tracker_list_get_trackers( GtkWidget * list,
382                           int *       trackerCount )
383{
384    struct tracker_page * page = g_object_get_data( G_OBJECT(
385                                                        list ), "page" );
386    GtkTreeModel *        model = GTK_TREE_MODEL( page->store );
387    const int             n = gtk_tree_model_iter_n_children( model, NULL );
388    tr_tracker_info *     trackers;
389    int                   i = 0;
390    GtkTreeIter           iter;
391
392    /* build the tracker list */
393    trackers = g_new0( tr_tracker_info, n );
394    if( gtk_tree_model_get_iter_first( model, &iter ) ) do
395        {
396            gtk_tree_model_get( model, &iter, TR_COL_TIER,
397                                &trackers[i].tier,
398                                TR_COL_ANNOUNCE, &trackers[i].announce,
399                                -1 );
400            ++i;
401        }
402        while( gtk_tree_model_iter_next( model, &iter ) );
403
404    g_assert( i == n );
405
406    *trackerCount = n;
407
408    return trackers;
409}
410
Note: See TracBrowser for help on using the repository browser.