source: trunk/gtk/torrent-inspector.c @ 5003

Last change on this file since 5003 was 5003, checked in by charles, 15 years ago

decouple the torrent file list from the torrent inspector so that it can be reused in the "add torrent" dialog

  • Property svn:keywords set to Date Rev Author Id
File size: 37.4 KB
Line 
1/*
2 * This file Copyright (C) 2007-2008 Charles Kerr <charles@rebelbase.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: torrent-inspector.c 5003 2008-02-10 22:25:42Z charles $
11 */
12
13#include <stddef.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <glib/gi18n.h>
17#include <gtk/gtk.h>
18
19#include <libtransmission/transmission.h>
20
21#include "actions.h"
22#include "file-list.h"
23#include "tr_torrent.h"
24#include "hig.h"
25#include "torrent-inspector.h"
26#include "util.h"
27
28#define UPDATE_INTERVAL_MSEC 2000
29
30/****
31*****  PIECES VIEW
32****/
33
34static int
35getGridSize (int pieceCount, int * n_rows, int * n_cols)
36{
37  const int MAX_ACROSS = 16;
38  if (pieceCount >= (MAX_ACROSS * MAX_ACROSS)) {
39    *n_rows = *n_cols = MAX_ACROSS;
40    return MAX_ACROSS * MAX_ACROSS;
41  }
42  else {
43    int i;
44    for (i=0; (i*i) < pieceCount; ++i);
45    *n_rows = *n_cols = i;
46    return pieceCount;
47  }
48}
49
50#define TO16(a) ((guint16)((a<<8)|(a)))
51#define RGB_2_GDK(R,G,B) { 0, TO16(R), TO16(G), TO16(B) }
52
53enum { DRAW_AVAIL, DRAW_PROG };
54
55static void
56release_gobject_array (gpointer data)
57{
58  int i;
59  GObject **objects = (GObject**) data;
60  for (i=0; objects[i]!=NULL; ++i)
61    g_object_unref (G_OBJECT(objects[i]));
62  g_free (objects);
63}
64
65static gboolean
66refresh_pieces (GtkWidget * da, GdkEventExpose * event UNUSED, gpointer gtor)
67{
68  tr_torrent * tor = tr_torrent_handle( TR_TORRENT(gtor) );
69  const tr_info * info = tr_torrent_info( TR_TORRENT(gtor) );
70  int mode = GPOINTER_TO_INT (g_object_get_data (G_OBJECT(da), "draw-mode"));
71
72  GdkColormap * colormap = gtk_widget_get_colormap (da);
73  const int widget_w = da->allocation.width;
74  const int widget_h = da->allocation.height;
75  int n_rows, n_cols;
76  const int n_cells = getGridSize (info->pieceCount,  &n_rows, &n_cols);
77  const GdkRectangle grid_bounds = { 0, 0, widget_w, widget_h };
78  const double piece_w = grid_bounds.width / (double)n_cols;
79  const double piece_h = grid_bounds.height / (double)n_rows;
80  const int piece_w_int = (int) (piece_w + 1); /* pad for roundoff */
81  const int piece_h_int = (int) (piece_h + 1); /* pad for roundoff */
82  guint8 * prev_color = NULL;
83  gboolean first_time = FALSE;
84
85  int i, x, y;
86  int8_t * pieces = NULL;
87  float * completeness = NULL;
88
89  /**
90  ***  Get the Graphics Contexts...
91  **/
92
93  enum { ALL, LOTS, SOME, FEW, NONE,
94         BLACK, GRAY, BLINK,
95         N_COLORS };
96  GdkGC **gcs = (GdkGC**) g_object_get_data (G_OBJECT(da), "graphics-contexts");
97  if (gcs == NULL)
98  {
99    const GdkColor colors [N_COLORS] = {
100      RGB_2_GDK ( 114, 159, 207 ), /* all */
101      RGB_2_GDK (  52, 101, 164 ), /* lots */
102      RGB_2_GDK (  32,  74, 135 ), /* some */
103      RGB_2_GDK (  85,  87,  83 ), /* few */
104      RGB_2_GDK ( 238, 238, 236 ), /* none - tango aluminum highlight */
105      RGB_2_GDK (  46,  52,  54 ), /* black - tango slate shadow */
106      RGB_2_GDK ( 186, 189, 182 ), /* gray - tango aluminum shadow */
107      RGB_2_GDK ( 252, 233,  79 ), /* blink - tango butter highlight */
108    };
109
110    gcs = g_new (GdkGC*, N_COLORS+1);
111
112    for (i=0; i<N_COLORS; ++i) {
113      gcs[i] = gdk_gc_new (da->window);
114      gdk_gc_set_colormap (gcs[i], colormap);
115      gdk_gc_set_rgb_fg_color (gcs[i], &colors[i]);
116      gdk_gc_set_rgb_bg_color (gcs[i], &colors[i]);
117    };
118
119    gcs[N_COLORS] = NULL; /* a sentinel in the release function */
120    g_object_set_data_full (G_OBJECT(da), "graphics-contexts",
121                            gcs, release_gobject_array);
122  }
123
124  /**
125  ***  Get the cells' previous colors...
126  ***  (this is used for blinking when the color changes)
127  **/
128
129  prev_color = (guint8*) g_object_get_data (G_OBJECT(da), "prev-color");
130  if (prev_color == NULL)
131  {
132    first_time = TRUE;
133    prev_color = g_new0 (guint8, n_cells);
134    g_object_set_data_full (G_OBJECT(da), "prev-color", prev_color, g_free);
135  }
136
137  /**
138  ***  Get the piece data values...
139  **/
140
141  switch (mode) {
142    case DRAW_AVAIL:
143      pieces = g_new (int8_t, n_cells);
144      tr_torrentAvailability ( tor, pieces, n_cells );
145      break;
146    case DRAW_PROG:
147      completeness = g_new (float, n_cells);
148      tr_torrentAmountFinished ( tor, completeness, n_cells );
149      break;
150    default: g_error("no mode defined!");
151  }
152
153  /**
154  ***  Draw...
155  **/
156
157  i = 0; 
158  for (y=0; y<n_rows; ++y) {
159    for (x=0; x<n_cols; ++x) {
160      const int draw_x = grid_bounds.x + (int)(x * piece_w);
161      const int draw_y = grid_bounds.y + (int)(y * piece_h);
162      int color = BLACK;
163      int border = BLACK;
164
165      if (i < n_cells)
166      {
167        border = GRAY;
168
169        if (mode == DRAW_AVAIL) {
170          const int8_t val = pieces[i];
171               if (val <  0) color = ALL;
172          else if (val == 0) color = NONE;
173          else if (val <= 4) color = FEW;
174          else if (val <= 8) color = SOME;
175          else               color = LOTS;
176        } else { /* completeness */
177          const float val = completeness[i];
178               if (val >= 1.00) color = ALL;
179          else if (val >= 0.66) color = LOTS;
180          else if (val >= 0.33) color = SOME;
181          else if (val >= 0.01) color = FEW;
182          else                  color = NONE;
183        }
184
185        /* draw a "blink" for one interval when a piece changes */
186        if (first_time)
187          prev_color[i] = color;
188        else if (color != prev_color[i]) {
189          prev_color[i] = color;
190          color = border = BLINK;
191        }
192      }
193
194      gdk_draw_rectangle (da->window, gcs[color], TRUE,
195                          draw_x, draw_y,
196                          piece_w_int, piece_h_int);
197
198      if (i < n_cells)
199        gdk_draw_rectangle (da->window, gcs[border], FALSE,
200                            draw_x, draw_y,
201                            piece_w_int, piece_h_int);
202
203      ++i;
204    }
205  }
206
207  gdk_draw_rectangle (da->window, gcs[GRAY], FALSE,
208                      grid_bounds.x, grid_bounds.y, 
209                      grid_bounds.width-1, grid_bounds.height-1);
210
211  g_free (pieces);
212  g_free (completeness);
213  return FALSE;
214}
215
216/****
217*****  PEERS TAB
218****/
219
220enum
221{
222  PEER_COL_ADDRESS,
223  PEER_COL_CLIENT,
224  PEER_COL_PROGRESS,
225  PEER_COL_IS_ENCRYPTED,
226  PEER_COL_DOWNLOAD_RATE,
227  PEER_COL_UPLOAD_RATE,
228  PEER_COL_STATUS,
229  N_PEER_COLS
230};
231
232static const char* peer_column_names[N_PEER_COLS] =
233{
234  N_("Address"),
235  N_("Client"),
236  N_("%"),
237  " ",
238  N_("Down"),
239  N_("Up"),
240  N_("Status")
241};
242
243static int compare_peers (const void * a, const void * b)
244{
245  const tr_peer_stat * pa = a;
246  const tr_peer_stat * pb = b;
247  return strcmp (pa->addr, pb->addr);
248}
249static int compare_addr_to_peer (const void * a, const void * b)
250{
251  const char * addr = (const char *) a;
252  const tr_peer_stat * peer = b;
253  return strcmp (addr, peer->addr);
254}
255
256static void
257peer_row_set (GtkTreeStore        * store,
258              GtkTreeIter         * iter,
259              const tr_peer_stat  * peer)
260{
261  const char * client = peer->client;
262
263  if (!client || !strcmp(client,"Unknown Client"))
264    client = " ";
265
266  gtk_tree_store_set (store, iter,
267                      PEER_COL_ADDRESS, peer->addr,
268                      PEER_COL_CLIENT, client,
269                      PEER_COL_IS_ENCRYPTED, peer->isEncrypted,
270                      PEER_COL_PROGRESS, (int)(100.0*peer->progress),
271                      PEER_COL_DOWNLOAD_RATE, peer->downloadFromRate,
272                      PEER_COL_UPLOAD_RATE, peer->uploadToRate,
273                      PEER_COL_STATUS, peer->flagStr,
274                      -1);
275}
276
277static void
278append_peers_to_model (GtkTreeStore        * store,
279                       const tr_peer_stat  * peers,
280                       int                   n_peers)
281{
282  int i;
283  for (i=0; i<n_peers; ++i) {
284    GtkTreeIter iter;
285    gtk_tree_store_append (store, &iter, NULL);
286    peer_row_set (store, &iter, &peers[i]);
287  }
288}
289
290static GtkTreeModel*
291peer_model_new (tr_torrent * tor)
292{
293  GtkTreeStore * m = gtk_tree_store_new (N_PEER_COLS,
294                                         G_TYPE_STRING,   /* addr */
295                                         G_TYPE_STRING,   /* client */
296                                         G_TYPE_INT,      /* progress [0..100] */
297                                         G_TYPE_BOOLEAN,  /* isEncrypted */
298                                         G_TYPE_FLOAT,    /* downloadFromRate */
299                                         G_TYPE_FLOAT,    /* uploadToRate */
300                                         G_TYPE_STRING ); /* flagString */
301
302  int n_peers = 0;
303  tr_peer_stat * peers = tr_torrentPeers (tor, &n_peers);
304  qsort (peers, n_peers, sizeof(tr_peer_stat), compare_peers);
305  append_peers_to_model (m, peers, n_peers);
306  tr_torrentPeersFree( peers, 0 );
307  return GTK_TREE_MODEL (m);
308}
309
310static void
311render_encrypted (GtkTreeViewColumn  * column UNUSED,
312                  GtkCellRenderer    * renderer,
313                  GtkTreeModel       * tree_model,
314                  GtkTreeIter        * iter,
315                  gpointer             data UNUSED)
316{
317  gboolean is_encrypted = FALSE;
318  gtk_tree_model_get (tree_model, iter, PEER_COL_IS_ENCRYPTED, &is_encrypted,
319                                        -1);
320  g_object_set (renderer, "xalign", (gfloat)0.0,
321                          "yalign", (gfloat)0.5,
322                          "stock-id", (is_encrypted ? "transmission-lock" : NULL),
323                          NULL);
324}
325
326static void
327render_ul_rate (GtkTreeViewColumn  * column UNUSED,
328                GtkCellRenderer    * renderer,
329                GtkTreeModel       * tree_model,
330                GtkTreeIter        * iter,
331                gpointer             data UNUSED)
332{
333  float rate = 0.0;
334  gtk_tree_model_get (tree_model, iter, PEER_COL_UPLOAD_RATE, &rate, -1);
335  if( rate < 0.01 )
336    g_object_set (renderer, "text", "", NULL);
337  else {
338    char speedStr[64];
339    tr_strlspeed( speedStr, rate, sizeof(speedStr) );
340    g_object_set( renderer, "text", speedStr, NULL );
341  }
342}
343
344static void
345render_dl_rate (GtkTreeViewColumn  * column UNUSED,
346                GtkCellRenderer    * renderer,
347                GtkTreeModel       * tree_model,
348                GtkTreeIter        * iter,
349                gpointer             data UNUSED)
350{
351  float rate = 0.0;
352  gtk_tree_model_get (tree_model, iter, PEER_COL_DOWNLOAD_RATE, &rate, -1);
353  if( rate < 0.01 )
354    g_object_set (renderer, "text", "", NULL);
355  else {
356    char speedStr[64];
357    tr_strlspeed( speedStr, rate, sizeof(speedStr) );
358    g_object_set( renderer, "text", speedStr, NULL );
359  }
360}
361
362static void
363render_client (GtkTreeViewColumn   * column UNUSED,
364               GtkCellRenderer     * renderer,
365               GtkTreeModel        * tree_model,
366               GtkTreeIter         * iter,
367               gpointer              data UNUSED)
368{
369  char * client = NULL;
370  gtk_tree_model_get (tree_model, iter, PEER_COL_CLIENT, &client,
371                                        -1);
372  g_object_set (renderer, "text", (client ? client : ""), NULL);
373  g_free (client);
374}
375
376typedef struct
377{
378  TrTorrent * gtor;
379  GtkTreeModel * model; /* same object as store, but recast */
380  GtkTreeStore * store; /* same object as model, but recast */
381  GtkWidget * completeness;
382  GtkWidget * seeders_lb;
383  GtkWidget * leechers_lb;
384  GtkWidget * completed_lb;
385}
386PeerData;
387
388static void
389fmtpeercount (GtkWidget * l, int count)
390{
391  if( 0 > count ) {
392    gtk_label_set_text( GTK_LABEL(l), "?" );
393  } else {
394    char str[16];
395    g_snprintf( str, sizeof str, "%i", count );
396    gtk_label_set_text( GTK_LABEL(l), str );
397  }
398}
399
400static void
401refresh_peers (GtkWidget * top)
402{
403  int i;
404  int n_peers;
405  GtkTreeIter iter;
406  PeerData * p = (PeerData*) g_object_get_data (G_OBJECT(top), "peer-data");
407  tr_torrent * tor = tr_torrent_handle ( p->gtor );
408  GtkTreeModel * model = p->model;
409  GtkTreeStore * store = p->store;
410  tr_peer_stat * peers;
411  const tr_stat * stat = tr_torrent_stat( p->gtor );
412
413  /**
414  ***  merge the peer diffs into the tree model.
415  ***
416  ***  this is more complicated than creating a new model,
417  ***  but is also (a) more efficient and (b) doesn't undo
418  ***  the view's visible area and sorting on every refresh.
419  **/
420
421  n_peers = 0;
422  peers = tr_torrentPeers (tor, &n_peers);
423  qsort (peers, n_peers, sizeof(tr_peer_stat), compare_peers);
424
425  i = 0;
426  if (gtk_tree_model_get_iter_first (model, &iter)) do
427  {
428    char * addr = NULL;
429    tr_peer_stat * peer = NULL;
430    gtk_tree_model_get (model, &iter, PEER_COL_ADDRESS, &addr, -1);
431    peer = bsearch (addr, peers, n_peers, sizeof(tr_peer_stat),
432                    compare_addr_to_peer);
433    g_free (addr);
434
435    if (peer) /* update a pre-existing row */
436    {
437      const int pos = peer - peers;
438      const int n_rhs = n_peers - (pos+1);
439      g_assert (n_rhs >= 0);
440
441      peer_row_set (store, &iter, peer);
442
443      /* remove it from the tr_peer_stat list */
444      g_memmove (peer, peer+1, sizeof(tr_peer_stat)*n_rhs);
445      --n_peers;
446    }
447    else if (!gtk_tree_store_remove (store, &iter))
448      break; /* we removed the model's last item */
449  }
450  while (gtk_tree_model_iter_next (model, &iter));
451
452  append_peers_to_model (store, peers, n_peers);  /* all these are new */
453
454  if (GDK_IS_DRAWABLE (p->completeness->window))
455    refresh_pieces (p->completeness, NULL, p->gtor);
456
457  fmtpeercount (p->seeders_lb, stat->seeders);
458  fmtpeercount (p->leechers_lb, stat->leechers);
459  fmtpeercount (p->completed_lb, stat->completedFromTracker );
460
461  free( peers );
462}
463
464static GtkWidget* peer_page_new ( TrTorrent * gtor )
465{
466  guint i;
467  GtkTreeModel *m;
468  GtkWidget *h, *v, *w, *ret, *da, *sw, *l, *vbox, *hbox;
469  tr_torrent * tor = tr_torrent_handle (gtor);
470  PeerData * p = g_new (PeerData, 1);
471  char name[64];
472
473  /* TODO: make this configurable? */
474  int view_columns[] = { PEER_COL_IS_ENCRYPTED,
475                         PEER_COL_UPLOAD_RATE,
476                         PEER_COL_DOWNLOAD_RATE,
477                         PEER_COL_PROGRESS,
478                         PEER_COL_STATUS,
479                         PEER_COL_ADDRESS,
480                         PEER_COL_CLIENT };
481
482  m  = peer_model_new (tor);
483  v = gtk_tree_view_new_with_model (m);
484  gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(v), TRUE);
485  g_object_unref (G_OBJECT(m));
486
487  for (i=0; i<G_N_ELEMENTS(view_columns); ++i)
488  {
489    const int col = view_columns[i];
490    const char * t = _(peer_column_names[col]);
491    gboolean resizable = TRUE;
492    GtkTreeViewColumn * c;
493    GtkCellRenderer * r;
494
495    switch (col)
496    {
497      case PEER_COL_ADDRESS:
498        r = gtk_cell_renderer_text_new ();
499        g_object_set( G_OBJECT( r ), "ellipsize", PANGO_ELLIPSIZE_END, NULL );
500        c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
501        break;
502
503      case PEER_COL_CLIENT:
504        r = gtk_cell_renderer_text_new ();
505        g_object_set( G_OBJECT( r ), "ellipsize", PANGO_ELLIPSIZE_END, NULL );
506        c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
507        gtk_tree_view_column_set_cell_data_func (c, r, render_client,
508                                                 NULL, NULL);
509        break;
510
511      case PEER_COL_PROGRESS:
512        r = gtk_cell_renderer_progress_new ();
513        c = gtk_tree_view_column_new_with_attributes (t, r, "value", PEER_COL_PROGRESS, NULL);
514        break;
515
516      case PEER_COL_IS_ENCRYPTED:
517        resizable = FALSE;
518        r = gtk_cell_renderer_pixbuf_new ();
519        c = gtk_tree_view_column_new_with_attributes (t, r, NULL);
520        gtk_tree_view_column_set_sizing (c, GTK_TREE_VIEW_COLUMN_FIXED);
521        gtk_tree_view_column_set_fixed_width (c, 20);
522        gtk_tree_view_column_set_cell_data_func (c, r, render_encrypted,
523                                                 NULL, NULL);
524        break;
525
526      case PEER_COL_DOWNLOAD_RATE:
527        r = gtk_cell_renderer_text_new ();
528        c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
529        gtk_tree_view_column_set_cell_data_func (c, r, render_dl_rate,
530                                                 NULL, NULL);
531        break;
532
533      case PEER_COL_UPLOAD_RATE:
534        r = gtk_cell_renderer_text_new ();
535        c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
536        gtk_tree_view_column_set_cell_data_func (c, r, render_ul_rate,
537                                                 NULL, NULL);
538        break;
539
540      case PEER_COL_STATUS:
541        r = gtk_cell_renderer_text_new( );
542        c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
543        break;
544
545      default:
546        abort ();
547    }
548
549    gtk_tree_view_column_set_resizable (c, resizable);
550    gtk_tree_view_column_set_sort_column_id (c, col);
551    gtk_tree_view_append_column (GTK_TREE_VIEW(v), c);
552  }
553
554  /* the 'expander' column has a 10-pixel margin on the left
555     that doesn't look quite correct in any of these columns...
556     so create a non-visible column and assign it as the
557     'expander column. */
558  {
559    GtkTreeViewColumn *c = gtk_tree_view_column_new ();
560    gtk_tree_view_column_set_visible (c, FALSE);
561    gtk_tree_view_append_column (GTK_TREE_VIEW(v), c);
562    gtk_tree_view_set_expander_column (GTK_TREE_VIEW(v), c);
563  }
564
565  w = sw = gtk_scrolled_window_new (NULL, NULL);
566  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(w),
567                                  GTK_POLICY_NEVER,
568                                  GTK_POLICY_AUTOMATIC);
569  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(w),
570                                       GTK_SHADOW_IN);
571  gtk_container_add (GTK_CONTAINER(w), v);
572
573
574  vbox = gtk_vbox_new (FALSE, GUI_PAD);
575  gtk_container_set_border_width (GTK_CONTAINER(vbox), GUI_PAD_BIG);
576
577    g_snprintf (name, sizeof(name), "<b>%s</b>", _("Piece Availability"));
578    l = gtk_label_new (NULL);
579    gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f);
580    gtk_label_set_markup (GTK_LABEL(l), name);
581    gtk_box_pack_start (GTK_BOX(vbox), l, FALSE, FALSE, 0);
582
583    w = da = p->completeness = gtk_drawing_area_new ();
584    gtk_widget_set_size_request (w, 0u, 100u);
585    g_object_set_data (G_OBJECT(w), "draw-mode", GINT_TO_POINTER(DRAW_AVAIL));
586    g_signal_connect (w, "expose-event", G_CALLBACK(refresh_pieces), gtor);
587
588    h = gtk_hbox_new (FALSE, GUI_PAD);
589    w = gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f);
590    gtk_widget_set_size_request (w, GUI_PAD_BIG, 0);
591    gtk_box_pack_start (GTK_BOX(h), w, FALSE, FALSE, 0);
592    gtk_box_pack_start_defaults (GTK_BOX(h), da);
593    gtk_box_pack_start (GTK_BOX(vbox), h, FALSE, FALSE, 0);
594
595    /* a small vertical spacer */
596    w = gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f);
597    gtk_widget_set_size_request (w, 0u, GUI_PAD);
598    gtk_box_pack_start (GTK_BOX(vbox), w, FALSE, FALSE, 0);
599
600    g_snprintf (name, sizeof(name), "<b>%s</b>", _("Connected Peers"));
601    l = gtk_label_new (NULL);
602    gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f);
603    gtk_label_set_markup (GTK_LABEL(l), name);
604    gtk_box_pack_start (GTK_BOX(vbox), l, FALSE, FALSE, 0);
605
606    h = gtk_hbox_new (FALSE, GUI_PAD);
607    w = gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f);
608    gtk_widget_set_size_request (w, GUI_PAD_BIG, 0);
609    gtk_box_pack_start (GTK_BOX(h), w, FALSE, FALSE, 0);
610    gtk_box_pack_start_defaults (GTK_BOX(h), sw);
611    gtk_box_pack_start_defaults (GTK_BOX(vbox), h);
612
613    hbox = gtk_hbox_new (FALSE, GUI_PAD);
614    w = gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f);
615    gtk_widget_set_size_request (w, GUI_PAD_BIG, 0);
616    gtk_box_pack_start (GTK_BOX(hbox), w, FALSE, FALSE, 0);
617        g_snprintf (name, sizeof(name), "<b>%s:</b>", _("Seeders"));
618        l = gtk_label_new (NULL);
619        gtk_label_set_markup (GTK_LABEL(l), name);
620        gtk_box_pack_start (GTK_BOX(hbox), l, FALSE, FALSE, 0);
621        l = p->seeders_lb = gtk_label_new (NULL);
622        gtk_box_pack_start (GTK_BOX(hbox), l, FALSE, FALSE, 0);
623    gtk_box_pack_start_defaults (GTK_BOX(hbox),
624                                 gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f));
625        g_snprintf (name, sizeof(name), "<b>%s:</b>", _("Leechers"));
626        l = gtk_label_new (NULL);
627        gtk_label_set_markup (GTK_LABEL(l), name);
628        gtk_box_pack_start (GTK_BOX(hbox), l, FALSE, FALSE, 0);
629        l = p->leechers_lb = gtk_label_new (NULL);
630        gtk_box_pack_start (GTK_BOX(hbox), l, FALSE, FALSE, 0);
631    gtk_box_pack_start_defaults (GTK_BOX(hbox),
632                                 gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f));
633        g_snprintf (name, sizeof(name), "<b>%s:</b>", _("Completed"));
634        l = gtk_label_new (NULL);
635        gtk_label_set_markup (GTK_LABEL(l), name);
636        gtk_box_pack_start (GTK_BOX(hbox), l, FALSE, FALSE, 0);
637        l = p->completed_lb = gtk_label_new (NULL);
638        gtk_box_pack_start (GTK_BOX(hbox), l, FALSE, FALSE, 0);
639    gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
640
641  ret = vbox;
642  p->gtor = gtor;
643  p->model = m;
644  p->store = GTK_TREE_STORE(m);
645  g_object_set_data_full (G_OBJECT(ret), "peer-data", p, g_free);
646  return ret;
647}
648
649/****
650*****  INFO TAB
651****/
652
653static GtkWidget* info_page_new (tr_torrent * tor)
654{
655  int row = 0;
656  GtkWidget *t = hig_workarea_create ();
657  GtkWidget *l, *w, *fr;
658  char *pch;
659  char *dname, *bname;
660  char sizeStr[128];
661  char buf[256];
662  char name[128];
663  const char * namefmt = "%s:";
664  GtkTextBuffer * b;
665  tr_tracker_info * track;
666  const tr_info * info = tr_torrentInfo(tor);
667
668  hig_workarea_add_section_title (t, &row, _("Torrent Information"));
669  hig_workarea_add_section_spacer (t, row, 5);
670
671    g_snprintf (name, sizeof(name), namefmt, _("Tracker"));
672    track = info->trackerList->list;
673    pch = track->port==80
674      ? g_strdup_printf( "http://%s%s", track->address, track->announce )
675      : g_strdup_printf( "http://%s:%d%s", track->address, track->port, track->announce );
676    l = gtk_label_new( pch );
677    gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END );
678
679    hig_workarea_add_row (t, &row, name, l, NULL);
680    g_free (pch);
681
682    g_snprintf (name, sizeof(name), namefmt, _("Pieces"));
683    tr_strlsize( sizeStr, info->pieceSize, sizeof(sizeStr) );
684    g_snprintf( buf, sizeof(buf), "%d (%s)", info->pieceCount, sizeStr );
685    l = gtk_label_new (buf);
686    hig_workarea_add_row (t, &row, name, l, NULL);
687
688    g_snprintf (name, sizeof(name), namefmt, _("Hash"));
689    l = gtk_label_new (info->hashString);
690    gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END );
691    hig_workarea_add_row (t, &row, name, l, NULL);
692
693    g_snprintf (name, sizeof(name), namefmt, _("Secure"));
694    pch = (info->isPrivate )
695      ? _("Private Torrent, PEX disabled")
696      : _("Public Torrent");
697    l = gtk_label_new (pch);
698    hig_workarea_add_row (t, &row, name, l, NULL);
699
700    g_snprintf (name, sizeof(name), namefmt, _("Comment"));
701    b = gtk_text_buffer_new (NULL);
702    gtk_text_buffer_set_text (b, info->comment, -1);
703    w = gtk_text_view_new_with_buffer (b);
704    gtk_widget_set_size_request (w, 0u, 100u);
705    gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW(w), GTK_WRAP_WORD); 
706    gtk_text_view_set_editable (GTK_TEXT_VIEW(w), FALSE);
707    fr = gtk_frame_new (NULL);
708    gtk_frame_set_shadow_type (GTK_FRAME(fr), GTK_SHADOW_IN);
709    gtk_container_add (GTK_CONTAINER(fr), w);
710    w = hig_workarea_add_row (t, &row, name, fr, NULL);
711    gtk_misc_set_alignment (GTK_MISC(w), 0.0f, 0.0f);
712
713  hig_workarea_add_section_divider (t, &row);
714  hig_workarea_add_section_title (t, &row, _("Created by"));
715  hig_workarea_add_section_spacer (t, row, 2);
716 
717    g_snprintf (name, sizeof(name), namefmt, _("Creator"));
718    l = gtk_label_new (*info->creator ? info->creator : _("N/A"));
719    hig_workarea_add_row (t, &row, name, l, NULL);
720
721    g_snprintf (name, sizeof(name), namefmt, _("Date"));
722    pch = rfc822date ((guint64)info->dateCreated * 1000u);
723    l = gtk_label_new (pch);
724    hig_workarea_add_row (t, &row, name, l, NULL); 
725    g_free (pch);
726
727  hig_workarea_add_section_divider (t, &row);
728  hig_workarea_add_section_title (t, &row, _("Location"));
729  hig_workarea_add_section_spacer (t, row, 3);
730
731    g_snprintf (name, sizeof(name), namefmt, _("Downloaded data"));
732    l = gtk_label_new (tr_torrentGetFolder (tor));
733    gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END );
734    hig_workarea_add_row (t, &row, name, l, NULL); 
735
736    g_snprintf (name, sizeof(name), namefmt, _("Torrent file path"));
737    dname = g_path_get_dirname (info->torrent);
738    l = gtk_label_new ( dname );
739    gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END );
740    hig_workarea_add_row (t, &row, name, l, NULL); 
741    g_free (dname);
742
743    g_snprintf (name, sizeof(name), namefmt, _("Torrent file name"));
744    bname = g_path_get_basename (info->torrent);
745    l = gtk_label_new (bname);
746    gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END );
747    hig_workarea_add_row (t, &row, name, l, NULL); 
748    g_free (bname);
749
750  hig_workarea_finish (t, &row);
751  return t;
752}
753
754/****
755*****  ACTIVITY TAB
756****/
757
758typedef struct
759{
760  GtkWidget * state_lb;
761  GtkWidget * progress_lb;
762  GtkWidget * have_lb;
763  GtkWidget * dl_lb;
764  GtkWidget * ul_lb;
765  GtkWidget * failed_lb;
766  GtkWidget * ratio_lb;
767  GtkWidget * err_lb;
768  GtkWidget * swarm_lb;
769  GtkWidget * date_added_lb;
770  GtkWidget * last_activity_lb;
771  GtkWidget * availability_da;
772  TrTorrent * gtor;
773}
774Activity;
775
776static void
777refresh_activity (GtkWidget * top)
778{
779  Activity * a = (Activity*) g_object_get_data (G_OBJECT(top), "activity-data");
780  const tr_stat * stat = tr_torrent_stat( a->gtor );
781  char *pch;
782  char sizeStr[64];
783  char sizeStr2[64];
784  char buf[128];
785
786  pch = tr_torrent_status_str( a->gtor );
787  gtk_label_set_text (GTK_LABEL(a->state_lb), pch);
788  g_free (pch);
789
790  pch = g_strdup_printf( _( "%.1f%% (%.1f%% selected)" ), stat->percentComplete*100.0, stat->percentDone*100.0 );
791  gtk_label_set_text (GTK_LABEL(a->progress_lb), pch);
792  g_free (pch);
793
794  tr_strlsize( sizeStr,  stat->haveValid + stat->haveUnchecked, sizeof(sizeStr) );
795  tr_strlsize( sizeStr2, stat->haveValid,                       sizeof(sizeStr2) );
796  g_snprintf( buf, sizeof(buf), _("%s (%s verified)"), sizeStr, sizeStr2 );
797  gtk_label_set_text( GTK_LABEL( a->have_lb ), buf );
798
799  tr_strlsize( sizeStr, stat->downloadedEver, sizeof(sizeStr) );
800  gtk_label_set_text( GTK_LABEL(a->dl_lb), sizeStr );
801
802  tr_strlsize( sizeStr, stat->uploadedEver, sizeof(sizeStr) );
803  gtk_label_set_text( GTK_LABEL(a->ul_lb), sizeStr );
804
805  tr_strlsize( sizeStr, stat->corruptEver, sizeof( sizeStr ) );
806  gtk_label_set_text( GTK_LABEL( a->failed_lb ), sizeStr );
807
808  tr_strlratio( buf, stat->ratio, sizeof( buf ) );
809  gtk_label_set_text( GTK_LABEL( a->ratio_lb ), buf );
810
811  tr_strlspeed( buf, stat->swarmspeed, sizeof(buf) );
812  gtk_label_set_text (GTK_LABEL(a->swarm_lb), buf );
813
814  gtk_label_set_text (GTK_LABEL(a->err_lb),
815                      *stat->errorString ? stat->errorString : _("None"));
816
817  pch = stat->startDate ? rfc822date (stat->startDate)
818                        : g_strdup_printf ("?");
819  gtk_label_set_text (GTK_LABEL(a->date_added_lb), pch);
820  g_free (pch);
821
822  pch = stat->activityDate ? rfc822date (stat->activityDate)
823                           : g_strdup_printf ("?");
824  gtk_label_set_text (GTK_LABEL(a->last_activity_lb), pch);
825  g_free (pch);
826
827  if (GDK_IS_DRAWABLE (a->availability_da->window))
828    refresh_pieces (a->availability_da, NULL, a->gtor);
829}
830 
831
832static GtkWidget*
833activity_page_new (TrTorrent * gtor)
834{
835  Activity * a = g_new (Activity, 1);
836  int row = 0;
837  GtkWidget *t = hig_workarea_create ();
838  GtkWidget *l, *w;
839
840  a->gtor = gtor;
841
842  hig_workarea_add_section_title (t, &row, _("Transfer"));
843  hig_workarea_add_section_spacer (t, row, 8);
844
845    l = a->state_lb = gtk_label_new (NULL);
846    hig_workarea_add_row (t, &row, _("State:"), l, NULL);
847
848    l = a->progress_lb = gtk_label_new (NULL);
849    hig_workarea_add_row (t, &row, _("Progress:"), l, NULL);
850
851    l = a->have_lb = gtk_label_new (NULL);
852    hig_workarea_add_row (t, &row, _("Have:"), l, NULL);
853
854    l = a->dl_lb = gtk_label_new (NULL);
855    hig_workarea_add_row (t, &row, _("Downloaded:"), l, NULL);
856
857    l = a->ul_lb = gtk_label_new (NULL);
858    hig_workarea_add_row (t, &row, _("Uploaded:"), l, NULL);
859
860    l = a->failed_lb = gtk_label_new (NULL);
861    hig_workarea_add_row (t, &row, _("Failed DL:"), l, NULL);
862
863    l = a->ratio_lb = gtk_label_new (NULL);
864    hig_workarea_add_row (t, &row, _("Ratio:"), l, NULL);
865
866    l = a->swarm_lb = gtk_label_new (NULL);
867    hig_workarea_add_row (t, &row, _("Swarm rate:"), l, NULL);
868
869    l = a->err_lb = gtk_label_new (NULL);
870    hig_workarea_add_row (t, &row, _("Error:"), l, NULL);
871
872  hig_workarea_add_section_divider (t, &row);
873  hig_workarea_add_section_title (t, &row, _("Completeness"));
874  hig_workarea_add_section_spacer (t, row, 1);
875
876    w = a->availability_da = gtk_drawing_area_new ();
877    gtk_widget_set_size_request (w, 0u, 100u);
878    g_object_set_data (G_OBJECT(w), "draw-mode", GINT_TO_POINTER(DRAW_PROG));
879    g_signal_connect (w, "expose-event", G_CALLBACK(refresh_pieces), gtor);
880    hig_workarea_add_wide_control( t, &row, w );
881
882  hig_workarea_add_section_divider (t, &row);
883  hig_workarea_add_section_title (t, &row, _("Dates"));
884  hig_workarea_add_section_spacer (t, row, 3);
885
886    l = a->date_added_lb = gtk_label_new (NULL);
887    hig_workarea_add_row (t, &row, _("Date added:"), l, NULL);
888
889    l = a->last_activity_lb = gtk_label_new (NULL);
890    hig_workarea_add_row (t, &row, _("Last activity"), l, NULL);
891
892  hig_workarea_add_section_divider (t, &row);
893  hig_workarea_finish (t, &row);
894  g_object_set_data_full (G_OBJECT(t), "activity-data", a, g_free);
895  return t;
896}
897
898
899/****
900*****  OPTIONS
901****/
902
903static void
904speed_toggled_cb( GtkToggleButton * tb, gpointer gtor, int up_or_down )
905{
906  tr_torrent * tor = tr_torrent_handle (gtor);
907  gboolean b = gtk_toggle_button_get_active(tb);
908  tr_torrentSetSpeedMode( tor, up_or_down, b ? TR_SPEEDLIMIT_SINGLE
909                                             : TR_SPEEDLIMIT_GLOBAL );
910}
911static void
912ul_speed_toggled_cb (GtkToggleButton *tb, gpointer gtor)
913{
914  speed_toggled_cb( tb, gtor, TR_UP );
915}
916static void
917dl_speed_toggled_cb (GtkToggleButton *tb, gpointer gtor)
918{
919  speed_toggled_cb( tb, gtor, TR_DOWN );
920}
921
922
923#if 0
924static void
925seeding_cap_toggled_cb (GtkToggleButton *tb, gpointer gtor)
926{
927  tr_torrent_set_seeding_cap_enabled (TR_TORRENT(gtor),
928                                      gtk_toggle_button_get_active(tb));
929}
930#endif
931
932static void
933sensitize_from_check_cb (GtkToggleButton *toggle, gpointer w)
934{
935  gtk_widget_set_sensitive (GTK_WIDGET(w),
936                            gtk_toggle_button_get_active(toggle));
937}
938
939static void
940setSpeedLimit( GtkSpinButton* spin, gpointer gtor, int up_or_down )
941{
942  tr_torrent * tor = tr_torrent_handle (gtor);
943  int KiB_sec = gtk_spin_button_get_value_as_int (spin);
944  tr_torrentSetSpeedLimit( tor, up_or_down, KiB_sec );
945}
946static void
947ul_speed_spun_cb (GtkSpinButton *spin, gpointer gtor)
948{
949  setSpeedLimit( spin, gtor, TR_UP );
950}
951static void
952dl_speed_spun_cb (GtkSpinButton *spin, gpointer gtor)
953{
954  setSpeedLimit( spin, gtor, TR_DOWN );
955}
956
957#if 0
958static void
959seeding_ratio_spun_cb (GtkSpinButton *spin, gpointer gtor)
960{
961  tr_torrent_set_seeding_cap_ratio (TR_TORRENT(gtor),
962                                    gtk_spin_button_get_value(spin));
963}
964#endif
965
966static void
967max_peers_spun_cb( GtkSpinButton * spin, gpointer gtor )
968{
969  const uint16_t n = gtk_spin_button_get_value( spin );
970  tr_torrentSetMaxConnectedPeers( tr_torrent_handle( gtor ), n );
971}
972
973static GtkWidget*
974options_page_new ( TrTorrent * gtor )
975{
976  uint16_t maxConnectedPeers;
977  int i, row;
978  gboolean b;
979  GtkAdjustment *a;
980  GtkWidget *t, *w, *tb, *hb, *mis;
981  tr_torrent * tor = tr_torrent_handle (gtor);
982
983  row = 0;
984  t = hig_workarea_create ();
985  hig_workarea_add_section_title (t, &row, _("Speed Limits") );
986  hig_workarea_add_section_spacer (t, row, 2);
987
988    tb = gtk_check_button_new_with_mnemonic (_("Limit _download speed to:"));
989    b = tr_torrentGetSpeedMode(tor,TR_DOWN) == TR_SPEEDLIMIT_SINGLE;
990    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(tb), b );
991    g_signal_connect (tb, "toggled", G_CALLBACK(dl_speed_toggled_cb), gtor);
992
993    i = tr_torrentGetSpeedLimit( tor, TR_DOWN );
994    hb = gtk_hbox_new ( FALSE, 6 );
995    a = (GtkAdjustment*) gtk_adjustment_new (i, 0.0, G_MAXDOUBLE, 1, 1, 1);
996    w = gtk_spin_button_new (a, 1, 0);
997    g_signal_connect (w, "value-changed", G_CALLBACK(dl_speed_spun_cb), gtor);
998    g_signal_connect (tb, "toggled", G_CALLBACK(sensitize_from_check_cb), w);
999    sensitize_from_check_cb (GTK_TOGGLE_BUTTON(tb), w);
1000    gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (w), FALSE, TRUE, 0);
1001    mis = gtk_label_new (_("KiB/s"));
1002    gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (mis), FALSE, TRUE, 0);
1003    hig_workarea_add_row_w (t, &row, tb, hb, NULL);
1004
1005    tb = gtk_check_button_new_with_mnemonic (_("Limit _upload speed to:"));
1006    b = tr_torrentGetSpeedMode(tor,TR_UP) == TR_SPEEDLIMIT_SINGLE;
1007    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(tb), b );
1008    g_signal_connect (tb, "toggled", G_CALLBACK(ul_speed_toggled_cb), gtor);
1009
1010    i = tr_torrentGetSpeedLimit( tor, TR_UP );
1011    hb = gtk_hbox_new ( FALSE, 6 );
1012    a = (GtkAdjustment*) gtk_adjustment_new (i, 0.0, G_MAXDOUBLE, 1, 1, 1);
1013    w = gtk_spin_button_new (a, 1, 0);
1014    g_signal_connect (w, "value-changed", G_CALLBACK(ul_speed_spun_cb), gtor);
1015    g_signal_connect (tb, "toggled", G_CALLBACK(sensitize_from_check_cb), w);
1016    sensitize_from_check_cb (GTK_TOGGLE_BUTTON(tb), w);
1017    gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (w), FALSE, TRUE, 0);
1018    mis = gtk_label_new (_("KiB/s"));
1019    gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (mis), FALSE, TRUE, 0);   
1020    hig_workarea_add_row_w (t, &row, tb, hb, NULL);
1021
1022  hig_workarea_add_section_divider (t, &row);
1023  hig_workarea_add_section_title (t, &row, _("Peer Connections"));
1024  hig_workarea_add_section_spacer (t, row, 1);
1025
1026    maxConnectedPeers = tr_torrentGetMaxConnectedPeers( tor );
1027    w = gtk_spin_button_new_with_range( 1, 3000, 5 );
1028    gtk_spin_button_set_value( GTK_SPIN_BUTTON( w ), maxConnectedPeers );
1029    hb = gtk_hbox_new ( FALSE, 6 );
1030    gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (w), FALSE, TRUE, 0);
1031    mis = gtk_label_new (_("peers"));
1032    gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (mis), FALSE, TRUE, 0);   
1033    hig_workarea_add_row( t, &row, _( "Connect at _maximum to:" ), hb, w );
1034    g_signal_connect( w, "value-changed", G_CALLBACK( max_peers_spun_cb ), gtor );
1035
1036#if 0
1037    tb = gtk_check_button_new_with_mnemonic (_("_Stop seeding at ratio:"));
1038    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(tb), gtor->seeding_cap_enabled);
1039    g_signal_connect (tb, "toggled", G_CALLBACK(seeding_cap_toggled_cb), gtor);
1040    a = (GtkAdjustment*) gtk_adjustment_new (gtor->seeding_cap, 0.0, G_MAXDOUBLE, 1, 1, 1);
1041    w = gtk_spin_button_new (a, 1, 1);
1042    g_signal_connect (w, "value-changed", G_CALLBACK(seeding_ratio_spun_cb), gtor);
1043    g_signal_connect (tb, "toggled", G_CALLBACK(sensitize_from_check_cb), w);
1044    sensitize_from_check_cb (GTK_TOGGLE_BUTTON(tb), w);
1045    hig_workarea_add_row_w (t, &row, tb, w, NULL);
1046#endif
1047
1048  hig_workarea_finish (t, &row);
1049  return t;
1050}
1051
1052static void
1053refresh_options (GtkWidget * top UNUSED)
1054{
1055}
1056
1057/****
1058*****  DIALOG
1059****/
1060
1061static void
1062torrent_destroyed (gpointer dialog, GObject * dead_torrent UNUSED)
1063{
1064  gtk_widget_destroy (GTK_WIDGET(dialog));
1065}
1066
1067static void
1068remove_tag (gpointer tag)
1069{
1070  g_source_remove (GPOINTER_TO_UINT(tag)); /* stop the periodic refresh */
1071}
1072
1073static void
1074response_cb (GtkDialog *dialog, int response UNUSED, gpointer gtor)
1075{
1076  g_object_weak_unref (G_OBJECT(gtor), torrent_destroyed, dialog);
1077  gtk_widget_destroy (GTK_WIDGET(dialog));
1078}
1079
1080static gboolean
1081periodic_refresh (gpointer data)
1082{
1083  refresh_peers     (g_object_get_data (G_OBJECT(data), "peers-top"));
1084  refresh_activity  (g_object_get_data (G_OBJECT(data), "activity-top"));
1085  refresh_options   (g_object_get_data (G_OBJECT(data), "options-top"));
1086  file_list_refresh (g_object_get_data (G_OBJECT(data), "files-top"));
1087
1088  return TRUE;
1089}
1090
1091GtkWidget*
1092torrent_inspector_new ( GtkWindow * parent, TrTorrent * gtor )
1093{
1094  guint tag;
1095  GtkWidget *d, *n, *w;
1096  tr_torrent * tor = tr_torrent_handle (gtor);
1097  char sizeStr[64];
1098  char title[512];
1099  const tr_info * info = tr_torrent_info (gtor);
1100
1101  /* create the dialog */
1102  tr_strlsize( sizeStr, info->totalSize, sizeof(sizeStr) );
1103  g_snprintf( title, sizeof(title), _( "Details for %s (%s)" ), info->name, sizeStr );
1104  d = gtk_dialog_new_with_buttons (title, parent, 0,
1105                                   GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1106                                   NULL);
1107  gtk_window_set_role (GTK_WINDOW(d), "tr-info" );
1108  g_signal_connect (d, "response", G_CALLBACK (response_cb), gtor);
1109  gtk_dialog_set_has_separator( GTK_DIALOG( d ), FALSE );
1110  gtk_container_set_border_width (GTK_CONTAINER (d), 5);
1111  gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (d)->vbox), 2);
1112  g_object_weak_ref (G_OBJECT(gtor), torrent_destroyed, d);
1113 
1114
1115  /* add the notebook */
1116  n = gtk_notebook_new ();
1117  gtk_container_set_border_width ( GTK_CONTAINER ( n ), 5 );
1118
1119  w = activity_page_new (gtor);
1120  g_object_set_data (G_OBJECT(d), "activity-top", w);
1121  gtk_notebook_append_page (GTK_NOTEBOOK(n), w, 
1122                            gtk_label_new (_("Activity")));
1123
1124  w = peer_page_new (gtor);
1125  g_object_set_data (G_OBJECT(d), "peers-top", w);
1126  gtk_notebook_append_page (GTK_NOTEBOOK(n),  w,
1127                            gtk_label_new (_("Peers")));
1128
1129  gtk_notebook_append_page (GTK_NOTEBOOK(n),
1130                            info_page_new (tor),
1131                            gtk_label_new (_("Info")));
1132
1133  w = file_list_new( gtor );
1134  g_object_set_data (G_OBJECT(d), "files-top", w);
1135  gtk_notebook_append_page (GTK_NOTEBOOK(n), w,
1136                            gtk_label_new (_("Files")));
1137
1138  w = options_page_new (gtor);
1139  g_object_set_data (G_OBJECT(d), "options-top", w);
1140  gtk_notebook_append_page (GTK_NOTEBOOK(n), w,
1141                            gtk_label_new (_("Options")));
1142
1143  gtk_box_pack_start_defaults (GTK_BOX(GTK_DIALOG(d)->vbox), n);
1144
1145  tag = g_timeout_add (UPDATE_INTERVAL_MSEC, periodic_refresh, d);
1146  g_object_set_data_full (G_OBJECT(d), "tag",
1147                          GUINT_TO_POINTER(tag), remove_tag);
1148
1149  /* return the results */
1150  periodic_refresh (d);
1151  gtk_widget_show_all (GTK_DIALOG(d)->vbox);
1152  return d;
1153}
Note: See TracBrowser for help on using the repository browser.