]> granicus.if.org Git - transmission/commitdiff
(trunk libT) #3898 "Add 'Add' and 'Remove' buttons to the tracker list" -- done.
authorJordan Lee <jordan@transmissionbt.com>
Fri, 14 Jan 2011 21:57:20 +0000 (21:57 +0000)
committerJordan Lee <jordan@transmissionbt.com>
Fri, 14 Jan 2011 21:57:20 +0000 (21:57 +0000)
gtk/add-dialog.c
gtk/details.c
gtk/util.c
gtk/util.h

index b6aaf954194ed2db37139c8da090f24026b3d6be..45b5cb5d68467da3752c5f99ea5aa9a78fbbafec 100644 (file)
@@ -491,28 +491,6 @@ onAddURLResponse( GtkDialog * dialog, int response, gpointer user_data )
         gtk_widget_destroy( GTK_WIDGET( dialog ) );
 }
 
-static void
-paste_clipboard_url_into_entry( GtkWidget * e )
-{
-  size_t i;
-
-  char * text[] = {
-    gtk_clipboard_wait_for_text( gtk_clipboard_get( GDK_SELECTION_PRIMARY ) ),
-    gtk_clipboard_wait_for_text( gtk_clipboard_get( GDK_SELECTION_CLIPBOARD ) )
-  };
-
-  for( i=0; i<G_N_ELEMENTS(text); ++i ) {
-      char * s = text[i];
-      if( s && ( gtr_is_supported_url( s ) || gtr_is_magnet_link( s ) ) ) {
-          gtk_entry_set_text( GTK_ENTRY( e ), s );
-          break;
-      }
-  }
-
-  for( i=0; i<G_N_ELEMENTS(text); ++i )
-    g_free( text[i] );
-}
-
 GtkWidget*
 gtr_torrent_add_from_url_dialog_new( GtkWindow * parent, TrCore * core )
 {
@@ -537,7 +515,7 @@ gtr_torrent_add_from_url_dialog_new( GtkWindow * parent, TrCore * core )
     hig_workarea_add_section_title( t, &row, _( "Add torrent from URL" ) );
     e = gtk_entry_new( );
     gtk_widget_set_size_request( e, 400, -1 );
-    paste_clipboard_url_into_entry( e );
+    gtr_paste_clipboard_url_into_entry( e );
     g_object_set_data( G_OBJECT( w ), "url-entry", e );
     hig_workarea_add_row( t, &row, _( "_URL" ), e, NULL );
 
index c78d67de758e3ac32de61fb0a7a58704ca778395..0f7b932845370b5a8778a71a9934183af2e00595 100644 (file)
@@ -84,13 +84,15 @@ struct DetailsImpl
     GtkWidget * peer_view;
     GtkWidget * more_peer_details_check;
 
-    GtkListStore * trackers;
-    GtkTreeModel * trackers_filtered;
-    GtkWidget * edit_trackers_button;
-    GtkWidget * tracker_view;
-    GtkWidget * scrape_check;
-    GtkWidget * all_check;
-    GtkTextBuffer * tracker_buffer;
+    GtkListStore  * tracker_store;
+    GHashTable    * tracker_hash;
+    GtkTreeModel  * trackers_filtered;
+    GtkWidget     * add_tracker_button;
+    GtkWidget     * edit_trackers_button;
+    GtkWidget     * remove_tracker_button;
+    GtkWidget     * tracker_view;
+    GtkWidget     * scrape_check;
+    GtkWidget     * all_check;
 
     GtkWidget * file_list;
     GtkWidget * file_label;
@@ -1762,7 +1764,9 @@ peer_page_new( struct DetailsImpl * di )
 
 
 /****
+*****
 *****  TRACKER
+*****
 ****/
 
 /* if it's been longer than a minute, don't bother showing the seconds */
@@ -1884,12 +1888,12 @@ buildTrackerSummary( const char * key, const tr_tracker_stat * st, gboolean show
 enum
 {
   TRACKER_COL_TORRENT_ID,
-  TRACKER_COL_TRACKER_INDEX,
   TRACKER_COL_TEXT,
-  TRACKER_COL_BACKUP,
-  TRACKER_COL_TORRENT_NAME,
-  TRACKER_COL_TRACKER_NAME,
+  TRACKER_COL_IS_BACKUP,
+  TRACKER_COL_TRACKER_ID,
   TRACKER_COL_FAVICON,
+  TRACKER_COL_WAS_UPDATED,
+  TRACKER_COL_KEY,
   TRACKER_N_COLS
 };
 
@@ -1904,32 +1908,39 @@ trackerVisibleFunc( GtkTreeModel * model, GtkTreeIter * iter, gpointer data )
         return TRUE;
 
      /* don't show the backups... */
-     gtk_tree_model_get( model, iter, TRACKER_COL_BACKUP, &isBackup, -1 );
+     gtk_tree_model_get( model, iter, TRACKER_COL_IS_BACKUP, &isBackup, -1 );
      return !isBackup;
 }
 
-static void
-populate_tracker_buffer( GtkTextBuffer * buffer, const tr_torrent * tor )
+static int
+tracker_list_get_current_torrent_id( struct DetailsImpl * di )
 {
-    int i;
-    int tier = 0;
-    GString * gstr = g_string_new( NULL );
-    const tr_info * inf = tr_torrentInfo( tor );
-    for( i=0; i<inf->trackerCount; ++i ) {
-        const tr_tracker_info * t = &inf->trackers[i];
-        if( tier != t->tier ) {
-            tier = t->tier;
-            g_string_append_c( gstr, '\n' );
-        }
-        g_string_append_printf( gstr, "%s\n", t->announce );
+    int torrent_id = -1;
+
+    /* if there's only one torrent in the dialog, always use it */
+    if( torrent_id < 0 )
+        if( g_slist_length( di->ids ) == 1 )
+            torrent_id = GPOINTER_TO_INT( di->ids->data );
+
+    /* otherwise, use the selected tracker's torrent */
+    if( torrent_id < 0 ) {
+        GtkTreeIter iter;
+        GtkTreeModel * model;
+        GtkTreeSelection * sel = gtk_tree_view_get_selection( GTK_TREE_VIEW( di->tracker_view ) );
+        if( gtk_tree_selection_get_selected( sel, &model, &iter ) )
+            gtk_tree_model_get( model, &iter, TRACKER_COL_TORRENT_ID, &torrent_id, -1 );
     }
-    if( gstr->len > 0 )
-        g_string_truncate( gstr, gstr->len-1 );
-    gtk_text_buffer_set_text( buffer, gstr->str, -1 );
-    g_string_free( gstr, TRUE );
+
+    return torrent_id;
 }
 
-#define TORRENT_PTR_KEY "torrent-pointer"
+static tr_torrent*
+tracker_list_get_current_torrent( struct DetailsImpl * di )
+{
+    const int torrent_id = tracker_list_get_current_torrent_id( di );
+    tr_session * session = tr_core_session( di->core );
+    return tr_torrentFindFromId( session, torrent_id );
+}
 
 static void
 favicon_ready_cb( gpointer pixbuf, gpointer vreference )
@@ -1962,112 +1973,100 @@ refreshTracker( struct DetailsImpl * di, tr_torrent ** torrents, int n )
     int * statCount;
     tr_tracker_stat ** stats;
     GtkTreeIter iter;
-    GtkListStore * store = di->trackers;
     GtkTreeModel * model;
+    GHashTable * hash = di->tracker_hash;
+    GtkListStore * store = di->tracker_store;
+    tr_session * session = tr_core_session( di->core );
     const gboolean showScrape = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( di->scrape_check ) );
 
+    /* step 1: get all the trackers */
     statCount = g_new0( int, n );
     stats = g_new0( tr_tracker_stat *, n );
     for( i=0; i<n; ++i )
         stats[i] = tr_torrentTrackers( torrents[i], &statCount[i] );
 
-    /* "edit trackers" button */
-    gtk_widget_set_sensitive( di->edit_trackers_button, n==1 );
-    if( n==1 )
-        g_object_set_data( G_OBJECT( di->edit_trackers_button ), TORRENT_PTR_KEY, torrents[0] );
-
-    /* build the store if we don't already have it */
-    if( store == NULL )
-    {
-        GtkTreeModel * filter;
-
-        store = gtk_list_store_new( TRACKER_N_COLS, G_TYPE_INT,
-                                                    G_TYPE_INT,
-                                                    G_TYPE_STRING,
-                                                    G_TYPE_BOOLEAN,
-                                                    G_TYPE_STRING,
-                                                    G_TYPE_STRING,
-                                                    GDK_TYPE_PIXBUF );
-
-        filter = gtk_tree_model_filter_new( GTK_TREE_MODEL( store ), NULL );
-        gtk_tree_model_filter_set_visible_func( GTK_TREE_MODEL_FILTER( filter ),
-                                                trackerVisibleFunc, di, NULL );
-
-        di->trackers = store;
-        di->trackers_filtered = filter;
-
-        gtk_tree_view_set_model( GTK_TREE_VIEW( di->tracker_view ), filter );
-
-        g_object_unref( filter );
-        g_object_unref( store );
-    }
-
-    if( ( di->tracker_buffer == NULL ) && ( n == 1 ) )
-    {
-        di->tracker_buffer = gtk_text_buffer_new( NULL );
-        populate_tracker_buffer( di->tracker_buffer, torrents[0] );
-    }
-
-    /* add any missing rows (FIXME: doesn't handle edited trackers) */
+    /* step 2: mark all the trackers in the list as not-updated */
     model = GTK_TREE_MODEL( store );
-    if( n && !gtk_tree_model_get_iter_first( model, &iter ) )
-    {
-        tr_session * session = tr_core_session( di->core );
+    if( gtk_tree_model_get_iter_first( model, &iter ) ) do
+        gtk_list_store_set( store, &iter, TRACKER_COL_WAS_UPDATED, FALSE, -1 );
+    while( gtk_tree_model_iter_next( model, &iter ) );
 
-        for( i=0; i<n; ++i )
-        {
-            int j;
+    /* step 3: add any new trackers */
+    for( i=0; i<n; ++i ) {
+        int j;
+        const int jn = statCount[i];
+        for( j=0; j<jn; ++j ) {
             const tr_torrent * tor = torrents[i];
-            const int torrentId = tr_torrentId( tor );
-            const tr_info * inf = tr_torrentInfo( tor );
+            const tr_tracker_stat * st = &stats[i][j];
+            const int torrent_id = tr_torrentId( tor );
+            char * key = g_strdup_printf( "%d\t%d\t%s", torrent_id, st->tier, st->announce );
 
-            for( j=0; j<statCount[i]; ++j )
-            {
-                GtkTreePath * path;
-                GtkTreeRowReference * reference;
-                const tr_tracker_stat * st = &stats[i][j];
+            if( g_hash_table_lookup( hash, key ) == NULL ) {
+                GtkTreePath * p;
+                GtkTreeIter iter;
+                GtkTreeRowReference * ref;
 
                 gtk_list_store_insert_with_values( store, &iter, -1,
-                    TRACKER_COL_TORRENT_ID, torrentId,
-                    TRACKER_COL_TRACKER_INDEX, j,
-                    TRACKER_COL_TORRENT_NAME, inf->name,
-                    TRACKER_COL_TRACKER_NAME, st->host,
+                    TRACKER_COL_TORRENT_ID, torrent_id,
+                    TRACKER_COL_TRACKER_ID, st->id,
+                    TRACKER_COL_KEY, key,
                     -1 );
 
-                path = gtk_tree_model_get_path( model, &iter );
-                reference = gtk_tree_row_reference_new( model, path );
-                gtr_get_favicon_from_url( session, st->announce, favicon_ready_cb, reference );
-                gtk_tree_path_free( path );
+                p = gtk_tree_model_get_path( model, &iter );
+                ref = gtk_tree_row_reference_new( model, p );
+                g_hash_table_insert( hash, g_strdup( key ), ref );
+                ref = gtk_tree_row_reference_new( model, p );
+                gtr_get_favicon_from_url( session, st->announce, favicon_ready_cb, ref );
+                gtk_tree_path_free( p );
             }
+
+            g_free( key );
         }
     }
 
-    /* update the store */
-    if( gtk_tree_model_get_iter_first( model, &iter ) ) do
-    {
-        int torrentId;
-        int trackerIndex;
-
-        gtk_tree_model_get( model, &iter, TRACKER_COL_TORRENT_ID, &torrentId,
-                                          TRACKER_COL_TRACKER_INDEX, &trackerIndex,
-                                          -1 );
-
-        for( i=0; i<n; ++i )
-            if( tr_torrentId( torrents[i] ) == torrentId )
-                break;
-
-        if( i<n && trackerIndex<statCount[i] )
-        {
-            const tr_tracker_stat * st = &stats[i][trackerIndex];
-            const char * key = n>1 ? tr_torrentInfo( torrents[i] )->name : NULL;
-            char * text = buildTrackerSummary( key, st, showScrape );
-            gtk_list_store_set( store, &iter, TRACKER_COL_TEXT, text,
-                                              TRACKER_COL_BACKUP, st->isBackup,
+    /* step 4: update the peers */
+    for( i=0; i<n; ++i ) {
+        int j;
+        const tr_torrent * tor = torrents[i];
+        const char * summary_name = n>1 ? tr_torrentInfo( tor )->name : NULL;
+        for( j=0; j<statCount[i]; ++j ) {
+            const tr_tracker_stat * st = &stats[i][j];
+            char * summary = buildTrackerSummary( summary_name, st, showScrape );
+            char * key = g_strdup_printf( "%d\t%d\t%s", tr_torrentId( tor ), st->tier, st->announce );
+            GtkTreeRowReference * ref = g_hash_table_lookup( hash, key );
+            GtkTreePath * p = gtk_tree_row_reference_get_path( ref );
+            gtk_tree_model_get_iter( model, &iter, p );
+            gtk_list_store_set( store, &iter, TRACKER_COL_TEXT, summary,
+                                              TRACKER_COL_IS_BACKUP, st->isBackup,
+                                              TRACKER_COL_TRACKER_ID, st->id,
+                                              TRACKER_COL_WAS_UPDATED, TRUE,
                                               -1 );
-            g_free( text );
+            gtk_tree_path_free( p );
+            g_free( key );
+            g_free( summary );
+        }
+    }
+
+    /* step 5: remove trackers that have disappeared */
+    if( gtk_tree_model_get_iter_first( model, &iter ) ) {
+        gboolean more = TRUE;
+        while( more ) {
+            gboolean b;
+            gtk_tree_model_get( model, &iter, TRACKER_COL_WAS_UPDATED, &b, -1 );
+            if( b )
+                more = gtk_tree_model_iter_next( model, &iter );
+            else {
+                char * key;
+                gtk_tree_model_get( model, &iter, TRACKER_COL_KEY, &key, -1 );
+                g_hash_table_remove( hash, key );
+                more = gtk_list_store_remove( store, &iter );
+                g_free( key );
+            }
         }
     }
-    while( gtk_tree_model_iter_next( model, &iter ) );
+
+    gtk_widget_set_sensitive( di->edit_trackers_button,
+                              tracker_list_get_current_torrent_id( di ) >= 0 );
 
     /* cleanup */
     for( i=0; i<n; ++i )
@@ -2095,7 +2094,7 @@ onBackupToggled( GtkToggleButton * button, struct DetailsImpl * di )
 }
 
 static void
-onEditTrackersResponse( GtkDialog * dialog, int response, gpointer data )
+on_edit_trackers_response( GtkDialog * dialog, int response, gpointer data )
 {
     gboolean do_destroy = TRUE;
     struct DetailsImpl * di = data;
@@ -2105,85 +2104,107 @@ onEditTrackersResponse( GtkDialog * dialog, int response, gpointer data )
         int i, n;
         int tier;
         GtkTextIter start, end;
-        char * tracker_text;
-        char ** tracker_strings;
-        tr_tracker_info * trackers;
-        tr_torrent * tor = g_object_get_data( G_OBJECT( dialog ), TORRENT_PTR_KEY );
-
-        /* build the array of trackers */
-        gtk_text_buffer_get_bounds( di->tracker_buffer, &start, &end );
-        tracker_text = gtk_text_buffer_get_text( di->tracker_buffer, &start, &end, FALSE );
-        tracker_strings = g_strsplit( tracker_text, "\n", 0 );
-        for( i=0; tracker_strings[i]; )
-            ++i;
-        trackers = g_new0( tr_tracker_info, i );
-        for( i=n=tier=0; tracker_strings[i]; ++i ) {
-            const char * str = tracker_strings[i];
-            if( !*str )
-                ++tier;
-            else {
-                trackers[n].tier = tier;
-                trackers[n].announce = tracker_strings[i];
-                ++n;
-            }
-        }
+        const int torrent_id = GPOINTER_TO_INT( g_object_get_data( G_OBJECT( dialog ), "torrent-id" ) );
+        GtkTextBuffer * text_buffer = g_object_get_data( G_OBJECT( dialog ), "text-buffer" );
+        tr_session * session = tr_core_session( di->core );
+        tr_torrent * tor = tr_torrentFindFromId( session, torrent_id );
 
-        /* update the torrent */
-        if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
-        {
-            GtkWidget * w;
-            const char * text = _( "List contains invalid URLs" );
-            w = gtk_message_dialog_new( GTK_WINDOW( dialog ),
-                                        GTK_DIALOG_MODAL,
-                                        GTK_MESSAGE_ERROR,
-                                        GTK_BUTTONS_CLOSE, "%s", text );
-            gtk_dialog_run( GTK_DIALOG( w ) );
-            gtk_widget_destroy( w );
-            do_destroy = FALSE;
-        }
-        else
+        if( tor != NULL )
         {
-            di->trackers = NULL;
-            di->tracker_buffer = NULL;
-        }
+            tr_tracker_info * trackers;
+            char ** tracker_strings;
+            char * tracker_text;
+
+            /* build the array of trackers */
+            gtk_text_buffer_get_bounds( text_buffer, &start, &end );
+            tracker_text = gtk_text_buffer_get_text( text_buffer, &start, &end, FALSE );
+            tracker_strings = g_strsplit( tracker_text, "\n", 0 );
+            for( i=0; tracker_strings[i]; )
+                ++i;
+            trackers = g_new0( tr_tracker_info, i );
+            for( i=n=tier=0; tracker_strings[i]; ++i ) {
+                const char * str = tracker_strings[i];
+                if( !*str )
+                    ++tier;
+                else {
+                    trackers[n].tier = tier;
+                    trackers[n].announce = tracker_strings[i];
+                    ++n;
+                }
+            }
 
-        /* cleanup */
-        g_free( trackers );
-        g_strfreev( tracker_strings );
-        g_free( tracker_text );
-    }
+            /* update the torrent */
+            if( tr_torrentSetAnnounceList( tor, trackers, n ) )
+                refresh( di );
+            else {
+                GtkWidget * w;
+                const char * text = _( "List contains invalid URLs" );
+                w = gtk_message_dialog_new( GTK_WINDOW( dialog ),
+                                            GTK_DIALOG_MODAL,
+                                            GTK_MESSAGE_ERROR,
+                                            GTK_BUTTONS_CLOSE, "%s", text );
+                gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( w ), "%s", _( "Please correct the errors and try again." ) );
+                gtk_dialog_run( GTK_DIALOG( w ) );
+                gtk_widget_destroy( w );
+                do_destroy = FALSE;
+            }
 
-    if( response == GTK_RESPONSE_CANCEL )
-    {
-        tr_torrent * tor = g_object_get_data( G_OBJECT( dialog ), TORRENT_PTR_KEY );
-        populate_tracker_buffer( di->tracker_buffer, tor );
+            /* cleanup */
+            g_free( trackers );
+            g_strfreev( tracker_strings );
+            g_free( tracker_text );
+        }
     }
 
     if( do_destroy )
         gtk_widget_destroy( GTK_WIDGET( dialog ) );
 }
 
+static char*
+get_editable_tracker_list( const tr_torrent * tor )
+{
+    int i;
+    int tier = 0;
+    GString * gstr = g_string_new( NULL );
+    const tr_info * inf = tr_torrentInfo( tor );
+    for( i=0; i<inf->trackerCount; ++i ) {
+        const tr_tracker_info * t = &inf->trackers[i];
+        if( tier != t->tier ) {
+            tier = t->tier;
+            g_string_append_c( gstr, '\n' );
+        }
+        g_string_append_printf( gstr, "%s\n", t->announce );
+    }
+    if( gstr->len > 0 )
+        g_string_truncate( gstr, gstr->len-1 );
+    return g_string_free( gstr, FALSE );
+}
+
 static void
-onEditTrackers( GtkButton * button, gpointer data )
+on_edit_trackers( GtkButton * button, gpointer data )
 {
-    int row;
-    GtkWidget *w, *d, *fr, *t, *l, *sw;
-    GtkWindow * win = GTK_WINDOW( gtk_widget_get_toplevel( GTK_WIDGET( button ) ) );
     struct DetailsImpl * di = data;
+    tr_torrent * tor = tracker_list_get_current_torrent( di );
 
-    d = gtk_dialog_new_with_buttons( _( "Edit Trackers" ), win,
-                                     GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
-                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                                     GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
-                                     NULL );
-    g_object_set_data( G_OBJECT( d ), TORRENT_PTR_KEY,
-                       g_object_get_data( G_OBJECT( button ), TORRENT_PTR_KEY ) );
-    g_signal_connect( d, "response",
-                      G_CALLBACK( onEditTrackersResponse ), data );
-
-    row = 0;
-    t = hig_workarea_create( );
-    hig_workarea_add_section_title( t, &row, _( "Tracker Announce URLs" ) );
+    if( tor != NULL )
+    {
+        int row;
+        GtkWidget *w, *d, *fr, *t, *l, *sw;
+        GtkWindow * win = GTK_WINDOW( gtk_widget_get_toplevel( GTK_WIDGET( button ) ) );
+        char * text = get_editable_tracker_list( tor );
+        const int torrent_id = tr_torrentId( tor );
+        char * title = g_strdup_printf( _( "%s - Edit Trackers" ), tr_torrentInfo( tor )->name );
+
+        d = gtk_dialog_new_with_buttons( title, win,
+                                         GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
+                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                         GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+                                         NULL );
+        g_signal_connect( d, "response", G_CALLBACK( on_edit_trackers_response ), data );
+
+        row = 0;
+        t = hig_workarea_create( );
+        hig_workarea_add_section_title( t, &row, _( "Tracker Announce URLs" ) );
 
         l = gtk_label_new( NULL );
         gtk_label_set_markup( GTK_LABEL( l ), _( "To add a backup URL, add it on the line after the primary URL.\n"
@@ -2192,7 +2213,8 @@ onEditTrackers( GtkButton * button, gpointer data )
         gtk_misc_set_alignment( GTK_MISC( l ), 0.0, 0.5 );
         hig_workarea_add_wide_control( t, &row, l );
 
-        w = gtk_text_view_new_with_buffer( di->tracker_buffer );
+        w = gtk_text_view_new( );
+        gtk_text_buffer_set_text( gtk_text_view_get_buffer( GTK_TEXT_VIEW( w ) ), text, -1 );
         gtk_widget_set_size_request( w, 500u, 166u );
         fr = gtk_frame_new( NULL );
         gtk_frame_set_shadow_type( GTK_FRAME( fr ), GTK_SHADOW_IN );
@@ -2204,71 +2226,230 @@ onEditTrackers( GtkButton * button, gpointer data )
         gtk_container_add( GTK_CONTAINER( fr ), sw );
         hig_workarea_add_wide_tall_control( t, &row, fr );
 
-    hig_workarea_finish( t, &row );
-    gtr_dialog_set_content( GTK_DIALOG( d ), t );
-    gtk_widget_show( d );
+        hig_workarea_finish( t, &row );
+        gtr_dialog_set_content( GTK_DIALOG( d ), t );
+
+        g_object_set_data( G_OBJECT( d ), "torrent-id", GINT_TO_POINTER( torrent_id ) );
+        g_object_set_data( G_OBJECT( d ), "text-buffer", gtk_text_view_get_buffer( GTK_TEXT_VIEW( w ) ) );
+        gtk_widget_show( d );
+
+        g_free( title );
+        g_free( text );
+    }
+}
+
+static void
+on_tracker_list_selection_changed( GtkTreeSelection * sel, gpointer gdi )
+{
+    struct DetailsImpl * di = gdi;
+    const int n = gtk_tree_selection_count_selected_rows( sel );
+    tr_torrent * tor = tracker_list_get_current_torrent( di );
+
+    gtk_widget_set_sensitive( di->remove_tracker_button, n>0 );
+    gtk_widget_set_sensitive( di->add_tracker_button, tor!=NULL );
+    gtk_widget_set_sensitive( di->edit_trackers_button, tor!=NULL );
+}
+
+static void
+on_add_tracker_response( GtkDialog * dialog, int response, gpointer gdi )
+{
+    gboolean destroy = TRUE;
+
+    if( response == GTK_RESPONSE_ACCEPT )
+    {
+        struct DetailsImpl * di = gdi;
+        GtkWidget * e = GTK_WIDGET( g_object_get_data( G_OBJECT( dialog ), "url-entry" ) );
+        const int torrent_id = GPOINTER_TO_INT( g_object_get_data( G_OBJECT( dialog ), "torrent-id" ) );
+        char * url = g_strdup( gtk_entry_get_text( GTK_ENTRY( e ) ) );
+        g_strstrip( url );
+
+        if( url && *url )
+        {
+            if( gtr_is_supported_url( url ) )
+            {
+                char * json = g_strdup_printf(
+                    "{\n"
+                    "  \"method\": \"torrent-set\",\n"
+                    "  \"arguments\": { \"id\": %d, \"trackerAdd\": [ \"%s\" ] }\n"
+                    "}\n",
+                    torrent_id, url );
+                tr_core_exec_json( di->core, json );
+                refresh( di );
+                g_free( json );
+            }
+            else
+            {
+                gtr_unrecognized_url_dialog( GTK_WIDGET( dialog ), url );
+                destroy = FALSE;
+            }
+        }
+
+        g_free( url );
+    }
+
+    if( destroy )
+        gtk_widget_destroy( GTK_WIDGET( dialog ) );
+}
+
+static void
+on_tracker_list_add_button_clicked( GtkButton * button UNUSED, gpointer gdi )
+{
+    struct DetailsImpl * di = gdi;
+    tr_torrent * tor = tracker_list_get_current_torrent( di );
+
+    if( tor != NULL )
+    {
+        int row;
+        GtkWidget * e;
+        GtkWidget * t;
+        GtkWidget * w;
+        char * title = g_strdup_printf( _( "%s - Add Tracker" ), tr_torrentInfo( tor )->name );
+
+        w = gtk_dialog_new_with_buttons( title, GTK_WINDOW( di->dialog ),
+                                         GTK_DIALOG_DESTROY_WITH_PARENT,
+                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                         GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT,
+                                         NULL );
+        gtk_dialog_set_alternative_button_order( GTK_DIALOG( w ),
+                                                 GTK_RESPONSE_ACCEPT,
+                                                 GTK_RESPONSE_CANCEL,
+                                                 -1 );
+        g_signal_connect( w, "response", G_CALLBACK( on_add_tracker_response ), gdi );
+
+        row = 0;
+        t = hig_workarea_create( );
+        hig_workarea_add_section_title( t, &row, _( "Tracker" ) );
+        e = gtk_entry_new( );
+        gtk_widget_set_size_request( e, 400, -1 );
+        gtr_paste_clipboard_url_into_entry( e );
+        g_object_set_data( G_OBJECT( w ), "url-entry", e );
+        g_object_set_data( G_OBJECT( w ), "torrent-id", GINT_TO_POINTER( tr_torrentId( tor ) ) );
+        hig_workarea_add_row( t, &row, _( "_Announce URL:" ), e, NULL );
+        gtr_dialog_set_content( GTK_DIALOG( w ), t );
+        gtk_widget_show_all( w );
+
+        g_free( title );
+    }
+}
+
+static void
+on_tracker_list_remove_button_clicked( GtkButton * button UNUSED, gpointer gdi )
+{
+    GtkTreeIter iter;
+    GtkTreeModel * model;
+    struct DetailsImpl * di = gdi;
+    GtkTreeView * v = GTK_TREE_VIEW( di->tracker_view );
+    GtkTreeSelection * sel = gtk_tree_view_get_selection( v );
+
+    if( gtk_tree_selection_get_selected( sel, &model, &iter ) )
+    {
+        char * json;
+        int torrent_id;
+        int tracker_id;
+        gtk_tree_model_get( model, &iter, TRACKER_COL_TRACKER_ID, &tracker_id,
+                                          TRACKER_COL_TORRENT_ID, &torrent_id,
+                                          -1 );
+        json = g_strdup_printf( "{\n"
+                                "  \"method\": \"torrent-set\",\n"
+                                "  \"arguments\": { \"id\": %d, \"trackerRemove\": [ %d ] }\n"
+                                "}\n",
+                                torrent_id, tracker_id );
+        tr_core_exec_json( di->core, json );
+        refresh( di );
+        g_free( json );
+    }
 }
 
 static GtkWidget*
 tracker_page_new( struct DetailsImpl * di )
 {
     gboolean b;
-    GtkCellRenderer *r;
-    GtkTreeViewColumn *c;
+    GtkCellRenderer * r;
+    GtkTreeViewColumn * c;
+    GtkTreeSelection * sel;
     GtkWidget *vbox, *sw, *w, *v, *hbox;
     const int pad = ( GUI_PAD + GUI_PAD_BIG ) / 2;
 
     vbox = gtk_vbox_new( FALSE, GUI_PAD );
     gtk_container_set_border_width( GTK_CONTAINER( vbox ), GUI_PAD_BIG );
 
-    v = di->tracker_view = gtk_tree_view_new( );
-    gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( v ), FALSE );
-    g_signal_connect( v, "button-press-event",
-                      G_CALLBACK( on_tree_view_button_pressed ), NULL );
-    g_signal_connect( v, "button-release-event",
-                      G_CALLBACK( on_tree_view_button_released ), NULL );
-    gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( v ), TRUE );
+    di->tracker_store = gtk_list_store_new( TRACKER_N_COLS, G_TYPE_INT,
+                                                            G_TYPE_STRING,
+                                                            G_TYPE_BOOLEAN,
+                                                            G_TYPE_INT,
+                                                            GDK_TYPE_PIXBUF,
+                                                            G_TYPE_BOOLEAN,
+                                                            G_TYPE_STRING );
+    di->tracker_hash = g_hash_table_new_full( g_str_hash,
+                                              g_str_equal,
+                                              (GDestroyNotify)g_free,
+                                              (GDestroyNotify)gtk_tree_row_reference_free );
+    di->trackers_filtered = gtk_tree_model_filter_new( GTK_TREE_MODEL( di->tracker_store ), NULL );
+    gtk_tree_model_filter_set_visible_func( GTK_TREE_MODEL_FILTER( di->trackers_filtered ),
+                                            trackerVisibleFunc, di, NULL );
+
+    hbox = gtk_hbox_new( FALSE, GUI_PAD_BIG );
+
+        v = di->tracker_view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( di->trackers_filtered ) );
+        gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( v ), FALSE );
+        g_signal_connect( v, "button-press-event", G_CALLBACK( on_tree_view_button_pressed ), NULL );
+        g_signal_connect( v, "button-release-event", G_CALLBACK( on_tree_view_button_released ), NULL );
+        gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( v ), TRUE );
+
+        sel = gtk_tree_view_get_selection( GTK_TREE_VIEW( v ) );
+        g_signal_connect( sel, "changed", G_CALLBACK( on_tracker_list_selection_changed ), di );
+        
+        c = gtk_tree_view_column_new( );
+        gtk_tree_view_column_set_title( c, _( "Trackers" ) );
+        gtk_tree_view_append_column( GTK_TREE_VIEW( v ), c );
+
+        r = gtk_cell_renderer_pixbuf_new( );
+        g_object_set( r, "width", 20 + (GUI_PAD_SMALL*2), "xpad", GUI_PAD_SMALL, "ypad", pad, "yalign", 0.0f, NULL );
+        gtk_tree_view_column_pack_start( c, r, FALSE );
+        gtk_tree_view_column_add_attribute( c, r, "pixbuf", TRACKER_COL_FAVICON );
+
+        r = gtk_cell_renderer_text_new( );
+        g_object_set( G_OBJECT( r ), "ellipsize", PANGO_ELLIPSIZE_END, "xpad", GUI_PAD_SMALL, "ypad", pad, NULL );
+        gtk_tree_view_column_pack_start( c, r, TRUE );
+        gtk_tree_view_column_add_attribute( c, r, "markup", TRACKER_COL_TEXT );
 
-    c = gtk_tree_view_column_new( );
-    gtk_tree_view_column_set_title( c, _( "Trackers" ) );
-    gtk_tree_view_append_column( GTK_TREE_VIEW( v ), c );
+        sw = gtk_scrolled_window_new( NULL, NULL );
+        gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sw ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
+        gtk_container_add( GTK_CONTAINER( sw ), v );
+        w = gtk_frame_new( NULL );
+        gtk_frame_set_shadow_type( GTK_FRAME( w ), GTK_SHADOW_IN );
+        gtk_container_add( GTK_CONTAINER( w ), sw );
 
-    r = gtk_cell_renderer_pixbuf_new( );
-    g_object_set( r, "width", 20 + (GUI_PAD_SMALL*2), "xpad", GUI_PAD_SMALL, "ypad", pad, "yalign", 0.0f, NULL );
-    gtk_tree_view_column_pack_start( c, r, FALSE );
-    gtk_tree_view_column_add_attribute( c, r, "pixbuf", TRACKER_COL_FAVICON );
+    gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
 
-    r = gtk_cell_renderer_text_new( );
-    g_object_set( G_OBJECT( r ), "ellipsize", PANGO_ELLIPSIZE_END, "xpad", GUI_PAD_SMALL, "ypad", pad, NULL );
-    gtk_tree_view_column_pack_start( c, r, TRUE );
-    gtk_tree_view_column_add_attribute( c, r, "markup", TRACKER_COL_TEXT );
+        v = gtk_vbox_new( FALSE, GUI_PAD );
 
-    sw = gtk_scrolled_window_new( NULL, NULL );
-    gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sw ),
-                                    GTK_POLICY_AUTOMATIC,
-                                    GTK_POLICY_AUTOMATIC );
-    gtk_container_add( GTK_CONTAINER( sw ), v );
-    w = gtk_frame_new( NULL );
-    gtk_frame_set_shadow_type( GTK_FRAME( w ), GTK_SHADOW_IN );
-    gtk_container_add( GTK_CONTAINER( w ), sw );
-    gtk_box_pack_start( GTK_BOX( vbox ), w, TRUE, TRUE, 0 );
+        w = gtk_button_new_with_mnemonic( _( "_Add" ) );
+        di->add_tracker_button = w;
+        g_signal_connect( w, "clicked", G_CALLBACK( on_tracker_list_add_button_clicked ), di );
+        gtk_box_pack_start( GTK_BOX( v ), w, FALSE, FALSE, 0 );
+
+        w = gtk_button_new_with_mnemonic( _( "_Edit" ) );
+        gtk_button_set_image( GTK_BUTTON( w ), gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON ) );
+        g_signal_connect( w, "clicked", G_CALLBACK( on_edit_trackers ), di );
+        di->edit_trackers_button = w;
+        gtk_box_pack_start( GTK_BOX( v ), w, FALSE, FALSE, 0 );
 
