Changeset 13761


Ignore:
Timestamp:
Jan 4, 2013, 7:45:39 PM (8 years ago)
Author:
jordan
Message:

(gtk) copyediting: update several files to be closer to the gtk+ indentation style.

Location:
trunk/gtk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/gtk/details.c

    r13760 r13761  
    3737struct DetailsImpl
    3838{
    39     GtkWidget * dialog;
    40 
    41     GtkWidget * honor_limits_check;
    42     GtkWidget * up_limited_check;
    43     GtkWidget * up_limit_sping;
    44     GtkWidget * down_limited_check;
    45     GtkWidget * down_limit_spin;
    46     GtkWidget * bandwidth_combo;
    47 
    48     GtkWidget * ratio_combo;
    49     GtkWidget * ratio_spin;
    50     GtkWidget * idle_combo;
    51     GtkWidget * idle_spin;
    52     GtkWidget * max_peers_spin;
    53 
    54     gulong honor_limits_check_tag;
    55     gulong up_limited_check_tag;
    56     gulong down_limited_check_tag;
    57     gulong down_limit_spin_tag;
    58     gulong up_limit_spin_tag;
    59     gulong bandwidth_combo_tag;
    60     gulong ratio_combo_tag;
    61     gulong ratio_spin_tag;
    62     gulong idle_combo_tag;
    63     gulong idle_spin_tag;
    64     gulong max_peers_spin_tag;
    65 
    66     GtkWidget * size_lb;
    67     GtkWidget * state_lb;
    68     GtkWidget * have_lb;
    69     GtkWidget * dl_lb;
    70     GtkWidget * ul_lb;
    71     GtkWidget * error_lb;
    72     GtkWidget * date_started_lb;
    73     GtkWidget * eta_lb;
    74     GtkWidget * last_activity_lb;
    75 
    76     GtkWidget * hash_lb;
    77     GtkWidget * privacy_lb;
    78     GtkWidget * origin_lb;
    79     GtkWidget * destination_lb;
    80     GtkTextBuffer * comment_buffer;
    81 
    82     GHashTable * peer_hash;
    83     GHashTable * webseed_hash;
    84     GtkListStore * peer_store;
    85     GtkListStore * webseed_store;
    86     GtkWidget * webseed_view;
    87     GtkWidget * peer_view;
    88     GtkWidget * more_peer_details_check;
    89 
    90     GtkListStore  * tracker_store;
    91     GHashTable    * tracker_hash;
    92     GtkTreeModel  * trackers_filtered;
    93     GtkWidget     * add_tracker_button;
    94     GtkWidget     * edit_trackers_button;
    95     GtkWidget     * remove_tracker_button;
    96     GtkWidget     * tracker_view;
    97     GtkWidget     * scrape_check;
    98     GtkWidget     * all_check;
    99 
    100     GtkWidget * file_list;
    101     GtkWidget * file_label;
    102 
    103     GSList * ids;
    104     TrCore * core;
    105     guint periodic_refresh_tag;
    106 
    107     GString * gstr;
     39  GtkWidget * dialog;
     40
     41  GtkWidget * honor_limits_check;
     42  GtkWidget * up_limited_check;
     43  GtkWidget * up_limit_sping;
     44  GtkWidget * down_limited_check;
     45  GtkWidget * down_limit_spin;
     46  GtkWidget * bandwidth_combo;
     47
     48  GtkWidget * ratio_combo;
     49  GtkWidget * ratio_spin;
     50  GtkWidget * idle_combo;
     51  GtkWidget * idle_spin;
     52  GtkWidget * max_peers_spin;
     53
     54  gulong honor_limits_check_tag;
     55  gulong up_limited_check_tag;
     56  gulong down_limited_check_tag;
     57  gulong down_limit_spin_tag;
     58  gulong up_limit_spin_tag;
     59  gulong bandwidth_combo_tag;
     60  gulong ratio_combo_tag;
     61  gulong ratio_spin_tag;
     62  gulong idle_combo_tag;
     63  gulong idle_spin_tag;
     64  gulong max_peers_spin_tag;
     65
     66  GtkWidget * size_lb;
     67  GtkWidget * state_lb;
     68  GtkWidget * have_lb;
     69  GtkWidget * dl_lb;
     70  GtkWidget * ul_lb;
     71  GtkWidget * error_lb;
     72  GtkWidget * date_started_lb;
     73  GtkWidget * eta_lb;
     74  GtkWidget * last_activity_lb;
     75
     76  GtkWidget * hash_lb;
     77  GtkWidget * privacy_lb;
     78  GtkWidget * origin_lb;
     79  GtkWidget * destination_lb;
     80  GtkTextBuffer * comment_buffer;
     81
     82  GHashTable * peer_hash;
     83  GHashTable * webseed_hash;
     84  GtkListStore * peer_store;
     85  GtkListStore * webseed_store;
     86  GtkWidget * webseed_view;
     87  GtkWidget * peer_view;
     88  GtkWidget * more_peer_details_check;
     89
     90  GtkListStore  * tracker_store;
     91  GHashTable    * tracker_hash;
     92  GtkTreeModel  * trackers_filtered;
     93  GtkWidget     * add_tracker_button;
     94  GtkWidget     * edit_trackers_button;
     95  GtkWidget     * remove_tracker_button;
     96  GtkWidget     * tracker_view;
     97  GtkWidget     * scrape_check;
     98  GtkWidget     * all_check;
     99
     100  GtkWidget * file_list;
     101  GtkWidget * file_label;
     102
     103  GSList * ids;
     104  TrCore * core;
     105  guint periodic_refresh_tag;
     106
     107  GString * gstr;
    108108};
    109109
     
    111111getTorrents (struct DetailsImpl * d, int * setmeCount)
    112112{
    113     GSList * l;
    114     int torrentCount = 0;
    115     const int n = g_slist_length (d->ids);
    116     tr_torrent ** torrents = g_new (tr_torrent*, n);
    117 
    118     for (l=d->ids; l!=NULL; l=l->next)
    119         if ((torrents[torrentCount] = gtr_core_find_torrent (d->core, GPOINTER_TO_INT (l->data))))
    120             ++torrentCount;
    121 
    122     *setmeCount = torrentCount;
    123     return torrents;
     113  GSList * l;
     114  int torrentCount = 0;
     115  const int n = g_slist_length (d->ids);
     116  tr_torrent ** torrents = g_new (tr_torrent*, n);
     117
     118  for (l=d->ids; l!=NULL; l=l->next)
     119    if ((torrents[torrentCount] = gtr_core_find_torrent (d->core, GPOINTER_TO_INT (l->data))))
     120      ++torrentCount;
     121
     122  *setmeCount = torrentCount;
     123  return torrents;
    124124}
    125125
     
    133133set_togglebutton_if_different (GtkWidget * w, gulong tag, gboolean value)
    134134{
    135     GtkToggleButton * toggle = GTK_TOGGLE_BUTTON (w);
    136     const gboolean currentValue = gtk_toggle_button_get_active (toggle);
    137     if (currentValue != value)
    138     {
    139         g_signal_handler_block (toggle, tag);
    140         gtk_toggle_button_set_active (toggle, value);
    141         g_signal_handler_unblock (toggle, tag);
     135  GtkToggleButton * toggle = GTK_TOGGLE_BUTTON (w);
     136  const gboolean currentValue = gtk_toggle_button_get_active (toggle);
     137
     138  if (currentValue != value)
     139    {
     140      g_signal_handler_block (toggle, tag);
     141      gtk_toggle_button_set_active (toggle, value);
     142      g_signal_handler_unblock (toggle, tag);
    142143    }
    143144}
     
    146147set_int_spin_if_different (GtkWidget * w, gulong tag, int value)
    147148{
    148     GtkSpinButton * spin = GTK_SPIN_BUTTON (w);
    149     const int currentValue = gtk_spin_button_get_value_as_int (spin);
    150     if (currentValue != value)
    151     {
    152         g_signal_handler_block (spin, tag);
    153         gtk_spin_button_set_value (spin, value);
    154         g_signal_handler_unblock (spin, tag);
     149  GtkSpinButton * spin = GTK_SPIN_BUTTON (w);
     150  const int currentValue = gtk_spin_button_get_value_as_int (spin);
     151
     152  if (currentValue != value)
     153    {
     154      g_signal_handler_block (spin, tag);
     155      gtk_spin_button_set_value (spin, value);
     156      g_signal_handler_unblock (spin, tag);
    155157    }
    156158}
     
    159161set_double_spin_if_different (GtkWidget * w, gulong tag, double value)
    160162{
    161     GtkSpinButton * spin = GTK_SPIN_BUTTON (w);
    162     const double currentValue = gtk_spin_button_get_value (spin);
    163     if (((int)(currentValue*100) != (int)(value*100)))
    164     {
    165         g_signal_handler_block (spin, tag);
    166         gtk_spin_button_set_value (spin, value);
    167         g_signal_handler_unblock (spin, tag);
     163  GtkSpinButton * spin = GTK_SPIN_BUTTON (w);
     164  const double currentValue = gtk_spin_button_get_value (spin);
     165
     166  if (((int)(currentValue*100) != (int)(value*100)))
     167    {
     168      g_signal_handler_block (spin, tag);
     169      gtk_spin_button_set_value (spin, value);
     170      g_signal_handler_unblock (spin, tag);
    168171    }
    169172}
     
    172175unset_combo (GtkWidget * w, gulong tag)
    173176{
    174     GtkComboBox * combobox = GTK_COMBO_BOX (w);
    175 
    176     g_signal_handler_block (combobox, tag);
    177     gtk_combo_box_set_active (combobox, -1);
    178     g_signal_handler_unblock (combobox, tag);
     177  GtkComboBox * combobox = GTK_COMBO_BOX (w);
     178
     179  g_signal_handler_block (combobox, tag);
     180  gtk_combo_box_set_active (combobox, -1);
     181  g_signal_handler_unblock (combobox, tag);
    179182}
    180183
     
    182185refreshOptions (struct DetailsImpl * di, tr_torrent ** torrents, int n)
    183186{
    184     /***
    185     ****  Options Page
    186     ***/
    187 
    188     /* honor_limits_check */
    189     if (n) {
    190         const bool baseline = tr_torrentUsesSessionLimits (torrents[0]);
    191         int i;
    192         for (i=1; i<n; ++i)
    193             if (baseline != tr_torrentUsesSessionLimits (torrents[i]))
    194                 break;
    195         if (i == n)
    196             set_togglebutton_if_different (di->honor_limits_check,
    197                                            di->honor_limits_check_tag, baseline);
    198     }
    199 
    200     /* down_limited_check */
    201     if (n) {
    202         const bool baseline = tr_torrentUsesSpeedLimit (torrents[0], TR_DOWN);
    203         int i;
    204         for (i=1; i<n; ++i)
    205             if (baseline != tr_torrentUsesSpeedLimit (torrents[i], TR_DOWN))
    206                 break;
    207         if (i == n)
    208             set_togglebutton_if_different (di->down_limited_check,
    209                                            di->down_limited_check_tag, baseline);
    210     }
    211 
    212     /* down_limit_spin */
    213     if (n) {
    214         const unsigned int baseline = tr_torrentGetSpeedLimit_KBps (torrents[0], TR_DOWN);
    215         int i;
    216         for (i=1; i<n; ++i)
    217             if (baseline != (tr_torrentGetSpeedLimit_KBps (torrents[i], TR_DOWN)))
    218                 break;
    219         if (i == n)
    220             set_int_spin_if_different (di->down_limit_spin,
    221                                        di->down_limit_spin_tag, baseline);
    222     }
    223 
    224     /* up_limited_check */
    225     if (n) {
    226         const bool baseline = tr_torrentUsesSpeedLimit (torrents[0], TR_UP);
    227         int i;
    228         for (i=1; i<n; ++i)
    229             if (baseline != tr_torrentUsesSpeedLimit (torrents[i], TR_UP))
    230                 break;
    231         if (i == n)
    232             set_togglebutton_if_different (di->up_limited_check,
    233                                            di->up_limited_check_tag, baseline);
    234     }
    235 
    236     /* up_limit_sping */
    237     if (n) {
    238         const unsigned int  baseline = tr_torrentGetSpeedLimit_KBps (torrents[0], TR_UP);
    239         int i;
    240         for (i=1; i<n; ++i)
    241             if (baseline != (tr_torrentGetSpeedLimit_KBps (torrents[i], TR_UP)))
    242                 break;
    243         if (i == n)
    244             set_int_spin_if_different (di->up_limit_sping,
    245                                        di->up_limit_spin_tag, baseline);
    246     }
    247 
    248     /* bandwidth_combo */
    249     if (n) {
    250         const int baseline = tr_torrentGetPriority (torrents[0]);
    251         int i;
    252         for (i=1; i<n; ++i)
    253             if (baseline != tr_torrentGetPriority (torrents[i]))
    254                 break;
    255         if (i == n) {
    256             GtkWidget * w = di->bandwidth_combo;
    257             g_signal_handler_block (w, di->bandwidth_combo_tag);
    258             gtr_priority_combo_set_value (GTK_COMBO_BOX (w), baseline);
    259             g_signal_handler_unblock (w, di->bandwidth_combo_tag);
    260         }
    261         else
    262             unset_combo (di->bandwidth_combo, di->bandwidth_combo_tag);
    263     }
    264 
    265     /* ratio_combo */
    266     /* ratio_spin */
    267     if (n) {
    268         int i;
    269         const int baseline = tr_torrentGetRatioMode (torrents[0]);
    270         for (i=1; i<n; ++i)
    271             if (baseline != (int)tr_torrentGetRatioMode (torrents[i]))
    272                 break;
    273         if (i == n) {
    274             GtkWidget * w = di->ratio_combo;
    275             g_signal_handler_block (w, di->ratio_combo_tag);
    276             gtr_combo_box_set_active_enum (GTK_COMBO_BOX (w), baseline);
    277             gtr_widget_set_visible (di->ratio_spin, baseline == TR_RATIOLIMIT_SINGLE);
    278             g_signal_handler_unblock (w, di->ratio_combo_tag);
    279         }
    280     }
    281     if (n) {
    282         const double baseline = tr_torrentGetRatioLimit (torrents[0]);
    283         set_double_spin_if_different (di->ratio_spin,
    284                                       di->ratio_spin_tag, baseline);
    285     }
    286 
    287     /* idle_combo */
    288     /* idle_spin */
    289     if (n) {
    290         int i;
    291         const int baseline = tr_torrentGetIdleMode (torrents[0]);
    292         for (i=1; i<n; ++i)
    293             if (baseline != (int)tr_torrentGetIdleMode (torrents[i]))
    294                 break;
    295         if (i == n) {
    296             GtkWidget * w = di->idle_combo;
    297             g_signal_handler_block (w, di->idle_combo_tag);
    298             gtr_combo_box_set_active_enum (GTK_COMBO_BOX (w), baseline);
    299             gtr_widget_set_visible (di->idle_spin, baseline == TR_IDLELIMIT_SINGLE);
    300             g_signal_handler_unblock (w, di->idle_combo_tag);
    301         }
    302     }
    303     if (n) {
    304         const int baseline = tr_torrentGetIdleLimit (torrents[0]);
    305         set_int_spin_if_different (di->idle_spin,
    306                                    di->idle_spin_tag, baseline);
    307     }
    308 
    309     /* max_peers_spin */
    310     if (n) {
    311         const int baseline = tr_torrentGetPeerLimit (torrents[0]);
    312         set_int_spin_if_different (di->max_peers_spin,
    313                                    di->max_peers_spin_tag, baseline);
     187  /***
     188  ****  Options Page
     189  ***/
     190
     191  /* honor_limits_check */
     192  if (n)
     193    {
     194      int i;
     195      const bool baseline = tr_torrentUsesSessionLimits (torrents[0]);
     196
     197      for (i=1; i<n; ++i)
     198        if (baseline != tr_torrentUsesSessionLimits (torrents[i]))
     199          break;
     200
     201      if (i == n)
     202        set_togglebutton_if_different (di->honor_limits_check, di->honor_limits_check_tag, baseline);
     203    }
     204
     205  /* down_limited_check */
     206  if (n)
     207    {
     208      int i;
     209      const bool baseline = tr_torrentUsesSpeedLimit (torrents[0], TR_DOWN);
     210
     211      for (i=1; i<n; ++i)
     212        if (baseline != tr_torrentUsesSpeedLimit (torrents[i], TR_DOWN))
     213          break;
     214
     215      if (i == n)
     216        set_togglebutton_if_different (di->down_limited_check, di->down_limited_check_tag, baseline);
     217    }
     218
     219  /* down_limit_spin */
     220  if (n)
     221    {
     222      int i;
     223      const unsigned int baseline = tr_torrentGetSpeedLimit_KBps (torrents[0], TR_DOWN);
     224
     225      for (i=1; i<n; ++i)
     226          if (baseline != (tr_torrentGetSpeedLimit_KBps (torrents[i], TR_DOWN)))
     227            break;
     228
     229      if (i == n)
     230        set_int_spin_if_different (di->down_limit_spin, di->down_limit_spin_tag, baseline);
     231    }
     232
     233  /* up_limited_check */
     234  if (n)
     235    {
     236      int i;
     237      const bool baseline = tr_torrentUsesSpeedLimit (torrents[0], TR_UP);
     238
     239      for (i=1; i<n; ++i)
     240        if (baseline != tr_torrentUsesSpeedLimit (torrents[i], TR_UP))
     241          break;
     242
     243      if (i == n)
     244        set_togglebutton_if_different (di->up_limited_check, di->up_limited_check_tag, baseline);
     245    }
     246
     247  /* up_limit_sping */
     248  if (n)
     249    {
     250      int i;
     251      const unsigned int  baseline = tr_torrentGetSpeedLimit_KBps (torrents[0], TR_UP);
     252
     253      for (i=1; i<n; ++i)
     254        if (baseline != (tr_torrentGetSpeedLimit_KBps (torrents[i], TR_UP)))
     255          break;
     256
     257      if (i == n)
     258        set_int_spin_if_different (di->up_limit_sping, di->up_limit_spin_tag, baseline);
     259    }
     260
     261  /* bandwidth_combo */
     262  if (n)
     263    {
     264      int i;
     265      const int baseline = tr_torrentGetPriority (torrents[0]);
     266
     267      for (i=1; i<n; ++i)
     268        if (baseline != tr_torrentGetPriority (torrents[i]))
     269          break;
     270
     271      if (i == n)
     272        {
     273          GtkWidget * w = di->bandwidth_combo;
     274          g_signal_handler_block (w, di->bandwidth_combo_tag);
     275          gtr_priority_combo_set_value (GTK_COMBO_BOX (w), baseline);
     276          g_signal_handler_unblock (w, di->bandwidth_combo_tag);
     277        }
     278      else
     279        {
     280          unset_combo (di->bandwidth_combo, di->bandwidth_combo_tag);
     281        }
     282    }
     283
     284  /* ratio_combo */
     285  if (n)
     286    {
     287      int i;
     288      const int baseline = tr_torrentGetRatioMode (torrents[0]);
     289
     290      for (i=1; i<n; ++i)
     291        if (baseline != (int)tr_torrentGetRatioMode (torrents[i]))
     292          break;
     293
     294      if (i == n)
     295        {
     296          GtkWidget * w = di->ratio_combo;
     297          g_signal_handler_block (w, di->ratio_combo_tag);
     298          gtr_combo_box_set_active_enum (GTK_COMBO_BOX (w), baseline);
     299          gtr_widget_set_visible (di->ratio_spin, baseline == TR_RATIOLIMIT_SINGLE);
     300          g_signal_handler_unblock (w, di->ratio_combo_tag);
     301        }
     302    }
     303
     304  /* ratio_spin */
     305  if (n)
     306    {
     307      const double baseline = tr_torrentGetRatioLimit (torrents[0]);
     308      set_double_spin_if_different (di->ratio_spin, di->ratio_spin_tag, baseline);
     309    }
     310
     311  /* idle_combo */
     312  if (n)
     313    {
     314      int i;
     315      const int baseline = tr_torrentGetIdleMode (torrents[0]);
     316
     317      for (i=1; i<n; ++i)
     318        if (baseline != (int)tr_torrentGetIdleMode (torrents[i]))
     319          break;
     320
     321      if (i == n)
     322        {
     323          GtkWidget * w = di->idle_combo;
     324          g_signal_handler_block (w, di->idle_combo_tag);
     325          gtr_combo_box_set_active_enum (GTK_COMBO_BOX (w), baseline);
     326          gtr_widget_set_visible (di->idle_spin, baseline == TR_IDLELIMIT_SINGLE);
     327          g_signal_handler_unblock (w, di->idle_combo_tag);
     328        }
     329    }
     330
     331  /* idle_spin */
     332  if (n)
     333    {
     334      const int baseline = tr_torrentGetIdleLimit (torrents[0]);
     335      set_int_spin_if_different (di->idle_spin, di->idle_spin_tag, baseline);
     336    }
     337
     338  /* max_peers_spin */
     339  if (n)
     340    {
     341      const int baseline = tr_torrentGetPeerLimit (torrents[0]);
     342      set_int_spin_if_different (di->max_peers_spin, di->max_peers_spin_tag, baseline);
    314343    }
    315344}
     
    318347torrent_set_bool (struct DetailsImpl * di, const tr_quark key, gboolean value)
    319348{
    320     GSList *l;
    321     tr_variant top, *args, *ids;
    322 
    323     tr_variantInitDict (&top, 2);
    324     tr_variantDictAddStr (&top, TR_KEY_method, "torrent-set");
    325     args = tr_variantDictAddDict (&top, TR_KEY_arguments, 2);
    326     tr_variantDictAddBool (args, key, value);
    327     ids = tr_variantDictAddList (args, TR_KEY_ids, g_slist_length (di->ids));
    328     for (l=di->ids; l; l=l->next)
    329         tr_variantListAddInt (ids, GPOINTER_TO_INT (l->data));
    330 
    331     gtr_core_exec (di->core, &top);
    332     tr_variantFree (&top);
     349  GSList *l;
     350  tr_variant top, *args, *ids;
     351
     352  tr_variantInitDict (&top, 2);
     353  tr_variantDictAddStr (&top, TR_KEY_method, "torrent-set");
     354  args = tr_variantDictAddDict (&top, TR_KEY_arguments, 2);
     355  tr_variantDictAddBool (args, key, value);
     356  ids = tr_variantDictAddList (args, TR_KEY_ids, g_slist_length (di->ids));
     357  for (l=di->ids; l; l=l->next)
     358    tr_variantListAddInt (ids, GPOINTER_TO_INT (l->data));
     359
     360  gtr_core_exec (di->core, &top);
     361  tr_variantFree (&top);
    333362}
    334363
     
    336365torrent_set_int (struct DetailsImpl * di, const tr_quark key, int value)
    337366{
    338     GSList *l;
    339     tr_variant top, *args, *ids;
    340 
    341     tr_variantInitDict (&top, 2);
    342     tr_variantDictAddStr (&top, TR_KEY_method, "torrent-set");
    343     args = tr_variantDictAddDict (&top, TR_KEY_arguments, 2);
    344     tr_variantDictAddInt (args, key, value);
    345     ids = tr_variantDictAddList (args, TR_KEY_ids, g_slist_length (di->ids));
    346     for (l=di->ids; l; l=l->next)
    347         tr_variantListAddInt (ids, GPOINTER_TO_INT (l->data));
    348 
    349     gtr_core_exec (di->core, &top);
    350     tr_variantFree (&top);
     367  GSList *l;
     368  tr_variant top, *args, *ids;
     369
     370  tr_variantInitDict (&top, 2);
     371  tr_variantDictAddStr (&top, TR_KEY_method, "torrent-set");
     372  args = tr_variantDictAddDict (&top, TR_KEY_arguments, 2);
     373  tr_variantDictAddInt (args, key, value);
     374  ids = tr_variantDictAddList (args, TR_KEY_ids, g_slist_length (di->ids));
     375  for (l=di->ids; l; l=l->next)
     376    tr_variantListAddInt (ids, GPOINTER_TO_INT (l->data));
     377
     378  gtr_core_exec (di->core, &top);
     379  tr_variantFree (&top);
    351380}
    352381
     
    354383torrent_set_real (struct DetailsImpl * di, const tr_quark key, double value)
    355384{
    356     GSList *l;
    357     tr_variant top, *args, *ids;
    358 
    359     tr_variantInitDict (&top, 2);
    360     tr_variantDictAddStr (&top, TR_KEY_method, "torrent-set");
    361     args = tr_variantDictAddDict (&top, TR_KEY_arguments, 2);
    362     tr_variantDictAddReal (args, key, value);
    363     ids = tr_variantDictAddList (args, TR_KEY_ids, g_slist_length (di->ids));
    364     for (l=di->ids; l; l=l->next)
    365         tr_variantListAddInt (ids, GPOINTER_TO_INT (l->data));
    366 
    367     gtr_core_exec (di->core, &top);
    368     tr_variantFree (&top);
     385  GSList *l;
     386  tr_variant top, *args, *ids;
     387
     388  tr_variantInitDict (&top, 2);
     389  tr_variantDictAddStr (&top, TR_KEY_method, "torrent-set");
     390  args = tr_variantDictAddDict (&top, TR_KEY_arguments, 2);
     391  tr_variantDictAddReal (args, key, value);
     392  ids = tr_variantDictAddList (args, TR_KEY_ids, g_slist_length (di->ids));
     393  for (l=di->ids; l; l=l->next)
     394    tr_variantListAddInt (ids, GPOINTER_TO_INT (l->data));
     395
     396  gtr_core_exec (di->core, &top);
     397  tr_variantFree (&top);
    369398}
    370399
     
    372401up_speed_toggled_cb (GtkToggleButton * tb, gpointer d)
    373402{
    374     torrent_set_bool (d, TR_KEY_uploadLimited, gtk_toggle_button_get_active (tb));
     403  torrent_set_bool (d, TR_KEY_uploadLimited, gtk_toggle_button_get_active (tb));
    375404}
    376405
     
    378407down_speed_toggled_cb (GtkToggleButton *tb, gpointer d)
    379408{
    380     torrent_set_bool (d, TR_KEY_downloadLimited, gtk_toggle_button_get_active (tb));
     409  torrent_set_bool (d, TR_KEY_downloadLimited, gtk_toggle_button_get_active (tb));
    381410}
    382411
     
    384413global_speed_toggled_cb (GtkToggleButton * tb, gpointer d)
    385414{
    386     torrent_set_bool (d, TR_KEY_honorsSessionLimits, gtk_toggle_button_get_active (tb));
     415  torrent_set_bool (d, TR_KEY_honorsSessionLimits, gtk_toggle_button_get_active (tb));
    387416}
    388417
     
    390419up_speed_spun_cb (GtkSpinButton * s, struct DetailsImpl * di)
    391420{
    392     torrent_set_int (di, TR_KEY_uploadLimit, gtk_spin_button_get_value_as_int (s));
     421  torrent_set_int (di, TR_KEY_uploadLimit, gtk_spin_button_get_value_as_int (s));
    393422}
    394423
     
    396425down_speed_spun_cb (GtkSpinButton * s, struct DetailsImpl * di)
    397426{
    398     torrent_set_int (di, TR_KEY_downloadLimit, gtk_spin_button_get_value_as_int (s));
     427  torrent_set_int (di, TR_KEY_downloadLimit, gtk_spin_button_get_value_as_int (s));
    399428}
    400429
     
    402431idle_spun_cb (GtkSpinButton * s, struct DetailsImpl * di)
    403432{
    404     torrent_set_int (di, TR_KEY_seedIdleLimit, gtk_spin_button_get_value_as_int (s));
     433  torrent_set_int (di, TR_KEY_seedIdleLimit, gtk_spin_button_get_value_as_int (s));
    405434}
    406435
     
    408437ratio_spun_cb (GtkSpinButton * s, struct DetailsImpl * di)
    409438{
    410     torrent_set_real (di, TR_KEY_seedRatioLimit, gtk_spin_button_get_value (s));
     439  torrent_set_real (di, TR_KEY_seedRatioLimit, gtk_spin_button_get_value (s));
    411440}
    412441
     
    414443max_peers_spun_cb (GtkSpinButton * s, struct DetailsImpl * di)
    415444{
    416     torrent_set_int (di, TR_KEY_peer_limit, gtk_spin_button_get_value (s));
     445  torrent_set_int (di, TR_KEY_peer_limit, gtk_spin_button_get_value (s));
    417446}
    418447
     
    420449onPriorityChanged (GtkComboBox * combo_box, struct DetailsImpl * di)
    421450{
    422     const tr_priority_t priority = gtr_priority_combo_get_value (combo_box);
    423     torrent_set_int (di, TR_KEY_bandwidthPriority, priority);
     451  const tr_priority_t priority = gtr_priority_combo_get_value (combo_box);
     452  torrent_set_int (di, TR_KEY_bandwidthPriority, priority);
    424453}
    425454
     
    427456new_priority_combo (struct DetailsImpl * di)
    428457{
    429     GtkWidget * w = gtr_priority_combo_new ();
    430     di->bandwidth_combo_tag = g_signal_connect (w, "changed", G_CALLBACK (onPriorityChanged), di);
    431     return w;
     458  GtkWidget * w = gtr_priority_combo_new ();
     459  di->bandwidth_combo_tag = g_signal_connect (w, "changed", G_CALLBACK (onPriorityChanged), di);
     460  return w;
    432461}
    433462
     
    437466onComboEnumChanged (GtkComboBox * combo_box, struct DetailsImpl * di)
    438467{
    439     const tr_quark key = GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (combo_box), ARG_KEY));
    440     torrent_set_int (di, key, gtr_combo_box_get_active_enum (combo_box));
    441     refresh (di);
     468  const tr_quark key = GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (combo_box), ARG_KEY));
     469  torrent_set_int (di, key, gtr_combo_box_get_active_enum (combo_box));
     470  refresh (di);
    442471}
    443472
     
    445474ratio_combo_new (void)
    446475{
    447     GtkWidget * w = gtr_combo_box_new_enum (
    448         _("Use global settings"),       TR_RATIOLIMIT_GLOBAL,
    449         _("Seed regardless of ratio"),  TR_RATIOLIMIT_UNLIMITED,
    450         _("Stop seeding at ratio:"),    TR_RATIOLIMIT_SINGLE,
    451         NULL);
    452     g_object_set_qdata (G_OBJECT (w), ARG_KEY, GINT_TO_POINTER(TR_KEY_seedRatioMode));
    453     return w;
     476  GtkWidget * w = gtr_combo_box_new_enum (
     477    _("Use global settings"),       TR_RATIOLIMIT_GLOBAL,
     478    _("Seed regardless of ratio"),  TR_RATIOLIMIT_UNLIMITED,
     479    _("Stop seeding at ratio:"),    TR_RATIOLIMIT_SINGLE,
     480    NULL);
     481  g_object_set_qdata (G_OBJECT (w), ARG_KEY, GINT_TO_POINTER(TR_KEY_seedRatioMode));
     482  return w;
    454483}
    455484
     
    457486idle_combo_new (void)
    458487{
    459     GtkWidget * w = gtr_combo_box_new_enum (
    460         _("Use global settings"),                 TR_IDLELIMIT_GLOBAL,
    461         _("Seed regardless of activity"),         TR_IDLELIMIT_UNLIMITED,
    462         _("Stop seeding if idle for N minutes:"), TR_IDLELIMIT_SINGLE,
    463         NULL);
    464     g_object_set_qdata (G_OBJECT (w), ARG_KEY, GINT_TO_POINTER(TR_KEY_seedIdleMode));
    465     return w;
     488  GtkWidget * w = gtr_combo_box_new_enum (
     489    _("Use global settings"),                 TR_IDLELIMIT_GLOBAL,
     490    _("Seed regardless of activity"),         TR_IDLELIMIT_UNLIMITED,
     491    _("Stop seeding if idle for N minutes:"), TR_IDLELIMIT_SINGLE,
     492    NULL);
     493  g_object_set_qdata (G_OBJECT (w), ARG_KEY, GINT_TO_POINTER(TR_KEY_seedIdleMode));
     494  return w;
    466495}
    467496
     
    469498options_page_new (struct DetailsImpl * d)
    470499{
    471     guint row;
    472     gulong tag;
    473     char buf[128];
    474     GtkWidget *t, *w, *tb, *h;
    475 
    476     row = 0;
    477     t = hig_workarea_create ();
    478     hig_workarea_add_section_title (t, &row, _("Speed"));
    479 
    480     tb = hig_workarea_add_wide_checkbutton (t, &row, _("Honor global _limits"), 0);
    481     d->honor_limits_check = tb;
    482     tag = g_signal_connect (tb, "toggled", G_CALLBACK (global_speed_toggled_cb), d);
    483     d->honor_limits_check_tag = tag;
    484 
    485     g_snprintf (buf, sizeof (buf), _("Limit _download speed (%s):"), _ (speed_K_str));
    486     tb = gtk_check_button_new_with_mnemonic (buf);
    487     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tb), FALSE);
    488     d->down_limited_check = tb;
    489     tag = g_signal_connect (tb, "toggled", G_CALLBACK (down_speed_toggled_cb), d);
    490     d->down_limited_check_tag = tag;
    491 
    492     w = gtk_spin_button_new_with_range (0, INT_MAX, 5);
    493     tag = g_signal_connect (w, "value-changed", G_CALLBACK (down_speed_spun_cb), d);
    494     d->down_limit_spin_tag = tag;
    495     hig_workarea_add_row_w (t, &row, tb, w, NULL);
    496     d->down_limit_spin = w;
    497 
    498     g_snprintf (buf, sizeof (buf), _("Limit _upload speed (%s):"), _ (speed_K_str));
    499     tb = gtk_check_button_new_with_mnemonic (buf);
    500     d->up_limited_check = tb;
    501     tag = g_signal_connect (tb, "toggled", G_CALLBACK (up_speed_toggled_cb), d);
    502     d->up_limited_check_tag = tag;
    503 
    504     w = gtk_spin_button_new_with_range (0, INT_MAX, 5);
    505     tag = g_signal_connect (w, "value-changed", G_CALLBACK (up_speed_spun_cb), d);
    506     d->up_limit_spin_tag = tag;
    507     hig_workarea_add_row_w (t, &row, tb, w, NULL);
    508     d->up_limit_sping = w;
    509 
    510     w = new_priority_combo (d);
    511     hig_workarea_add_row (t, &row, _("Torrent _priority:"), w, NULL);
    512     d->bandwidth_combo = w;
    513 
    514     hig_workarea_add_section_divider (t, &row);
    515     hig_workarea_add_section_title (t, &row, _("Seeding Limits"));
    516 
    517     h = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GUI_PAD);
    518     w = d->ratio_combo = ratio_combo_new ();
    519     d->ratio_combo_tag = g_signal_connect (w, "changed", G_CALLBACK (onComboEnumChanged), d);
    520     gtk_box_pack_start (GTK_BOX (h), w, TRUE, TRUE, 0);
    521     w = d->ratio_spin = gtk_spin_button_new_with_range (0, 1000, .05);
    522     gtk_entry_set_width_chars (GTK_ENTRY (w), 7);
    523     d->ratio_spin_tag = g_signal_connect (w, "value-changed", G_CALLBACK (ratio_spun_cb), d);
    524     gtk_box_pack_start (GTK_BOX (h), w, FALSE, FALSE, 0);
    525     hig_workarea_add_row (t, &row, _("_Ratio:"), h, NULL);
    526 
    527     h = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GUI_PAD);
    528     w = d->idle_combo = idle_combo_new ();
    529     d->idle_combo_tag = g_signal_connect (w, "changed", G_CALLBACK (onComboEnumChanged), d);
    530     gtk_box_pack_start (GTK_BOX (h), w, TRUE, TRUE, 0);
    531     w = d->idle_spin = gtk_spin_button_new_with_range (1, INT_MAX, 5);
    532     d->idle_spin_tag = g_signal_connect (w, "value-changed", G_CALLBACK (idle_spun_cb), d);
    533     gtk_box_pack_start (GTK_BOX (h), w, FALSE, FALSE, 0);
    534     hig_workarea_add_row (t, &row, _("_Idle:"), h, NULL);
    535 
    536     hig_workarea_add_section_divider (t, &row);
    537     hig_workarea_add_section_title (t, &row, _("Peer Connections"));
    538 
    539     w = gtk_spin_button_new_with_range (1, 3000, 5);
    540     hig_workarea_add_row (t, &row, _("_Maximum peers:"), w, w);
    541     tag = g_signal_connect (w, "value-changed", G_CALLBACK (max_peers_spun_cb), d);
    542     d->max_peers_spin = w;
    543     d->max_peers_spin_tag = tag;
    544 
    545     return t;
     500  guint row;
     501  gulong tag;
     502  char buf[128];
     503  GtkWidget *t, *w, *tb, *h;
     504
     505  row = 0;
     506  t = hig_workarea_create ();
     507  hig_workarea_add_section_title (t, &row, _("Speed"));
     508
     509  tb = hig_workarea_add_wide_checkbutton (t, &row, _("Honor global _limits"), 0);
     510  d->honor_limits_check = tb;
     511  tag = g_signal_connect (tb, "toggled", G_CALLBACK (global_speed_toggled_cb), d);
     512  d->honor_limits_check_tag = tag;
     513
     514  g_snprintf (buf, sizeof (buf), _("Limit _download speed (%s):"), _ (speed_K_str));
     515  tb = gtk_check_button_new_with_mnemonic (buf);
     516  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tb), FALSE);
     517  d->down_limited_check = tb;
     518  tag = g_signal_connect (tb, "toggled", G_CALLBACK (down_speed_toggled_cb), d);
     519  d->down_limited_check_tag = tag;
     520
     521  w = gtk_spin_button_new_with_range (0, INT_MAX, 5);
     522  tag = g_signal_connect (w, "value-changed", G_CALLBACK (down_speed_spun_cb), d);
     523  d->down_limit_spin_tag = tag;
     524  hig_workarea_add_row_w (t, &row, tb, w, NULL);
     525  d->down_limit_spin = w;
     526
     527  g_snprintf (buf, sizeof (buf), _("Limit _upload speed (%s):"), _ (speed_K_str));
     528  tb = gtk_check_button_new_with_mnemonic (buf);
     529  d->up_limited_check = tb;
     530  tag = g_signal_connect (tb, "toggled", G_CALLBACK (up_speed_toggled_cb), d);
     531  d->up_limited_check_tag = tag;
     532
     533  w = gtk_spin_button_new_with_range (0, INT_MAX, 5);
     534  tag = g_signal_connect (w, "value-changed", G_CALLBACK (up_speed_spun_cb), d);
     535  d->up_limit_spin_tag = tag;
     536  hig_workarea_add_row_w (t, &row, tb, w, NULL);
     537  d->up_limit_sping = w;
     538
     539  w = new_priority_combo (d);
     540  hig_workarea_add_row (t, &row, _("Torrent _priority:"), w, NULL);
     541  d->bandwidth_combo = w;
     542
     543  hig_workarea_add_section_divider (t, &row);
     544  hig_workarea_add_section_title (t, &row, _("Seeding Limits"));
     545
     546  h = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GUI_PAD);
     547  w = d->ratio_combo = ratio_combo_new ();
     548  d->ratio_combo_tag = g_signal_connect (w, "changed", G_CALLBACK (onComboEnumChanged), d);
     549  gtk_box_pack_start (GTK_BOX (h), w, TRUE, TRUE, 0);
     550  w = d->ratio_spin = gtk_spin_button_new_with_range (0, 1000, .05);
     551  gtk_entry_set_width_chars (GTK_ENTRY (w), 7);
     552  d->ratio_spin_tag = g_signal_connect (w, "value-changed", G_CALLBACK (ratio_spun_cb), d);
     553  gtk_box_pack_start (GTK_BOX (h), w, FALSE, FALSE, 0);
     554  hig_workarea_add_row (t, &row, _("_Ratio:"), h, NULL);
     555
     556  h = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GUI_PAD);
     557  w = d->idle_combo = idle_combo_new ();
     558  d->idle_combo_tag = g_signal_connect (w, "changed", G_CALLBACK (onComboEnumChanged), d);
     559  gtk_box_pack_start (GTK_BOX (h), w, TRUE, TRUE, 0);
     560  w = d->idle_spin = gtk_spin_button_new_with_range (1, INT_MAX, 5);
     561  d->idle_spin_tag = g_signal_connect (w, "value-changed", G_CALLBACK (idle_spun_cb), d);
     562  gtk_box_pack_start (GTK_BOX (h), w, FALSE, FALSE, 0);
     563  hig_workarea_add_row (t, &row, _("_Idle:"), h, NULL);
     564
     565  hig_workarea_add_section_divider (t, &row);
     566  hig_workarea_add_section_title (t, &row, _("Peer Connections"));
     567
     568  w = gtk_spin_button_new_with_range (1, 3000, 5);
     569  hig_workarea_add_row (t, &row, _("_Maximum peers:"), w, w);
     570  tag = g_signal_connect (w, "value-changed", G_CALLBACK (max_peers_spun_cb), d);
     571  d->max_peers_spin = w;
     572  d->max_peers_spin_tag = tag;
     573
     574  return t;
    546575}
    547576
     
    555584activityString (int activity, bool finished)
    556585{
    557     switch (activity)
    558     {
    559         case TR_STATUS_CHECK_WAIT:    return  _("Queued for verification");
    560         case TR_STATUS_CHECK:         return  _("Verifying local data");
    561         case TR_STATUS_DOWNLOAD_WAIT: return  _("Queued for download");
    562         case TR_STATUS_DOWNLOAD:      return C_("Verb", "Downloading");
    563         case TR_STATUS_SEED_WAIT:     return  _("Queued for seeding");
    564         case TR_STATUS_SEED:          return C_("Verb", "Seeding");
    565         case TR_STATUS_STOPPED:       return  finished ? _("Finished") : _("Paused");
    566     }
    567 
    568     return "";
     586  switch (activity)
     587    {
     588      case TR_STATUS_CHECK_WAIT:    return  _("Queued for verification");
     589      case TR_STATUS_CHECK:         return  _("Verifying local data");
     590      case TR_STATUS_DOWNLOAD_WAIT: return  _("Queued for download");
     591      case TR_STATUS_DOWNLOAD:      return C_("Verb", "Downloading");
     592      case TR_STATUS_SEED_WAIT:     return  _("Queued for seeding");
     593      case TR_STATUS_SEED:          return C_("Verb", "Seeding");
     594      case TR_STATUS_STOPPED:       return  finished ? _("Finished") : _("Paused");
     595    }
     596
     597  return "";
    569598}
    570599
     
    574603gtr_text_buffer_set_text (GtkTextBuffer * b, const char * str)
    575604{
    576     char * old_str;
    577     GtkTextIter start, end;
    578 
    579     if (str == NULL)
    580         str = "";
    581 
    582     gtk_text_buffer_get_bounds (b, &start, &end);
    583     old_str = gtk_text_buffer_get_text (b, &start, &end, FALSE);
    584 
    585     if ((old_str == NULL) || strcmp (old_str, str))
    586         gtk_text_buffer_set_text (b, str, -1);
    587 
    588     g_free (old_str);
     605  char * old_str;
     606  GtkTextIter start, end;
     607
     608  if (str == NULL)
     609    str = "";
     610
     611  gtk_text_buffer_get_bounds (b, &start, &end);
     612  old_str = gtk_text_buffer_get_text (b, &start, &end, FALSE);
     613
     614  if ((old_str == NULL) || strcmp (old_str, str))
     615    gtk_text_buffer_set_text (b, str, -1);
     616
     617  g_free (old_str);
    589618}
    590619
     
    592621get_short_date_string (time_t t)
    593622{
    594     char buf[64];
    595     struct tm tm;
    596 
    597     if (!t)
    598         return g_strdup (_("N/A"));
    599 
    600     tr_localtime_r (&t, &tm);
    601     strftime (buf, sizeof (buf), "%d %b %Y", &tm);
    602     return g_locale_to_utf8 (buf, -1, NULL, NULL, NULL);
     623  char buf[64];
     624  struct tm tm;
     625
     626  if (!t)
     627    return g_strdup (_("N/A"));
     628
     629  tr_localtime_r (&t, &tm);
     630  strftime (buf, sizeof (buf), "%d %b %Y", &tm);
     631  return g_locale_to_utf8 (buf, -1, NULL, NULL, NULL);
    603632};
    604633
     
    606635refreshInfo (struct DetailsImpl * di, tr_torrent ** torrents, int n)
    607636{
    608     int i;
    609     const char * str;
    610     const char * mixed = _("Mixed");
    611     const char * no_torrent = _("No Torrents Selected");
    612     const char * stateString;
    613     char buf[512];
    614     uint64_t sizeWhenDone = 0;
    615     const tr_stat ** stats = g_new (const tr_stat*, n);
    616     const tr_info ** infos = g_new (const tr_info*, n);
    617     for (i=0; i<n; ++i) {
    618         stats[i] = tr_torrentStatCached (torrents[i]);
    619         infos[i] = tr_torrentInfo (torrents[i]);
    620     }
    621 
    622     /* privacy_lb */
    623     if (n<=0)
    624         str = no_torrent;
    625     else {
    626         const bool baseline = infos[0]->isPrivate;
    627         for (i=1; i<n; ++i)
    628             if (baseline != infos[i]->isPrivate)
    629                 break;
    630         if (i!=n)
    631             str = mixed;
    632         else if (baseline)
    633             str = _("Private to this tracker -- DHT and PEX disabled");
     637  int i;
     638  const char * str;
     639  const char * mixed = _("Mixed");
     640  const char * no_torrent = _("No Torrents Selected");
     641  const char * stateString;
     642  char buf[512];
     643  uint64_t sizeWhenDone = 0;
     644  const tr_stat ** stats = g_new (const tr_stat*, n);
     645  const tr_info ** infos = g_new (const tr_info*, n);
     646
     647  for (i=0; i<n; ++i)
     648    {
     649      stats[i] = tr_torrentStatCached (torrents[i]);
     650      infos[i] = tr_torrentInfo (torrents[i]);
     651    }
     652
     653  /* privacy_lb */
     654  if (n<=0)
     655    {
     656      str = no_torrent;
     657    }
     658  else
     659    {
     660      const bool baseline = infos[0]->isPrivate;
     661
     662      for (i=1; i<n; ++i)
     663        if (baseline != infos[i]->isPrivate)
     664          break;
     665
     666      if (i!=n)
     667        str = mixed;
     668      else if (baseline)
     669        str = _("Private to this tracker -- DHT and PEX disabled");
     670      else
     671        str = _("Public torrent");
     672    }
     673  gtr_label_set_text (GTK_LABEL (di->privacy_lb), str);
     674
     675
     676  /* origin_lb */
     677  if (n<=0)
     678    {
     679      str = no_torrent;
     680    }
     681  else
     682    {
     683      const char * creator = infos[0]->creator ? infos[0]->creator : "";
     684      const time_t date = infos[0]->dateCreated;
     685      char * datestr = get_short_date_string (date);
     686      gboolean mixed_creator = FALSE;
     687      gboolean mixed_date = FALSE;
     688
     689      for (i=1; i<n; ++i)
     690        {
     691          mixed_creator |= strcmp (creator, infos[i]->creator ? infos[i]->creator : "");
     692          mixed_date |= (date != infos[i]->dateCreated);
     693        }
     694
     695      if (mixed_date && mixed_creator)
     696        {
     697          str = mixed;
     698        }
     699      else
     700        {
     701          if (mixed_date)
     702            g_snprintf (buf, sizeof (buf), _("Created by %1$s"), creator);
     703          else if (mixed_creator || !*creator)
     704            g_snprintf (buf, sizeof (buf), _("Created on %1$s"), datestr);
     705          else
     706            g_snprintf (buf, sizeof (buf), _("Created by %1$s on %2$s"), creator, datestr);
     707          str = buf;
     708        }
     709
     710      g_free (datestr);
     711    }
     712  gtr_label_set_text (GTK_LABEL (di->origin_lb), str);
     713
     714
     715  /* comment_buffer */
     716  if (n<=0)
     717    {
     718      str = "";
     719    }
     720  else
     721    {
     722      const char * baseline = infos[0]->comment ? infos[0]->comment : "";
     723
     724      for (i=1; i<n; ++i)
     725        if (strcmp (baseline, infos[i]->comment ? infos[i]->comment : ""))
     726          break;
     727
     728      if (i==n)
     729        str = baseline;
     730      else
     731        str = mixed;
     732    }
     733  gtr_text_buffer_set_text (di->comment_buffer, str);
     734
     735  /* destination_lb */
     736  if (n<=0)
     737    {
     738      str = no_torrent;
     739    }
     740  else
     741    {
     742      const char * baseline = tr_torrentGetDownloadDir (torrents[0]);
     743
     744      for (i=1; i<n; ++i)
     745        if (strcmp (baseline, tr_torrentGetDownloadDir (torrents[i])))
     746          break;
     747
     748      if (i==n)
     749        str = baseline;
     750      else
     751        str = mixed;
     752    }
     753  gtr_label_set_text (GTK_LABEL (di->destination_lb), str);
     754
     755  /* state_lb */
     756  if (n<=0)
     757    {
     758      str = no_torrent;
     759    }
     760  else
     761    {
     762      const tr_torrent_activity activity = stats[0]->activity;
     763      bool allFinished = stats[0]->finished;
     764
     765      for (i=1; i<n; ++i)
     766        {
     767          if (activity != stats[i]->activity)
     768            break;
     769          if (!stats[i]->finished)
     770            allFinished = FALSE;
     771        }
     772
     773      str = i<n ? mixed : activityString (activity, allFinished);
     774    }
     775  stateString = str;
     776  gtr_label_set_text (GTK_LABEL (di->state_lb), str);
     777
     778
     779  /* date started */
     780  if (n<=0)
     781    {
     782      str = no_torrent;
     783    }
     784  else
     785    {
     786      const time_t baseline = stats[0]->startDate;
     787
     788      for (i=1; i<n; ++i)
     789        if (baseline != stats[i]->startDate)
     790          break;
     791
     792      if (i != n)
     793        str = mixed;
     794      else if ((baseline<=0) || (stats[0]->activity == TR_STATUS_STOPPED))
     795        str = stateString;
     796      else
     797        str = tr_strltime (buf, time (NULL)-baseline, sizeof (buf));
     798    }
     799  gtr_label_set_text (GTK_LABEL (di->date_started_lb), str);
     800
     801
     802  /* eta */
     803  if (n<=0)
     804    {
     805      str = no_torrent;
     806    }
     807  else
     808    {
     809      const int baseline = stats[0]->eta;
     810
     811      for (i=1; i<n; ++i)
     812        if (baseline != stats[i]->eta)
     813          break;
     814
     815      if (i!=n)
     816        str = mixed;
     817      else if (baseline < 0)
     818        str = _("Unknown");
     819      else
     820        str = tr_strltime (buf, baseline, sizeof (buf));
     821    }
     822  gtr_label_set_text (GTK_LABEL (di->eta_lb), str);
     823
     824
     825  /* size_lb */
     826  {
     827    char sizebuf[128];
     828    uint64_t size = 0;
     829    int pieces = 0;
     830    int32_t pieceSize = 0;
     831
     832    for (i=0; i<n; ++i)
     833      {
     834        size += infos[i]->totalSize;
     835        pieces += infos[i]->pieceCount;
     836
     837        if (!pieceSize)
     838          pieceSize = infos[i]->pieceSize;
     839        else if (pieceSize != (int)infos[i]->pieceSize)
     840          pieceSize = -1;
     841      }
     842
     843    tr_strlsize (sizebuf, size, sizeof (sizebuf));
     844    if (!size)
     845      str = "";
     846    else if (pieceSize >= 0)
     847      {
     848        char piecebuf[128];
     849        tr_formatter_mem_B (piecebuf, pieceSize, sizeof (piecebuf));
     850        g_snprintf (buf, sizeof (buf),
     851                    ngettext ("%1$s (%2$'d piece @ %3$s)",
     852                              "%1$s (%2$'d pieces @ %3$s)", pieces),
     853                    sizebuf, pieces, piecebuf);
     854        str = buf;
     855      }
     856    else
     857      {
     858        g_snprintf (buf, sizeof (buf),
     859                    ngettext ("%1$s (%2$'d piece)",
     860                              "%1$s (%2$'d pieces)", pieces),
     861                    sizebuf, pieces);
     862        str = buf;
     863      }
     864    gtr_label_set_text (GTK_LABEL (di->size_lb), str);
     865  }
     866
     867
     868  /* have_lb */
     869  if (n <= 0)
     870    {
     871      str = no_torrent;
     872    }
     873  else
     874    {
     875      uint64_t leftUntilDone = 0;
     876      uint64_t haveUnchecked = 0;
     877      uint64_t haveValid = 0;
     878      uint64_t available = 0;
     879
     880      for (i=0; i<n; ++i)
     881        {
     882          const tr_stat * st = stats[i];
     883          haveUnchecked += st->haveUnchecked;
     884          haveValid += st->haveValid;
     885          sizeWhenDone += st->sizeWhenDone;
     886          leftUntilDone += st->leftUntilDone;
     887          available += st->sizeWhenDone - st->leftUntilDone + st->desiredAvailable;
     888        }
     889
     890      {
     891        char buf2[32], unver[64], total[64], avail[32];
     892        const double d = sizeWhenDone ? (100.0 * available) / sizeWhenDone : 0;
     893        const double ratio = 100.0 * (sizeWhenDone ? (haveValid + haveUnchecked) / (double)sizeWhenDone : 1);
     894
     895        tr_strlpercent (avail, d, sizeof (avail));
     896        tr_strlpercent (buf2, ratio, sizeof (buf2));
     897        tr_strlsize (total, haveUnchecked + haveValid, sizeof (total));
     898        tr_strlsize (unver, haveUnchecked,             sizeof (unver));
     899
     900        if (!haveUnchecked && !leftUntilDone)
     901          g_snprintf (buf, sizeof (buf), _("%1$s (%2$s%%)"), total, buf2);
     902        else if (!haveUnchecked)
     903          g_snprintf (buf, sizeof (buf), _("%1$s (%2$s%% of %3$s%% Available)"), total, buf2, avail);
    634904        else
    635             str = _("Public torrent");
    636     }
    637     gtr_label_set_text (GTK_LABEL (di->privacy_lb), str);
    638 
    639 
    640     /* origin_lb */
    641     if (n<=0)
    642         str = no_torrent;
    643     else {
    644         const char * creator = infos[0]->creator ? infos[0]->creator : "";
    645         const time_t date = infos[0]->dateCreated;
    646         char * datestr = get_short_date_string (date);
    647         gboolean mixed_creator = FALSE;
    648         gboolean mixed_date = FALSE;
    649 
    650         for (i=1; i<n; ++i) {
    651             mixed_creator |= strcmp (creator, infos[i]->creator ? infos[i]->creator : "");
    652             mixed_date |= (date != infos[i]->dateCreated);
    653         }
    654         if (mixed_date && mixed_creator)
    655             str = mixed;
    656         else {
    657             if (mixed_date)
    658                 g_snprintf (buf, sizeof (buf), _("Created by %1$s"), creator);
    659             else if (mixed_creator || !*creator)
    660                 g_snprintf (buf, sizeof (buf), _("Created on %1$s"), datestr);
    661             else
    662                 g_snprintf (buf, sizeof (buf), _("Created by %1$s on %2$s"), creator, datestr);
    663             str = buf;
    664         }
    665 
    666         g_free (datestr);
    667     }
    668     gtr_label_set_text (GTK_LABEL (di->origin_lb), str);
    669 
    670 
    671     /* comment_buffer */
    672     if (n<=0)
    673         str = "";
    674     else {
    675         const char * baseline = infos[0]->comment ? infos[0]->comment : "";
    676         for (i=1; i<n; ++i)
    677             if (strcmp (baseline, infos[i]->comment ? infos[i]->comment : ""))
    678                 break;
    679         if (i==n)
    680             str = baseline;
    681         else
    682             str = mixed;
    683     }
    684     gtr_text_buffer_set_text (di->comment_buffer, str);
    685 
    686     /* destination_lb */
    687     if (n<=0)
    688         str = no_torrent;
    689     else {
    690         const char * baseline = tr_torrentGetDownloadDir (torrents[0]);
    691         for (i=1; i<n; ++i)
    692             if (strcmp (baseline, tr_torrentGetDownloadDir (torrents[i])))
    693                 break;
    694         if (i==n)
    695             str = baseline;
    696         else
    697             str = mixed;
    698     }
    699     gtr_label_set_text (GTK_LABEL (di->destination_lb), str);
    700 
    701     /* state_lb */
    702     if (n<=0)
    703         str = no_torrent;
    704     else {
    705         const tr_torrent_activity activity = stats[0]->activity;
    706         bool allFinished = stats[0]->finished;
    707         for (i=1; i<n; ++i) {
    708             if (activity != stats[i]->activity)
    709                 break;
    710             if (!stats[i]->finished)
    711                 allFinished = FALSE;
    712         }
    713         str = i<n ? mixed : activityString (activity, allFinished);
    714     }
    715     stateString = str;
    716     gtr_label_set_text (GTK_LABEL (di->state_lb), str);
    717 
    718 
    719     /* date started */
    720     if (n<=0)
    721         str = no_torrent;
    722     else {
    723         const time_t baseline = stats[0]->startDate;
    724         for (i=1; i<n; ++i)
    725             if (baseline != stats[i]->startDate)
    726                 break;
    727         if (i != n)
    728             str = mixed;
    729         else if ((baseline<=0) || (stats[0]->activity == TR_STATUS_STOPPED))
    730             str = stateString;
    731         else
    732             str = tr_strltime (buf, time (NULL)-baseline, sizeof (buf));
    733     }
    734     gtr_label_set_text (GTK_LABEL (di->date_started_lb), str);
    735 
    736 
    737     /* eta */
    738     if (n<=0)
    739         str = no_torrent;
    740     else {
    741         const int baseline = stats[0]->eta;
    742         for (i=1; i<n; ++i)
    743             if (baseline != stats[i]->eta)
    744                 break;
    745         if (i!=n)
    746             str = mixed;
    747         else if (baseline < 0)
    748             str = _("Unknown");
    749         else
    750             str = tr_strltime (buf, baseline, sizeof (buf));
    751     }
    752     gtr_label_set_text (GTK_LABEL (di->eta_lb), str);
    753 
    754 
    755     /* size_lb */
    756     {
    757         char sizebuf[128];
    758         uint64_t size = 0;
    759         int pieces = 0;
    760         int32_t pieceSize = 0;
    761         for (i=0; i<n; ++i) {
    762             size += infos[i]->totalSize;
    763             pieces += infos[i]->pieceCount;
    764             if (!pieceSize)
    765                 pieceSize = infos[i]->pieceSize;
    766             else if (pieceSize != (int)infos[i]->pieceSize)
    767                 pieceSize = -1;
    768         }
    769         tr_strlsize (sizebuf, size, sizeof (sizebuf));
    770         if (!size)
    771             str = "";
    772         else if (pieceSize >= 0) {
    773             char piecebuf[128];
    774             tr_formatter_mem_B (piecebuf, pieceSize, sizeof (piecebuf));
    775             g_snprintf (buf, sizeof (buf),
    776                         ngettext ("%1$s (%2$'d piece @ %3$s)",
    777                                       "%1$s (%2$'d pieces @ %3$s)", pieces),
    778                         sizebuf, pieces, piecebuf);
    779             str = buf;
    780         } else {
    781             g_snprintf (buf, sizeof (buf),
    782                         ngettext ("%1$s (%2$'d piece)",
    783                                       "%1$s (%2$'d pieces)", pieces),
    784                         sizebuf, pieces);
    785             str = buf;
    786         }
    787         gtr_label_set_text (GTK_LABEL (di->size_lb), str);
    788     }
    789 
    790 
    791     /* have_lb */
    792     if (n <= 0)
    793         str = no_torrent;
    794     else {
    795         uint64_t leftUntilDone = 0;
    796         uint64_t haveUnchecked = 0;
    797         uint64_t haveValid = 0;
    798         uint64_t available = 0;
    799         for (i=0; i<n; ++i) {
    800             const tr_stat * st = stats[i];
    801             haveUnchecked += st->haveUnchecked;
    802             haveValid += st->haveValid;
    803             sizeWhenDone += st->sizeWhenDone;
    804             leftUntilDone += st->leftUntilDone;
    805             available += st->sizeWhenDone - st->leftUntilDone + st->desiredAvailable;
    806         }
    807         {
    808             char buf2[32], unver[64], total[64], avail[32];
    809             const double d = sizeWhenDone ? (100.0 * available) / sizeWhenDone : 0;
    810             const double ratio = 100.0 * (sizeWhenDone ? (haveValid + haveUnchecked) / (double)sizeWhenDone : 1);
    811             tr_strlpercent (avail, d, sizeof (avail));
    812             tr_strlpercent (buf2, ratio, sizeof (buf2));
    813             tr_strlsize (total, haveUnchecked + haveValid, sizeof (total));
    814             tr_strlsize (unver, haveUnchecked,             sizeof (unver));
    815             if (!haveUnchecked && !leftUntilDone)
    816                 g_snprintf (buf, sizeof (buf), _("%1$s (%2$s%%)"), total, buf2);
    817             else if (!haveUnchecked)
    818                 g_snprintf (buf, sizeof (buf), _("%1$s (%2$s%% of %3$s%% Available)"), total, buf2, avail);
    819             else
    820                 g_snprintf (buf, sizeof (buf), _("%1$s (%2$s%% of %3$s%% Available); %4$s Unverified"), total, buf2, avail, unver);
    821             str = buf;
    822         }
    823     }
    824     gtr_label_set_text (GTK_LABEL (di->have_lb), str);
    825 
    826     /* dl_lb */
    827     if (n <= 0)
    828         str = no_torrent;
    829     else {
    830         char dbuf[64], fbuf[64];
    831         uint64_t d=0, f=0;
    832         for (i=0; i<n; ++i) {
    833             d += stats[i]->downloadedEver;
    834             f += stats[i]->corruptEver;
    835         }
    836         tr_strlsize (dbuf, d, sizeof (dbuf));
    837         tr_strlsize (fbuf, f, sizeof (fbuf));
    838         if (f)
    839             g_snprintf (buf, sizeof (buf), _("%1$s (+%2$s corrupt)"), dbuf, fbuf);
    840         else
    841             tr_strlcpy (buf, dbuf, sizeof (buf));
     905          g_snprintf (buf, sizeof (buf), _("%1$s (%2$s%% of %3$s%% Available); %4$s Unverified"), total, buf2, avail, unver);
     906
    842907        str = buf;
    843     }
    844     gtr_label_set_text (GTK_LABEL (di->dl_lb), str);
    845 
    846 
    847     /* ul_lb */
    848     if (n <= 0)
    849         str = no_torrent;
    850     else {
    851         char upstr[64];
    852         char ratiostr[64];
    853         uint64_t up = 0;
    854         uint64_t down = 0;
    855         for (i=0; i<n; ++i) {
    856             up += stats[i]->uploadedEver;
    857             down += stats[i]->downloadedEver;
    858         }
    859         tr_strlsize (upstr, up, sizeof (upstr));
    860         tr_strlratio (ratiostr, tr_getRatio (up, down), sizeof (ratiostr));
    861         g_snprintf (buf, sizeof (buf), _("%s (Ratio: %s)"), upstr, ratiostr);
    862         str = buf;
    863     }
    864     gtr_label_set_text (GTK_LABEL (di->ul_lb), str);
    865 
    866     /* hash_lb */
    867     if (n <= 0)
    868         str = no_torrent;
    869     else if (n==1)
    870         str = infos[0]->hashString;
    871     else
     908      }
     909    }
     910  gtr_label_set_text (GTK_LABEL (di->have_lb), str);
     911
     912  /* dl_lb */
     913  if (n <= 0)
     914    {
     915      str = no_torrent;
     916    }
     917  else
     918    {
     919      char dbuf[64], fbuf[64];
     920      uint64_t d=0, f=0;
     921
     922      for (i=0; i<n; ++i)
     923        {
     924          d += stats[i]->downloadedEver;
     925          f += stats[i]->corruptEver;
     926        }
     927
     928      tr_strlsize (dbuf, d, sizeof (dbuf));
     929      tr_strlsize (fbuf, f, sizeof (fbuf));
     930
     931      if (f)
     932        g_snprintf (buf, sizeof (buf), _("%1$s (+%2$s corrupt)"), dbuf, fbuf);
     933      else
     934        tr_strlcpy (buf, dbuf, sizeof (buf));
     935      str = buf;
     936    }
     937  gtr_label_set_text (GTK_LABEL (di->dl_lb), str);
     938
     939
     940  /* ul_lb */
     941  if (n <= 0)
     942    {
     943      str = no_torrent;
     944    }
     945  else
     946    {
     947      char upstr[64];
     948      char ratiostr[64];
     949      uint64_t up = 0;
     950      uint64_t down = 0;
     951
     952      for (i=0; i<n; ++i)
     953        {
     954          up += stats[i]->uploadedEver;
     955          down += stats[i]->downloadedEver;
     956        }
     957
     958      tr_strlsize (upstr, up, sizeof (upstr));
     959      tr_strlratio (ratiostr, tr_getRatio (up, down), sizeof (ratiostr));
     960      g_snprintf (buf, sizeof (buf), _("%s (Ratio: %s)"), upstr, ratiostr);
     961      str = buf;
     962    }
     963  gtr_label_set_text (GTK_LABEL (di->ul_lb), str);
     964
     965  /* hash_lb */
     966  if (n <= 0)
     967    str = no_torrent;
     968  else if (n==1)
     969    str = infos[0]->hashString;
     970  else
     971    str = mixed;
     972  gtr_label_set_text (GTK_LABEL (di->hash_lb), str);
     973
     974  /* error */
     975  if (n <= 0)
     976    {
     977      str = no_torrent;
     978    }
     979  else
     980    {
     981      const char * baseline = stats[0]->errorString;
     982
     983      for (i=1; i<n; ++i)
     984        if (strcmp (baseline, stats[i]->errorString))
     985          break;
     986
     987      if (i==n)
     988        str = baseline;
     989      else
    872990        str = mixed;
    873     gtr_label_set_text (GTK_LABEL (di->hash_lb), str);
    874 
    875     /* error */
    876     if (n <= 0)
    877         str = no_torrent;
    878     else {
    879         const char * baseline = stats[0]->errorString;
    880         for (i=1; i<n; ++i)
    881             if (strcmp (baseline, stats[i]->errorString))
    882                 break;
    883         if (i==n)
    884             str = baseline;
    885         else
    886             str = mixed;
    887     }
    888     if (!str || !*str)
    889         str = _("No errors");
    890     gtr_label_set_text (GTK_LABEL (di->error_lb), str);
    891 
    892 
    893     /* activity date */
    894     if (n <= 0)
    895         str = no_torrent;
    896     else {
    897         time_t latest = 0;
    898         for (i=0; i<n; ++i)
    899             if (latest < stats[i]->activityDate)
    900                 latest = stats[i]->activityDate;
    901         if (latest <= 0)
    902             str = _("Never");
    903         else {
    904             const int period = time (NULL) - latest;
    905             if (period < 5)
    906                 tr_strlcpy (buf, _("Active now"), sizeof (buf));
    907             else {
    908                 char tbuf[128];
    909                 tr_strltime (tbuf, period, sizeof (tbuf));
    910                 g_snprintf (buf, sizeof (buf), _("%1$s ago"), tbuf);
     991    }
     992  if (!str || !*str)
     993    str = _("No errors");
     994  gtr_label_set_text (GTK_LABEL (di->error_lb), str);
     995
     996
     997  /* activity date */
     998  if (n <= 0)
     999    {
     1000      str = no_torrent;
     1001    }
     1002  else
     1003    {
     1004      time_t latest = 0;
     1005
     1006      for (i=0; i<n; ++i)
     1007        if (latest < stats[i]->activityDate)
     1008          latest = stats[i]->activityDate;
     1009
     1010      if (latest <= 0)
     1011        {
     1012          str = _("Never");
     1013        }
     1014      else
     1015        {
     1016          const int period = time (NULL) - latest;
     1017          if (period < 5)
     1018            {
     1019              tr_strlcpy (buf, _("Active now"), sizeof (buf));
    9111020            }
    912             str = buf;
    913         }
    914     }
    915     gtr_label_set_text (GTK_LABEL (di->last_activity_lb), str);
    916 
    917     g_free (stats);
    918     g_free (infos);
     1021          else
     1022            {
     1023              char tbuf[128];
     1024              tr_strltime (tbuf, period, sizeof (tbuf));
     1025              g_snprintf (buf, sizeof (buf), _("%1$s ago"), tbuf);
     1026            }
     1027          str = buf;
     1028        }
     1029    }
     1030  gtr_label_set_text (GTK_LABEL (di->last_activity_lb), str);
     1031
     1032  g_free (stats);
     1033  g_free (infos);
    9191034}
    9201035
     
    9221037info_page_new (struct DetailsImpl * di)
    9231038{
    924     guint row = 0;
    925     GtkTextBuffer * b;
    926     GtkWidget *l, *w, *fr, *sw;
    927     GtkWidget *t = hig_workarea_create ();
    928 
    929     hig_workarea_add_section_title (t, &row, _("Activity"));
    930 
    931         /* size */
    932         l = di->size_lb = gtk_label_new (NULL);
    933         gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
    934         hig_workarea_add_row (t, &row, _("Torrent size:"), l, NULL);
    935 
    936         /* have */
    937         l = di->have_lb = gtk_label_new (NULL);
    938         gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
    939         hig_workarea_add_row (t, &row, _("Have:"), l, NULL);
    940 
    941         /* downloaded */
    942         l = di->dl_lb = gtk_label_new (NULL);
    943         gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
    944         hig_workarea_add_row (t, &row, _("Downloaded:"), l, NULL);
    945 
    946         /* uploaded */
    947         l = di->ul_lb = gtk_label_new (NULL);
    948         gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
    949         hig_workarea_add_row (t, &row, _("Uploaded:"), l, NULL);
    950 
    951         /* state */
    952         l = di->state_lb = gtk_label_new (NULL);
    953         gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
    954         hig_workarea_add_row (t, &row, _("State:"), l, NULL);
    955 
    956         /* running for */
    957         l = di->date_started_lb = gtk_label_new (NULL);
    958         gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
    959         hig_workarea_add_row (t, &row, _("Running time:"), l, NULL);
    960 
    961         /* eta */
    962         l = di->eta_lb = gtk_label_new (NULL);
    963         gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
    964         hig_workarea_add_row (t, &row, _("Remaining time:"), l, NULL);
    965 
    966         /* last activity */
    967         l = di->last_activity_lb = gtk_label_new (NULL);
    968         gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
    969         hig_workarea_add_row (t, &row, _("Last activity:"), l, NULL);
    970 
    971         /* error */
    972         l = g_object_new (GTK_TYPE_LABEL, "selectable", TRUE,
    973                                           "ellipsize", PANGO_ELLIPSIZE_END,
    974                                           NULL);
    975         hig_workarea_add_row (t, &row, _("Error:"), l, NULL);
    976         di->error_lb = l;
    977 
    978 
    979     hig_workarea_add_section_divider (t, &row);
    980     hig_workarea_add_section_title (t, &row, _("Details"));
    981 
    982         /* destination */
    983         l = g_object_new (GTK_TYPE_LABEL, "selectable", TRUE,
    984                                           "ellipsize", PANGO_ELLIPSIZE_END,
    985                                           NULL);
    986         hig_workarea_add_row (t, &row, _("Location:"), l, NULL);
    987         di->destination_lb = l;
    988 
    989         /* hash */
    990         l = g_object_new (GTK_TYPE_LABEL, "selectable", TRUE,
    991                                           "ellipsize", PANGO_ELLIPSIZE_END,
    992                                            NULL);
    993         hig_workarea_add_row (t, &row, _("Hash:"), l, NULL);
    994         di->hash_lb = l;
    995 
    996         /* privacy */
    997         l = gtk_label_new (NULL);
    998         gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
    999         hig_workarea_add_row (t, &row, _("Privacy:"), l, NULL);
    1000         di->privacy_lb = l;
    1001 
    1002         /* origins */
    1003         l = g_object_new (GTK_TYPE_LABEL, "selectable", TRUE,
    1004                                           "ellipsize", PANGO_ELLIPSIZE_END,
    1005                                           NULL);
    1006         hig_workarea_add_row (t, &row, _("Origin:"), l, NULL);
    1007         di->origin_lb = l;
    1008 
    1009         /* comment */
    1010         b = di->comment_buffer = gtk_text_buffer_new (NULL);
    1011         w = gtk_text_view_new_with_buffer (b);
    1012         gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (w), GTK_WRAP_WORD);
    1013         gtk_text_view_set_editable (GTK_TEXT_VIEW (w), FALSE);
    1014         sw = gtk_scrolled_window_new (NULL, NULL);
    1015         gtk_widget_set_size_request (sw, 350, 36);
    1016         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
    1017                                         GTK_POLICY_AUTOMATIC,
    1018                                         GTK_POLICY_AUTOMATIC);
    1019         gtk_container_add (GTK_CONTAINER (sw), w);
    1020         fr = gtk_frame_new (NULL);
    1021         gtk_frame_set_shadow_type (GTK_FRAME (fr), GTK_SHADOW_IN);
    1022         gtk_container_add (GTK_CONTAINER (fr), sw);
    1023         w = hig_workarea_add_tall_row (t, &row, _("Comment:"), fr, NULL);
    1024         gtk_misc_set_alignment (GTK_MISC (w), 0.0f, 0.0f);
    1025 
    1026     hig_workarea_add_section_divider (t, &row);
    1027     return t;
     1039  guint row = 0;
     1040  GtkTextBuffer * b;
     1041  GtkWidget *l, *w, *fr, *sw;
     1042  GtkWidget *t = hig_workarea_create ();
     1043
     1044  hig_workarea_add_section_title (t, &row, _("Activity"));
     1045
     1046    /* size */
     1047    l = di->size_lb = gtk_label_new (NULL);
     1048    gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
     1049    hig_workarea_add_row (t, &row, _("Torrent size:"), l, NULL);
     1050
     1051    /* have */
     1052    l = di->have_lb = gtk_label_new (NULL);
     1053    gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
     1054    hig_workarea_add_row (t, &row, _("Have:"), l, NULL);
     1055
     1056    /* downloaded */
     1057    l = di->dl_lb = gtk_label_new (NULL);
     1058    gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
     1059    hig_workarea_add_row (t, &row, _("Downloaded:"), l, NULL);
     1060
     1061    /* uploaded */
     1062    l = di->ul_lb = gtk_label_new (NULL);
     1063    gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
     1064    hig_workarea_add_row (t, &row, _("Uploaded:"), l, NULL);
     1065
     1066    /* state */
     1067    l = di->state_lb = gtk_label_new (NULL);
     1068    gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
     1069    hig_workarea_add_row (t, &row, _("State:"), l, NULL);
     1070
     1071    /* running for */
     1072    l = di->date_started_lb = gtk_label_new (NULL);
     1073    gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
     1074    hig_workarea_add_row (t, &row, _("Running time:"), l, NULL);
     1075
     1076    /* eta */
     1077    l = di->eta_lb = gtk_label_new (NULL);
     1078    gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
     1079    hig_workarea_add_row (t, &row, _("Remaining time:"), l, NULL);
     1080
     1081    /* last activity */
     1082    l = di->last_activity_lb = gtk_label_new (NULL);
     1083    gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
     1084    hig_workarea_add_row (t, &row, _("Last activity:"), l, NULL);
     1085
     1086    /* error */
     1087    l = g_object_new (GTK_TYPE_LABEL, "selectable", TRUE,
     1088                                      "ellipsize", PANGO_ELLIPSIZE_END,
     1089                                      NULL);
     1090    hig_workarea_add_row (t, &row, _("Error:"), l, NULL);
     1091    di->error_lb = l;
     1092
     1093  hig_workarea_add_section_divider (t, &row);
     1094  hig_workarea_add_section_title (t, &row, _("Details"));
     1095
     1096    /* destination */
     1097    l = g_object_new (GTK_TYPE_LABEL, "selectable", TRUE,
     1098                                      "ellipsize", PANGO_ELLIPSIZE_END,
     1099                                      NULL);
     1100    hig_workarea_add_row (t, &row, _("Location:"), l, NULL);
     1101    di->destination_lb = l;
     1102
     1103    /* hash */
     1104    l = g_object_new (GTK_TYPE_LABEL, "selectable", TRUE,
     1105                                      "ellipsize", PANGO_ELLIPSIZE_END,
     1106                                      NULL);
     1107    hig_workarea_add_row (t, &row, _("Hash:"), l, NULL);
     1108    di->hash_lb = l;
     1109
     1110    /* privacy */
     1111    l = gtk_label_new (NULL);
     1112    gtk_label_set_single_line_mode (GTK_LABEL (l), TRUE);
     1113    hig_workarea_add_row (t, &row, _("Privacy:"), l, NULL);
     1114    di->privacy_lb = l;
     1115
     1116    /* origins */
     1117    l = g_object_new (GTK_TYPE_LABEL, "selectable", TRUE,
     1118                                      "ellipsize", PANGO_ELLIPSIZE_END,
     1119                                      NULL);
     1120    hig_workarea_add_row (t, &row, _("Origin:"), l, NULL);
     1121    di->origin_lb = l;
     1122
     1123    /* comment */
     1124    b = di->comment_buffer = gtk_text_buffer_new (NULL);
     1125    w = gtk_text_view_new_with_buffer (b);
     1126    gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (w), GTK_WRAP_WORD);
     1127    gtk_text_view_set_editable (GTK_TEXT_VIEW (w), FALSE);
     1128    sw = gtk_scrolled_window_new (NULL, NULL);
     1129    gtk_widget_set_size_request (sw, 350, 36);
     1130    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
     1131                                    GTK_POLICY_AUTOMATIC,
     1132                                    GTK_POLICY_AUTOMATIC);
     1133    gtk_container_add (GTK_CONTAINER (sw), w);
     1134    fr = gtk_frame_new (NULL);
     1135    gtk_frame_set_shadow_type (GTK_FRAME (fr), GTK_SHADOW_IN);
     1136    gtk_container_add (GTK_CONTAINER (fr), sw);
     1137    w = hig_workarea_add_tall_row (t, &row, _("Comment:"), fr, NULL);
     1138    gtk_misc_set_alignment (GTK_MISC (w), 0.0f, 0.0f);
     1139
     1140  hig_workarea_add_section_divider (t, &row);
     1141  return t;
    10281142}
    10291143
     
    10361150enum
    10371151{
    1038     WEBSEED_COL_KEY,
    1039     WEBSEED_COL_WAS_UPDATED,
    1040     WEBSEED_COL_URL,
    1041     WEBSEED_COL_DOWNLOAD_RATE_DOUBLE,
    1042     WEBSEED_COL_DOWNLOAD_RATE_STRING,
    1043     N_WEBSEED_COLS
     1152  WEBSEED_COL_KEY,
     1153  WEBSEED_COL_WAS_UPDATED,
     1154  WEBSEED_COL_URL,
     1155  WEBSEED_COL_DOWNLOAD_RATE_DOUBLE,
     1156  WEBSEED_COL_DOWNLOAD_RATE_STRING,
     1157  N_WEBSEED_COLS
    10441158};
    10451159
     
    10471161getWebseedColumnNames (int column)
    10481162{
    1049     switch (column)
    1050     {
    1051         case WEBSEED_COL_URL: return _("Web Seeds");
    1052         case WEBSEED_COL_DOWNLOAD_RATE_DOUBLE:
    1053         case WEBSEED_COL_DOWNLOAD_RATE_STRING: return _("Down");
    1054         default: return "";
     1163  switch (column)
     1164    {
     1165      case WEBSEED_COL_URL: return _("Web Seeds");
     1166      case WEBSEED_COL_DOWNLOAD_RATE_DOUBLE:
     1167      case WEBSEED_COL_DOWNLOAD_RATE_STRING: return _("Down");
     1168      default: return "";
    10551169    }
    10561170}
     
    10591173webseed_model_new (void)
    10601174{
    1061     return gtk_list_store_new (N_WEBSEED_COLS,
    1062                                G_TYPE_STRING,   /* key */
    1063                                G_TYPE_BOOLEAN,  /* was-updated */
    1064                                G_TYPE_STRING,   /* url */
    1065                                G_TYPE_DOUBLE,   /* download rate double */
    1066                                G_TYPE_STRING); /* download rate string */
     1175  return gtk_list_store_new (N_WEBSEED_COLS,
     1176                             G_TYPE_STRING,   /* key */
     1177                             G_TYPE_BOOLEAN,  /* was-updated */
     1178                             G_TYPE_STRING,   /* url */
     1179                             G_TYPE_DOUBLE,   /* download rate double */
     1180                             G_TYPE_STRING); /* download rate string */
    10671181}
    10681182
    10691183enum
    10701184{
    1071     PEER_COL_KEY,
    1072     PEER_COL_WAS_UPDATED,
    1073     PEER_COL_ADDRESS,
    1074     PEER_COL_ADDRESS_COLLATED,
    1075     PEER_COL_DOWNLOAD_RATE_DOUBLE,
    1076     PEER_COL_DOWNLOAD_RATE_STRING,
    1077     PEER_COL_UPLOAD_RATE_DOUBLE,
    1078     PEER_COL_UPLOAD_RATE_STRING,
    1079     PEER_COL_CLIENT,
    1080     PEER_COL_PROGRESS,
    1081     PEER_COL_UPLOAD_REQUEST_COUNT_INT,
    1082     PEER_COL_UPLOAD_REQUEST_COUNT_STRING,
    1083     PEER_COL_DOWNLOAD_REQUEST_COUNT_INT,
    1084     PEER_COL_DOWNLOAD_REQUEST_COUNT_STRING,
    1085     PEER_COL_BLOCKS_DOWNLOADED_COUNT_INT,
    1086     PEER_COL_BLOCKS_DOWNLOADED_COUNT_STRING,
    1087     PEER_COL_BLOCKS_UPLOADED_COUNT_INT,
    1088     PEER_COL_BLOCKS_UPLOADED_COUNT_STRING,
    1089     PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_INT,
    1090     PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_STRING,
    1091     PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_INT,
    1092     PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_STRING,
    1093     PEER_COL_ENCRYPTION_STOCK_ID,
    1094     PEER_COL_FLAGS,
    1095     PEER_COL_TORRENT_NAME,
    1096     N_PEER_COLS
     1185  PEER_COL_KEY,
     1186  PEER_COL_WAS_UPDATED,
     1187  PEER_COL_ADDRESS,
     1188  PEER_COL_ADDRESS_COLLATED,
     1189  PEER_COL_DOWNLOAD_RATE_DOUBLE,
     1190  PEER_COL_DOWNLOAD_RATE_STRING,
     1191  PEER_COL_UPLOAD_RATE_DOUBLE,
     1192  PEER_COL_UPLOAD_RATE_STRING,
     1193  PEER_COL_CLIENT,
     1194  PEER_COL_PROGRESS,
     1195  PEER_COL_UPLOAD_REQUEST_COUNT_INT,
     1196  PEER_COL_UPLOAD_REQUEST_COUNT_STRING,
     1197  PEER_COL_DOWNLOAD_REQUEST_COUNT_INT,
     1198  PEER_COL_DOWNLOAD_REQUEST_COUNT_STRING,
     1199  PEER_COL_BLOCKS_DOWNLOADED_COUNT_INT,
     1200  PEER_COL_BLOCKS_DOWNLOADED_COUNT_STRING,
     1201  PEER_COL_BLOCKS_UPLOADED_COUNT_INT,
     1202  PEER_COL_BLOCKS_UPLOADED_COUNT_STRING,
     1203  PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_INT,
     1204  PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_STRING,
     1205  PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_INT,
     1206  PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_STRING,
     1207  PEER_COL_ENCRYPTION_STOCK_ID,
     1208  PEER_COL_FLAGS,
     1209  PEER_COL_TORRENT_NAME,
     1210  N_PEER_COLS
    10971211};
    10981212
     
    11001214getPeerColumnName (int column)
    11011215{
    1102     switch (column)
    1103     {
    1104         case PEER_COL_ADDRESS: return _("Address");
    1105         case PEER_COL_DOWNLOAD_RATE_STRING:
    1106         case PEER_COL_DOWNLOAD_RATE_DOUBLE: return _("Down");
    1107         case PEER_COL_UPLOAD_RATE_STRING:
    1108         case PEER_COL_UPLOAD_RATE_DOUBLE: return _("Up");
    1109         case PEER_COL_CLIENT: return _("Client");
    1110         case PEER_COL_PROGRESS: return _("%");
    1111         case PEER_COL_UPLOAD_REQUEST_COUNT_INT:
    1112         case PEER_COL_UPLOAD_REQUEST_COUNT_STRING: return _("Up Reqs");
    1113         case PEER_COL_DOWNLOAD_REQUEST_COUNT_INT:
    1114         case PEER_COL_DOWNLOAD_REQUEST_COUNT_STRING: return _("Dn Reqs");
    1115         case PEER_COL_BLOCKS_DOWNLOADED_COUNT_INT:
    1116         case PEER_COL_BLOCKS_DOWNLOADED_COUNT_STRING: return _("Dn Blocks");
    1117         case PEER_COL_BLOCKS_UPLOADED_COUNT_INT:
    1118         case PEER_COL_BLOCKS_UPLOADED_COUNT_STRING: return _("Up Blocks");
    1119         case PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_INT:
    1120         case PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_STRING: return _("We Cancelled");
    1121         case PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_INT:
    1122         case PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_STRING: return _("They Cancelled");
    1123         case PEER_COL_FLAGS: return _("Flags");
    1124         default: return "";
     1216  switch (column)
     1217    {
     1218      case PEER_COL_ADDRESS: return _("Address");
     1219      case PEER_COL_DOWNLOAD_RATE_STRING:
     1220      case PEER_COL_DOWNLOAD_RATE_DOUBLE: return _("Down");
     1221      case PEER_COL_UPLOAD_RATE_STRING:
     1222      case PEER_COL_UPLOAD_RATE_DOUBLE: return _("Up");
     1223      case PEER_COL_CLIENT: return _("Client");
     1224      case PEER_COL_PROGRESS: return _("%");
     1225      case PEER_COL_UPLOAD_REQUEST_COUNT_INT:
     1226      case PEER_COL_UPLOAD_REQUEST_COUNT_STRING: return _("Up Reqs");
     1227      case PEER_COL_DOWNLOAD_REQUEST_COUNT_INT:
     1228      case PEER_COL_DOWNLOAD_REQUEST_COUNT_STRING: return _("Dn Reqs");
     1229      case PEER_COL_BLOCKS_DOWNLOADED_COUNT_INT:
     1230      case PEER_COL_BLOCKS_DOWNLOADED_COUNT_STRING: return _("Dn Blocks");
     1231      case PEER_COL_BLOCKS_UPLOADED_COUNT_INT:
     1232      case PEER_COL_BLOCKS_UPLOADED_COUNT_STRING: return _("Up Blocks");
     1233      case PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_INT:
     1234      case PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_STRING: return _("We Cancelled");
     1235      case PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_INT:
     1236      case PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_STRING: return _("They Cancelled");
     1237      case PEER_COL_FLAGS: return _("Flags");
     1238      default: return "";
    11251239    }
    11261240}
     
    11291243peer_store_new (void)
    11301244{
    1131     return gtk_list_store_new (N_PEER_COLS,
    1132                                G_TYPE_STRING,   /* key */
    1133                                G_TYPE_BOOLEAN,  /* was-updated */
    1134                                G_TYPE_STRING,   /* address */
    1135                                G_TYPE_STRING,   /* collated address */
    1136                                G_TYPE_DOUBLE,   /* download speed int */
    1137                                G_TYPE_STRING,   /* download speed string */
    1138                                G_TYPE_DOUBLE,   /* upload speed int */
    1139                                G_TYPE_STRING,   /* upload speed string  */
    1140                                G_TYPE_STRING,   /* client */
    1141                                G_TYPE_INT,      /* progress [0..100] */
    1142                                G_TYPE_INT,      /* upload request count int */
    1143                                G_TYPE_STRING,   /* upload request count string */
    1144                                G_TYPE_INT,      /* download request count int */
    1145                                G_TYPE_STRING,   /* download request count string */
    1146                                G_TYPE_INT,      /* # blocks downloaded int */
    1147                                G_TYPE_STRING,   /* # blocks downloaded string */
    1148                                G_TYPE_INT,      /* # blocks uploaded int */
    1149                                G_TYPE_STRING,   /* # blocks uploaded string */
    1150                                G_TYPE_INT,      /* # blocks cancelled by client int */
    1151                                G_TYPE_STRING,   /* # blocks cancelled by client string */
    1152                                G_TYPE_INT,      /* # blocks cancelled by peer int */
    1153                                G_TYPE_STRING,   /* # blocks cancelled by peer string */
    1154                                G_TYPE_STRING,   /* encryption stock id */
    1155                                G_TYPE_STRING,   /* flagString */
    1156                                G_TYPE_STRING);  /* torrent name */
     1245  return gtk_list_store_new (N_PEER_COLS,
     1246                             G_TYPE_STRING,   /* key */
     1247                             G_TYPE_BOOLEAN,  /* was-updated */
     1248                             G_TYPE_STRING,   /* address */
     1249                             G_TYPE_STRING,   /* collated address */
     1250                             G_TYPE_DOUBLE,   /* download speed int */
     1251                             G_TYPE_STRING,   /* download speed string */
     1252                             G_TYPE_DOUBLE,   /* upload speed int */
     1253                             G_TYPE_STRING,   /* upload speed string  */
     1254                             G_TYPE_STRING,   /* client */
     1255                             G_TYPE_INT,      /* progress [0..100] */
     1256                             G_TYPE_INT,      /* upload request count int */
     1257                             G_TYPE_STRING,   /* upload request count string */
     1258                             G_TYPE_INT,      /* download request count int */
     1259                             G_TYPE_STRING,   /* download request count string */
     1260                             G_TYPE_INT,      /* # blocks downloaded int */
     1261                             G_TYPE_STRING,   /* # blocks downloaded string */
     1262                             G_TYPE_INT,      /* # blocks uploaded int */
     1263                             G_TYPE_STRING,   /* # blocks uploaded string */
     1264                             G_TYPE_INT,      /* # blocks cancelled by client int */
     1265                             G_TYPE_STRING,   /* # blocks cancelled by client string */
     1266                             G_TYPE_INT,      /* # blocks cancelled by peer int */
     1267                             G_TYPE_STRING,   /* # blocks cancelled by peer string */
     1268                             G_TYPE_STRING,   /* encryption stock id */
     1269                             G_TYPE_STRING,   /* flagString */
     1270                             G_TYPE_STRING);  /* torrent name */
    11571271}
    11581272
     
    11641278             const tr_peer_stat  * peer)
    11651279{
    1166     int q[4];
    1167     char collated_name[128];
    1168     const char * client = peer->client;
    1169 
    1170     if (!client || !strcmp (client, "Unknown Client"))
    1171         client = "";
    1172 
    1173     if (sscanf (peer->addr, "%d.%d.%d.%d", q, q+1, q+2, q+3) != 4)
    1174         g_strlcpy (collated_name, peer->addr, sizeof (collated_name));
    1175     else
    1176         g_snprintf (collated_name, sizeof (collated_name),
    1177                     "%03d.%03d.%03d.%03d", q[0], q[1], q[2], q[3]);
    1178 
    1179     gtk_list_store_set (store, iter,
    1180                         PEER_COL_ADDRESS, peer->addr,
    1181                         PEER_COL_ADDRESS_COLLATED, collated_name,
    1182                         PEER_COL_CLIENT, client,
    1183                         PEER_COL_ENCRYPTION_STOCK_ID, peer->isEncrypted ? "transmission-lock" : NULL,
    1184                         PEER_COL_KEY, key,
    1185                         PEER_COL_TORRENT_NAME, torrentName,
    1186                         -1);
     1280  int q[4];
     1281  char collated_name[128];
     1282  const char * client = peer->client;
     1283
     1284  if (!client || !strcmp (client, "Unknown Client"))
     1285    client = "";
     1286
     1287  if (sscanf (peer->addr, "%d.%d.%d.%d", q, q+1, q+2, q+3) != 4)
     1288    g_strlcpy (collated_name, peer->addr, sizeof (collated_name));
     1289  else
     1290    g_snprintf (collated_name, sizeof (collated_name),
     1291                "%03d.%03d.%03d.%03d", q[0], q[1], q[2], q[3]);
     1292
     1293  gtk_list_store_set (store, iter,
     1294                      PEER_COL_ADDRESS, peer->addr,
     1295                      PEER_COL_ADDRESS_COLLATED, collated_name,
     1296                      PEER_COL_CLIENT, client,
     1297                      PEER_COL_ENCRYPTION_STOCK_ID, peer->isEncrypted ? "transmission-lock" : NULL,
     1298                      PEER_COL_KEY, key,
     1299                      PEER_COL_TORRENT_NAME, torrentName,
     1300                      -1);
    11871301}
    11881302
     
    11921306                const tr_peer_stat  * peer)
    11931307{
    1194     char up_speed[64] = { '\0' };
    1195     char down_speed[64] = { '\0' };
    1196     char up_count[64] = { '\0' };
    1197     char down_count[64] = { '\0' };
    1198     char blocks_to_peer[64] = { '\0' };
    1199     char blocks_to_client[64] = { '\0' };
    1200     char cancelled_by_peer[64] = { '\0' };
    1201     char cancelled_by_client[64] = { '\0' };
    1202 
    1203     if (peer->rateToPeer_KBps > 0.01)
    1204         tr_formatter_speed_KBps (up_speed, peer->rateToPeer_KBps, sizeof (up_speed));
    1205 
    1206     if (peer->rateToClient_KBps > 0)
    1207         tr_formatter_speed_KBps (down_speed, peer->rateToClient_KBps, sizeof (down_speed));
    1208 
    1209     if (peer->pendingReqsToPeer > 0)
    1210         g_snprintf (down_count, sizeof (down_count), "%d", peer->pendingReqsToPeer);
    1211 
    1212     if (peer->pendingReqsToClient > 0)
    1213         g_snprintf (up_count, sizeof (down_count), "%d", peer->pendingReqsToClient);
    1214 
    1215     if (peer->blocksToPeer > 0)
    1216         g_snprintf (blocks_to_peer, sizeof (blocks_to_peer), "%"PRIu32, peer->blocksToPeer);
    1217 
    1218     if (peer->blocksToClient > 0)
    1219         g_snprintf (blocks_to_client, sizeof (blocks_to_client), "%"PRIu32, peer->blocksToClient);
    1220 
    1221     if (peer->cancelsToPeer > 0)
    1222         g_snprintf (cancelled_by_client, sizeof (cancelled_by_client), "%"PRIu32, peer->cancelsToPeer);
    1223 
    1224     if (peer->cancelsToClient > 0)
    1225         g_snprintf (cancelled_by_peer, sizeof (cancelled_by_peer), "%"PRIu32, peer->cancelsToClient);
    1226 
    1227     gtk_list_store_set (store, iter,
    1228         PEER_COL_PROGRESS, (int)(100.0 * peer->progress),
    1229         PEER_COL_UPLOAD_REQUEST_COUNT_INT, peer->pendingReqsToClient,
    1230         PEER_COL_UPLOAD_REQUEST_COUNT_STRING, up_count,
    1231         PEER_COL_DOWNLOAD_REQUEST_COUNT_INT, peer->pendingReqsToPeer,
    1232         PEER_COL_DOWNLOAD_REQUEST_COUNT_STRING, down_count,
    1233         PEER_COL_DOWNLOAD_RATE_DOUBLE, peer->rateToClient_KBps,
    1234         PEER_COL_DOWNLOAD_RATE_STRING, down_speed,
    1235         PEER_COL_UPLOAD_RATE_DOUBLE, peer->rateToPeer_KBps,
    1236         PEER_COL_UPLOAD_RATE_STRING, up_speed,
    1237         PEER_COL_FLAGS, peer->flagStr,
    1238         PEER_COL_WAS_UPDATED, TRUE,
    1239         PEER_COL_BLOCKS_DOWNLOADED_COUNT_INT, (int)peer->blocksToClient,
    1240         PEER_COL_BLOCKS_DOWNLOADED_COUNT_STRING, blocks_to_client,
    1241         PEER_COL_BLOCKS_UPLOADED_COUNT_INT, (int)peer->blocksToPeer,
    1242         PEER_COL_BLOCKS_UPLOADED_COUNT_STRING, blocks_to_peer,
    1243         PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_INT, (int)peer->cancelsToPeer,
    1244         PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_STRING, cancelled_by_client,
    1245         PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_INT, (int)peer->cancelsToClient,
    1246         PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_STRING, cancelled_by_peer,
    1247         -1);
     1308  char up_speed[64] = { '\0' };
     1309  char down_speed[64] = { '\0' };
     1310  char up_count[64] = { '\0' };
     1311  char down_count[64] = { '\0' };
     1312  char blocks_to_peer[64] = { '\0' };
     1313  char blocks_to_client[64] = { '\0' };
     1314  char cancelled_by_peer[64] = { '\0' };
     1315  char cancelled_by_client[64] = { '\0' };
     1316
     1317  if (peer->rateToPeer_KBps > 0.01)
     1318    tr_formatter_speed_KBps (up_speed, peer->rateToPeer_KBps, sizeof (up_speed));
     1319
     1320  if (peer->rateToClient_KBps > 0)
     1321    tr_formatter_speed_KBps (down_speed, peer->rateToClient_KBps, sizeof (down_speed));
     1322
     1323  if (peer->pendingReqsToPeer > 0)
     1324    g_snprintf (down_count, sizeof (down_count), "%d", peer->pendingReqsToPeer);
     1325
     1326  if (peer->pendingReqsToClient > 0)
     1327    g_snprintf (up_count, sizeof (down_count), "%d", peer->pendingReqsToClient);
     1328
     1329  if (peer->blocksToPeer > 0)
     1330    g_snprintf (blocks_to_peer, sizeof (blocks_to_peer), "%"PRIu32, peer->blocksToPeer);
     1331
     1332  if (peer->blocksToClient > 0)
     1333    g_snprintf (blocks_to_client, sizeof (blocks_to_client), "%"PRIu32, peer->blocksToClient);
     1334
     1335  if (peer->cancelsToPeer > 0)
     1336    g_snprintf (cancelled_by_client, sizeof (cancelled_by_client), "%"PRIu32, peer->cancelsToPeer);
     1337
     1338  if (peer->cancelsToClient > 0)
     1339    g_snprintf (cancelled_by_peer, sizeof (cancelled_by_peer), "%"PRIu32, peer->cancelsToClient);
     1340
     1341  gtk_list_store_set (store, iter,
     1342    PEER_COL_PROGRESS, (int)(100.0 * peer->progress),
     1343    PEER_COL_UPLOAD_REQUEST_COUNT_INT, peer->pendingReqsToClient,
     1344    PEER_COL_UPLOAD_REQUEST_COUNT_STRING, up_count,
     1345    PEER_COL_DOWNLOAD_REQUEST_COUNT_INT, peer->pendingReqsToPeer,
     1346    PEER_COL_DOWNLOAD_REQUEST_COUNT_STRING, down_count,
     1347    PEER_COL_DOWNLOAD_RATE_DOUBLE, peer->rateToClient_KBps,
     1348    PEER_COL_DOWNLOAD_RATE_STRING, down_speed,
     1349    PEER_COL_UPLOAD_RATE_DOUBLE, peer->rateToPeer_KBps,
     1350    PEER_COL_UPLOAD_RATE_STRING, up_speed,
     1351    PEER_COL_FLAGS, peer->flagStr,
     1352    PEER_COL_WAS_UPDATED, TRUE,
     1353    PEER_COL_BLOCKS_DOWNLOADED_COUNT_INT, (int)peer->blocksToClient,
     1354    PEER_COL_BLOCKS_DOWNLOADED_COUNT_STRING, blocks_to_client,
     1355    PEER_COL_BLOCKS_UPLOADED_COUNT_INT, (int)peer->blocksToPeer,
     1356    PEER_COL_BLOCKS_UPLOADED_COUNT_STRING, blocks_to_peer,
     1357    PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_INT, (int)peer->cancelsToPeer,
     1358    PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_STRING, cancelled_by_client,
     1359    PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_INT, (int)peer->cancelsToClient,
     1360    PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_STRING, cancelled_by_peer,
     1361    -1);
    12481362}
    12491363
     
    12511365refreshPeerList (struct DetailsImpl * di, tr_torrent ** torrents, int n)
    12521366{
    1253     int i;
    1254     int * peerCount;
    1255     GtkTreeIter iter;
    1256     GtkTreeModel * model;
    1257     GHashTable * hash = di->peer_hash;
    1258     GtkListStore * store = di->peer_store;
    1259     struct tr_peer_stat ** peers;
    1260 
    1261     /* step 1: get all the peers */
    1262     peers = g_new (struct tr_peer_stat*, n);
    1263     peerCount = g_new (int, n);
    1264     for (i=0; i<n; ++i)
    1265         peers[i] = tr_torrentPeers (torrents[i], &peerCount[i]);
    1266 
    1267     /* step 2: mark all the peers in the list as not-updated */
    1268     model = GTK_TREE_MODEL (store);
    1269     if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 0)) do
    1270         gtk_list_store_set (store, &iter, PEER_COL_WAS_UPDATED, FALSE, -1);
    1271     while (gtk_tree_model_iter_next (model, &iter));
    1272 
    1273     /* step 3: add any new peers */
    1274     for (i=0; i<n; ++i) {
    1275         int j;
    1276         const tr_torrent * tor = torrents[i];
    1277         for (j=0; j<peerCount[i]; ++j) {
    1278             const tr_peer_stat * s = &peers[i][j];
    1279             char key[128];
    1280             g_snprintf (key, sizeof (key), "%d.%s", tr_torrentId (tor), s->addr);
    1281             if (g_hash_table_lookup (hash, key) == NULL) {
    1282                 GtkTreePath * p;
    1283                 gtk_list_store_append (store, &iter);
    1284                 initPeerRow (store, &iter, key, tr_torrentName (tor), s);
    1285                 p = gtk_tree_model_get_path (model, &iter);
    1286                 g_hash_table_insert (hash, g_strdup (key),
    1287                                      gtk_tree_row_reference_new (model, p));
    1288                 gtk_tree_path_free (p);
     1367  int i;
     1368  int * peerCount;
     1369  GtkTreeIter iter;
     1370  GtkTreeModel * model;
     1371  GHashTable * hash = di->peer_hash;
     1372  GtkListStore * store = di->peer_store;
     1373  struct tr_peer_stat ** peers;
     1374
     1375  /* step 1: get all the peers */
     1376  peers = g_new (struct tr_peer_stat*, n);
     1377  peerCount = g_new (int, n);
     1378  for (i=0; i<n; ++i)
     1379    peers[i] = tr_torrentPeers (torrents[i], &peerCount[i]);
     1380
     1381  /* step 2: mark all the peers in the list as not-updated */
     1382  model = GTK_TREE_MODEL (store);
     1383  if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 0)) do
     1384    gtk_list_store_set (store, &iter, PEER_COL_WAS_UPDATED, FALSE, -1);
     1385  while (gtk_tree_model_iter_next (model, &iter));
     1386
     1387  /* step 3: add any new peers */
     1388  for (i=0; i<n; ++i)
     1389    {
     1390      int j;
     1391      const tr_torrent * tor = torrents[i];
     1392
     1393      for (j=0; j<peerCount[i]; ++j)
     1394        {
     1395          char key[128];
     1396          const tr_peer_stat * s = &peers[i][j];
     1397
     1398          g_snprintf (key, sizeof (key), "%d.%s", tr_torrentId (tor), s->addr);
     1399          if (g_hash_table_lookup (hash, key) == NULL)
     1400            {
     1401              GtkTreePath * p;
     1402              gtk_list_store_append (store, &iter);
     1403              initPeerRow (store, &iter, key, tr_torrentName (tor), s);
     1404              p = gtk_tree_model_get_path (model, &iter);
     1405              g_hash_table_insert (hash, g_strdup (key), gtk_tree_row_reference_new (model, p));
     1406              gtk_tree_path_free (p);
    12891407            }
    12901408        }
    12911409    }
    12921410
    1293     /* step 4: update the peers */
    1294     for (i=0; i<n; ++i) {
    1295         int j;
    1296         const tr_torrent * tor = torrents[i];
    1297         for (j=0; j<peerCount[i]; ++j) {
    1298             const tr_peer_stat * s = &peers[i][j];
    1299             char key[128];
    1300             GtkTreeRowReference * ref;
    1301             GtkTreePath * p;
    1302             g_snprintf (key, sizeof (key), "%d.%s", tr_torrentId (tor), s->addr);
    1303             ref = g_hash_table_lookup (hash, key);
    1304             p = gtk_tree_row_reference_get_path (ref);
    1305             gtk_tree_model_get_iter (model, &iter, p);
    1306             refreshPeerRow (store, &iter, s);
    1307             gtk_tree_path_free (p);
    1308         }
    1309     }
    1310 
    1311     /* step 5: remove peers that have disappeared */
    1312     model = GTK_TREE_MODEL (store);
    1313     if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 0)) {
    1314         gboolean more = TRUE;
    1315         while (more) {
    1316             gboolean b;
    1317             gtk_tree_model_get (model, &iter, PEER_COL_WAS_UPDATED, &b, -1);
    1318             if (b)
    1319                 more = gtk_tree_model_iter_next (model, &iter);
    1320             else {
    1321                 char * key;
    1322                 gtk_tree_model_get (model, &iter, PEER_COL_KEY, &key, -1);
     1411  /* step 4: update the peers */
     1412  for (i=0; i<n; ++i)
     1413    {
     1414      int j;
     1415      const tr_torrent * tor = torrents[i];
     1416
     1417      for (j=0; j<peerCount[i]; ++j)
     1418        {
     1419          char key[128];
     1420          GtkTreePath * p;
     1421          GtkTreeRowReference * ref;
     1422          const tr_peer_stat * s = &peers[i][j];
     1423
     1424          g_snprintf (key, sizeof (key), "%d.%s", tr_torrentId (tor), s->addr);
     1425          ref = g_hash_table_lookup (hash, key);
     1426          p = gtk_tree_row_reference_get_path (ref);
     1427          gtk_tree_model_get_iter (model, &iter, p);
     1428          refreshPeerRow (store, &iter, s);
     1429          gtk_tree_path_free (p);
     1430        }
     1431    }
     1432
     1433  /* step 5: remove peers that have disappeared */
     1434  model = GTK_TREE_MODEL (store);
     1435  if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 0))
     1436    {
     1437      gboolean more = TRUE;
     1438
     1439      while (more)
     1440        {
     1441          gboolean b;
     1442          gtk_tree_model_get (model, &iter, PEER_COL_WAS_UPDATED, &b, -1);
     1443          if (b)
     1444            {
     1445              more = gtk_tree_model_iter_next (model, &iter);
     1446            }
     1447          else
     1448            {
     1449              char * key;
     1450              gtk_tree_model_get (model, &iter, PEER_COL_KEY, &key, -1);
     1451              g_hash_table_remove (hash, key);
     1452              more = gtk_list_store_remove (store, &iter);
     1453              g_free (key);
     1454            }
     1455        }
     1456    }
     1457
     1458  /* step 6: cleanup */
     1459  for (i=0; i<n; ++i)
     1460    tr_torrentPeersFree (peers[i], peerCount[i]);
     1461  tr_free (peers);
     1462  tr_free (peerCount);
     1463}
     1464
     1465static void
     1466refreshWebseedList (struct DetailsImpl * di, tr_torrent ** torrents, int n)
     1467{
     1468  int i;
     1469  int total = 0;
     1470  GtkTreeIter iter;
     1471  GHashTable * hash = di->webseed_hash;
     1472  GtkListStore * store = di->webseed_store;
     1473  GtkTreeModel * model = GTK_TREE_MODEL (store);
     1474
     1475  /* step 1: mark all webseeds as not-updated */
     1476  if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 0)) do
     1477    gtk_list_store_set (store, &iter, WEBSEED_COL_WAS_UPDATED, FALSE, -1);
     1478  while (gtk_tree_model_iter_next (model, &iter));
     1479
     1480  /* step 2: add any new webseeds */
     1481  for (i=0; i<n; ++i)
     1482    {
     1483      unsigned int j;
     1484      const tr_torrent * tor = torrents[i];
     1485      const tr_info * inf = tr_torrentInfo (tor);
     1486
     1487      total += inf->webseedCount;
     1488
     1489      for (j=0; j<inf->webseedCount; ++j)
     1490        {
     1491          char key[256];
     1492          const char * url = inf->webseeds[j];
     1493          g_snprintf (key, sizeof (key), "%d.%s", tr_torrentId (tor), url);
     1494          if (g_hash_table_lookup (hash, key) == NULL)
     1495            {
     1496              GtkTreePath * p;
     1497              gtk_list_store_append (store, &iter);
     1498              gtk_list_store_set (store, &iter, WEBSEED_COL_URL, url,
     1499                                                WEBSEED_COL_KEY, key,
     1500                                                -1);
     1501              p = gtk_tree_model_get_path (model, &iter);
     1502              g_hash_table_insert (hash, g_strdup (key), gtk_tree_row_reference_new (model, p));
     1503              gtk_tree_path_free (p);
     1504            }
     1505        }
     1506    }
     1507
     1508  /* step 3: update the webseeds */
     1509  for (i=0; i<n; ++i)
     1510    {
     1511      unsigned int j;
     1512      tr_torrent * tor = torrents[i];
     1513      const tr_info * inf = tr_torrentInfo (tor);
     1514      double * speeds_KBps = tr_torrentWebSpeeds_KBps (tor);
     1515
     1516      for (j=0; j<inf->webseedCount; ++j)
     1517        {
     1518          char buf[128];
     1519          char key[256];
     1520          GtkTreePath * p;
     1521          GtkTreeRowReference * ref;
     1522          const char * url = inf->webseeds[j];
     1523
     1524          g_snprintf (key, sizeof (key), "%d.%s", tr_torrentId (tor), url);
     1525          ref = g_hash_table_lookup (hash, key);
     1526          p = gtk_tree_row_reference_get_path (ref);
     1527          gtk_tree_model_get_iter (model, &iter, p);
     1528          if (speeds_KBps[j] > 0)
     1529            tr_formatter_speed_KBps (buf, speeds_KBps[j], sizeof (buf));
     1530          else
     1531            *buf = '\0';
     1532          gtk_list_store_set (store, &iter,
     1533            WEBSEED_COL_DOWNLOAD_RATE_DOUBLE, speeds_KBps[j],
     1534            WEBSEED_COL_DOWNLOAD_RATE_STRING, buf,
     1535            WEBSEED_COL_WAS_UPDATED, TRUE,
     1536            -1);
     1537
     1538          gtk_tree_path_free (p);
     1539        }
     1540
     1541      tr_free (speeds_KBps);
     1542    }
     1543
     1544  /* step 4: remove webseeds that have disappeared */
     1545  if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 0))
     1546    {
     1547      gboolean more = TRUE;
     1548      while (more)
     1549        {
     1550          gboolean b;
     1551          gtk_tree_model_get (model, &iter, WEBSEED_COL_WAS_UPDATED, &b, -1);
     1552          if (b)
     1553            {
     1554              more = gtk_tree_model_iter_next (model, &iter);
     1555            }
     1556          else
     1557            {
     1558              char * key;
     1559              gtk_tree_model_get (model, &iter, WEBSEED_COL_KEY, &key, -1);
     1560              if (key != NULL)
    13231561                g_hash_table_remove (hash, key);
    1324                 more = gtk_list_store_remove (store, &iter);
    1325                 g_free (key);
     1562              more = gtk_list_store_remove (store, &iter);
     1563              g_free (key);
    13261564            }
    13271565        }
    13281566    }
    13291567
    1330     /* step 6: cleanup */
    1331     for (i=0; i<n; ++i)
    1332         tr_torrentPeersFree (peers[i], peerCount[i]);
    1333     tr_free (peers);
    1334     tr_free (peerCount);
    1335 }
    1336 
    1337 static void
    1338 refreshWebseedList (struct DetailsImpl * di, tr_torrent ** torrents, int n)
    1339 {
    1340     int i;
    1341     int total = 0;
    1342     GtkTreeIter iter;
    1343     GHashTable * hash = di->webseed_hash;
    1344     GtkListStore * store = di->webseed_store;
    1345     GtkTreeModel * model = GTK_TREE_MODEL (store);
    1346 
    1347     /* step 1: mark all webseeds as not-updated */
    1348     if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 0)) do
    1349         gtk_list_store_set (store, &iter, WEBSEED_COL_WAS_UPDATED, FALSE, -1);
    1350     while (gtk_tree_model_iter_next (model, &iter));
    1351 
    1352     /* step 2: add any new webseeds */
    1353     for (i=0; i<n; ++i) {
    1354         unsigned int j;
    1355         const tr_torrent * tor = torrents[i];
    1356         const tr_info * inf = tr_torrentInfo (tor);
    1357         total += inf->webseedCount;
    1358         for (j=0; j<inf->webseedCount; ++j) {
    1359             char key[256];
    1360             const char * url = inf->webseeds[j];
    1361             g_snprintf (key, sizeof (key), "%d.%s", tr_torrentId (tor), url);
    1362             if (g_hash_table_lookup (hash, key) == NULL) {
    1363                 GtkTreePath * p;
    1364                 gtk_list_store_append (store, &iter);
    1365                 gtk_list_store_set (store, &iter, WEBSEED_COL_URL, url,
    1366                                                   WEBSEED_COL_KEY, key,
    1367                                                   -1);
    1368                 p = gtk_tree_model_get_path (model, &iter);
    1369                 g_hash_table_insert (hash, g_strdup (key),
    1370                                      gtk_tree_row_reference_new (model, p));
    1371                 gtk_tree_path_free (p);
    1372             }
    1373         }
    1374     }
    1375 
    1376     /* step 3: update the webseeds */
    1377     for (i=0; i<n; ++i) {
    1378         unsigned int j;
    1379         tr_torrent * tor = torrents[i];
    1380         const tr_info * inf = tr_torrentInfo (tor);
    1381         double * speeds_KBps = tr_torrentWebSpeeds_KBps (tor);
    1382         for (j=0; j<inf->webseedCount; ++j) {
    1383             char buf[128];
    1384             char key[256];
    1385             const char * url = inf->webseeds[j];
    1386             GtkTreePath * p;
    1387             GtkTreeRowReference * ref;
    1388             g_snprintf (key, sizeof (key), "%d.%s", tr_torrentId (tor), url);
    1389             ref = g_hash_table_lookup (hash, key);
    1390             p = gtk_tree_row_reference_get_path (ref);
    1391             gtk_tree_model_get_iter (model, &iter, p);
    1392             if (speeds_KBps[j] > 0)
    1393                 tr_formatter_speed_KBps (buf, speeds_KBps[j], sizeof (buf));
    1394             else
    1395                 *buf = '\0';
    1396             gtk_list_store_set (store, &iter,
    1397                 WEBSEED_COL_DOWNLOAD_RATE_DOUBLE, speeds_KBps[j],
    1398                 WEBSEED_COL_DOWNLOAD_RATE_STRING, buf,
    1399                 WEBSEED_COL_WAS_UPDATED, TRUE,
    1400                 -1);
    1401             gtk_tree_path_free (p);
    1402         }
    1403         tr_free (speeds_KBps);
    1404     }
    1405 
    1406     /* step 4: remove webseeds that have disappeared */
    1407     if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 0)) {
    1408         gboolean more = TRUE;
    1409         while (more) {
    1410             gboolean b;
    1411             gtk_tree_model_get (model, &iter, WEBSEED_COL_WAS_UPDATED, &b, -1);
    1412             if (b)
    1413                 more = gtk_tree_model_iter_next (model, &iter);
    1414             else {
    1415                 char * key;
    1416                 gtk_tree_model_get (model, &iter, WEBSEED_COL_KEY, &key, -1);
    1417                 if (key != NULL)
    1418                     g_hash_table_remove (hash, key);
    1419                 more = gtk_list_store_remove (store, &iter);
    1420                 g_free (key);
    1421             }
    1422         }
    1423     }
    1424 
    1425     /* most of the time there are no webseeds...
    1426        don't waste space showing an empty list */
    1427     if (total > 0)
    1428         gtk_widget_show (di->webseed_view);
    1429     else
    1430         gtk_widget_hide (di->webseed_view);
     1568  /* most of the time there are no webseeds...
     1569     don't waste space showing an empty list */
     1570  gtk_widget_set_visible (di->webseed_view, total > 0);
    14311571}
    14321572
     
    14341574refreshPeers (struct DetailsImpl * di, tr_torrent ** torrents, int n)
    14351575{
    1436     refreshPeerList (di, torrents, n);
    1437     refreshWebseedList (di, torrents, n);
     1576  refreshPeerList (di, torrents, n);
     1577  refreshWebseedList (di, torrents, n);
    14381578}
    14391579
     
    14461586                        gpointer      gdi)
    14471587{
    1448     gboolean       show_tip = FALSE;
    1449     GtkTreeModel * model;
    1450     GtkTreeIter    iter;
    1451 
    1452     if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget),
    1453                                            &x, &y, keyboard_tip,
    1454                                            &model, NULL, &iter))
    1455     {
    1456         struct DetailsImpl * di = gdi;
    1457         const char * pch;
    1458         char * name = NULL;
    1459         char * addr = NULL;
    1460         char * markup = NULL;
    1461         char * flagstr = NULL;
    1462         GString * gstr = di->gstr;
    1463         gtk_tree_model_get (model, &iter, PEER_COL_TORRENT_NAME, &name,
    1464                                           PEER_COL_ADDRESS, &addr,
    1465                                           PEER_COL_FLAGS, &flagstr,
    1466                                           -1);
    1467 
    1468         g_string_truncate (gstr, 0);
    1469         markup = g_markup_escape_text (name, -1);
    1470         g_string_append_printf (gstr, "<b>%s</b>\n%s\n \n", markup, addr);
    1471         g_free (markup);
    1472 
    1473         for (pch = flagstr; pch && *pch; ++pch)
    1474         {
    1475             const char * s = NULL;
    1476             switch (*pch)
     1588  GtkTreeIter iter;
     1589  GtkTreeModel * model;
     1590  gboolean show_tip = FALSE;
     1591
     1592  if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget),
     1593                                         &x, &y, keyboard_tip,
     1594                                         &model, NULL, &iter))
     1595    {
     1596      const char * pch;
     1597      char * name = NULL;
     1598      char * addr = NULL;
     1599      char * markup = NULL;
     1600      char * flagstr = NULL;
     1601      struct DetailsImpl * di = gdi;
     1602      GString * gstr = di->gstr;
     1603
     1604      gtk_tree_model_get (model, &iter, PEER_COL_TORRENT_NAME, &name,
     1605                                        PEER_COL_ADDRESS, &addr,
     1606                                        PEER_COL_FLAGS, &flagstr,
     1607                                        -1);
     1608
     1609      g_string_truncate (gstr, 0);
     1610      markup = g_markup_escape_text (name, -1);
     1611      g_string_append_printf (gstr, "<b>%s</b>\n%s\n \n", markup, addr);
     1612      g_free (markup);
     1613
     1614      for (pch=flagstr; pch && *pch; ++pch)
     1615        {
     1616          const char * s = NULL;
     1617
     1618          switch (*pch)
    14771619            {
    1478                 case 'O': s = _("Optimistic unchoke"); break;
    1479                 case 'D': s = _("Downloading from this peer"); break;
    1480                 case 'd': s = _("We would download from this peer if they would let us"); break;
    1481                 case 'U': s = _("Uploading to peer"); break;
    1482                 case 'u': s = _("We would upload to this peer if they asked"); break;
    1483                 case 'K': s = _("Peer has unchoked us, but we're not interested"); break;
    1484                 case '?': s = _("We unchoked this peer, but they're not interested"); break;
    1485                 case 'E': s = _("Encrypted connection"); break;
    1486                 case 'X': s = _("Peer was found through Peer Exchange (PEX)"); break;
    1487                 case 'H': s = _("Peer was found through DHT"); break;
    1488                 case 'I': s = _("Peer is an incoming connection"); break;
    1489                 case 'T': s = _("Peer is connected over µTP"); break;
     1620              case 'O': s = _("Optimistic unchoke"); break;
     1621              case 'D': s = _("Downloading from this peer"); break;
     1622              case 'd': s = _("We would download from this peer if they would let us"); break;
     1623              case 'U': s = _("Uploading to peer"); break;
     1624              case 'u': s = _("We would upload to this peer if they asked"); break;
     1625              case 'K': s = _("Peer has unchoked us, but we're not interested"); break;
     1626              case '?': s = _("We unchoked this peer, but they're not interested"); break;
     1627              case 'E': s = _("Encrypted connection"); break;
     1628              case 'X': s = _("Peer was found through Peer Exchange (PEX)"); break;
     1629              case 'H': s = _("Peer was found through DHT"); break;
     1630              case 'I': s = _("Peer is an incoming connection"); break;
     1631              case 'T': s = _("Peer is connected over µTP"); break;
    14901632            }
    1491             if (s)
    1492                 g_string_append_printf (gstr, "%c: %s\n", *pch, s);
    1493         }
    1494         if (gstr->len) /* remove the last linefeed */
    1495             g_string_set_size (gstr, gstr->len - 1);
    1496 
    1497         gtk_tooltip_set_markup (tooltip, gstr->str);
    1498 
    1499         g_free (flagstr);
    1500         g_free (addr);
    1501         g_free (name);
    1502         show_tip = TRUE;
    1503     }
    1504 
    1505     return show_tip;
     1633
     1634          if (s)
     1635            g_string_append_printf (gstr, "%c: %s\n", *pch, s);
     1636        }
     1637
     1638      if (gstr->len) /* remove the last linefeed */
     1639        g_string_set_size (gstr, gstr->len - 1);
     1640
     1641      gtk_tooltip_set_markup (tooltip, gstr->str);
     1642
     1643      g_free (flagstr);
     1644      g_free (addr);
     1645      g_free (name);
     1646      show_tip = TRUE;
     1647    }
     1648
     1649  return show_tip;
    15061650}
    15071651
     
    15091653setPeerViewColumns (GtkTreeView * peer_view)
    15101654{
    1511     int i;
    1512     int n = 0;
    1513     const bool more = gtr_pref_flag_get (TR_KEY_show_extra_peer_details);
    1514     int view_columns[32];
    1515     GtkTreeViewColumn * c;
    1516     GtkCellRenderer *   r;
    1517 
    1518     view_columns[n++] = PEER_COL_ENCRYPTION_STOCK_ID;
    1519     view_columns[n++] = PEER_COL_UPLOAD_RATE_STRING;
    1520     if (more) view_columns[n++] = PEER_COL_UPLOAD_REQUEST_COUNT_STRING;
    1521     view_columns[n++] = PEER_COL_DOWNLOAD_RATE_STRING;
    1522     if (more) view_columns[n++] = PEER_COL_DOWNLOAD_REQUEST_COUNT_STRING;
    1523     if (more) view_columns[n++] = PEER_COL_BLOCKS_DOWNLOADED_COUNT_STRING;
    1524     if (more) view_columns[n++] = PEER_COL_BLOCKS_UPLOADED_COUNT_STRING;
    1525     if (more) view_columns[n++] = PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_STRING;
    1526     if (more) view_columns[n++] = PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_STRING;
    1527     view_columns[n++] = PEER_COL_PROGRESS;
    1528     view_columns[n++] = PEER_COL_FLAGS;
    1529     view_columns[n++] = PEER_COL_ADDRESS;
    1530     view_columns[n++] = PEER_COL_CLIENT;
    1531 
    1532     /* remove any existing columns */
    1533     while ((c = gtk_tree_view_get_column (peer_view, 0)))
    1534         gtk_tree_view_remove_column (peer_view, c);
    1535 
    1536     for (i=0; i<n; ++i)
    1537     {
    1538         const int col = view_columns[i];
    1539         const char * t = getPeerColumnName (col);
    1540         int sort_col = col;
    1541 
    1542         switch (col)
    1543         {
    1544             case PEER_COL_ADDRESS:
    1545                 r = gtk_cell_renderer_text_new ();
    1546                 c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
    1547                 sort_col = PEER_COL_ADDRESS_COLLATED;
    1548                 break;
    1549 
    1550             case PEER_COL_CLIENT:
    1551                 r = gtk_cell_renderer_text_new ();
    1552                 c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
    1553                 break;
    1554 
    1555             case PEER_COL_PROGRESS:
    1556                 r = gtk_cell_renderer_progress_new ();
    1557                 c = gtk_tree_view_column_new_with_attributes (t, r, "value", PEER_COL_PROGRESS, NULL);
    1558                 break;
    1559 
    1560             case PEER_COL_ENCRYPTION_STOCK_ID:
    1561                 r = gtk_cell_renderer_pixbuf_new ();
    1562                 g_object_set (r, "xalign", (gfloat)0.0,
    1563                                  "yalign", (gfloat)0.5,
    1564                                  NULL);
    1565                 c = gtk_tree_view_column_new_with_attributes (t, r, "stock-id", PEER_COL_ENCRYPTION_STOCK_ID, NULL);
    1566                 gtk_tree_view_column_set_sizing (c, GTK_TREE_VIEW_COLUMN_FIXED);
    1567                 gtk_tree_view_column_set_fixed_width (c, 20);
    1568                 break;
    1569 
    1570             case PEER_COL_DOWNLOAD_REQUEST_COUNT_STRING:
    1571                 r = gtk_cell_renderer_text_new ();
    1572                 c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
    1573                 sort_col = PEER_COL_DOWNLOAD_REQUEST_COUNT_INT;
    1574                 break;
    1575             case PEER_COL_UPLOAD_REQUEST_COUNT_STRING:
    1576                 r = gtk_cell_renderer_text_new ();
    1577                 c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
    1578                 sort_col = PEER_COL_UPLOAD_REQUEST_COUNT_INT;
    1579                 break;
    1580 
    1581             case PEER_COL_BLOCKS_DOWNLOADED_COUNT_STRING:
    1582                 r = gtk_cell_renderer_text_new ();
    1583                 c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
    1584                 sort_col = PEER_COL_BLOCKS_DOWNLOADED_COUNT_INT;
    1585                 break;
    1586             case PEER_COL_BLOCKS_UPLOADED_COUNT_STRING:
    1587                 r = gtk_cell_renderer_text_new ();
    1588                 c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
    1589                 sort_col = PEER_COL_BLOCKS_UPLOADED_COUNT_INT;
    1590                 break;
    1591 
    1592             case PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_STRING:
    1593                 r = gtk_cell_renderer_text_new ();
    1594                 c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
    1595                 sort_col = PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_INT;
    1596                 break;
    1597             case PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_STRING:
    1598                 r = gtk_cell_renderer_text_new ();
    1599                 c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
    1600                 sort_col = PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_INT;
    1601                 break;
    1602 
    1603             case PEER_COL_DOWNLOAD_RATE_STRING:
    1604                 r = gtk_cell_renderer_text_new ();
    1605                 g_object_set (G_OBJECT (r), "xalign", 1.0f, NULL);
    1606                 c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
    1607                 sort_col = PEER_COL_DOWNLOAD_RATE_DOUBLE;
    1608                 break;
    1609             case PEER_COL_UPLOAD_RATE_STRING:
    1610                 r = gtk_cell_renderer_text_new ();
    1611                 g_object_set (G_OBJECT (r), "xalign", 1.0f, NULL);
    1612                 c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
    1613                 sort_col = PEER_COL_UPLOAD_RATE_DOUBLE;
    1614                 break;
    1615 
    1616             case PEER_COL_FLAGS:
    1617                 r = gtk_cell_renderer_text_new ();
    1618                 c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
    1619                 break;
    1620 
    1621             default:
    1622                 abort ();
    1623         }
    1624 
    1625         gtk_tree_view_column_set_resizable (c, FALSE);
    1626         gtk_tree_view_column_set_sort_column_id (c, sort_col);
    1627         gtk_tree_view_append_column (GTK_TREE_VIEW (peer_view), c);
    1628     }
    1629 
    1630     /* the 'expander' column has a 10-pixel margin on the left
    1631        that doesn't look quite correct in any of these columns...
    1632        so create a non-visible column and assign it as the
    1633        'expander column. */
    1634     {
    1635         GtkTreeViewColumn *c = gtk_tree_view_column_new ();
    1636         gtk_tree_view_column_set_visible (c, FALSE);
    1637         gtk_tree_view_append_column (GTK_TREE_VIEW (peer_view), c);
    1638         gtk_tree_view_set_expander_column (GTK_TREE_VIEW (peer_view), c);
    1639     }
     1655  int i;
     1656  int n;
     1657  int view_columns[32];
     1658  GtkCellRenderer * r;
     1659  GtkTreeViewColumn * c;
     1660  const bool more = gtr_pref_flag_get (TR_KEY_show_extra_peer_details);
     1661
     1662  n = 0;
     1663  view_columns[n++] = PEER_COL_ENCRYPTION_STOCK_ID;
     1664  view_columns[n++] = PEER_COL_UPLOAD_RATE_STRING;
     1665  if (more) view_columns[n++] = PEER_COL_UPLOAD_REQUEST_COUNT_STRING;
     1666  view_columns[n++] = PEER_COL_DOWNLOAD_RATE_STRING;
     1667  if (more) view_columns[n++] = PEER_COL_DOWNLOAD_REQUEST_COUNT_STRING;
     1668  if (more) view_columns[n++] = PEER_COL_BLOCKS_DOWNLOADED_COUNT_STRING;
     1669  if (more) view_columns[n++] = PEER_COL_BLOCKS_UPLOADED_COUNT_STRING;
     1670  if (more) view_columns[n++] = PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_STRING;
     1671  if (more) view_columns[n++] = PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_STRING;
     1672  view_columns[n++] = PEER_COL_PROGRESS;
     1673  view_columns[n++] = PEER_COL_FLAGS;
     1674  view_columns[n++] = PEER_COL_ADDRESS;
     1675  view_columns[n++] = PEER_COL_CLIENT;
     1676
     1677  /* remove any existing columns */
     1678  while ((c = gtk_tree_view_get_column (peer_view, 0)))
     1679    gtk_tree_view_remove_column (peer_view, c);
     1680
     1681  for (i=0; i<n; ++i)
     1682    {
     1683      const int col = view_columns[i];
     1684      const char * t = getPeerColumnName (col);
     1685      int sort_col = col;
     1686
     1687      switch (col)
     1688        {
     1689          case PEER_COL_ADDRESS:
     1690            r = gtk_cell_renderer_text_new ();
     1691            c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
     1692            sort_col = PEER_COL_ADDRESS_COLLATED;
     1693            break;
     1694
     1695          case PEER_COL_CLIENT:
     1696            r = gtk_cell_renderer_text_new ();
     1697            c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
     1698            break;
     1699
     1700          case PEER_COL_PROGRESS:
     1701            r = gtk_cell_renderer_progress_new ();
     1702            c = gtk_tree_view_column_new_with_attributes (t, r, "value", PEER_COL_PROGRESS, NULL);
     1703            break;
     1704
     1705          case PEER_COL_ENCRYPTION_STOCK_ID:
     1706            r = gtk_cell_renderer_pixbuf_new ();
     1707            g_object_set (r, "xalign", (gfloat)0.0,
     1708                             "yalign", (gfloat)0.5,
     1709                             NULL);
     1710            c = gtk_tree_view_column_new_with_attributes (t, r, "stock-id", PEER_COL_ENCRYPTION_STOCK_ID, NULL);
     1711            gtk_tree_view_column_set_sizing (c, GTK_TREE_VIEW_COLUMN_FIXED);
     1712            gtk_tree_view_column_set_fixed_width (c, 20);
     1713            break;
     1714
     1715          case PEER_COL_DOWNLOAD_REQUEST_COUNT_STRING:
     1716            r = gtk_cell_renderer_text_new ();
     1717            c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
     1718            sort_col = PEER_COL_DOWNLOAD_REQUEST_COUNT_INT;
     1719            break;
     1720
     1721          case PEER_COL_UPLOAD_REQUEST_COUNT_STRING:
     1722            r = gtk_cell_renderer_text_new ();
     1723            c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
     1724            sort_col = PEER_COL_UPLOAD_REQUEST_COUNT_INT;
     1725            break;
     1726
     1727          case PEER_COL_BLOCKS_DOWNLOADED_COUNT_STRING:
     1728            r = gtk_cell_renderer_text_new ();
     1729            c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
     1730            sort_col = PEER_COL_BLOCKS_DOWNLOADED_COUNT_INT;
     1731            break;
     1732
     1733          case PEER_COL_BLOCKS_UPLOADED_COUNT_STRING:
     1734            r = gtk_cell_renderer_text_new ();
     1735            c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
     1736            sort_col = PEER_COL_BLOCKS_UPLOADED_COUNT_INT;
     1737            break;
     1738
     1739          case PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_STRING:
     1740            r = gtk_cell_renderer_text_new ();
     1741            c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
     1742            sort_col = PEER_COL_REQS_CANCELLED_BY_CLIENT_COUNT_INT;
     1743            break;
     1744
     1745          case PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_STRING:
     1746            r = gtk_cell_renderer_text_new ();
     1747            c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
     1748            sort_col = PEER_COL_REQS_CANCELLED_BY_PEER_COUNT_INT;
     1749            break;
     1750
     1751          case PEER_COL_DOWNLOAD_RATE_STRING:
     1752            r = gtk_cell_renderer_text_new ();
     1753            g_object_set (G_OBJECT (r), "xalign", 1.0f, NULL);
     1754            c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
     1755            sort_col = PEER_COL_DOWNLOAD_RATE_DOUBLE;
     1756            break;
     1757
     1758          case PEER_COL_UPLOAD_RATE_STRING:
     1759            r = gtk_cell_renderer_text_new ();
     1760            g_object_set (G_OBJECT (r), "xalign", 1.0f, NULL);
     1761            c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
     1762            sort_col = PEER_COL_UPLOAD_RATE_DOUBLE;
     1763            break;
     1764
     1765          case PEER_COL_FLAGS:
     1766            r = gtk_cell_renderer_text_new ();
     1767            c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
     1768            break;
     1769
     1770          default:
     1771            abort ();
     1772        }
     1773
     1774      gtk_tree_view_column_set_resizable (c, FALSE);
     1775      gtk_tree_view_column_set_sort_column_id (c, sort_col);
     1776      gtk_tree_view_append_column (GTK_TREE_VIEW (peer_view), c);
     1777    }
     1778
     1779  /* the 'expander' column has a 10-pixel margin on the left
     1780     that doesn't look quite correct in any of these columns...
     1781     so create a non-visible column and assign it as the
     1782     'expander column. */
     1783  {
     1784    GtkTreeViewColumn *c = gtk_tree_view_column_new ();
     1785    gtk_tree_view_column_set_visible (c, FALSE);
     1786    gtk_tree_view_append_column (GTK_TREE_VIEW (peer_view), c);
     1787    gtk_tree_view_set_expander_column (GTK_TREE_VIEW (peer_view), c);
     1788  }
    16401789}
    16411790
     
    16431792onMorePeerInfoToggled (GtkToggleButton * button, struct DetailsImpl * di)
    16441793{
    1645     const tr_quark key = TR_KEY_show_extra_peer_details;
    1646     const gboolean value = gtk_toggle_button_get_active (button);
    1647     gtr_core_set_pref_bool (di->core, key, value);
    1648     setPeerViewColumns (GTK_TREE_VIEW (di->peer_view));
     1794  const tr_quark key = TR_KEY_show_extra_peer_details;
     1795  const gboolean value = gtk_toggle_button_get_active (button);
     1796  gtr_core_set_pref_bool (di->core, key, value);
     1797  setPeerViewColumns (GTK_TREE_VIEW (di->peer_view));
    16491798}
    16501799
     
    16521801peer_page_new (struct DetailsImpl * di)
    16531802{
    1654     gboolean b;
    1655     const char * str;
    1656     GtkListStore *store;
    1657     GtkWidget *v, *w, *ret, *sw, *vbox;
    1658     GtkWidget *webtree = NULL;
    1659     GtkTreeModel * m;
    1660     GtkTreeViewColumn * c;
    1661     GtkCellRenderer *   r;
    1662 
    1663     /* webseeds */
    1664 
    1665     store = di->webseed_store = webseed_model_new ();
    1666     v = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
    1667     g_signal_connect (v, "button-release-event", G_CALLBACK (on_tree_view_button_released), NULL);
    1668     gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (v), TRUE);
    1669     g_object_unref (store);
    1670 
    1671     str = getWebseedColumnNames (WEBSEED_COL_URL);
    1672     r = gtk_cell_renderer_text_new ();
    1673     g_object_set (G_OBJECT (r), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
    1674     c = gtk_tree_view_column_new_with_attributes (str, r, "text", WEBSEED_COL_URL, NULL);
    1675     g_object_set (G_OBJECT (c), "expand", TRUE, NULL);
    1676     gtk_tree_view_column_set_sort_column_id (c, WEBSEED_COL_URL);
    1677     gtk_tree_view_append_column (GTK_TREE_VIEW (v), c);
    1678 
    1679     str = getWebseedColumnNames (WEBSEED_COL_DOWNLOAD_RATE_STRING);
    1680     r = gtk_cell_renderer_text_new ();
    1681     c = gtk_tree_view_column_new_with_attributes (str, r, "text", WEBSEED_COL_DOWNLOAD_RATE_STRING, NULL);
    1682     gtk_tree_view_column_set_sort_column_id (c, WEBSEED_COL_DOWNLOAD_RATE_DOUBLE);
    1683     gtk_tree_view_append_column (GTK_TREE_VIEW (v), c);
    1684 
    1685     w = gtk_scrolled_window_new (NULL, NULL);
    1686     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w),
    1687                                     GTK_POLICY_AUTOMATIC,
    1688                                     GTK_POLICY_AUTOMATIC);
    1689     gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w),
    1690                                          GTK_SHADOW_IN);
    1691     gtk_container_add (GTK_CONTAINER (w), v);
    1692 
    1693     webtree = w;
    1694     di->webseed_view = w;
    1695 
    1696     /* peers */
    1697 
    1698     store  = di->peer_store = peer_store_new ();
    1699     m = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store));
    1700     gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (m),
    1701                                           PEER_COL_PROGRESS,
    1702                                           GTK_SORT_DESCENDING);
    1703     v = GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW,
    1704                                   "model",  m,
    1705                                   "rules-hint", TRUE,
    1706                                   "has-tooltip", TRUE,
    1707                                   NULL));
    1708     di->peer_view = v;
    1709 
    1710     g_signal_connect (v, "query-tooltip",
    1711                       G_CALLBACK (onPeerViewQueryTooltip), di);
    1712     g_object_unref (store);
    1713     g_signal_connect (v, "button-release-event",
    1714                       G_CALLBACK (on_tree_view_button_released), NULL);
    1715 
    1716     setPeerViewColumns (GTK_TREE_VIEW (v));
    1717 
    1718     w = sw = gtk_scrolled_window_new (NULL, NULL);
    1719     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w),
    1720                                     GTK_POLICY_AUTOMATIC,
    1721                                     GTK_POLICY_AUTOMATIC);
    1722     gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w),
    1723                                          GTK_SHADOW_IN);
    1724     gtk_container_add (GTK_CONTAINER (w), v);
    1725 
    1726     vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, GUI_PAD);
    1727     gtk_container_set_border_width (GTK_CONTAINER (vbox), GUI_PAD_BIG);
    1728 
    1729     v = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
    1730     gtk_paned_pack1 (GTK_PANED (v), webtree, FALSE, TRUE);
    1731     gtk_paned_pack2 (GTK_PANED (v), sw, TRUE, TRUE);
    1732     gtk_box_pack_start (GTK_BOX (vbox), v, TRUE, TRUE, 0);
    1733 
    1734     w = gtk_check_button_new_with_mnemonic (_("Show _more details"));
    1735     di->more_peer_details_check = w;
    1736     b = gtr_pref_flag_get (TR_KEY_show_extra_peer_details);
    1737     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), b);
    1738     g_signal_connect (w, "toggled", G_CALLBACK (onMorePeerInfoToggled), di);
    1739     gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0);
    1740 
    1741 
    1742     /* ip-to-GtkTreeRowReference */
    1743     di->peer_hash = g_hash_table_new_full (g_str_hash,
    1744                                            g_str_equal,
     1803  gboolean b;
     1804  const char * str;
     1805  GtkListStore *store;
     1806  GtkWidget *v, *w, *ret, *sw, *vbox;
     1807  GtkWidget *webtree = NULL;
     1808  GtkTreeModel * m;
     1809  GtkTreeViewColumn * c;
     1810  GtkCellRenderer *   r;
     1811
     1812  /* webseeds */
     1813
     1814  store = di->webseed_store = webseed_model_new ();
     1815  v = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
     1816  g_signal_connect (v, "button-release-event", G_CALLBACK (on_tree_view_button_released), NULL);
     1817  gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (v), TRUE);
     1818  g_object_unref (store);
     1819
     1820  str = getWebseedColumnNames (WEBSEED_COL_URL);
     1821  r = gtk_cell_renderer_text_new ();
     1822  g_object_set (G_OBJECT (r), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
     1823  c = gtk_tree_view_column_new_with_attributes (str, r, "text", WEBSEED_COL_URL, NULL);
     1824  g_object_set (G_OBJECT (c), "expand", TRUE, NULL);
     1825  gtk_tree_view_column_set_sort_column_id (c, WEBSEED_COL_URL);
     1826  gtk_tree_view_append_column (GTK_TREE_VIEW (v), c);
     1827
     1828  str = getWebseedColumnNames (WEBSEED_COL_DOWNLOAD_RATE_STRING);
     1829  r = gtk_cell_renderer_text_new ();
     1830  c = gtk_tree_view_column_new_with_attributes (str, r, "text", WEBSEED_COL_DOWNLOAD_RATE_STRING, NULL);
     1831  gtk_tree_view_column_set_sort_column_id (c, WEBSEED_COL_DOWNLOAD_RATE_DOUBLE);
     1832  gtk_tree_view_append_column (GTK_TREE_VIEW (v), c);
     1833
     1834  w = gtk_scrolled_window_new (NULL, NULL);
     1835  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w),
     1836                                  GTK_POLICY_AUTOMATIC,
     1837                                  GTK_POLICY_AUTOMATIC);
     1838  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w),
     1839                                       GTK_SHADOW_IN);
     1840  gtk_container_add (GTK_CONTAINER (w), v);
     1841
     1842  webtree = w;
     1843  di->webseed_view = w;
     1844
     1845  /* peers */
     1846
     1847  store  = di->peer_store = peer_store_new ();
     1848  m = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store));
     1849  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (m),
     1850                                        PEER_COL_PROGRESS,
     1851                                        GTK_SORT_DESCENDING);
     1852  v = GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW,
     1853                                "model",  m,
     1854                                "rules-hint", TRUE,
     1855                                "has-tooltip", TRUE,
     1856                                NULL));
     1857  di->peer_view = v;
     1858
     1859  g_signal_connect (v, "query-tooltip",
     1860                    G_CALLBACK (onPeerViewQueryTooltip), di);
     1861  g_object_unref (store);
     1862  g_signal_connect (v, "button-release-event",
     1863                    G_CALLBACK (on_tree_view_button_released), NULL);
     1864
     1865  setPeerViewColumns (GTK_TREE_VIEW (v));
     1866
     1867  w = sw = gtk_scrolled_window_new (NULL, NULL);
     1868  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w),
     1869                                  GTK_POLICY_AUTOMATIC,
     1870                                  GTK_POLICY_AUTOMATIC);
     1871  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w),
     1872                                       GTK_SHADOW_IN);
     1873  gtk_container_add (GTK_CONTAINER (w), v);
     1874
     1875  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, GUI_PAD);
     1876  gtk_container_set_border_width (GTK_CONTAINER (vbox), GUI_PAD_BIG);
     1877
     1878  v = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
     1879  gtk_paned_pack1 (GTK_PANED (v), webtree, FALSE, TRUE);
     1880  gtk_paned_pack2 (GTK_PANED (v), sw, TRUE, TRUE);
     1881  gtk_box_pack_start (GTK_BOX (vbox), v, TRUE, TRUE, 0);
     1882
     1883  w = gtk_check_button_new_with_mnemonic (_("Show _more details"));
     1884  di->more_peer_details_check = w;
     1885  b = gtr_pref_flag_get (TR_KEY_show_extra_peer_details);
     1886  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), b);
     1887  g_signal_connect (w, "toggled", G_CALLBACK (onMorePeerInfoToggled), di);
     1888  gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0);
     1889
     1890
     1891  /* ip-to-GtkTreeRowReference */
     1892  di->peer_hash = g_hash_table_new_full (g_str_hash,
     1893                                         g_str_equal,
    17451894                                         (GDestroyNotify)g_free,
    17461895                                         (GDestroyNotify)gtk_tree_row_reference_free);
    17471896
    1748     /* url-to-GtkTreeRowReference */
    1749     di->webseed_hash = g_hash_table_new_full (g_str_hash,
    1750                                               g_str_equal,
     1897  /* url-to-GtkTreeRowReference */
     1898  di->webseed_hash = g_hash_table_new_full (g_str_hash,
     1899                                            g_str_equal,
    17511900                                            (GDestroyNotify)g_free,
    17521901                                            (GDestroyNotify)gtk_tree_row_reference_free);
    1753     ret = vbox;
    1754     return ret;
     1902  ret = vbox;
     1903  return ret;
    17551904}
    17561905
     
    17671916tr_strltime_rounded (char * buf, time_t t, size_t buflen)
    17681917{
    1769     if (t > 60) t -= (t % 60);
    1770     tr_strltime (buf, t, buflen);
     1918  if (t > 60) t -= (t % 60);
     1919  tr_strltime (buf, t, buflen);
    17711920}
    17721921
     
    17741923buildTrackerSummary (GString * gstr, const char * key, const tr_tracker_stat * st, gboolean showScrape)
    17751924{
    1776     char * str;
    1777     char timebuf[256];
    1778     const time_t now = time (NULL);
    1779     const char * err_markup_begin = "<span color=\"red\">";
    1780     const char * err_markup_end = "</span>";
    1781     const char * timeout_markup_begin = "<span color=\"#224466\">";
    1782     const char * timeout_markup_end = "</span>";
    1783     const char * success_markup_begin = "<span color=\"#008B00\">";
    1784     const char * success_markup_end = "</span>";
    1785 
    1786     /* hostname */
    1787     {
    1788         g_string_append (gstr, st->isBackup ? "<i>" : "<b>");
    1789         if (key)
    1790             str = g_markup_printf_escaped ("%s - %s", st->host, key);
    1791         else
    1792             str = g_markup_printf_escaped ("%s", st->host);
    1793         g_string_append (gstr, str);
    1794         g_free (str);
    1795         g_string_append (gstr, st->isBackup ? "</i>" : "</b>");
    1796     }
    1797 
    1798     if (!st->isBackup)
    1799     {
    1800         if (st->hasAnnounced && st->announceState != TR_TRACKER_INACTIVE)
    1801         {
     1925  char * str;
     1926  char timebuf[256];
     1927  const time_t now = time (NULL);
     1928  const char * err_markup_begin = "<span color=\"red\">";
     1929  const char * err_markup_end = "</span>";
     1930  const char * timeout_markup_begin = "<span color=\"#224466\">";
     1931  const char * timeout_markup_end = "</span>";
     1932  const char * success_markup_begin = "<span color=\"#008B00\">";
     1933  const char * success_markup_end = "</span>";
     1934
     1935  /* hostname */
     1936  {
     1937    g_string_append (gstr, st->isBackup ? "<i>" : "<b>");
     1938    if (key)
     1939      str = g_markup_printf_escaped ("%s - %s", st->host, key);
     1940    else
     1941      str = g_markup_printf_escaped ("%s", st->host);
     1942    g_string_append (gstr, str);
     1943    g_free (str);
     1944    g_string_append (gstr, st->isBackup ? "</i>" : "</b>");
     1945  }
     1946
     1947  if (!st->isBackup)
     1948    {
     1949      if (st->hasAnnounced && st->announceState != TR_TRACKER_INACTIVE)
     1950        {
     1951          g_string_append_c (gstr, '\n');
     1952          tr_strltime_rounded (timebuf, now - st->lastAnnounceTime, sizeof (timebuf));
     1953          if (st->lastAnnounceSucceeded)
     1954            g_string_append_printf (gstr, _("Got a list of %1$s%2$'d peers%3$s %4$s ago"),
     1955                                    success_markup_begin, st->lastAnnouncePeerCount, success_markup_end,
     1956                                    timebuf);
     1957          else if (st->lastAnnounceTimedOut)
     1958            g_string_append_printf (gstr, _("Peer list request %1$stimed out%2$s %3$s ago; will retry"),
     1959                                    timeout_markup_begin, timeout_markup_end, timebuf);
     1960          else
     1961            g_string_append_printf (gstr, _("Got an error %1$s\"%2$s\"%3$s %4$s ago"),
     1962                                    err_markup_begin, st->lastAnnounceResult, err_markup_end, timebuf);
     1963        }
     1964
     1965      switch (st->announceState)
     1966        {
     1967          case TR_TRACKER_INACTIVE:
    18021968            g_string_append_c (gstr, '\n');
    1803             tr_strltime_rounded (timebuf, now - st->lastAnnounceTime, sizeof (timebuf));
    1804             if (st->lastAnnounceSucceeded)
    1805                 g_string_append_printf (gstr, _("Got a list of %1$s%2$'d peers%3$s %4$s ago"),
    1806                                         success_markup_begin, st->lastAnnouncePeerCount, success_markup_end,
     1969            g_string_append (gstr, _("No updates scheduled"));
     1970            break;
     1971
     1972          case TR_TRACKER_WAITING:
     1973            tr_strltime_rounded (timebuf, st->nextAnnounceTime - now, sizeof (timebuf));
     1974            g_string_append_c (gstr, '\n');
     1975            g_string_append_printf (gstr, _("Asking for more peers in %s"), timebuf);
     1976            break;
     1977
     1978          case TR_TRACKER_QUEUED:
     1979            g_string_append_c (gstr, '\n');
     1980            g_string_append (gstr, _("Queued to ask for more peers"));
     1981            break;
     1982
     1983          case TR_TRACKER_ACTIVE:
     1984            tr_strltime_rounded (timebuf, now - st->lastAnnounceStartTime, sizeof (timebuf));
     1985            g_string_append_c (gstr, '\n');
     1986            g_string_append_printf (gstr, _("Asking for more peers now
 <small>%s</small>"), timebuf);
     1987            break;
     1988        }
     1989
     1990      if (showScrape)
     1991        {
     1992          if (st->hasScraped)
     1993            {
     1994              g_string_append_c (gstr, '\n');
     1995              tr_strltime_rounded (timebuf, now - st->lastScrapeTime, sizeof (timebuf));
     1996              if (st->lastScrapeSucceeded)
     1997                g_string_append_printf (gstr, _("Tracker had %s%'d seeders and %'d leechers%s %s ago"),
     1998                                        success_markup_begin, st->seederCount, st->leecherCount, success_markup_end,
    18071999                                        timebuf);
    1808             else if (st->lastAnnounceTimedOut)
    1809                 g_string_append_printf (gstr, _("Peer list request %1$stimed out%2$s %3$s ago; will retry"),
    1810                                         timeout_markup_begin, timeout_markup_end, timebuf);
    1811             else
    1812                 g_string_append_printf (gstr, _("Got an error %1$s\"%2$s\"%3$s %4$s ago"),
    1813                                         err_markup_begin, st->lastAnnounceResult, err_markup_end, timebuf);
    1814         }
    1815 
    1816         switch (st->announceState)
    1817         {
     2000              else
     2001                g_string_append_printf (gstr, _("Got a scrape error \"%s%s%s\" %s ago"), err_markup_begin, st->lastScrapeResult, err_markup_end, timebuf);
     2002            }
     2003
     2004          switch (st->scrapeState)
     2005            {
    18182006            case TR_TRACKER_INACTIVE:
    1819                 g_string_append_c (gstr, '\n');
    1820                 g_string_append (gstr, _("No updates scheduled"));
    1821                 break;
     2007              break;
     2008
    18222009            case TR_TRACKER_WAITING:
    1823                 tr_strltime_rounded (timebuf, st->nextAnnounceTime - now, sizeof (timebuf));
    1824                 g_string_append_c (gstr, '\n');
    1825                 g_string_append_printf (gstr, _("Asking for more peers in %s"), timebuf);
    1826                 break;
     2010              g_string_append_c (gstr, '\n');
     2011              tr_strltime_rounded (timebuf, st->nextScrapeTime - now, sizeof (timebuf));
     2012              g_string_append_printf (gstr, _("Asking for peer counts in %s"), timebuf);
     2013              break;
     2014
    18272015            case TR_TRACKER_QUEUED:
    1828                 g_string_append_c (gstr, '\n');
    1829                 g_string_append (gstr, _("Queued to ask for more peers"));
    1830                 break;
     2016              g_string_append_c (gstr, '\n');
     2017              g_string_append (gstr, _("Queued to ask for peer counts"));
     2018              break;
     2019
    18312020            case TR_TRACKER_ACTIVE:
    1832                 tr_strltime_rounded (timebuf, now - st->lastAnnounceStartTime, sizeof (timebuf));
    1833                 g_string_append_c (gstr, '\n');
    1834                 g_string_append_printf (gstr, _("Asking for more peers now
 <small>%s</small>"), timebuf);
    1835                 break;
    1836         }
    1837 
    1838         if (showScrape)
    1839         {
    1840             if (st->hasScraped) {
    1841                 g_string_append_c (gstr, '\n');
    1842                 tr_strltime_rounded (timebuf, now - st->lastScrapeTime, sizeof (timebuf));
    1843                 if (st->lastScrapeSucceeded)
    1844                     g_string_append_printf (gstr, _("Tracker had %s%'d seeders and %'d leechers%s %s ago"),
    1845                                             success_markup_begin, st->seederCount, st->leecherCount, success_markup_end,
    1846                                             timebuf);
    1847                 else
    1848                     g_string_append_printf (gstr, _("Got a scrape error \"%s%s%s\" %s ago"), err_markup_begin, st->lastScrapeResult, err_markup_end, timebuf);
    1849             }
    1850 
    1851             switch (st->scrapeState)
    1852             {
    1853                 case TR_TRACKER_INACTIVE:
    1854                     break;
    1855                 case TR_TRACKER_WAITING:
    1856                     g_string_append_c (gstr, '\n');
    1857                     tr_strltime_rounded (timebuf, st->nextScrapeTime - now, sizeof (timebuf));
    1858                     g_string_append_printf (gstr, _("Asking for peer counts in %s"), timebuf);
    1859                     break;
    1860                 case TR_TRACKER_QUEUED:
    1861                     g_string_append_c (gstr, '\n');
    1862                     g_string_append (gstr, _("Queued to ask for peer counts"));
    1863                     break;
    1864                 case TR_TRACKER_ACTIVE:
    1865                     g_string_append_c (gstr, '\n');
    1866                     tr_strltime_rounded (timebuf, now - st->lastScrapeStartTime, sizeof (timebuf));
    1867                     g_string_append_printf (gstr, _("Asking for peer counts now
 <small>%s</small>"), timebuf);
    1868                     break;
     2021              g_string_append_c (gstr, '\n');
     2022              tr_strltime_rounded (timebuf, now - st->lastScrapeStartTime, sizeof (timebuf));
     2023              g_string_append_printf (gstr, _("Asking for peer counts now
 <small>%s</small>"), timebuf);
     2024              break;
    18692025            }
    18702026        }
     
    18872043trackerVisibleFunc (GtkTreeModel * model, GtkTreeIter * iter, gpointer data)
    18882044{
    1889     gboolean isBackup;
    1890     struct DetailsImpl * di = data;
    1891 
    1892     /* show all */
    1893     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (di->all_check)))
    1894         return TRUE;
    1895 
    1896      /* don't show the backups... */
    1897      gtk_tree_model_get (model, iter, TRACKER_COL_IS_BACKUP, &isBackup, -1);
    1898      return !isBackup;
     2045  gboolean isBackup;
     2046  struct DetailsImpl * di = data;
     2047
     2048  /* show all */
     2049  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (di->all_check)))
     2050    return TRUE;
     2051
     2052  /* don't show the backups... */
     2053  gtk_tree_model_get (model, iter, TRACKER_COL_IS_BACKUP, &isBackup, -1);
     2054  return !isBackup;
    18992055}
    19002056
     
    19022058tracker_list_get_current_torrent_id (struct DetailsImpl * di)
    19032059{
    1904     int torrent_id = -1;
    1905 
    1906     /* if there's only one torrent in the dialog, always use it */
    1907     if (torrent_id < 0)
    1908         if (g_slist_length (di->ids) == 1)
    1909             torrent_id = GPOINTER_TO_INT (di->ids->data);
    1910 
    1911     /* otherwise, use the selected tracker's torrent */
    1912     if (torrent_id < 0) {
    1913         GtkTreeIter iter;
    1914         GtkTreeModel * model;
    1915         GtkTreeSelection * sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (di->tracker_view));
    1916         if (gtk_tree_selection_get_selected (sel, &model, &iter))
    1917             gtk_tree_model_get (model, &iter, TRACKER_COL_TORRENT_ID, &torrent_id, -1);
    1918     }
    1919 
    1920     return torrent_id;
     2060  int torrent_id = -1;
     2061
     2062  /* if there's only one torrent in the dialog, always use it */
     2063  if (torrent_id < 0)
     2064    if (g_slist_length (di->ids) == 1)
     2065      torrent_id = GPOINTER_TO_INT (di->ids->data);
     2066
     2067  /* otherwise, use the selected tracker's torrent */
     2068  if (torrent_id < 0)
     2069    {
     2070      GtkTreeIter iter;
     2071      GtkTreeModel * model;
     2072      GtkTreeSelection * sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (di->tracker_view));
     2073      if (gtk_tree_selection_get_selected (sel, &model, &iter))
     2074        gtk_tree_model_get (model, &iter, TRACKER_COL_TORRENT_ID, &torrent_id, -1);
     2075    }
     2076
     2077  return torrent_id;
    19212078}
    19222079
     
    19242081tracker_list_get_current_torrent (struct DetailsImpl * di)
    19252082{
    1926     const int torrent_id = tracker_list_get_current_torrent_id (di);
    1927     return gtr_core_find_torrent (di->core, torrent_id);
     2083  const int torrent_id = tracker_list_get_current_torrent_id (di);
     2084  return gtr_core_find_torrent (di->core, torrent_id);
    19282085}
    19292086
     
    19312088favicon_ready_cb (gpointer pixbuf, gpointer vreference)
    19322089{
    1933     GtkTreeIter iter;
    1934     GtkTreeRowReference * reference = vreference;
    1935 
    1936     if (pixbuf != NULL)
    1937     {
    1938         GtkTreePath * path = gtk_tree_row_reference_get_path (reference);
    1939         GtkTreeModel * model = gtk_tree_row_reference_get_model (reference);
    1940 
    1941         if (gtk_tree_model_get_iter (model, &iter, path))
    1942             gtk_list_store_set (GTK_LIST_STORE (model), &iter,
    1943                                 TRACKER_COL_FAVICON, pixbuf,
    1944                                 -1);
    1945 
    1946         gtk_tree_path_free (path);
    1947 
    1948         g_object_unref (pixbuf);
    1949     }
    1950 
    1951     gtk_tree_row_reference_free (reference);
     2090  GtkTreeIter iter;
     2091  GtkTreeRowReference * reference = vreference;
     2092
     2093  if (pixbuf != NULL)
     2094    {
     2095      GtkTreePath * path = gtk_tree_row_reference_get_path (reference);
     2096      GtkTreeModel * model = gtk_tree_row_reference_get_model (reference);
     2097
     2098      if (gtk_tree_model_get_iter (model, &iter, path))
     2099        gtk_list_store_set (GTK_LIST_STORE (model), &iter, TRACKER_COL_FAVICON, pixbuf, -1);
     2100
     2101      gtk_tree_path_free (path);
     2102      g_object_unref (pixbuf);
     2103    }
     2104
     2105  gtk_tree_row_reference_free (reference);
    19522106}
    19532107
     
    19552109refreshTracker (struct DetailsImpl * di, tr_torrent ** torrents, int n)
    19562110{
    1957     int i;
    1958     int * statCount;
    1959     tr_tracker_stat ** stats;
    1960     GtkTreeIter iter;
    1961     GtkTreeModel * model;
    1962     GString * gstr = di->gstr; /* buffer for temporary strings */
    1963     GHashTable * hash = di->tracker_hash;
    1964     GtkListStore * store = di->tracker_store;
    1965     tr_session * session = gtr_core_session (di->core);
    1966     const gboolean showScrape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (di->scrape_check));
    1967 
    1968     /* step 1: get all the trackers */
    1969     statCount = g_new0 (int, n);
    1970     stats = g_new0 (tr_tracker_stat *, n);
    1971     for (i=0; i<n; ++i)
    1972         stats[i] = tr_torrentTrackers (torrents[i], &statCount[i]);
    1973 
    1974     /* step 2: mark all the trackers in the list as not-updated */
    1975     model = GTK_TREE_MODEL (store);
    1976     if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 0)) do
    1977         gtk_list_store_set (store, &iter, TRACKER_COL_WAS_UPDATED, FALSE, -1);
    1978     while (gtk_tree_model_iter_next (model, &iter));
    1979 
    1980     /* step 3: add any new trackers */
    1981     for (i=0; i<n; ++i) {
    1982         int j;
    1983         const int jn = statCount[i];
    1984         for (j=0; j<jn; ++j) {
    1985             const tr_torrent * tor = torrents[i];
    1986             const tr_tracker_stat * st = &stats[i][j];
    1987             const int torrent_id = tr_torrentId (tor);
    1988 
    1989             /* build the key to find the row */
    1990             g_string_truncate (gstr, 0);
    1991             g_string_append_printf (gstr, "%d\t%d\t%s", torrent_id, st->tier, st->announce);
    1992 
    1993             if (g_hash_table_lookup (hash, gstr->str) == NULL) {
    1994                 GtkTreePath * p;
    1995                 GtkTreeIter iter;
    1996                 GtkTreeRowReference * ref;
    1997 
    1998                 gtk_list_store_insert_with_values (store, &iter, -1,
    1999                     TRACKER_COL_TORRENT_ID, torrent_id,
    2000                     TRACKER_COL_TRACKER_ID, st->id,
    2001                     TRACKER_COL_KEY, gstr->str,
    2002                     -1);
    2003 
    2004                 p = gtk_tree_model_get_path (model, &iter);
    2005                 ref = gtk_tree_row_reference_new (model, p);
    2006                 g_hash_table_insert (hash, g_strdup (gstr->str), ref);
    2007                 ref = gtk_tree_row_reference_new (model, p);
    2008                 gtr_get_favicon_from_url (session, st->announce, favicon_ready_cb, ref);
    2009                 gtk_tree_path_free (p);
     2111  int i;
     2112  int * statCount;
     2113  tr_tracker_stat ** stats;
     2114  GtkTreeIter iter;
     2115  GtkTreeModel * model;
     2116  GString * gstr = di->gstr; /* buffer for temporary strings */
     2117  GHashTable * hash = di->tracker_hash;
     2118  GtkListStore * store = di->tracker_store;
     2119  tr_session * session = gtr_core_session (di->core);
     2120  const gboolean showScrape = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (di->scrape_check));
     2121
     2122  /* step 1: get all the trackers */
     2123  statCount = g_new0 (int, n);
     2124  stats = g_new0 (tr_tracker_stat *, n);
     2125  for (i=0; i<n; ++i)
     2126    stats[i] = tr_torrentTrackers (torrents[i], &statCount[i]);
     2127
     2128  /* step 2: mark all the trackers in the list as not-updated */
     2129  model = GTK_TREE_MODEL (store);
     2130  if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 0)) do
     2131    gtk_list_store_set (store, &iter, TRACKER_COL_WAS_UPDATED, FALSE, -1);
     2132  while (gtk_tree_model_iter_next (model, &iter));
     2133
     2134  /* step 3: add any new trackers */
     2135  for (i=0; i<n; ++i)
     2136    {
     2137      int j;
     2138      const int jn = statCount[i];
     2139      for (j=0; j<jn; ++j)
     2140        {
     2141          const tr_torrent * tor = torrents[i];
     2142          const tr_tracker_stat * st = &stats[i][j];
     2143          const int torrent_id = tr_torrentId (tor);
     2144
     2145          /* build the key to find the row */
     2146          g_string_truncate (gstr, 0);
     2147          g_string_append_printf (gstr, "%d\t%d\t%s", torrent_id, st->tier, st->announce);
     2148
     2149          if (g_hash_table_lookup (hash, gstr->str) == NULL)
     2150            {
     2151              GtkTreePath * p;
     2152              GtkTreeIter iter;
     2153              GtkTreeRowReference * ref;
     2154
     2155              gtk_list_store_insert_with_values (store, &iter, -1,
     2156                TRACKER_COL_TORRENT_ID, torrent_id,
     2157                TRACKER_COL_TRACKER_ID, st->id,
     2158                TRACKER_COL_KEY, gstr->str,
     2159                -1);
     2160
     2161              p = gtk_tree_model_get_path (model, &iter);
     2162              ref = gtk_tree_row_reference_new (model, p);
     2163              g_hash_table_insert (hash, g_strdup (gstr->str), ref);
     2164              ref = gtk_tree_row_reference_new (model, p);
     2165              gtr_get_favicon_from_url (session, st->announce, favicon_ready_cb, ref);
     2166              gtk_tree_path_free (p);
    20102167            }
    20112168        }
    20122169    }
    20132170
    2014     /* step 4: update the peers */
    2015     for (i=0; i<n; ++i)
    2016     {
    2017         int j;
    2018         const tr_torrent * tor = torrents[i];
    2019         const char * summary_name = n>1 ? tr_torrentName (tor) : NULL;
    2020         for (j=0; j<statCount[i]; ++j)
    2021         {
    2022             GtkTreePath * p;
    2023             GtkTreeRowReference * ref;
    2024             const tr_tracker_stat * st = &stats[i][j];
    2025 
    2026             /* build the key to find the row */
    2027             g_string_truncate (gstr, 0);
    2028             g_string_append_printf (gstr, "%d\t%d\t%s", tr_torrentId (tor), st->tier, st->announce);
    2029             ref = g_hash_table_lookup (hash, gstr->str);
    2030             p = gtk_tree_row_reference_get_path (ref);
    2031             gtk_tree_model_get_iter (model, &iter, p);
    2032 
    2033             /* update the row */
    2034             g_string_truncate (gstr, 0);
    2035             buildTrackerSummary (gstr, summary_name, st, showScrape);
    2036             gtk_list_store_set (store, &iter, TRACKER_COL_TEXT, gstr->str,
    2037                                               TRACKER_COL_IS_BACKUP, st->isBackup,
    2038                                               TRACKER_COL_TRACKER_ID, st->id,
    2039                                               TRACKER_COL_WAS_UPDATED, TRUE,
    2040                                               -1);
    2041 
    2042             /* cleanup */
    2043             gtk_tree_path_free (p);
    2044         }
    2045     }
    2046 
    2047     /* step 5: remove trackers that have disappeared */
    2048     if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 0)) {
    2049         gboolean more = TRUE;
    2050         while (more) {
    2051             gboolean b;
    2052             gtk_tree_model_get (model, &iter, TRACKER_COL_WAS_UPDATED, &b, -1);
    2053             if (b)
    2054                 more = gtk_tree_model_iter_next (model, &iter);
    2055             else {
    2056                 char * key;
    2057                 gtk_tree_model_get (model, &iter, TRACKER_COL_KEY, &key, -1);
    2058                 g_hash_table_remove (hash, key);
    2059                 more = gtk_list_store_remove (store, &iter);
    2060                 g_free (key);
     2171  /* step 4: update the peers */
     2172  for (i=0; i<n; ++i)
     2173    {
     2174      int j;
     2175      const tr_torrent * tor = torrents[i];
     2176      const char * summary_name = n>1 ? tr_torrentName (tor) : NULL;
     2177
     2178      for (j=0; j<statCount[i]; ++j)
     2179        {
     2180          GtkTreePath * p;
     2181          GtkTreeRowReference * ref;
     2182          const tr_tracker_stat * st = &stats[i][j];
     2183
     2184          /* build the key to find the row */
     2185          g_string_truncate (gstr, 0);
     2186          g_string_append_printf (gstr, "%d\t%d\t%s", tr_torrentId (tor), st->tier, st->announce);
     2187          ref = g_hash_table_lookup (hash, gstr->str);
     2188          p = gtk_tree_row_reference_get_path (ref);
     2189          gtk_tree_model_get_iter (model, &iter, p);
     2190
     2191          /* update the row */
     2192          g_string_truncate (gstr, 0);
     2193          buildTrackerSummary (gstr, summary_name, st, showScrape);
     2194          gtk_list_store_set (store, &iter, TRACKER_COL_TEXT, gstr->str,
     2195                                            TRACKER_COL_IS_BACKUP, st->isBackup,
     2196                                            TRACKER_COL_TRACKER_ID, st->id,
     2197                                            TRACKER_COL_WAS_UPDATED, TRUE,
     2198                                            -1);
     2199
     2200          /* cleanup */
     2201          gtk_tree_path_free (p);
     2202        }
     2203    }
     2204
     2205  /* step 5: remove trackers that have disappeared */
     2206  if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 0))
     2207    {
     2208      gboolean more = TRUE;
     2209
     2210      while (more)
     2211        {
     2212          gboolean b;
     2213          gtk_tree_model_get (model, &iter, TRACKER_COL_WAS_UPDATED, &b, -1);
     2214          if (b)
     2215            {
     2216              more = gtk_tree_model_iter_next (model, &iter);
    20612217            }
    2062         }
    2063     }
    2064 
    2065     gtk_widget_set_sensitive (di->edit_trackers_button,
    2066                               tracker_list_get_current_torrent_id (di) >= 0);
    2067 
    2068     /* cleanup */
    2069     for (i=0; i<n; ++i)
    2070         tr_torrentTrackersFree (stats[i], statCount[i]);
    2071     g_free (stats);
    2072     g_free (statCount);
     2218          else
     2219            {
     2220              char * key;
     2221              gtk_tree_model_get (model, &iter, TRACKER_COL_KEY, &key, -1);
     2222              g_hash_table_remove (hash, key);
     2223              more = gtk_list_store_remove (store, &iter);
     2224              g_free (key);
     2225            }
     2226        }
     2227    }
     2228
     2229  gtk_widget_set_sensitive (di->edit_trackers_button,
     2230                            tracker_list_get_current_torrent_id (di) >= 0);
     2231
     2232  /* cleanup */
     2233  for (i=0; i<n; ++i)
     2234    tr_torrentTrackersFree (stats[i], statCount[i]);
     2235  g_free (stats);
     2236  g_free (statCount);
    20732237}
    20742238
     
    20942258on_edit_trackers_response (GtkDialog * dialog, int response, gpointer data)
    20952259{
    2096     gboolean do_destroy = TRUE;
    2097     struct DetailsImpl * di = data;
    2098 
    2099     if (response == GTK_RESPONSE_ACCEPT)
    2100     {
    2101         int i, n;
    2102         int tier;
    2103         GtkTextIter start, end;
    2104         const int torrent_id = GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (dialog), TORRENT_ID_KEY));
    2105         GtkTextBuffer * text_buffer = g_object_get_qdata (G_OBJECT (dialog), TEXT_BUFFER_KEY);
    2106         tr_torrent * tor = gtr_core_find_torrent (di->core, torrent_id);
    2107 
    2108         if (tor != NULL)
    2109         {
    2110             tr_tracker_info * trackers;
    2111             char ** tracker_strings;
    2112             char * tracker_text;
    2113 
    2114             /* build the array of trackers */
    2115             gtk_text_buffer_get_bounds (text_buffer, &start, &end);
    2116             tracker_text = gtk_text_buffer_get_text (text_buffer, &start, &end, FALSE);
    2117             tracker_strings = g_strsplit (tracker_text, "\n", 0);
    2118             for (i=0; tracker_strings[i];)
    2119                 ++i;
    2120             trackers = g_new0 (tr_tracker_info, i);
    2121             for (i=n=tier=0; tracker_strings[i]; ++i) {
    2122                 const char * str = tracker_strings[i];
    2123                 if (!*str)
    2124                     ++tier;
    2125                 else {
    2126                     trackers[n].tier = tier;
    2127                     trackers[n].announce = tracker_strings[i];
    2128                     ++n;
     2260  gboolean do_destroy = TRUE;
     2261  struct DetailsImpl * di = data;
     2262
     2263  if (response == GTK_RESPONSE_ACCEPT)
     2264    {
     2265      int i, n;
     2266      int tier;
     2267      GtkTextIter start, end;
     2268      const int torrent_id = GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (dialog), TORRENT_ID_KEY));
     2269      GtkTextBuffer * text_buffer = g_object_get_qdata (G_OBJECT (dialog), TEXT_BUFFER_KEY);
     2270      tr_torrent * tor = gtr_core_find_torrent (di->core, torrent_id);
     2271
     2272      if (tor != NULL)
     2273        {
     2274          tr_tracker_info * trackers;
     2275          char ** tracker_strings;
     2276          char * tracker_text;
     2277
     2278          /* build the array of trackers */
     2279          gtk_text_buffer_get_bounds (text_buffer, &start, &end);
     2280          tracker_text = gtk_text_buffer_get_text (text_buffer, &start, &end, FALSE);
     2281          tracker_strings = g_strsplit (tracker_text, "\n", 0);
     2282          for (i=0; tracker_strings[i];)
     2283            ++i;
     2284          trackers = g_new0 (tr_tracker_info, i);
     2285          for (i=n=tier=0; tracker_strings[i]; ++i)
     2286            {
     2287              const char * str = tracker_strings[i];
     2288              if (!*str)
     2289                {
     2290                  ++tier;
     2291                }
     2292              else
     2293                {
     2294                  trackers[n].tier = tier;
     2295                  trackers[n].announce = tracker_strings[i];
     2296                  ++n;
    21292297                }
    21302298            }
    21312299
    2132             /* update the torrent */
    2133             if (tr_torrentSetAnnounceList (tor, trackers, n))
    2134                 refresh (di);
    2135             else {
    2136                 GtkWidget * w;
    2137                 const char * text = _("List contains invalid URLs");
    2138                 w = gtk_message_dialog_new (GTK_WINDOW (dialog),
    2139                                             GTK_DIALOG_MODAL,
    2140                                             GTK_MESSAGE_ERROR,
    2141                                             GTK_BUTTONS_CLOSE, "%s", text);
    2142                 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (w), "%s", _("Please correct the errors and try again."));
    2143                 gtk_dialog_run (GTK_DIALOG (w));
    2144                 gtk_widget_destroy (w);
    2145                 do_destroy = FALSE;
     2300          /* update the torrent */
     2301          if (tr_torrentSetAnnounceList (tor, trackers, n))
     2302            {
     2303              refresh (di);
    21462304            }
    2147 
    2148             /* cleanup */
    2149             g_free (trackers);
    2150             g_strfreev (tracker_strings);
    2151             g_free (tracker_text);
    2152         }
    2153     }
    2154 
    2155     if (do_destroy)
    2156         gtk_widget_destroy (GTK_WIDGET (dialog));
     2305          else
     2306            {
     2307              GtkWidget * w;
     2308              const char * text = _("List contains invalid URLs");
     2309              w = gtk_message_dialog_new (GTK_WINDOW (dialog),
     2310                                          GTK_DIALOG_MODAL,
     2311                                          GTK_MESSAGE_ERROR,
     2312                                          GTK_BUTTONS_CLOSE, "%s", text);
     2313              gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (w), "%s", _("Please correct the errors and try again."));
     2314              gtk_dialog_run (GTK_DIALOG (w));
     2315              gtk_widget_destroy (w);
     2316              do_destroy = FALSE;
     2317            }
     2318
     2319          /* cleanup */
     2320          g_free (trackers);
     2321          g_strfreev (tracker_strings);
     2322          g_free (tracker_text);
     2323        }
     2324    }
     2325
     2326  if (do_destroy)
     2327    gtk_widget_destroy (GTK_WIDGET (dialog));
    21572328}
    21582329
     
    21602331get_editable_tracker_list (GString * gstr, const tr_torrent * tor)
    21612332{
    2162     unsigned int i;
    2163     int tier = 0;
    2164     const tr_info * inf = tr_torrentInfo (tor);
    2165     for (i=0; i<inf->trackerCount; ++i) {
    2166         const tr_tracker_info * t = &inf->trackers[i];
    2167         if (tier != t->tier) {
    2168             tier = t->tier;
    2169             g_string_append_c (gstr, '\n');
    2170         }
    2171         g_string_append_printf (gstr, "%s\n", t->announce);
    2172     }
    2173     if (gstr->len > 0)
    2174         g_string_truncate (gstr, gstr->len-1);
     2333  unsigned int i;
     2334  int tier = 0;
     2335  const tr_info * inf = tr_torrentInfo (tor);
     2336
     2337  for (i=0; i<inf->trackerCount; ++i)
     2338    {
     2339      const tr_tracker_info * t = &inf->trackers[i];
     2340
     2341      if (tier != t->tier)
     2342        {
     2343          tier = t->tier;
     2344          g_string_append_c (gstr, '\n');
     2345        }
     2346
     2347      g_string_append_printf (gstr, "%s\n", t->announce);
     2348    }
     2349
     2350  if (gstr->len > 0)
     2351    g_string_truncate (gstr, gstr->len-1);
    21752352}
    21762353
     
    21782355on_edit_trackers (GtkButton * button, gpointer data)
    21792356{
    2180     struct DetailsImpl * di = data;
    2181     tr_torrent * tor = tracker_list_get_current_torrent (di);
    2182 
    2183     if (tor != NULL)
    2184     {
    2185         guint row;
    2186         GtkWidget *w, *d, *fr, *t, *l, *sw;
    2187         GtkWindow * win = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (button)));
    2188         GString * gstr = di->gstr; /* buffer for temporary strings */
    2189         const int torrent_id = tr_torrentId (tor);
    2190 
    2191         g_string_truncate (gstr, 0);
    2192         g_string_append_printf (gstr, _("%s - Edit Trackers"), tr_torrentName (tor));
    2193         d = gtk_dialog_new_with_buttons (gstr->str, win,
    2194                 GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
    2195                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
    2196                 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
    2197                 NULL);
    2198         g_signal_connect (d, "response", G_CALLBACK (on_edit_trackers_response), data);
    2199 
    2200         row = 0;
    2201         t = hig_workarea_create ();
    2202         hig_workarea_add_section_title (t, &row, _("Tracker Announce URLs"));
    2203 
    2204         l = gtk_label_new (NULL);
    2205         gtk_label_set_markup (GTK_LABEL (l), _("To add a backup URL, add it on the line after the primary URL.\n"
    2206                                                  "To add another primary URL, add it after a blank line."));
    2207         gtk_label_set_justify (GTK_LABEL (l), GTK_JUSTIFY_LEFT);
    2208         gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
    2209         hig_workarea_add_wide_control (t, &row, l);
    2210 
    2211         w = gtk_text_view_new ();
    2212         g_string_truncate (gstr, 0);
    2213         get_editable_tracker_list (gstr, tor);
    2214         gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (w)), gstr->str, -1);
    2215         fr = gtk_frame_new (NULL);
    2216         gtk_frame_set_shadow_type (GTK_FRAME (fr), GTK_SHADOW_IN);
    2217         sw = gtk_scrolled_window_new (NULL, NULL);
    2218         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
    2219                                         GTK_POLICY_AUTOMATIC,
    2220                                         GTK_POLICY_AUTOMATIC);
    2221         gtk_container_add (GTK_CONTAINER (sw), w);
    2222         gtk_container_add (GTK_CONTAINER (fr), sw);
    2223         gtk_widget_set_size_request (fr, 500u, 166u);
    2224         hig_workarea_add_wide_tall_control (t, &row, fr);
    2225 
    2226         gtr_dialog_set_content (GTK_DIALOG (d), t);
    2227 
    2228         g_object_set_qdata (G_OBJECT (d), TORRENT_ID_KEY, GINT_TO_POINTER (torrent_id));
    2229         g_object_set_qdata (G_OBJECT (d), TEXT_BUFFER_KEY, gtk_text_view_get_buffer (GTK_TEXT_VIEW (w)));
    2230         gtk_widget_show (d);
     2357  struct DetailsImpl * di = data;
     2358  tr_torrent * tor = tracker_list_get_current_torrent (di);
     2359
     2360  if (tor != NULL)
     2361    {
     2362      guint row;
     2363      GtkWidget *w, *d, *fr, *t, *l, *sw;
     2364      GtkWindow * win = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (button)));
     2365      GString * gstr = di->gstr; /* buffer for temporary strings */
     2366      const int torrent_id = tr_torrentId (tor);
     2367
     2368      g_string_truncate (gstr, 0);
     2369      g_string_append_printf (gstr, _("%s - Edit Trackers"), tr_torrentName (tor));
     2370      d = gtk_dialog_new_with_buttons (gstr->str, win,
     2371            GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
     2372            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
     2373            GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
     2374            NULL);
     2375      g_signal_connect (d, "response", G_CALLBACK (on_edit_trackers_response), data);
     2376
     2377      row = 0;
     2378      t = hig_workarea_create ();
     2379      hig_workarea_add_section_title (t, &row, _("Tracker Announce URLs"));
     2380
     2381      l = gtk_label_new (NULL);
     2382      gtk_label_set_markup (GTK_LABEL (l), _("To add a backup URL, add it on the line after the primary URL.\n"
     2383                                             "To add another primary URL, add it after a blank line."));
     2384      gtk_label_set_justify (GTK_LABEL (l), GTK_JUSTIFY_LEFT);
     2385      gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
     2386      hig_workarea_add_wide_control (t, &row, l);
     2387
     2388      w = gtk_text_view_new ();
     2389      g_string_truncate (gstr, 0);
     2390      get_editable_tracker_list (gstr, tor);
     2391      gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (w)), gstr->str, -1);
     2392      fr = gtk_frame_new (NULL);
     2393      gtk_frame_set_shadow_type (GTK_FRAME (fr), GTK_SHADOW_IN);
     2394      sw = gtk_scrolled_window_new (NULL, NULL);
     2395      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
     2396                                      GTK_POLICY_AUTOMATIC,
     2397                                      GTK_POLICY_AUTOMATIC);
     2398      gtk_container_add (GTK_CONTAINER (sw), w);
     2399      gtk_container_add (GTK_CONTAINER (fr), sw);
     2400      gtk_widget_set_size_request (fr, 500u, 166u);
     2401      hig_workarea_add_wide_tall_control (t, &row, fr);
     2402
     2403      gtr_dialog_set_content (GTK_DIALOG (d), t);
     2404
     2405      g_object_set_qdata (G_OBJECT (d), TORRENT_ID_KEY, GINT_TO_POINTER (torrent_id));
     2406      g_object_set_qdata (G_OBJECT (d), TEXT_BUFFER_KEY, gtk_text_view_get_buffer (GTK_TEXT_VIEW (w)));
     2407      gtk_widget_show (d);
    22312408    }
    22322409}
     
    22352412on_tracker_list_selection_changed (GtkTreeSelection * sel, gpointer gdi)
    22362413{
    2237     struct DetailsImpl * di = gdi;
    2238     const int n = gtk_tree_selection_count_selected_rows (sel);
    2239     tr_torrent * tor = tracker_list_get_current_torrent (di);
    2240 
    2241     gtk_widget_set_sensitive (di->remove_tracker_button, n>0);
    2242     gtk_widget_set_sensitive (di->add_tracker_button, tor!=NULL);
    2243     gtk_widget_set_sensitive (di->edit_trackers_button, tor!=NULL);
     2414  struct DetailsImpl * di = gdi;
     2415  const int n = gtk_tree_selection_count_selected_rows (sel);
     2416  tr_torrent * tor = tracker_list_get_current_torrent (di);
     2417
     2418  gtk_widget_set_sensitive (di->remove_tracker_button, n>0);
     2419  gtk_widget_set_sensitive (di->add_tracker_button, tor!=NULL);
     2420  gtk_widget_set_sensitive (di->edit_trackers_button, tor!=NULL);
    22442421}
    22452422
     
    22472424on_add_tracker_response (GtkDialog * dialog, int response, gpointer gdi)
    22482425{
    2249     gboolean destroy = TRUE;
    2250 
    2251     if (response == GTK_RESPONSE_ACCEPT)
    2252     {
    2253         struct DetailsImpl * di = gdi;
    2254         GtkWidget * e = GTK_WIDGET (g_object_get_qdata (G_OBJECT (dialog), URL_ENTRY_KEY));
    2255         const int torrent_id = GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (dialog), TORRENT_ID_KEY));
    2256         char * url = g_strdup (gtk_entry_get_text (GTK_ENTRY (e)));
    2257         g_strstrip (url);
    2258 
    2259         if (url && *url)
    2260         {
    2261             if (tr_urlIsValidTracker (url))
     2426  gboolean destroy = TRUE;
     2427
     2428  if (response == GTK_RESPONSE_ACCEPT)
     2429    {
     2430      struct DetailsImpl * di = gdi;
     2431      GtkWidget * e = GTK_WIDGET (g_object_get_qdata (G_OBJECT (dialog), URL_ENTRY_KEY));
     2432      const int torrent_id = GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (dialog), TORRENT_ID_KEY));
     2433      char * url = g_strdup (gtk_entry_get_text (GTK_ENTRY (e)));
     2434      g_strstrip (url);
     2435
     2436      if (url && *url)
     2437        {
     2438          if (tr_urlIsValidTracker (url))
    22622439            {
    2263                 char * json = g_strdup_printf (
    2264                     "{\n"
    2265                     "  \"method\": \"torrent-set\",\n"
    2266                     "  \"arguments\": { \"id\": %d, \"trackerAdd\": [ \"%s\" ] }\n"
    2267                     "}\n",
    2268                     torrent_id, url);
    2269                 gtr_core_exec_json (di->core, json);
    2270                 refresh (di);
    2271                 g_free (json);
     2440              char * json = g_strdup_printf (
     2441                "{\n"
     2442                "  \"method\": \"torrent-set\",\n"
     2443                "  \"arguments\": { \"id\": %d, \"trackerAdd\": [ \"%s\" ] }\n"
     2444                "}\n",
     2445              torrent_id, url);
     2446              gtr_core_exec_json (di->core, json);
     2447              refresh (di);
     2448              g_free (json);
    22722449            }
    2273             else
     2450          else
    22742451            {
    2275                 gtr_unrecognized_url_dialog (GTK_WIDGET (dialog), url);
    2276                 destroy = FALSE;
     2452              gtr_unrecognized_url_dialog (GTK_WIDGET (dialog), url);
     2453              destroy = FALSE;
    22772454            }
    22782455        }
    22792456
    2280         g_free (url);
    2281     }
    2282 
    2283     if (destroy)
    2284         gtk_widget_destroy (GTK_WIDGET (dialog));
     2457      g_free (url);
     2458    }
     2459
     2460  if (destroy)
     2461    gtk_widget_destroy (GTK_WIDGET (dialog));
    22852462}
    22862463
     
    22882465on_tracker_list_add_button_clicked (GtkButton * button UNUSED, gpointer gdi)
    22892466{
    2290     struct DetailsImpl * di = gdi;
    2291     tr_torrent * tor = tracker_list_get_current_torrent (di);
    2292 
    2293     if (tor != NULL)
    2294     {
    2295         guint row;
    2296         GtkWidget * e;
    2297         GtkWidget * t;
    2298         GtkWidget * w;
    2299         GString * gstr = di->gstr; /* buffer for temporary strings */
    2300 
    2301         g_string_truncate (gstr, 0);
    2302         g_string_append_printf (gstr, _("%s - Add Tracker"), tr_torrentName (tor));
    2303         w = gtk_dialog_new_with_buttons (gstr->str, GTK_WINDOW (di->dialog),
    2304                                          GTK_DIALOG_DESTROY_WITH_PARENT,
    2305                                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
    2306                                          GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT,
    2307                                          NULL);
    2308         gtk_dialog_set_alternative_button_order (GTK_DIALOG (w),
    2309                                                  GTK_RESPONSE_ACCEPT,
    2310                                                  GTK_RESPONSE_CANCEL,
    2311                                                  -1);
    2312         g_signal_connect (w, "response", G_CALLBACK (on_add_tracker_response), gdi);
    2313 
    2314         row = 0;
    2315         t = hig_workarea_create ();
    2316         hig_workarea_add_section_title (t, &row, _("Tracker"));
    2317         e = gtk_entry_new ();
    2318         gtk_widget_set_size_request (e, 400, -1);
    2319         gtr_paste_clipboard_url_into_entry (e);
    2320         g_object_set_qdata (G_OBJECT (w), URL_ENTRY_KEY, e);
    2321         g_object_set_qdata (G_OBJECT (w), TORRENT_ID_KEY, GINT_TO_POINTER (tr_torrentId (tor)));
    2322         hig_workarea_add_row (t, &row, _("_Announce URL:"), e, NULL);
    2323         gtr_dialog_set_content (GTK_DIALOG (w), t);
    2324         gtk_widget_show_all (w);
     2467  struct DetailsImpl * di = gdi;
     2468  tr_torrent * tor = tracker_list_get_current_torrent (di);
     2469
     2470  if (tor != NULL)
     2471    {
     2472      guint row;
     2473      GtkWidget * e;
     2474      GtkWidget * t;
     2475      GtkWidget * w;
     2476      GString * gstr = di->gstr; /* buffer for temporary strings */
     2477
     2478      g_string_truncate (gstr, 0);
     2479      g_string_append_printf (gstr, _("%s - Add Tracker"), tr_torrentName (tor));
     2480      w = gtk_dialog_new_with_buttons (gstr->str, GTK_WINDOW (di->dialog),
     2481                                       GTK_DIALOG_DESTROY_WITH_PARENT,
     2482                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
     2483                                       GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT,
     2484                                       NULL);
     2485      gtk_dialog_set_alternative_button_order (GTK_DIALOG (w),
     2486                                               GTK_RESPONSE_ACCEPT,
     2487                                               GTK_RESPONSE_CANCEL,
     2488                                               -1);
     2489      g_signal_connect (w, "response", G_CALLBACK (on_add_tracker_response), gdi);
     2490
     2491      row = 0;
     2492      t = hig_workarea_create ();
     2493      hig_workarea_add_section_title (t, &row, _("Tracker"));
     2494      e = gtk_entry_new ();
     2495      gtk_widget_set_size_request (e, 400, -1);
     2496      gtr_paste_clipboard_url_into_entry (e);
     2497      g_object_set_qdata (G_OBJECT (w), URL_ENTRY_KEY, e);
     2498      g_object_set_qdata (G_OBJECT (w), TORRENT_ID_KEY, GINT_TO_POINTER (tr_torrentId (tor)));
     2499      hig_workarea_add_row (t, &row, _("_Announce URL:"), e, NULL);
     2500      gtr_dialog_set_content (GTK_DIALOG (w), t);
     2501      gtk_widget_show_all (w);
    23252502    }
    23262503}
     
    23292506on_tracker_list_remove_button_clicked (GtkButton * button UNUSED, gpointer gdi)
    23302507{
    2331     GtkTreeIter iter;
    2332     GtkTreeModel * model;
    2333     struct DetailsImpl * di = gdi;
    2334     GtkTreeView * v = GTK_TREE_VIEW (di->tracker_view);
    2335     GtkTreeSelection * sel = gtk_tree_view_get_selection (v);
    2336 
    2337     if (gtk_tree_selection_get_selected (sel, &model, &iter))
    2338     {
    2339         char * json;
    2340         int torrent_id;
    2341         int tracker_id;
    2342         gtk_tree_model_get (model, &iter, TRACKER_COL_TRACKER_ID, &tracker_id,
    2343                                           TRACKER_COL_TORRENT_ID, &torrent_id,
    2344                                           -1);
    2345         json = g_strdup_printf ("{\n"
    2346                                 "  \"method\": \"torrent-set\",\n"
    2347                                 "  \"arguments\": { \"id\": %d, \"trackerRemove\": [ %d ] }\n"
    2348                                 "}\n",
    2349                                 torrent_id, tracker_id);
    2350         gtr_core_exec_json (di->core, json);
    2351         refresh (di);
    2352         g_free (json);
     2508  GtkTreeIter iter;
     2509  GtkTreeModel * model;
     2510  struct DetailsImpl * di = gdi;
     2511  GtkTreeView * v = GTK_TREE_VIEW (di->tracker_view);
     2512  GtkTreeSelection * sel = gtk_tree_view_get_selection (v);
     2513
     2514  if (gtk_tree_selection_get_selected (sel, &model, &iter))
     2515    {
     2516      char * json;
     2517      int torrent_id;
     2518      int tracker_id;
     2519      gtk_tree_model_get (model, &iter, TRACKER_COL_TRACKER_ID, &tracker_id,
     2520                                        TRACKER_COL_TORRENT_ID, &torrent_id,
     2521                                        -1);
     2522      json = g_strdup_printf ("{\n"
     2523                              "  \"method\": \"torrent-set\",\n"
     2524                              "  \"arguments\": { \"id\": %d, \"trackerRemove\": [ %d ] }\n"
     2525                              "}\n",
     2526                              torrent_id, tracker_id);
     2527      gtr_core_exec_json (di->core, json);
     2528      refresh (di);
     2529      g_free (json);
    23532530    }
    23542531}
     
    23572534tracker_page_new (struct DetailsImpl * di)
    23582535{
    2359     gboolean b;
    2360     GtkCellRenderer * r;
    2361     GtkTreeViewColumn * c;
    2362     GtkTreeSelection * sel;
    2363     GtkWidget *vbox, *sw, *w, *v, *hbox;
    2364     const int pad = (GUI_PAD + GUI_PAD_BIG) / 2;
    2365 
    2366     vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, GUI_PAD);
    2367     gtk_container_set_border_width (GTK_CONTAINER (vbox), GUI_PAD_BIG);
    2368 
    2369     di->tracker_store = gtk_list_store_new (TRACKER_N_COLS, G_TYPE_INT,
    2370                                                             G_TYPE_STRING,
    2371                                                             G_TYPE_BOOLEAN,
    2372                                                             G_TYPE_INT,
    2373                                                             GDK_TYPE_PIXBUF,
    2374                                                             G_TYPE_BOOLEAN,
    2375                                                             G_TYPE_STRING);
    2376     di->tracker_hash = g_hash_table_new_full (g_str_hash,
    2377                                               g_str_equal,
     2536  gboolean b;
     2537  GtkCellRenderer * r;
     2538  GtkTreeViewColumn * c;
     2539  GtkTreeSelection * sel;
     2540  GtkWidget *vbox, *sw, *w, *v, *hbox;
     2541  const int pad = (GUI_PAD + GUI_PAD_BIG) / 2;
     2542
     2543  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, GUI_PAD);
     2544  gtk_container_set_border_width (GTK_CONTAINER (vbox), GUI_PAD_BIG);
     2545
     2546  di->tracker_store = gtk_list_store_new (TRACKER_N_COLS,
     2547                                          G_TYPE_INT,
     2548                                          G_TYPE_STRING,
     2549                                          G_TYPE_BOOLEAN,
     2550                                          G_TYPE_INT,
     2551                                          GDK_TYPE_PIXBUF,
     2552                                          G_TYPE_BOOLEAN,
     2553                                          G_TYPE_STRING);
     2554
     2555  di->tracker_hash = g_hash_table_new_full (g_str_hash,
     2556                                            g_str_equal,
    23782557                                            (GDestroyNotify)g_free,
    23792558                                            (GDestroyNotify)gtk_tree_row_reference_free);
    2380     di->trackers_filtered = gtk_tree_model_filter_new (GTK_TREE_MODEL (di->tracker_store), NULL);
    2381     gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (di->trackers_filtered),
    2382                                             trackerVisibleFunc, di, NULL);
    2383 
    2384     hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GUI_PAD_BIG);
    2385 
    2386         v = di->tracker_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (di->trackers_filtered));
    2387         g_object_unref (di->trackers_filtered);
    2388         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (v), FALSE);
    2389         g_signal_connect (v, "button-press-event", G_CALLBACK (on_tree_view_button_pressed), NULL);
    2390         g_signal_connect (v, "button-release-event", G_CALLBACK (on_tree_view_button_released), NULL);
    2391         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (v), TRUE);
    2392 
    2393         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (v));
    2394         g_signal_connect (sel, "changed", G_CALLBACK (on_tracker_list_selection_changed), di);
    2395 
    2396         c = gtk_tree_view_column_new ();
    2397         gtk_tree_view_column_set_title (c, _("Trackers"));
    2398         gtk_tree_view_append_column (GTK_TREE_VIEW (v), c);
    2399 
    2400         r = gtk_cell_renderer_pixbuf_new ();
    2401         g_object_set (r, "width", 20 + (GUI_PAD_SMALL*2), "xpad", GUI_PAD_SMALL, "ypad", pad, "yalign", 0.0f, NULL);
    2402         gtk_tree_view_column_pack_start (c, r, FALSE);
    2403         gtk_tree_view_column_add_attribute (c, r, "pixbuf", TRACKER_COL_FAVICON);
    2404 
    2405         r = gtk_cell_renderer_text_new ();
    2406         g_object_set (G_OBJECT (r), "ellipsize", PANGO_ELLIPSIZE_END, "xpad", GUI_PAD_SMALL, "ypad", pad, NULL);
    2407         gtk_tree_view_column_pack_start (c, r, TRUE);
    2408         gtk_tree_view_column_add_attribute (c, r, "markup", TRACKER_COL_TEXT);
    2409 
    2410         sw = gtk_scrolled_window_new (NULL, NULL);
    2411         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    2412         gtk_container_add (GTK_CONTAINER (sw), v);
    2413         w = gtk_frame_new (NULL);
    2414         gtk_frame_set_shadow_type (GTK_FRAME (w), GTK_SHADOW_IN);
    2415         gtk_container_add (GTK_CONTAINER (w), sw);
    2416 
    2417     gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
    2418 
    2419         v = gtk_box_new (GTK_ORIENTATION_VERTICAL, GUI_PAD);
    2420 
    2421         w = gtk_button_new_with_mnemonic (_("_Add"));
    2422         di->add_tracker_button = w;
    2423         g_signal_connect (w, "clicked", G_CALLBACK (on_tracker_list_add_button_clicked), di);
    2424         gtk_box_pack_start (GTK_BOX (v), w, FALSE, FALSE, 0);
    2425 
    2426         w = gtk_button_new_with_mnemonic (_("_Edit"));
    2427         gtk_button_set_image (GTK_BUTTON (w), gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON));
    2428         g_signal_connect (w, "clicked", G_CALLBACK (on_edit_trackers), di);
    2429         di->edit_trackers_button = w;
    2430         gtk_box_pack_start (GTK_BOX (v), w, FALSE, FALSE, 0);
    2431 
    2432         w = gtk_button_new_with_mnemonic (_("_Remove"));
    2433         di->remove_tracker_button = w;
    2434         g_signal_connect (w, "clicked", G_CALLBACK (on_tracker_list_remove_button_clicked), di);
    2435         gtk_box_pack_start (GTK_BOX (v), w, FALSE, FALSE, 0);
    2436 
    2437         gtk_box_pack_start (GTK_BOX (hbox), v, FALSE, FALSE, 0);
    2438 
    2439     gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
    2440 
    2441     w = gtk_check_button_new_with_mnemonic (_("Show _more details"));
    2442     di->scrape_check = w;
    2443     b = gtr_pref_flag_get (TR_KEY_show_tracker_scrapes);
    2444     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), b);
    2445     g_signal_connect (w, "toggled", G_CALLBACK (onScrapeToggled), di);
    2446     gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0);
    2447 
    2448     w = gtk_check_button_new_with_mnemonic (_("Show _backup trackers"));
    2449     di->all_check = w;
    2450     b = gtr_pref_flag_get (TR_KEY_show_backup_trackers);
    2451     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), b);
    2452     g_signal_connect (w, "toggled", G_CALLBACK (onBackupToggled), di);
    2453     gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0);
    2454 
    2455     return vbox;
     2559
     2560  di->trackers_filtered = gtk_tree_model_filter_new (GTK_TREE_MODEL (di->tracker_store), NULL);
     2561  gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (di->trackers_filtered),
     2562                                          trackerVisibleFunc, di, NULL);
     2563
     2564  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GUI_PAD_BIG);
     2565
     2566    v = di->tracker_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (di->trackers_filtered));
     2567    g_object_unref (di->trackers_filtered);
     2568    gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (v), FALSE);
     2569    g_signal_connect (v, "button-press-event", G_CALLBACK (on_tree_view_button_pressed), NULL);
     2570    g_signal_connect (v, "button-release-event", G_CALLBACK (on_tree_view_button_released), NULL);
     2571    gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (v), TRUE);
     2572
     2573    sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (v));
     2574    g_signal_connect (sel, "changed", G_CALLBACK (on_tracker_list_selection_changed), di);
     2575
     2576    c = gtk_tree_view_column_new ();
     2577    gtk_tree_view_column_set_title (c, _("Trackers"));
     2578    gtk_tree_view_append_column (GTK_TREE_VIEW (v), c);
     2579
     2580    r = gtk_cell_renderer_pixbuf_new ();
     2581    g_object_set (r, "width", 20 + (GUI_PAD_SMALL*2), "xpad", GUI_PAD_SMALL, "ypad", pad, "yalign", 0.0f, NULL);
     2582    gtk_tree_view_column_pack_start (c, r, FALSE);
     2583    gtk_tree_view_column_add_attribute (c, r, "pixbuf", TRACKER_COL_FAVICON);
     2584
     2585    r = gtk_cell_renderer_text_new ();
     2586    g_object_set (G_OBJECT (r), "ellipsize", PANGO_ELLIPSIZE_END, "xpad", GUI_PAD_SMALL, "ypad", pad, NULL);
     2587    gtk_tree_view_column_pack_start (c, r, TRUE);
     2588    gtk_tree_view_column_add_attribute (c, r, "markup", TRACKER_COL_TEXT);
     2589
     2590    sw = gtk_scrolled_window_new (NULL, NULL);
     2591    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
     2592    gtk_container_add (GTK_CONTAINER (sw), v);
     2593    w = gtk_frame_new (NULL);
     2594    gtk_frame_set_shadow_type (GTK_FRAME (w), GTK_SHADOW_IN);
     2595    gtk_container_add (GTK_CONTAINER (w), sw);
     2596
     2597  gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
     2598
     2599    v = gtk_box_new (GTK_ORIENTATION_VERTICAL, GUI_PAD);
     2600
     2601    w = gtk_button_new_with_mnemonic (_("_Add"));
     2602    di->add_tracker_button = w;
     2603    g_signal_connect (w, "clicked", G_CALLBACK (on_tracker_list_add_button_clicked), di);
     2604    gtk_box_pack_start (GTK_BOX (v), w, FALSE, FALSE, 0);
     2605
     2606    w = gtk_button_new_with_mnemonic (_("_Edit"));
     2607    gtk_button_set_image (GTK_BUTTON (w), gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON));
     2608    g_signal_connect (w, "clicked", G_CALLBACK (on_edit_trackers), di);
     2609    di->edit_trackers_button = w;
     2610    gtk_box_pack_start (GTK_BOX (v), w, FALSE, FALSE, 0);
     2611
     2612    w = gtk_button_new_with_mnemonic (_("_Remove"));
     2613    di->remove_tracker_button = w;
     2614    g_signal_connect (w, "clicked", G_CALLBACK (on_tracker_list_remove_button_clicked), di);
     2615    gtk_box_pack_start (GTK_BOX (v), w, FALSE, FALSE, 0);
     2616
     2617    gtk_box_pack_start (GTK_BOX (hbox), v, FALSE, FALSE, 0);
     2618
     2619  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
     2620
     2621  w = gtk_check_button_new_with_mnemonic (_("Show _more details"));
     2622  di->scrape_check = w;
     2623  b = gtr_pref_flag_get (TR_KEY_show_tracker_scrapes);
     2624  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), b);
     2625  g_signal_connect (w, "toggled", G_CALLBACK (onScrapeToggled), di);
     2626  gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0);
     2627
     2628  w = gtk_check_button_new_with_mnemonic (_("Show _backup trackers"));
     2629  di->all_check = w;
     2630  b = gtr_pref_flag_get (TR_KEY_show_backup_trackers);
     2631  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), b);
     2632  g_signal_connect (w, "toggled", G_CALLBACK (onBackupToggled), di);
     2633  gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0);
     2634
     2635  return vbox;
    24562636}
    24572637
     
    24642644refresh (struct DetailsImpl * di)
    24652645{
    2466     int n;
    2467     tr_torrent ** torrents = getTorrents (di, &n);
    2468 
    2469     refreshInfo (di, torrents, n);
    2470     refreshPeers (di, torrents, n);
    2471     refreshTracker (di, torrents, n);
    2472     refreshOptions (di, torrents, n);
    2473 
    2474     if (n == 0)
    2475         gtk_dialog_response (GTK_DIALOG (di->dialog), GTK_RESPONSE_CLOSE);
    2476 
    2477     g_free (torrents);
     2646  int n;
     2647  tr_torrent ** torrents = getTorrents (di, &n);
     2648
     2649  refreshInfo (di, torrents, n);
     2650  refreshPeers (di, torrents, n);
     2651  refreshTracker (di, torrents, n);
     2652  refreshOptions (di, torrents, n);
     2653
     2654  if (n == 0)
     2655    gtk_dialog_response (GTK_DIALOG (di->dialog), GTK_RESPONSE_CLOSE);
     2656
     2657  g_free (torrents);
    24782658}
    24792659
     
    24812661periodic_refresh (gpointer data)
    24822662{
    2483     refresh (data);
    2484 
    2485     return G_SOURCE_CONTINUE;
     2663  refresh (data);
     2664
     2665  return G_SOURCE_CONTINUE;
    24862666}
    24872667
     
    24892669details_free (gpointer gdata)
    24902670{
    2491     struct DetailsImpl * data = gdata;
    2492     g_source_remove (data->periodic_refresh_tag);
    2493     g_hash_table_destroy (data->tracker_hash);
    2494     g_hash_table_destroy (data->webseed_hash);
    2495     g_hash_table_destroy (data->peer_hash);
    2496     g_string_free (data->gstr, TRUE);
    2497     g_slist_free (data->ids);
    2498     g_free (data);
     2671  struct DetailsImpl * data = gdata;
     2672  g_source_remove (data->periodic_refresh_tag);
     2673  g_hash_table_destroy (data->tracker_hash);
     2674  g_hash_table_destroy (data->webseed_hash);
     2675  g_hash_table_destroy (data->peer_hash);
     2676  g_string_free (data->gstr, TRUE);
     2677  g_slist_free (data->ids);
     2678  g_free (data);
    24992679}
    25002680
     
    25022682gtr_torrent_details_dialog_new (GtkWindow * parent, TrCore * core)
    25032683{
    2504     GtkWidget *d, *n, *v, *w, *l;
    2505     struct DetailsImpl * di = g_new0 (struct DetailsImpl, 1);
    2506 
    2507     /* one-time setup */
    2508     if (ARG_KEY == 0)
    2509     {
    2510         ARG_KEY          = g_quark_from_static_string ("tr-arg-key");
    2511         DETAILS_KEY      = g_quark_from_static_string ("tr-details-data-key");
    2512         TORRENT_ID_KEY   = g_quark_from_static_string ("tr-torrent-id-key");
    2513         TEXT_BUFFER_KEY  = g_quark_from_static_string ("tr-text-buffer-key");
    2514         URL_ENTRY_KEY    = g_quark_from_static_string ("tr-url-entry-key");
    2515     }
    2516 
    2517     /* create the dialog */
    2518     di->core = core;
    2519     di->gstr = g_string_new (NULL);
    2520     d = gtk_dialog_new_with_buttons (NULL, parent, 0,
    2521                                      GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
    2522                                      NULL);
    2523     di->dialog = d;
    2524     gtk_window_set_role (GTK_WINDOW (d), "tr-info");
    2525     g_signal_connect_swapped (d, "response",
    2526                               G_CALLBACK (gtk_widget_destroy), d);
    2527     gtk_container_set_border_width (GTK_CONTAINER (d), GUI_PAD);
    2528     g_object_set_qdata_full (G_OBJECT (d), DETAILS_KEY, di, details_free);
    2529 
    2530     n = gtk_notebook_new ();
    2531     gtk_container_set_border_width (GTK_CONTAINER (n), GUI_PAD);
    2532 
    2533     w = info_page_new (di);
    2534     l = gtk_label_new (_("Information"));
    2535     gtk_notebook_append_page (GTK_NOTEBOOK (n), w, l);
    2536 
    2537     w = peer_page_new (di);
    2538     l = gtk_label_new (_("Peers"));
    2539     gtk_notebook_append_page (GTK_NOTEBOOK (n),  w, l);
    2540 
    2541     w = tracker_page_new (di);
    2542     l = gtk_label_new (_("Trackers"));
    2543     gtk_notebook_append_page (GTK_NOTEBOOK (n), w, l);
    2544 
    2545     v = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
    2546     di->file_list = gtr_file_list_new (core, 0);
    2547     di->file_label = gtk_label_new (_("File listing not available for combined torrent properties"));
    2548     gtk_box_pack_start (GTK_BOX (v), di->file_list, TRUE, TRUE, 0);
    2549     gtk_box_pack_start (GTK_BOX (v), di->file_label, TRUE, TRUE, 0);
    2550     gtk_container_set_border_width (GTK_CONTAINER (v), GUI_PAD_BIG);
    2551     l = gtk_label_new (_("Files"));
    2552     gtk_notebook_append_page (GTK_NOTEBOOK (n), v, l);
    2553 
    2554     w = options_page_new (di);
    2555     l = gtk_label_new (_("Options"));
    2556     gtk_notebook_append_page (GTK_NOTEBOOK (n), w, l);
    2557 
    2558     gtr_dialog_set_content (GTK_DIALOG (d), n);
    2559     di->periodic_refresh_tag = gdk_threads_add_timeout_seconds (SECONDARY_WINDOW_REFRESH_INTERVAL_SECONDS,
    2560                                                                 periodic_refresh, di);
    2561     return d;
     2684  GtkWidget *d, *n, *v, *w, *l;
     2685  struct DetailsImpl * di = g_new0 (struct DetailsImpl, 1);
     2686
     2687  /* one-time setup */
     2688  if (ARG_KEY == 0)
     2689    {
     2690      ARG_KEY          = g_quark_from_static_string ("tr-arg-key");
     2691      DETAILS_KEY      = g_quark_from_static_string ("tr-details-data-key");
     2692      TORRENT_ID_KEY   = g_quark_from_static_string ("tr-torrent-id-key");
     2693      TEXT_BUFFER_KEY  = g_quark_from_static_string ("tr-text-buffer-key");
     2694      URL_ENTRY_KEY    = g_quark_from_static_string ("tr-url-entry-key");
     2695    }
     2696
     2697  /* create the dialog */
     2698  di->core = core;
     2699  di->gstr = g_string_new (NULL);
     2700  d = gtk_dialog_new_with_buttons (NULL, parent, 0,
     2701                                   GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
     2702                                   NULL);
     2703  di->dialog = d;
     2704  gtk_window_set_role (GTK_WINDOW (d), "tr-info");
     2705  g_signal_connect_swapped (d, "response",
     2706                            G_CALLBACK (gtk_widget_destroy), d);
     2707  gtk_container_set_border_width (GTK_CONTAINER (d), GUI_PAD);
     2708  g_object_set_qdata_full (G_OBJECT (d), DETAILS_KEY, di, details_free);
     2709
     2710  n = gtk_notebook_new ();
     2711  gtk_container_set_border_width (GTK_CONTAINER (n), GUI_PAD);
     2712
     2713  w = info_page_new (di);
     2714  l = gtk_label_new (_("Information"));
     2715  gtk_notebook_append_page (GTK_NOTEBOOK (n), w, l);
     2716
     2717  w = peer_page_new (di);
     2718  l = gtk_label_new (_("Peers"));
     2719  gtk_notebook_append_page (GTK_NOTEBOOK (n),  w, l);
     2720
     2721  w = tracker_page_new (di);
     2722  l = gtk_label_new (_("Trackers"));
     2723  gtk_notebook_append_page (GTK_NOTEBOOK (n), w, l);
     2724
     2725  v = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
     2726  di->file_list = gtr_file_list_new (core, 0);
     2727  di->file_label = gtk_label_new (_("File listing not available for combined torrent properties"));
     2728  gtk_box_pack_start (GTK_BOX (v), di->file_list, TRUE, TRUE, 0);
     2729  gtk_box_pack_start (GTK_BOX (v), di->file_label, TRUE, TRUE, 0);
     2730  gtk_container_set_border_width (GTK_CONTAINER (v), GUI_PAD_BIG);
     2731  l = gtk_label_new (_("Files"));
     2732  gtk_notebook_append_page (GTK_NOTEBOOK (n), v, l);
     2733
     2734  w = options_page_new (di);
     2735  l = gtk_label_new (_("Options"));
     2736  gtk_notebook_append_page (GTK_NOTEBOOK (n), w, l);
     2737
     2738  gtr_dialog_set_content (GTK_DIALOG (d), n);
     2739  di->periodic_refresh_tag = gdk_threads_add_timeout_seconds (SECONDARY_WINDOW_REFRESH_INTERVAL_SECONDS,
     2740                                                              periodic_refresh, di);
     2741  return d;
    25622742}
    25632743
     
    25652745gtr_torrent_details_dialog_set_torrents (GtkWidget * w, GSList * ids)
    25662746{
    2567     char title[256];
    2568     const int len = g_slist_length (ids);
    2569     struct DetailsImpl * di = g_object_get_qdata (G_OBJECT (w), DETAILS_KEY);
    2570 
    2571     g_slist_free (di->ids);
    2572     di->ids = g_slist_copy (ids);
    2573 
    2574     if (len == 1)
    2575     {
    2576         const int id = GPOINTER_TO_INT (ids->data);
    2577         tr_torrent * tor = gtr_core_find_torrent (di->core, id);
    2578         const tr_info * inf = tr_torrentInfo (tor);
    2579         g_snprintf (title, sizeof (title), _("%s Properties"), inf->name);
    2580 
    2581         gtr_file_list_set_torrent (di->file_list, id);
    2582         gtk_widget_show (di->file_list);
    2583         gtk_widget_hide (di->file_label);
    2584     }
    2585    else
    2586    {
    2587         gtr_file_list_clear (di->file_list);
    2588         gtk_widget_hide (di->file_list);
    2589         gtk_widget_show (di->file_label);
    2590         g_snprintf (title, sizeof (title), _("%'d Torrent Properties"), len);
    2591     }
    2592 
    2593     gtk_window_set_title (GTK_WINDOW (w), title);
    2594 
    2595     refresh (di);
    2596 }
     2747  char title[256];
     2748  const int len = g_slist_length (ids);
     2749  struct DetailsImpl * di = g_object_get_qdata (G_OBJECT (w), DETAILS_KEY);
     2750
     2751  g_slist_free (di->ids);
     2752  di->ids = g_slist_copy (ids);
     2753
     2754  if (len == 1)
     2755    {
     2756      const int id = GPOINTER_TO_INT (ids->data);
     2757      tr_torrent * tor = gtr_core_find_torrent (di->core, id);
     2758      const tr_info * inf = tr_torrentInfo (tor);
     2759      g_snprintf (title, sizeof (title), _("%s Properties"), inf->name);
     2760
     2761      gtr_file_list_set_torrent (di->file_list, id);
     2762      gtk_widget_show (di->file_list);
     2763      gtk_widget_hide (di->file_label);
     2764    }
     2765  else
     2766    {
     2767      gtr_file_list_clear (di->file_list);
     2768      gtk_widget_hide (di->file_list);
     2769      gtk_widget_show (di->file_label);
     2770      g_snprintf (title, sizeof (title), _("%'d Torrent Properties"), len);
     2771    }
     2772
     2773  gtk_window_set_title (GTK_WINDOW (w), title);
     2774
     2775  refresh (di);
     2776}
  • trunk/gtk/makemeta-ui.c

    r13760 r13761  
    2828typedef struct
    2929{
    30     char * target;
    31     guint progress_tag;
    32     GtkWidget * file_radio;
    33     GtkWidget * file_chooser;
    34     GtkWidget * folder_radio;
    35     GtkWidget * folder_chooser;
    36     GtkWidget * pieces_lb;
    37     GtkWidget * destination_chooser;
    38     GtkWidget * comment_check;
    39     GtkWidget * comment_entry;
    40     GtkWidget * private_check;
    41     GtkWidget * progress_label;
    42     GtkWidget * progress_bar;
    43     GtkWidget * progress_dialog;
    44     GtkWidget * dialog;
    45     GtkTextBuffer * announce_text_buffer;
    46     TrCore * core;
    47     tr_metainfo_builder *  builder;
     30  char * target;
     31  guint progress_tag;
     32  GtkWidget * file_radio;
     33  GtkWidget * file_chooser;
     34  GtkWidget * folder_radio;
     35  GtkWidget * folder_chooser;
     36  GtkWidget * pieces_lb;
     37  GtkWidget * destination_chooser;
     38  GtkWidget * comment_check;
     39  GtkWidget * comment_entry;
     40  GtkWidget * private_check;
     41  GtkWidget * progress_label;
     42  GtkWidget * progress_bar;
     43  GtkWidget * progress_dialog;
     44  GtkWidget * dialog;
     45  GtkTextBuffer * announce_text_buffer;
     46  TrCore * core;
     47  tr_metainfo_builder *  builder;
    4848}
    4949MakeMetaUI;
     
    5252freeMetaUI (gpointer p)
    5353{
    54     MakeMetaUI * ui = p;
    55 
    56     tr_metaInfoBuilderFree (ui->builder);
    57     g_free (ui->target);
    58     memset (ui, ~0, sizeof (MakeMetaUI));
    59     g_free (ui);
     54  MakeMetaUI * ui = p;
     55
     56  tr_metaInfoBuilderFree (ui->builder);
     57  g_free (ui->target);
     58  memset (ui, ~0, sizeof (MakeMetaUI));
     59  g_free (ui);
    6060}
    6161
     
    6363onProgressDialogRefresh (gpointer data)
    6464{
    65     char * str = NULL;
    66     MakeMetaUI * ui = data;
    67     const tr_metainfo_builder * b = ui->builder;
    68     GtkDialog * d = GTK_DIALOG (ui->progress_dialog);
    69     GtkProgressBar * p = GTK_PROGRESS_BAR (ui->progress_bar);
    70     const double fraction = b->pieceCount ? ((double)b->pieceIndex / b->pieceCount) : 0;
    71     char * base = g_path_get_basename (b->top);
    72 
    73     /* progress label */
    74     if (!b->isDone)
    75         str = g_strdup_printf (_("Creating \"%s\""), base);
    76     else if (b->result == TR_MAKEMETA_OK)
    77         str = g_strdup_printf (_("Created \"%s\"!"), base);
    78     else if (b->result == TR_MAKEMETA_URL)
    79         str = g_strdup_printf (_("Error: invalid announce URL \"%s\""), b->errfile);
    80     else if (b->result == TR_MAKEMETA_CANCELLED)
    81         str = g_strdup_printf (_("Cancelled"));
    82     else if (b->result == TR_MAKEMETA_IO_READ)
    83         str = g_strdup_printf (_("Error reading \"%s\": %s"), b->errfile, g_strerror (b->my_errno));
    84     else if (b->result == TR_MAKEMETA_IO_WRITE)
    85         str = g_strdup_printf (_("Error writing \"%s\": %s"), b->errfile, g_strerror (b->my_errno));
    86     else
    87         g_assert_not_reached ();
    88 
    89     if (str != NULL) {
    90         gtr_label_set_text (GTK_LABEL (ui->progress_label), str);
    91         g_free (str);
    92     }
    93 
    94     /* progress bar */
    95     if (!b->pieceIndex)
    96         str = g_strdup ("");
    97     else {
    98         char sizebuf[128];
    99         tr_strlsize (sizebuf, (uint64_t)b->pieceIndex *
     65  char * str = NULL;
     66  MakeMetaUI * ui = data;
     67  const tr_metainfo_builder * b = ui->builder;
     68  GtkDialog * d = GTK_DIALOG (ui->progress_dialog);
     69  GtkProgressBar * p = GTK_PROGRESS_BAR (ui->progress_bar);
     70  const double fraction = b->pieceCount ? ((double)b->pieceIndex / b->pieceCount) : 0;
     71  char * base = g_path_get_basename (b->top);
     72
     73  /* progress label */
     74  if (!b->isDone)
     75    str = g_strdup_printf (_("Creating \"%s\""), base);
     76  else if (b->result == TR_MAKEMETA_OK)
     77    str = g_strdup_printf (_("Created \"%s\"!"), base);
     78  else if (b->result == TR_MAKEMETA_URL)
     79    str = g_strdup_printf (_("Error: invalid announce URL \"%s\""), b->errfile);
     80  else if (b->result == TR_MAKEMETA_CANCELLED)
     81    str = g_strdup_printf (_("Cancelled"));
     82  else if (b->result == TR_MAKEMETA_IO_READ)
     83    str = g_strdup_printf (_("Error reading \"%s\": %s"), b->errfile, g_strerror (b->my_errno));
     84  else if (b->result == TR_MAKEMETA_IO_WRITE)
     85    str = g_strdup_printf (_("Error writing \"%s\": %s"), b->errfile, g_strerror (b->my_errno));
     86  else
     87    g_assert_not_reached ();
     88
     89  if (str != NULL)
     90    {
     91      gtr_label_set_text (GTK_LABEL (ui->progress_label), str);
     92      g_free (str);
     93    }
     94
     95  /* progress bar */
     96  if (!b->pieceIndex)
     97    {
     98      str = g_strdup ("");
     99    }
     100  else
     101    {
     102      char sizebuf[128];
     103      tr_strlsize (sizebuf, (uint64_t)b->pieceIndex *
    100104                            (uint64_t)b->pieceSize, sizeof (sizebuf));
    101         /* how much data we've scanned through to generate checksums */
    102         str = g_strdup_printf (_("Scanned %s"), sizebuf);
    103     }
    104     gtk_progress_bar_set_fraction (p, fraction);
    105     gtk_progress_bar_set_text (p, str);
    106     g_free (str);
    107 
    108     /* buttons */
    109     gtk_dialog_set_response_sensitive (d, GTK_RESPONSE_CANCEL, !b->isDone);
    110     gtk_dialog_set_response_sensitive (d, GTK_RESPONSE_CLOSE, b->isDone);
    111     gtk_dialog_set_response_sensitive (d, GTK_RESPONSE_ACCEPT, b->isDone && !b->result);
    112 
    113     g_free (base);
    114 
    115     return G_SOURCE_CONTINUE;
     105      /* how much data we've scanned through to generate checksums */
     106      str = g_strdup_printf (_("Scanned %s"), sizebuf);
     107    }
     108  gtk_progress_bar_set_fraction (p, fraction);
     109  gtk_progress_bar_set_text (p, str);
     110  g_free (str);
     111
     112  /* buttons */
     113  gtk_dialog_set_response_sensitive (d, GTK_RESPONSE_CANCEL, !b->isDone);
     114  gtk_dialog_set_response_sensitive (d, GTK_RESPONSE_CLOSE, b->isDone);
     115  gtk_dialog_set_response_sensitive (d, GTK_RESPONSE_ACCEPT, b->isDone && !b->result);
     116
     117  g_free (base);
     118  return G_SOURCE_CONTINUE;
    116119}
    117120
     
    119122onProgressDialogDestroyed (gpointer data, GObject * dead UNUSED)
    120123{
    121     MakeMetaUI * ui = data;
    122     g_source_remove (ui->progress_tag);
     124  MakeMetaUI * ui = data;
     125  g_source_remove (ui->progress_tag);
    123126}
    124127
     
    126129addTorrent (MakeMetaUI * ui)
    127130{
    128     char * path;
    129     const tr_metainfo_builder * b = ui->builder;
    130     tr_ctor * ctor = tr_ctorNew (gtr_core_session (ui->core));
    131 
    132     tr_ctorSetMetainfoFromFile (ctor, ui->target);
    133 
    134     path = g_path_get_dirname (b->top);
    135     tr_ctorSetDownloadDir (ctor, TR_FORCE, path);
    136     g_free (path);
    137 
    138     gtr_core_add_ctor (ui->core, ctor);
     131  char * path;
     132  const tr_metainfo_builder * b = ui->builder;
     133  tr_ctor * ctor = tr_ctorNew (gtr_core_session (ui->core));
     134
     135  tr_ctorSetMetainfoFromFile (ctor, ui->target);
     136
     137  path = g_path_get_dirname (b->top);
     138  tr_ctorSetDownloadDir (ctor, TR_FORCE, path);
     139  g_free (path);
     140
     141  gtr_core_add_ctor (ui->core, ctor);
    139142}
    140143
     
    142145onProgressDialogResponse (GtkDialog * d, int response, gpointer data)
    143146{
    144     MakeMetaUI * ui = data;
    145 
    146     switch (response)
    147     {
    148         case GTK_RESPONSE_CANCEL:
    149             ui->builder->abortFlag = TRUE;
    150             gtk_widget_destroy (GTK_WIDGET (d));
    151             break;
    152         case GTK_RESPONSE_ACCEPT:
    153             addTorrent (ui);
    154             /* fall-through */
    155         case GTK_RESPONSE_CLOSE:
    156             gtk_widget_destroy (ui->builder->result ? GTK_WIDGET (d) : ui->dialog);
    157             break;
    158         default:
    159             g_assert (0 && "unhandled response");
     147  MakeMetaUI * ui = data;
     148
     149  switch (response)
     150    {
     151      case GTK_RESPONSE_CANCEL:
     152        ui->builder->abortFlag = TRUE;
     153        gtk_widget_destroy (GTK_WIDGET (d));
     154        break;
     155
     156      case GTK_RESPONSE_ACCEPT:
     157        addTorrent (ui);
     158        /* fall-through */
     159
     160      case GTK_RESPONSE_CLOSE:
     161        gtk_widget_destroy (ui->builder->result ? GTK_WIDGET (d) : ui->dialog);
     162        break;
     163
     164      default:
     165        g_assert (0 && "unhandled response");
    160166    }
    161167}
     
    164170makeProgressDialog (GtkWidget * parent, MakeMetaUI * ui)
    165171{
    166     GtkWidget *d, *l, *w, *v, *fr;
    167 
    168     d = gtk_dialog_new_with_buttons (_("New Torrent"),
    169             GTK_WINDOW (parent),
    170             GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
    171             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
    172             GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
    173             GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT,
    174             NULL);
    175     ui->progress_dialog = d;
    176     g_signal_connect (d, "response", G_CALLBACK (onProgressDialogResponse), ui);
    177 
    178     fr = gtk_frame_new (NULL);
    179     gtk_container_set_border_width (GTK_CONTAINER (fr), GUI_PAD_BIG);
    180     gtk_frame_set_shadow_type (GTK_FRAME (fr), GTK_SHADOW_NONE);
    181     v = gtk_box_new (GTK_ORIENTATION_VERTICAL, GUI_PAD);
    182     gtk_container_add (GTK_CONTAINER (fr), v);
    183 
    184     l = gtk_label_new (_("Creating torrent
"));
    185     gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
    186     gtk_label_set_justify (GTK_LABEL (l), GTK_JUSTIFY_LEFT);
    187     ui->progress_label = l;
    188     gtk_box_pack_start (GTK_BOX (v), l, FALSE, FALSE, 0);
    189 
    190     w = gtk_progress_bar_new ();
    191     ui->progress_bar = w;
    192     gtk_box_pack_start (GTK_BOX (v), w, FALSE, FALSE, 0);
    193 
    194     ui->progress_tag = gdk_threads_add_timeout_seconds (SECONDARY_WINDOW_REFRESH_INTERVAL_SECONDS, onProgressDialogRefresh, ui);
    195     g_object_weak_ref (G_OBJECT (d), onProgressDialogDestroyed, ui);
    196     onProgressDialogRefresh (ui);
    197 
    198     gtr_dialog_set_content (GTK_DIALOG (d), fr);
    199     gtk_widget_show (d);
     172  GtkWidget *d, *l, *w, *v, *fr;
     173
     174  d = gtk_dialog_new_with_buttons (_("New Torrent"),
     175        GTK_WINDOW (parent),
     176        GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
     177        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
     178        GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
     179        GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT,
     180        NULL);
     181  ui->progress_dialog = d;
     182  g_signal_connect (d, "response", G_CALLBACK (onProgressDialogResponse), ui);
     183
     184  fr = gtk_frame_new (NULL);
     185  gtk_container_set_border_width (GTK_CONTAINER (fr), GUI_PAD_BIG);
     186  gtk_frame_set_shadow_type (GTK_FRAME (fr), GTK_SHADOW_NONE);
     187  v = gtk_box_new (GTK_ORIENTATION_VERTICAL, GUI_PAD);
     188  gtk_container_add (GTK_CONTAINER (fr), v);
     189
     190  l = gtk_label_new (_("Creating torrent
"));
     191  gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
     192  gtk_label_set_justify (GTK_LABEL (l), GTK_JUSTIFY_LEFT);
     193  ui->progress_label = l;
     194  gtk_box_pack_start (GTK_BOX (v), l, FALSE, FALSE, 0);
     195
     196  w = gtk_progress_bar_new ();
     197  ui->progress_bar = w;
     198  gtk_box_pack_start (GTK_BOX (v), w, FALSE, FALSE, 0);
     199
     200  ui->progress_tag = gdk_threads_add_timeout_seconds (SECONDARY_WINDOW_REFRESH_INTERVAL_SECONDS, onProgressDialogRefresh, ui);
     201  g_object_weak_ref (G_OBJECT (d), onProgressDialogDestroyed, ui);
     202  onProgressDialogRefresh (ui);
     203
     204  gtr_dialog_set_content (GTK_DIALOG (d), fr);
     205  gtk_widget_show (d);
    200206}
    201207
     
    203209onResponse (GtkDialog* d, int response, gpointer user_data)
    204210{
    205     MakeMetaUI * ui = user_data;
    206 
    207     if (response == GTK_RESPONSE_ACCEPT)
    208     {
    209         if (ui->builder != NULL)
     211  MakeMetaUI * ui = user_data;
     212
     213  if (response == GTK_RESPONSE_ACCEPT)
     214    {
     215      if (ui->builder != NULL)
    210216        {
    211             int i;
    212             int n;
    213             int tier;
    214             GtkTextIter start, end;
    215             char * dir;
    216             char * base;
    217             char * tracker_text;
    218             char ** tracker_strings;
    219             GtkEntry * c_entry = GTK_ENTRY (ui->comment_entry);
    220             GtkToggleButton * p_check = GTK_TOGGLE_BUTTON (ui->private_check);
    221             GtkToggleButton * c_check = GTK_TOGGLE_BUTTON (ui->comment_check);
    222             const char * comment = gtk_entry_get_text (c_entry);
    223             const gboolean isPrivate = gtk_toggle_button_get_active (p_check);
    224             const gboolean useComment = gtk_toggle_button_get_active (c_check);
    225             tr_tracker_info * trackers;
    226 
    227             /* destination file */
    228             dir = gtk_file_chooser_get_filename (
    229                       GTK_FILE_CHOOSER (ui->destination_chooser));
    230             base = g_path_get_basename (ui->builder->top);
    231             g_free (ui->target);
    232             ui->target = g_strdup_printf ("%s/%s.torrent", dir, base);
    233 
    234             /* build the array of trackers */
    235             gtk_text_buffer_get_bounds (ui->announce_text_buffer, &start, &end);
    236             tracker_text = gtk_text_buffer_get_text (ui->announce_text_buffer,
     217          int i;
     218          int n;
     219          int tier;
     220          GtkTextIter start, end;
     221          char * dir;
     222          char * base;
     223          char * tracker_text;
     224          char ** tracker_strings;
     225          GtkEntry * c_entry = GTK_ENTRY (ui->comment_entry);
     226          GtkToggleButton * p_check = GTK_TOGGLE_BUTTON (ui->private_check);
     227          GtkToggleButton * c_check = GTK_TOGGLE_BUTTON (ui->comment_check);
     228          const char * comment = gtk_entry_get_text (c_entry);
     229          const gboolean isPrivate = gtk_toggle_button_get_active (p_check);
     230          const gboolean useComment = gtk_toggle_button_get_active (c_check);
     231          tr_tracker_info * trackers;
     232
     233          /* destination file */
     234          dir = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (ui->destination_chooser));
     235          base = g_path_get_basename (ui->builder->top);
     236          g_free (ui->target);
     237          ui->target = g_strdup_printf ("%s/%s.torrent", dir, base);
     238
     239          /* build the array of trackers */
     240          gtk_text_buffer_get_bounds (ui->announce_text_buffer, &start, &end);
     241          tracker_text = gtk_text_buffer_get_text (ui->announce_text_buffer,
    237242                                                     &start, &end, FALSE);
    238             tracker_strings = g_strsplit (tracker_text, "\n", 0);
    239             for (i=0; tracker_strings[i];)
    240                 ++i;
    241             trackers = g_new0 (tr_tracker_info, i);
    242             for (i=n=tier=0; tracker_strings[i]; ++i) {
    243                 const char * str = tracker_strings[i];
    244                 if (!*str)
    245                     ++tier;
    246                 else {
    247                     trackers[n].tier = tier;
    248                     trackers[n].announce = tracker_strings[i];
    249                     ++n;
     243          tracker_strings = g_strsplit (tracker_text, "\n", 0);
     244          for (i=0; tracker_strings[i];)
     245            ++i;
     246          trackers = g_new0 (tr_tracker_info, i);
     247          for (i=n=tier=0; tracker_strings[i]; ++i)
     248            {
     249              const char * str = tracker_strings[i];
     250              if (!*str)
     251                {
     252                  ++tier;
     253                }
     254              else
     255                {
     256                  trackers[n].tier = tier;
     257                  trackers[n].announce = tracker_strings[i];
     258                  ++n;
    250259                }
    251260            }
    252261
    253             /* build the .torrent */
    254             makeProgressDialog (GTK_WIDGET (d), ui);
    255             tr_makeMetaInfo (ui->builder, ui->target, trackers, n,
    256                              useComment ? comment : NULL, isPrivate);
    257 
    258             /* cleanup */
    259             g_free (trackers);
    260             g_strfreev (tracker_strings);
    261             g_free (tracker_text);
    262             g_free (base);
    263             g_free (dir);
     262          /* build the .torrent */
     263          makeProgressDialog (GTK_WIDGET (d), ui);
     264          tr_makeMetaInfo (ui->builder, ui->target, trackers, n,
     265                           useComment ? comment : NULL, isPrivate);
     266
     267          /* cleanup */
     268          g_free (trackers);
     269          g_strfreev (tracker_strings);
     270          g_free (tracker_text);
     271          g_free (base);
     272          g_free (dir);
    264273        }
    265274    }
    266     else if (response == GTK_RESPONSE_CLOSE)
    267     {
    268         gtk_widget_destroy (GTK_WIDGET (d));
     275  else if (response == GTK_RESPONSE_CLOSE)
     276    {
     277      gtk_widget_destroy (GTK_WIDGET (d));
    269278    }
    270279}
     
    277286onSourceToggled (GtkToggleButton * tb, gpointer user_data)
    278287{
    279     gtk_widget_set_sensitive (GTK_WIDGET (user_data),
    280                               gtk_toggle_button_get_active (tb));
     288  gtk_widget_set_sensitive (GTK_WIDGET (user_data),
     289                            gtk_toggle_button_get_active (tb));
    281290}
    282291
     
    284293updatePiecesLabel (MakeMetaUI * ui)
    285294{
    286     const tr_metainfo_builder * builder = ui->builder;
    287     const char * filename = builder ? builder->top : NULL;
    288     GString * gstr = g_string_new (NULL);
    289 
    290     g_string_append (gstr, "<i>");
    291     if (!filename)
    292     {
    293         g_string_append (gstr, _("No source selected"));
    294     }
    295     else
    296     {
    297         char buf[128];
    298         tr_strlsize (buf, builder->totalSize, sizeof (buf));
    299         g_string_append_printf (gstr, ngettext ("%1$s; %2$'d File",
    300                                                 "%1$s; %2$'d Files",
    301                                                 builder->fileCount),
    302                                 buf, builder->fileCount);
    303         g_string_append (gstr, "; ");
    304 
    305         tr_formatter_mem_B (buf, builder->pieceSize, sizeof (buf));
    306         g_string_append_printf (gstr, ngettext ("%1$'d Piece @ %2$s",
    307                                                 "%1$'d Pieces @ %2$s",
    308                                                 builder->pieceCount),
    309                                       builder->pieceCount, buf);
    310     }
    311     g_string_append (gstr, "</i>");
    312     gtk_label_set_markup (GTK_LABEL (ui->pieces_lb), gstr->str);
    313     g_string_free (gstr, TRUE);
     295  const tr_metainfo_builder * builder = ui->builder;
     296  const char * filename = builder ? builder->top : NULL;
     297  GString * gstr = g_string_new (NULL);
     298
     299  g_string_append (gstr, "<i>");
     300  if (!filename)
     301    {
     302      g_string_append (gstr, _("No source selected"));
     303    }
     304  else
     305    {
     306      char buf[128];
     307      tr_strlsize (buf, builder->totalSize, sizeof (buf));
     308      g_string_append_printf (gstr, ngettext ("%1$s; %2$'d File",
     309                                              "%1$s; %2$'d Files",
     310                                              builder->fileCount),
     311                              buf, builder->fileCount);
     312      g_string_append (gstr, "; ");
     313
     314      tr_formatter_mem_B (buf, builder->pieceSize, sizeof (buf));
     315      g_string_append_printf (gstr, ngettext ("%1$'d Piece @ %2$s",
     316                                              "%1$'d Pieces @ %2$s",
     317                                              builder->pieceCount),
     318                                    builder->pieceCount, buf);
     319    }
     320
     321  g_string_append (gstr, "</i>");
     322  gtk_label_set_markup (GTK_LABEL (ui->pieces_lb), gstr->str);
     323  g_string_free (gstr, TRUE);
    314324}
    315325
     
    317327setFilename (MakeMetaUI * ui, const char * filename)
    318328{
    319     if (ui->builder) {
    320         tr_metaInfoBuilderFree (ui->builder);
    321         ui->builder = NULL;
    322     }
    323 
    324     if (filename)
    325         ui->builder = tr_metaInfoBuilderCreate (filename);
    326 
    327     updatePiecesLabel (ui);
     329  g_clear_pointer (&ui->builder, (GDestroyNotify)tr_metaInfoBuilderFree);
     330
     331  if (filename)
     332    ui->builder = tr_metaInfoBuilderCreate (filename);
     333
     334  updatePiecesLabel (ui);
    328335}
    329336
     
    331338onChooserChosen (GtkFileChooser * chooser, gpointer user_data)
    332339{
    333     char * filename;
    334     MakeMetaUI * ui = user_data;
    335 
    336     g_object_set_data (G_OBJECT (chooser), FILE_CHOSEN_KEY,
    337                        GINT_TO_POINTER (TRUE));
    338 
    339     filename = gtk_file_chooser_get_filename (chooser);
    340     setFilename (ui, filename);
    341     g_free (filename);
     340  char * filename;
     341  MakeMetaUI * ui = user_data;
     342
     343  g_object_set_data (G_OBJECT (chooser), FILE_CHOSEN_KEY,
     344                     GINT_TO_POINTER (TRUE));
     345
     346  filename = gtk_file_chooser_get_filename (chooser);
     347  setFilename (ui, filename);
     348  g_free (filename);
    342349}
    343350
     
    345352onSourceToggled2 (GtkToggleButton * tb, GtkWidget * chooser, MakeMetaUI * ui)
    346353{
    347     if (gtk_toggle_button_get_active (tb))
    348     {
    349         if (g_object_get_data (G_OBJECT (chooser), FILE_CHOSEN_KEY) != NULL)
    350             onChooserChosen (GTK_FILE_CHOOSER (chooser), ui);
    351         else
    352             setFilename (ui, NULL);
     354  if (gtk_toggle_button_get_active (tb))
     355    {
     356      if (g_object_get_data (G_OBJECT (chooser), FILE_CHOSEN_KEY) != NULL)
     357        onChooserChosen (GTK_FILE_CHOOSER (chooser), ui);
     358      else
     359        setFilename (ui, NULL);
    353360    }
    354361}
     
    356363onFolderToggled (GtkToggleButton * tb, gpointer data)
    357364{
    358     MakeMetaUI * ui = data;
    359     onSourceToggled2 (tb, ui->folder_chooser, ui);
     365  MakeMetaUI * ui = data;
     366  onSourceToggled2 (tb, ui->folder_chooser, ui);
    360367}
    361368static void
    362369onFileToggled (GtkToggleButton * tb, gpointer data)
    363370{
    364     MakeMetaUI * ui = data;
    365     onSourceToggled2 (tb, ui->file_chooser, ui);
     371  MakeMetaUI * ui = data;
     372  onSourceToggled2 (tb, ui->file_chooser, ui);
    366373}
    367374
     
    369376getDefaultSavePath (void)
    370377{
    371     return g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
     378  return g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
    372379}
    373380
     
    382389                       gpointer            user_data)
    383390{
    384     gboolean success = FALSE;
    385     MakeMetaUI * ui = user_data;
    386     char ** uris = gtk_selection_data_get_uris (selection_data);
    387 
    388     if (uris && uris[0])
    389     {
    390         const char * uri = uris[ 0 ];
    391         gchar * filename = g_filename_from_uri (uri, NULL, NULL);
    392 
    393         if (g_file_test (filename, G_FILE_TEST_IS_DIR))
     391  gboolean success = FALSE;
     392  MakeMetaUI * ui = user_data;
     393  char ** uris = gtk_selection_data_get_uris (selection_data);
     394
     395  if (uris && uris[0])
     396    {
     397      const char * uri = uris[ 0 ];
     398      gchar * filename = g_filename_from_uri (uri, NULL, NULL);
     399
     400      if (g_file_test (filename, G_FILE_TEST_IS_DIR))
    394401        {
    395             /* a directory was dragged onto the dialog... */
    396             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ui->folder_radio), TRUE);
    397             gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (ui->folder_chooser), filename);
    398             success = TRUE;
     402          /* a directory was dragged onto the dialog... */
     403          gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ui->folder_radio), TRUE);
     404          gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (ui->folder_chooser), filename);
     405          success = TRUE;
    399406        }
    400         else if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
     407      else if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
    401408        {
    402             /* a file was dragged on to the dialog... */
    403             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ui->file_radio), TRUE);
    404             gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (ui->file_chooser), filename);
    405             success = TRUE;
     409          /* a file was dragged on to the dialog... */
     410          gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ui->file_radio), TRUE);
     411          gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (ui->file_chooser), filename);
     412          success = TRUE;
    406413        }
    407414
    408         g_free (filename);
    409     }
    410 
    411     g_strfreev (uris);
    412     gtk_drag_finish (drag_context, success, FALSE, time_);
     415      g_free (filename);
     416    }
     417
     418  g_strfreev (uris);
     419  gtk_drag_finish (drag_context, success, FALSE, time_);
    413420}
    414421
     
    416423gtr_torrent_creation_dialog_new (GtkWindow  * parent, TrCore * core)
    417424{
    418     const char * str;
    419     GtkWidget * d, *t, *w, *l, *fr, *sw, *v;
    420     GSList * slist;
    421     guint row = 0;
    422     MakeMetaUI * ui = g_new0 (MakeMetaUI, 1);
    423 
    424     ui->core = core;
    425 
    426     d = gtk_dialog_new_with_buttons (_("New Torrent"),
    427                                      parent,
    428                                      GTK_DIALOG_DESTROY_WITH_PARENT,
    429                                      GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
    430                                      GTK_STOCK_NEW, GTK_RESPONSE_ACCEPT,
    431                                      NULL);
    432     ui->dialog = d;
    433     g_signal_connect (d, "response", G_CALLBACK (onResponse), ui);
    434     g_object_set_data_full (G_OBJECT (d), "ui", ui, freeMetaUI);
    435 
    436     t = hig_workarea_create ();
    437 
    438     hig_workarea_add_section_title (t, &row, _("Files"));
    439 
    440         str = _("Sa_ve to:");
    441         w = gtk_file_chooser_button_new (NULL, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
    442         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (w), getDefaultSavePath ());
    443         ui->destination_chooser = w;
    444         hig_workarea_add_row (t, &row, str, w, NULL);
    445 
    446         l = gtk_radio_button_new_with_mnemonic (NULL, _("Source F_older:"));
    447         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (l), FALSE);
    448         w = gtk_file_chooser_button_new (NULL, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
    449         g_signal_connect (l, "toggled", G_CALLBACK (onFolderToggled), ui);
    450         g_signal_connect (l, "toggled", G_CALLBACK (onSourceToggled), w);
    451         g_signal_connect (w, "selection-changed", G_CALLBACK (onChooserChosen), ui);
    452         ui->folder_radio = l;
    453         ui->folder_chooser = w;
    454         gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
    455         hig_workarea_add_row_w (t, &row, l, w, NULL);
    456 
    457         slist = gtk_radio_button_get_group (GTK_RADIO_BUTTON (l)),
    458         l = gtk_radio_button_new_with_mnemonic (slist, _("Source _File:"));
    459         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (l), TRUE);
    460         w = gtk_file_chooser_button_new (NULL, GTK_FILE_CHOOSER_ACTION_OPEN);
    461         g_signal_connect (l, "toggled", G_CALLBACK (onFileToggled), ui);
    462         g_signal_connect (l, "toggled", G_CALLBACK (onSourceToggled), w);
    463         g_signal_connect (w, "selection-changed", G_CALLBACK (onChooserChosen), ui);
    464         ui->file_radio = l;
    465         ui->file_chooser = w;
    466         hig_workarea_add_row_w (t, &row, l, w, NULL);
    467 
    468         w = gtk_label_new (NULL);
    469         ui->pieces_lb = w;
    470         gtk_label_set_markup (GTK_LABEL (w), _("<i>No source selected</i>"));
    471         hig_workarea_add_row (t, &row, NULL, w, NULL);
    472 
    473     hig_workarea_add_section_divider (t, &row);
    474     hig_workarea_add_section_title (t, &row, _("Properties"));
    475 
    476         str = _("_Trackers:");
    477         v = gtk_box_new (GTK_ORIENTATION_VERTICAL, GUI_PAD_SMALL);
    478         ui->announce_text_buffer = gtk_text_buffer_new (NULL);
    479         w = gtk_text_view_new_with_buffer (ui->announce_text_buffer);
    480         gtk_widget_set_size_request (w, -1, 80);
    481         sw = gtk_scrolled_window_new (NULL, NULL);
    482         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
    483                                         GTK_POLICY_AUTOMATIC,
    484                                         GTK_POLICY_AUTOMATIC);
    485         gtk_container_add (GTK_CONTAINER (sw), w);
    486         fr = gtk_frame_new (NULL);
    487         gtk_frame_set_shadow_type (GTK_FRAME (fr), GTK_SHADOW_IN);
    488         gtk_container_add (GTK_CONTAINER (fr), sw);
    489         gtk_box_pack_start (GTK_BOX (v), fr, TRUE, TRUE, 0);
    490         l = gtk_label_new (NULL);
    491         gtk_label_set_markup (GTK_LABEL (l), _("To add a backup URL, add it on the line after the primary URL.\n"
    492                                                  "To add another primary URL, add it after a blank line."));
    493         gtk_label_set_justify (GTK_LABEL (l), GTK_JUSTIFY_LEFT);
    494         gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
    495         gtk_box_pack_start (GTK_BOX (v), l, FALSE, FALSE, 0);
    496         hig_workarea_add_tall_row (t, &row, str, v, NULL);
    497 
    498         l = gtk_check_button_new_with_mnemonic (_("Co_mment:"));
    499         ui->comment_check = l;
    500         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (l), FALSE);
    501         w = gtk_entry_new ();
    502         ui->comment_entry = w;
    503         gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
    504         g_signal_connect (l, "toggled", G_CALLBACK (onSourceToggled), w);
    505         hig_workarea_add_row_w (t, &row, l, w, NULL);
    506 
    507         w = hig_workarea_add_wide_checkbutton (t, &row, _("_Private torrent"), FALSE);
    508         ui->private_check = w;
    509 
    510     gtr_dialog_set_content (GTK_DIALOG (d), t);
    511 
    512     gtk_drag_dest_set (d, GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
    513     gtk_drag_dest_add_uri_targets (d);
    514     g_signal_connect (d, "drag-data-received", G_CALLBACK (on_drag_data_received), ui);
    515 
    516     return d;
    517 }
     425  const char * str;
     426  GtkWidget * d, *t, *w, *l, *fr, *sw, *v;
     427  GSList * slist;
     428  guint row = 0;
     429  MakeMetaUI * ui = g_new0 (MakeMetaUI, 1);
     430
     431  ui->core = core;
     432
     433  d = gtk_dialog_new_with_buttons (_("New Torrent"),
     434                                   parent,
     435                                   GTK_DIALOG_DESTROY_WITH_PARENT,
     436                                   GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
     437                                   GTK_STOCK_NEW, GTK_RESPONSE_ACCEPT,
     438                                   NULL);
     439  ui->dialog = d;
     440  g_signal_connect (d, "response", G_CALLBACK (onResponse), ui);
     441  g_object_set_data_full (G_OBJECT (d), "ui", ui, freeMetaUI);
     442
     443  t = hig_workarea_create ();
     444
     445  hig_workarea_add_section_title (t, &row, _("Files"));
     446
     447    str = _("Sa_ve to:");
     448    w = gtk_file_chooser_button_new (NULL, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
     449    gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (w), getDefaultSavePath ());
     450    ui->destination_chooser = w;
     451    hig_workarea_add_row (t, &row, str, w, NULL);
     452
     453    l = gtk_radio_button_new_with_mnemonic (NULL, _("Source F_older:"));
     454    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (l), FALSE);
     455    w = gtk_file_chooser_button_new (NULL, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
     456    g_signal_connect (l, "toggled", G_CALLBACK (onFolderToggled), ui);
     457    g_signal_connect (l, "toggled", G_CALLBACK (onSourceToggled), w);
     458    g_signal_connect (w, "selection-changed", G_CALLBACK (onChooserChosen), ui);
     459    ui->folder_radio = l;
     460    ui->folder_chooser = w;
     461    gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
     462    hig_workarea_add_row_w (t, &row, l, w, NULL);
     463
     464    slist = gtk_radio_button_get_group (GTK_RADIO_BUTTON (l)),
     465    l = gtk_radio_button_new_with_mnemonic (slist, _("Source _File:"));
     466    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (l), TRUE);
     467    w = gtk_file_chooser_button_new (NULL, GTK_FILE_CHOOSER_ACTION_OPEN);
     468    g_signal_connect (l, "toggled", G_CALLBACK (onFileToggled), ui);
     469    g_signal_connect (l, "toggled", G_CALLBACK (onSourceToggled), w);
     470    g_signal_connect (w, "selection-changed", G_CALLBACK (onChooserChosen), ui);
     471    ui->file_radio = l;
     472    ui->file_chooser = w;
     473    hig_workarea_add_row_w (t, &row, l, w, NULL);
     474
     475    w = gtk_label_new (NULL);
     476    ui->pieces_lb = w;
     477    gtk_label_set_markup (GTK_LABEL (w), _("<i>No source selected</i>"));
     478    hig_workarea_add_row (t, &row, NULL, w, NULL);
     479
     480  hig_workarea_add_section_divider (t, &row);
     481  hig_workarea_add_section_title (t, &row, _("Properties"));
     482
     483    str = _("_Trackers:");
     484    v = gtk_box_new (GTK_ORIENTATION_VERTICAL, GUI_PAD_SMALL);
     485    ui->announce_text_buffer = gtk_text_buffer_new (NULL);
     486    w = gtk_text_view_new_with_buffer (ui->announce_text_buffer);
     487    gtk_widget_set_size_request (w, -1, 80);
     488    sw = gtk_scrolled_window_new (NULL, NULL);
     489    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
     490                                    GTK_POLICY_AUTOMATIC,
     491                                    GTK_POLICY_AUTOMATIC);
     492    gtk_container_add (GTK_CONTAINER (sw), w);
     493    fr = gtk_frame_new (NULL);
     494    gtk_frame_set_shadow_type (GTK_FRAME (fr), GTK_SHADOW_IN);
     495    gtk_container_add (GTK_CONTAINER (fr), sw);
     496    gtk_box_pack_start (GTK_BOX (v), fr, TRUE, TRUE, 0);
     497    l = gtk_label_new (NULL);
     498    gtk_label_set_markup (GTK_LABEL (l), _("To add a backup URL, add it on the line after the primary URL.\n"
     499                                           "To add another primary URL, add it after a blank line."));
     500    gtk_label_set_justify (GTK_LABEL (l), GTK_JUSTIFY_LEFT);
     501    gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
     502    gtk_box_pack_start (GTK_BOX (v), l, FALSE, FALSE, 0);
     503    hig_workarea_add_tall_row (t, &row, str, v, NULL);
     504
     505    l = gtk_check_button_new_with_mnemonic (_("Co_mment:"));
     506    ui->comment_check = l;
     507    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (l), FALSE);
     508    w = gtk_entry_new ();
     509    ui->comment_entry = w;
     510    gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
     511    g_signal_connect (l, "toggled", G_CALLBACK (onSourceToggled), w);
     512    hig_workarea_add_row_w (t, &row, l, w, NULL);
     513
     514    w = hig_workarea_add_wide_checkbutton (t, &row, _("_Private torrent"), FALSE);
     515    ui->private_check = w;
     516
     517  gtr_dialog_set_content (GTK_DIALOG (d), t);
     518
     519  gtk_drag_dest_set (d, GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
     520  gtk_drag_dest_add_uri_targets (d);
     521  g_signal_connect (d, "drag-data-received", G_CALLBACK (on_drag_data_received), ui);
     522
     523  return d;
     524}
  • trunk/gtk/msgwin.c

    r13760 r13761  
    2929enum
    3030{
    31     COL_SEQUENCE,
    32     COL_NAME,
    33     COL_MESSAGE,
    34     COL_TR_MSG,
    35     N_COLUMNS
     31  COL_SEQUENCE,
     32  COL_NAME,
     33  COL_MESSAGE,
     34  COL_TR_MSG,
     35  N_COLUMNS
    3636};
    3737
    3838struct MsgData
    3939{
    40     TrCore        * core;
    41     GtkTreeView   * view;
    42     GtkListStore  * store;
    43     GtkTreeModel  * filter;
    44     GtkTreeModel  * sort;
    45     tr_msg_level    maxLevel;
    46     gboolean        isPaused;
    47     guint           refresh_tag;
     40  TrCore        * core;
     41  GtkTreeView   * view;
     42  GtkListStore  * store;
     43  GtkTreeModel  * filter;
     44  GtkTreeModel  * sort;
     45  tr_msg_level    maxLevel;
     46  gboolean        isPaused;
     47  guint           refresh_tag;
    4848};
    4949
     
    6565      pinned_to_new = TRUE;
    6666    }
    67     else
     67  else
    6868    {
    6969      GtkTreePath * last_visible;
  • trunk/gtk/relocate.c

    r13760 r13761  
    2727struct relocate_dialog_data
    2828{
    29     int done;
    30     bool do_move;
    31     TrCore * core;
    32     GSList * torrent_ids;
    33     GtkWidget * message_dialog;
    34     GtkWidget * chooser_dialog;
     29  int done;
     30  bool do_move;
     31  TrCore * core;
     32  GSList * torrent_ids;
     33  GtkWidget * message_dialog;
     34  GtkWidget * chooser_dialog;
    3535};
    3636
     
    3838data_free (gpointer gdata)
    3939{
    40     struct relocate_dialog_data * data = gdata;
    41     g_slist_free (data->torrent_ids);
    42     g_free (data);
     40  struct relocate_dialog_data * data = gdata;
     41  g_slist_free (data->torrent_ids);
     42  g_free (data);
    4343}
    4444
     
    5050startMovingNextTorrent (struct relocate_dialog_data * data)
    5151{
    52     char * str;
    53     const int id = GPOINTER_TO_INT (data->torrent_ids->data);
     52  char * str;
     53  const int id = GPOINTER_TO_INT (data->torrent_ids->data);
    5454
    55     tr_torrent * tor = gtr_core_find_torrent (data->core, id);
    56     if (tor != NULL)
    57         tr_torrentSetLocation (tor, previousLocation, data->do_move, NULL, &data->done);
     55  tr_torrent * tor = gtr_core_find_torrent (data->core, id);
     56  if (tor != NULL)
     57    tr_torrentSetLocation (tor, previousLocation, data->do_move, NULL, &data->done);
    5858
    59     data->torrent_ids = g_slist_delete_link (data->torrent_ids,
    60                                              data->torrent_ids);
     59  data->torrent_ids = g_slist_delete_link (data->torrent_ids,
     60                                           data->torrent_ids);
    6161
    62     str = g_strdup_printf (_("Moving \"%s\""), tr_torrentName (tor));
    63     gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (data->message_dialog), str);
    64     g_free (str);
     62  str = g_strdup_printf (_("Moving \"%s\""), tr_torrentName (tor));
     63  gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (data->message_dialog), str);
     64  g_free (str);
    6565}
    6666
     
    7070onTimer (gpointer gdata)
    7171{
    72     struct relocate_dialog_data * data = gdata;
    73     const int done = data->done;
     72  struct relocate_dialog_data * data = gdata;
     73  const int done = data->done;
    7474
    75     if (done == TR_LOC_ERROR)
     75  if (done == TR_LOC_ERROR)
    7676    {
    77         const int flags = GTK_DIALOG_MODAL
    78                         | GTK_DIALOG_DESTROY_WITH_PARENT;
    79         GtkWidget * w = gtk_message_dialog_new (GTK_WINDOW (data->message_dialog),
    80                                                 flags,
    81                                                 GTK_MESSAGE_ERROR,
    82                                                 GTK_BUTTONS_CLOSE,
    83                                                 "%s",
    84                                                 _("Couldn't move torrent"));
    85         gtk_dialog_run (GTK_DIALOG (w));
    86         gtk_widget_destroy (GTK_WIDGET (data->message_dialog));
    87         return FALSE;
     77      const int flags = GTK_DIALOG_MODAL
     78                      | GTK_DIALOG_DESTROY_WITH_PARENT;
     79      GtkWidget * w = gtk_message_dialog_new (GTK_WINDOW (data->message_dialog),
     80                                              flags,
     81                                              GTK_MESSAGE_ERROR,
     82                                              GTK_BUTTONS_CLOSE,
     83                                              "%s",
     84                                              _("Couldn't move torrent"));
     85      gtk_dialog_run (GTK_DIALOG (w));
     86      gtk_widget_destroy (GTK_WIDGET (data->message_dialog));
    8887    }
    8988    else if (done == TR_LOC_DONE)
    9089    {
    91         if (data->torrent_ids != NULL)
    92         {
    93             startMovingNextTorrent (data);
    94         }
    95         else
    96         {
    97             gtk_widget_destroy (GTK_WIDGET (data->chooser_dialog));
    98             return FALSE;
    99         }
     90      if (data->torrent_ids != NULL)
     91        startMovingNextTorrent (data);
     92      else
     93        gtk_widget_destroy (GTK_WIDGET (data->chooser_dialog));
    10094    }
    10195
    102     return G_SOURCE_CONTINUE;
     96  return G_SOURCE_CONTINUE;
    10397}
    10498
     
    106100onResponse (GtkDialog * dialog, int response, gpointer unused UNUSED)
    107101{
    108     if (response == GTK_RESPONSE_APPLY)
     102  if (response == GTK_RESPONSE_APPLY)
    109103    {
    110         GtkWidget * w;
    111         GObject * d = G_OBJECT (dialog);
    112         struct relocate_dialog_data * data = g_object_get_data (d, DATA_KEY);
    113         GtkFileChooser * chooser = g_object_get_data (d, "chooser");
    114         GtkToggleButton * move_tb = g_object_get_data (d, "move_rb");
    115         char * location = gtk_file_chooser_get_filename (chooser);
     104      GtkWidget * w;
     105      GObject * d = G_OBJECT (dialog);
     106      struct relocate_dialog_data * data = g_object_get_data (d, DATA_KEY);
     107      GtkFileChooser * chooser = g_object_get_data (d, "chooser");
     108      GtkToggleButton * move_tb = g_object_get_data (d, "move_rb");
     109      char * location = gtk_file_chooser_get_filename (chooser);
    116110
    117         data->do_move = gtk_toggle_button_get_active (move_tb);
     111      data->do_move = gtk_toggle_button_get_active (move_tb);
    118112
    119         /* pop up a dialog saying that the work is in progress */
    120         w = gtk_message_dialog_new (GTK_WINDOW (dialog),
    121                                     GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
    122                                     GTK_MESSAGE_INFO,
    123                                     GTK_BUTTONS_CLOSE,
    124                                     NULL);
    125         gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (w), _("This may take a moment
"));
    126         gtk_dialog_set_response_sensitive (GTK_DIALOG (w), GTK_RESPONSE_CLOSE, FALSE);
    127         gtk_widget_show (w);
     113      /* pop up a dialog saying that the work is in progress */
     114      w = gtk_message_dialog_new (GTK_WINDOW (dialog),
     115                                  GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
     116                                  GTK_MESSAGE_INFO,
     117                                  GTK_BUTTONS_CLOSE,
     118                                  NULL);
     119      gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (w), _("This may take a moment
"));
     120      gtk_dialog_set_response_sensitive (GTK_DIALOG (w), GTK_RESPONSE_CLOSE, FALSE);
     121      gtk_widget_show (w);
    128122
    129         /* remember this location so that it can be the default next time */
    130         g_free (previousLocation);
    131         previousLocation = location;