Changeset 5987


Ignore:
Timestamp:
Jun 1, 2008, 1:40:32 AM (14 years ago)
Author:
charles
Message:

#838 (Adding trackers to existing torrents): add a backend API for this (tr_torrentSetAnnounceList) and implement a GUI for it in the gtk+ client.

Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/gtk/details.c

    r5947 r5987  
    1111 */
    1212
     13#include <errno.h>
    1314#include <stddef.h>
    1415#include <stdio.h>
     
    1819
    1920#include <libtransmission/transmission.h>
     21#include <libtransmission/utils.h> /* tr_httpIsValidURL */
    2022
    2123#include "actions.h"
     
    10791081    TrTorrent * gtor;
    10801082
     1083    GtkTreeView * view;
     1084    GtkListStore * store;
     1085    GtkTreeSelection * sel;
     1086
     1087    GtkWidget * add_button;
     1088    GtkWidget * remove_button;
     1089    GtkWidget * save_button;
     1090    GtkWidget * revert_button;
     1091
    10811092    GtkWidget * last_scrape_time_lb;
    10821093    GtkWidget * last_scrape_response_lb;
     
    10891100};
    10901101
     1102enum
     1103{
     1104    TR_COL_TIER,
     1105    TR_COL_ANNOUNCE,
     1106    TR_N_COLS
     1107};
     1108
     1109static void
     1110setTrackerChangeState( struct tracker_page * page, gboolean changed )
     1111{
     1112    gtk_widget_set_sensitive( page->save_button, changed );
     1113    gtk_widget_set_sensitive( page->revert_button, changed );
     1114}
     1115
     1116static GtkTreeModel*
     1117tracker_model_new( tr_torrent * tor )
     1118{
     1119    int i;
     1120    const tr_info * inf = tr_torrentInfo( tor );
     1121    GtkListStore * store = gtk_list_store_new( TR_N_COLS, G_TYPE_INT, G_TYPE_STRING );
     1122
     1123    for( i=0; i<inf->trackerCount; ++i )
     1124    {
     1125        GtkTreeIter iter;
     1126        const tr_tracker_info * tinf = inf->trackers + i;
     1127        gtk_list_store_append( store, &iter );
     1128        gtk_list_store_set( store, &iter, TR_COL_TIER, tinf->tier + 1,
     1129                                          TR_COL_ANNOUNCE, tinf->announce,
     1130                                          -1 );
     1131    }
     1132
     1133    gtk_tree_sortable_set_sort_column_id( GTK_TREE_SORTABLE( store ),
     1134                                          TR_COL_TIER,
     1135                                          GTK_SORT_ASCENDING );
     1136
     1137    return GTK_TREE_MODEL( store );
     1138}
     1139
     1140static void
     1141onTrackerSelectionChanged( GtkTreeSelection  * sel,
     1142                           gpointer            gpage )
     1143{
     1144    struct tracker_page * page = gpage;
     1145    gboolean has_selection = gtk_tree_selection_get_selected( sel, NULL, NULL );
     1146    gtk_widget_set_sensitive( page->remove_button, has_selection );
     1147}
     1148
     1149static void
     1150onTrackerRemoveClicked( GtkButton * w UNUSED, gpointer gpage )
     1151{
     1152    struct tracker_page * page = gpage;
     1153    GtkTreeIter iter;
     1154    if( gtk_tree_selection_get_selected( page->sel, NULL, &iter ) ) {
     1155        gtk_list_store_remove( page->store, &iter );
     1156        setTrackerChangeState( page, TRUE );
     1157    }
     1158}
     1159
     1160static void
     1161onTrackerAddClicked( GtkButton * w UNUSED, gpointer gpage )
     1162{
     1163    GtkTreeIter iter;
     1164    struct tracker_page * page = gpage;
     1165    GtkTreePath * path;
     1166    gtk_list_store_append( page->store, &iter );
     1167    setTrackerChangeState( page, TRUE );
     1168    gtk_list_store_set( page->store, &iter, TR_COL_TIER, 1,
     1169                                      TR_COL_ANNOUNCE, _( "Announce URL" ),
     1170                                      -1 );
     1171    path = gtk_tree_model_get_path( GTK_TREE_MODEL( page->store ), &iter );
     1172    gtk_tree_view_set_cursor( page->view,
     1173                              path,
     1174                              gtk_tree_view_get_column( page->view, TR_COL_ANNOUNCE ),
     1175                              TRUE );
     1176    gtk_tree_path_free( path );
     1177}
     1178
     1179static void
     1180onTrackerSaveClicked( GtkButton * w UNUSED, gpointer gpage )
     1181{
     1182    struct tracker_page * page = gpage;
     1183    GtkTreeModel * model = GTK_TREE_MODEL( page->store );
     1184    const int n = gtk_tree_model_iter_n_children( model, NULL );
     1185
     1186    if( n > 0 ) /* must have at least one tracker */
     1187    {
     1188        int i = 0;
     1189        GtkTreeIter iter;
     1190        tr_tracker_info * trackers;
     1191
     1192        /* build the tracker list */
     1193        trackers = g_new0( tr_tracker_info, n );
     1194        if( gtk_tree_model_get_iter_first( model, &iter ) ) do {
     1195            gtk_tree_model_get( model, &iter, TR_COL_TIER, &trackers[i].tier,
     1196                                              TR_COL_ANNOUNCE, &trackers[i].announce,
     1197                                              -1 );
     1198            ++i;
     1199        } while( gtk_tree_model_iter_next( model, &iter ) );
     1200        g_assert( i == n );
     1201
     1202        /* set the tracker list */
     1203        tr_torrentSetAnnounceList( tr_torrent_handle( page->gtor ),
     1204                                   trackers, n );
     1205
     1206
     1207        setTrackerChangeState( page, FALSE );
     1208
     1209        /* cleanup */
     1210        for( i=0; i<n; ++i )
     1211            g_free( trackers[i].announce );
     1212        g_free( trackers );
     1213    }
     1214}
     1215
     1216static void
     1217onTrackerRevertClicked( GtkButton * w UNUSED, gpointer gpage )
     1218{
     1219    struct tracker_page * page = gpage;
     1220    GtkTreeModel * model = tracker_model_new( tr_torrent_handle( page->gtor ) );
     1221    gtk_tree_view_set_model( page->view, model );
     1222    page->store = GTK_LIST_STORE( model );
     1223    g_object_unref( G_OBJECT( model ) );
     1224    setTrackerChangeState( page, FALSE );
     1225}
     1226
     1227static void
     1228onAnnounceEdited( GtkCellRendererText * renderer UNUSED,
     1229                  gchar               * path_string,
     1230                  gchar               * new_text,
     1231                  gpointer              gpage )
     1232{
     1233    struct tracker_page * page = gpage;
     1234    GtkTreeModel * model = GTK_TREE_MODEL( page->store );
     1235    GtkTreeIter iter;
     1236    GtkTreePath * path = gtk_tree_path_new_from_string( path_string ) ;
     1237    if( gtk_tree_model_get_iter( model, &iter, path ) )
     1238    {
     1239        char * old_text;
     1240        gtk_tree_model_get( model, &iter, TR_COL_ANNOUNCE, &old_text, -1 );
     1241        if( tr_httpIsValidURL( new_text ) )
     1242        {
     1243            if( strcmp( old_text, new_text ) )
     1244            {
     1245                gtk_list_store_set( page->store, &iter, TR_COL_ANNOUNCE, new_text, -1 );
     1246                setTrackerChangeState( page, TRUE );
     1247            }
     1248        }
     1249        else if( !tr_httpIsValidURL( old_text ) )
     1250        {
     1251            /* both old and new are invalid...
     1252               they must've typed in an invalid URL
     1253               after hitting the "Add" button */
     1254            onTrackerRemoveClicked( NULL, page );
     1255            setTrackerChangeState( page, TRUE );
     1256        }
     1257        g_free( old_text );
     1258    }
     1259    gtk_tree_path_free( path );
     1260}
     1261
     1262static void
     1263onTierEdited( GtkCellRendererText  * renderer UNUSED,
     1264              gchar                * path_string,
     1265              gchar                * new_text,
     1266              gpointer               gpage )
     1267{
     1268    struct tracker_page * page = gpage;
     1269    GtkTreeModel * model = GTK_TREE_MODEL( page->store );
     1270    GtkTreeIter iter;
     1271    GtkTreePath * path;
     1272    char * end;
     1273    int new_tier;
     1274
     1275    errno = 0;
     1276    new_tier = strtol( new_text, &end, 10 );
     1277    if( new_tier<1 || *end || errno )
     1278        return;
     1279 
     1280    path = gtk_tree_path_new_from_string( path_string ) ;
     1281    if( gtk_tree_model_get_iter( model, &iter, path ) )
     1282    {
     1283        int old_tier;
     1284        gtk_tree_model_get( model, &iter, TR_COL_TIER, &old_tier, -1 );
     1285        if( old_tier != new_tier )
     1286        {
     1287            gtk_list_store_set( page->store, &iter, TR_COL_TIER, new_tier, -1 );
     1288            setTrackerChangeState( page, TRUE );
     1289        }
     1290    }
     1291    gtk_tree_path_free( path );
     1292}
     1293
    10911294GtkWidget*
    10921295tracker_page_new( TrTorrent * gtor )
     
    10941297    GtkWidget * t;
    10951298    GtkWidget * l;
     1299    GtkWidget * w;
     1300    GtkWidget * h;
     1301    GtkWidget * v;
     1302    GtkWidget * fr;
    10961303    int row = 0;
    10971304    const char * s;
     1305    GtkTreeModel * m;
     1306    GtkCellRenderer * r;
     1307    GtkTreeViewColumn * c;
     1308    GtkTreeSelection * sel;
    10981309    struct tracker_page * page = g_new0( struct tracker_page, 1 );
    10991310    const tr_info * info = tr_torrent_info (gtor);
     
    11021313
    11031314    t = hig_workarea_create( );
     1315    hig_workarea_add_section_title( t, &row, _( "Trackers" ) );
     1316
     1317        h = gtk_hbox_new( FALSE, GUI_PAD );
     1318        m = tracker_model_new( tr_torrent_handle( gtor ) );
     1319        page->store = GTK_LIST_STORE( m );
     1320        w = gtk_tree_view_new_with_model( m );
     1321        page->view = GTK_TREE_VIEW( w );
     1322        gtk_tree_view_set_enable_search( page->view, FALSE );
     1323        r = gtk_cell_renderer_text_new( );
     1324        g_object_set( G_OBJECT( r ),
     1325                "editable", TRUE,
     1326                NULL );
     1327        g_signal_connect( r, "edited",
     1328                          G_CALLBACK( onTierEdited ), page );
     1329        c = gtk_tree_view_column_new_with_attributes( _( "Tier" ), r,
     1330                "text", TR_COL_TIER,
     1331                NULL );
     1332        gtk_tree_view_column_set_sort_column_id( c, TR_COL_TIER );
     1333        gtk_tree_view_append_column( page->view, c );
     1334        r = gtk_cell_renderer_text_new( );
     1335        g_object_set( G_OBJECT( r ),
     1336                "editable", TRUE,
     1337                "ellipsize", PANGO_ELLIPSIZE_END,
     1338                NULL );
     1339        g_signal_connect( r, "edited",
     1340                          G_CALLBACK( onAnnounceEdited ), page );
     1341        c = gtk_tree_view_column_new_with_attributes( _( "Announce URL" ), r,
     1342                "text", TR_COL_ANNOUNCE,
     1343                NULL );
     1344        gtk_tree_view_column_set_sort_column_id( c, TR_COL_ANNOUNCE );
     1345        gtk_tree_view_append_column( page->view, c );
     1346        w = gtk_scrolled_window_new( NULL, NULL );
     1347        gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( w ),
     1348                                        GTK_POLICY_NEVER,
     1349                                        GTK_POLICY_AUTOMATIC );
     1350        sel = gtk_tree_view_get_selection( page->view );
     1351        page->sel = sel;
     1352        g_signal_connect( sel, "changed",
     1353                          G_CALLBACK( onTrackerSelectionChanged ), page );
     1354        gtk_container_add( GTK_CONTAINER( w ), GTK_WIDGET( page->view ) );
     1355        gtk_widget_set_size_request( w, -1, 133 );
     1356        fr = gtk_frame_new( NULL );
     1357        gtk_frame_set_shadow_type( GTK_FRAME( fr ), GTK_SHADOW_IN );
     1358        gtk_container_add( GTK_CONTAINER( fr ), w );
     1359        gtk_box_pack_start_defaults( GTK_BOX( h ), fr );
     1360        g_object_unref( G_OBJECT( m ) );
     1361
     1362        v = gtk_vbox_new( TRUE, GUI_PAD_SMALL );
     1363        w = gtk_button_new_from_stock( GTK_STOCK_ADD );
     1364        g_signal_connect( w, "clicked", G_CALLBACK( onTrackerAddClicked ), page );
     1365        gtk_box_pack_start( GTK_BOX( v ), w, FALSE, FALSE, 0 );
     1366        page->add_button = w;
     1367        w = gtk_button_new_from_stock( GTK_STOCK_REMOVE );
     1368        g_signal_connect( w, "clicked", G_CALLBACK( onTrackerRemoveClicked ), page );
     1369        gtk_box_pack_start( GTK_BOX( v ), w, FALSE, FALSE, 0 );
     1370        page->remove_button = w;
     1371        w = gtk_button_new_from_stock( GTK_STOCK_SAVE );
     1372        g_signal_connect( w, "clicked", G_CALLBACK( onTrackerSaveClicked ), page );
     1373        gtk_widget_set_sensitive( w, FALSE );
     1374        gtk_box_pack_start( GTK_BOX( v ), w, FALSE, FALSE, 0 );
     1375        page->save_button = w;
     1376        w = gtk_button_new_from_stock( GTK_STOCK_REVERT_TO_SAVED );
     1377        g_signal_connect( w, "clicked", G_CALLBACK( onTrackerRevertClicked ), page );
     1378        gtk_widget_set_sensitive( w, FALSE );
     1379        gtk_box_pack_start( GTK_BOX( v ), w, FALSE, FALSE, 0 );
     1380        page->revert_button = w;
     1381        gtk_box_pack_start( GTK_BOX( h ), v, FALSE, FALSE, 0 );
     1382
     1383        hig_workarea_add_wide_control( t, &row, h );
     1384        onTrackerSelectionChanged( sel, page );
     1385
     1386    hig_workarea_add_section_divider( t, &row );
    11041387    hig_workarea_add_section_title( t, &row, _( "Scrape" ) );
    11051388
     
    11481431        hig_workarea_add_row( t, &row, s, l, NULL );
    11491432
     1433    hig_workarea_finish( t, &row );
    11501434    g_object_set_data_full( G_OBJECT( t ), TRACKER_PAGE, page, g_free );
    1151     hig_workarea_finish( t, &row );
    11521435    return t;
    11531436}
  • trunk/gtk/file-list.c

    r5968 r5987  
    635635    /* add priority column */
    636636    rend = gtk_cell_renderer_text_new( );
    637     col = gtk_tree_view_column_new_with_attributes( _( "Priorities" ), rend, NULL );
     637    col = gtk_tree_view_column_new_with_attributes( _( "Priority" ), rend, NULL );
    638638    gtk_tree_view_column_set_cell_data_func( col, rend, renderPriority, NULL, NULL);
    639639    gtk_tree_view_append_column ( GTK_TREE_VIEW( view ), col);
     
    646646                                         GTK_SHADOW_IN);
    647647    gtk_container_add( GTK_CONTAINER( scroll ), view );
    648     gtk_widget_set_size_request (scroll, 0u, 200u);
     648    gtk_widget_set_size_request (scroll, -1, 200 );
    649649
    650650    data = g_new0( FileData, 1 );
  • trunk/gtk/main.c

    r5947 r5987  
    700700
    701701    b = gtk_alignment_new(0.0, 1.0, 0.01, 0.01);
    702     w = gtk_button_new_with_label( _( "_Quit Immediately" ) );
     702    w = gtk_button_new_with_label( _( "_Quit Now" ) );
    703703    gtk_button_set_image( GTK_BUTTON(w), gtk_image_new_from_stock( GTK_STOCK_QUIT, GTK_ICON_SIZE_BUTTON ) );
    704704    g_signal_connect(w, "clicked", G_CALLBACK(do_exit_cb), NULL);
  • trunk/libtransmission/bencode.c

    r5927 r5987  
    3838#include "utils.h" /* tr_new(), tr_free() */
    3939
     40const tr_benc BENC_NULL = { 0, { 0 } };
     41
    4042/**
    4143***
     
    329331***/
    330332
    331 tr_benc *
    332 tr_bencDictFind( tr_benc * val, const char * key )
     333static int
     334dictIndexOf( tr_benc * val, const char * key )
    333335{
    334336    int len, ii;
    335337
    336338    if( !tr_bencIsDict( val ) )
    337         return NULL;
     339        return -1;
    338340
    339341    len = strlen( key );
     
    347349            continue;
    348350        }
    349         return &val->val.l.vals[ii+1];
    350     }
    351 
    352     return NULL;
     351        return ii;
     352    }
     353
     354    return -1;
     355}
     356
     357tr_benc *
     358tr_bencDictFind( tr_benc * val, const char * key )
     359{
     360    const int i = dictIndexOf( val, key );
     361    return i<0 ? NULL : &val->val.l.vals[i+1];
    353362}
    354363
     
    646655    return child;
    647656}
     657
     658int
     659tr_bencDictRemove( tr_benc * dict, const char * key )
     660{
     661    int i = dictIndexOf( dict, key );
     662    if( i >= 0 )
     663    {
     664        const int n = dict->val.l.count;
     665fprintf( stderr, "i is %d... count is %d\n", i, dict->val.l.count );
     666fprintf( stderr, "moving %d items from pos %d to %d\n", dict->val.l.count-(i+2), i+2, i );
     667#if 0
     668        tr_bencFree( &dict->val.l.vals[i] );
     669        tr_bencFree( &dict->val.l.vals[i+1] );
     670#endif
     671        if( i + 2 < n )
     672        {
     673            dict->val.l.vals[i]   = dict->val.l.vals[n-2];
     674            dict->val.l.vals[i+1] = dict->val.l.vals[n-1];
     675        }
     676        dict->val.l.count -= 2;
     677    }
     678    return i >= 0; /* return true if found */
     679}
     680
    648681
    649682/***
     
    910943tr_bencFree( tr_benc * val )
    911944{
    912     if( val != NULL )
     945    if( val && val->type )
    913946    {
    914947        tr_ptrArray * freeme = tr_ptrArrayNew( );
  • trunk/libtransmission/bencode.h

    r5860 r5987  
    5353    } val;
    5454} tr_benc;
     55
     56const tr_benc BENC_INIT;
    5557
    5658/* backwards compatability */
     
    109111tr_benc    * tr_bencDictAddDict( tr_benc * dict, const char * key, int reserveCount );
    110112tr_benc    * tr_bencDictAddRaw( tr_benc * dict, const char * key, const void *, size_t len );
     113int          tr_bencDictRemove( tr_benc * dict, const char * key );
    111114
    112115char*  tr_bencSave( const tr_benc * val, int * len );
  • trunk/libtransmission/torrent.c

    r5986 r5987  
    308308    tr_piece_index_t pp;
    309309    uint64_t offset = 0;
    310 
    311     assert( tor != NULL );
    312 
    313     for( ff=0; ff<tor->info.fileCount; ++ff ) {
    314       tor->info.files[ff].offset = offset;
    315       offset += tor->info.files[ff].length;
    316       initFilePieces( &tor->info, ff );
    317     }
    318 
    319     for( pp=0; pp<tor->info.pieceCount; ++pp )
    320         tor->info.pieces[pp].priority = calculatePiecePriority( tor, pp, -1 );
     310    tr_info * inf = &tor->info;
     311
     312    assert( inf != NULL );
     313
     314    for( ff=0; ff<inf->fileCount; ++ff ) {
     315      inf->files[ff].offset = offset;
     316      offset += inf->files[ff].length;
     317      initFilePieces( inf, ff );
     318    }
     319
     320    for( pp=0; pp<inf->pieceCount; ++pp )
     321        inf->pieces[pp].priority = calculatePiecePriority( tor, pp, -1 );
    321322}
    322323
     
    15181519    return m;
    15191520}
     1521
     1522/***
     1523****
     1524***/
     1525
     1526void
     1527tr_torrentSetAnnounceList( tr_torrent             * tor,
     1528                           const tr_tracker_info  * trackers,
     1529                           int                      trackerCount )
     1530{
     1531    tr_benc metainfo = BENC_INIT;
     1532
     1533    /* save to the .torrent file */
     1534    if( !tr_bencLoadFile( tor->info.torrent, &metainfo ) )
     1535    {
     1536        int i;
     1537        int prevTier = -1;
     1538        tr_benc * tier = NULL;
     1539        tr_benc * announceList;
     1540        tr_info tmpInfo;
     1541
     1542        /* remove the old fields */
     1543        tr_bencDictRemove( &metainfo, "announce" );
     1544        tr_bencDictRemove( &metainfo, "announce-list" );
     1545
     1546        /* add the new fields */
     1547        tr_bencDictAddStr( &metainfo, "announce", trackers[0].announce );
     1548        announceList = tr_bencDictAddList( &metainfo, "announce-list", 0 );
     1549        for( i=0; i<trackerCount; ++i ) {
     1550            if( prevTier != trackers[i].tier ) {
     1551                prevTier = trackers[i].tier;
     1552                tier = tr_bencListAddList( announceList, 0 );
     1553            }
     1554            tr_bencListAddStr( tier, trackers[i].announce );
     1555        }
     1556
     1557        /* try to parse it back again, to make sure it's good */
     1558        memset( &tmpInfo, 0, sizeof( tr_info ) );
     1559        if( !tr_metainfoParse( tor->handle, &tmpInfo, &metainfo ) )
     1560        {
     1561            /* if it's good, save it and use it */
     1562            tr_metainfoFree( &tor->info );
     1563            tor->info = tmpInfo;
     1564            tr_torrentInitFilePieces( tor );
     1565            tr_bencSaveFile( tor->info.torrent, &metainfo );
     1566        }
     1567
     1568        /* cleanup */
     1569        tr_bencFree( &metainfo );
     1570    }
     1571}
  • trunk/libtransmission/transmission.h

    r5975 r5987  
    803803const char * tr_torrentGetDownloadDir( const tr_torrent * );
    804804
     805/**
     806***
     807**/
     808   
     809typedef struct tr_tracker_info
     810{
     811    int    tier;
     812    char * announce;
     813    char * scrape;
     814}
     815tr_tracker_info;
     816
     817/**
     818 * @brief Modify a torrent's tracker list.
     819 *
     820 * This updates the torrent in-memory and also the metainfo file
     821 * stored in the torrent folder in tr_sessionGetConfigDir().
     822 *
     823 * @param torrent The torrent whose tracker list is to be modified.
     824 * @param trackers An array of trackers, sorted by tier from first to last.
     825 *                 NOTE: only the `tier' and `announce' fields are used.
     826 *                 libtransmission derives `scrape' from `announce'.
     827 * @param trackerCount size of the `trackers' array.
     828 */
     829void tr_torrentSetAnnounceList( tr_torrent             * torrent,
     830                                const tr_tracker_info  * trackers,
     831                                int                      trackerCount );                   
     832
    805833
    806834/**
     
    938966}
    939967tr_piece;
    940    
    941 typedef struct tr_tracker_info
    942 {
    943     int    tier;
    944     char * announce;
    945     char * scrape;
    946 }
    947 tr_tracker_info;
    948968
    949969struct tr_info
Note: See TracChangeset for help on using the changeset viewer.