-    hbox = gtk_hbox_new( FALSE, 0 );
+        w = gtk_button_new_with_mnemonic( _( "_Remove" ) );
+        di->remove_tracker_button = w;
+        g_signal_connect( w, "clicked", G_CALLBACK( on_tracker_list_remove_button_clicked ), di );
+        gtk_box_pack_start( GTK_BOX( v ), w, FALSE, FALSE, 0 );
 
-      w = gtk_check_button_new_with_mnemonic( _( "Show _more details" ) );
-      di->scrape_check = w;
-      b = gtr_pref_flag_get( PREF_KEY_SHOW_MORE_TRACKER_INFO );
-      gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), b );
-      g_signal_connect( w, "toggled", G_CALLBACK( onScrapeToggled ), di );
-      gtk_box_pack_start( GTK_BOX( hbox ), w, FALSE, FALSE, 0 );
+        gtk_box_pack_start( GTK_BOX( hbox ), v, FALSE, FALSE, 0 );
 
-      w = gtk_button_new_with_mnemonic( _( "_Edit Trackers" ) );
-      gtk_button_set_image( GTK_BUTTON( w ), gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON ) );
-      g_signal_connect( w, "clicked", G_CALLBACK( onEditTrackers ), di );
-      gtk_box_pack_end( GTK_BOX( hbox ), w, FALSE, FALSE, 0 );
-      di->edit_trackers_button = w;
+    gtk_box_pack_start( GTK_BOX( vbox ), hbox, TRUE, TRUE, 0 );
 
-    gtk_box_pack_start( GTK_BOX( vbox ), hbox, FALSE, FALSE, 0 );
+    w = gtk_check_button_new_with_mnemonic( _( "Show _more details" ) );
+    di->scrape_check = w;
+    b = gtr_pref_flag_get( PREF_KEY_SHOW_MORE_TRACKER_INFO );
+    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), b );
+    g_signal_connect( w, "toggled", G_CALLBACK( onScrapeToggled ), di );
+    gtk_box_pack_start( GTK_BOX( vbox ), w, FALSE, FALSE, 0 );
 
     w = gtk_check_button_new_with_mnemonic( _( "Show _backup trackers" ) );
     di->all_check = w;
@@ -2314,6 +2495,7 @@ details_free( gpointer gdata )
 {
     struct DetailsImpl * data = gdata;
     g_source_remove( data->periodic_refresh_tag );
+    g_hash_table_destroy( data->tracker_hash );
     g_hash_table_destroy( data->webseed_hash );
     g_hash_table_destroy( data->peer_hash );
     g_slist_free( data->ids );
index 8f838e4b6e74da5759acb93f2b6babd31d62b9cd..23117845bf13031e35caf376064e02e9e3d74b25 100644 (file)
@@ -913,3 +913,30 @@ gtr_unrecognized_url_dialog( GtkWidget * parent, const char * url )
     gtk_widget_show( w );
     g_string_free( gstr, TRUE );
 }
+
+/***
+****
+***/
+
+void
+gtr_paste_clipboard_url_into_entry( GtkWidget * e )
+{
+  size_t i;
+
+  char * text[] = {
+    gtk_clipboard_wait_for_text( gtk_clipboard_get( GDK_SELECTION_PRIMARY ) ),
+    gtk_clipboard_wait_for_text( gtk_clipboard_get( GDK_SELECTION_CLIPBOARD ) )
+  };
+
+  for( i=0; i<G_N_ELEMENTS(text); ++i ) {
+      char * s = text[i];
+      if( s && ( gtr_is_supported_url( s ) || gtr_is_magnet_link( s ) ) ) {
+          gtk_entry_set_text( GTK_ENTRY( e ), s );
+          break;
+      }
+  }
+
+  for( i=0; i<G_N_ELEMENTS(text); ++i )
+    g_free( text[i] );
+}
+
index 03857f89f1275d8fb61de51669ceb3dee30aa077..a6f9af05749b7e95b5ba02dafc3d8f5e3fac0cd1 100644 (file)
@@ -198,4 +198,7 @@ gboolean on_tree_view_button_released( GtkWidget      * view,
 /* move a file to the trashcan if GIO is available; otherwise, delete it */
 int gtr_file_trash_or_remove( const char * filename );
 
+void gtr_paste_clipboard_url_into_entry( GtkWidget * entry );
+
+
 #endif /* GTR_UTIL_H */