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

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

(gtk) HIG window layout

  • Property svn:keywords set to Date Rev Author Id
File size: 37.0 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 5007 2008-02-12 16:56:44Z 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
670    g_snprintf (name, sizeof(name), namefmt, _("Tracker"));
671    track = info->trackerList->list;
672    pch = track->port==80
673      ? g_strdup_printf( "http://%s%s", track->address, track->announce )
674      : g_strdup_printf( "http://%s:%d%s", track->address, track->port, track->announce );
675    l = gtk_label_new( pch );
676    gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END );
677
678    hig_workarea_add_row (t, &row, name, l, NULL);
679    g_free (pch);
680
681    g_snprintf (name, sizeof(name), namefmt, _("Pieces"));
682    tr_strlsize( sizeStr, info->pieceSize, sizeof(sizeStr) );
683    g_snprintf( buf, sizeof(buf), "%d (%s)", info->pieceCount, sizeStr );
684    l = gtk_label_new (buf);
685    hig_workarea_add_row (t, &row, name, l, NULL);
686
687    g_snprintf (name, sizeof(name), namefmt, _("Hash"));
688    l = gtk_label_new (info->hashString);
689    gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END );
690    hig_workarea_add_row (t, &row, name, l, NULL);
691
692    g_snprintf (name, sizeof(name), namefmt, _("Secure"));
693    pch = (info->isPrivate )
694      ? _("Private Torrent, PEX disabled")
695      : _("Public Torrent");
696    l = gtk_label_new (pch);
697    hig_workarea_add_row (t, &row, name, l, NULL);
698
699    g_snprintf (name, sizeof(name), namefmt, _("Comment"));
700    b = gtk_text_buffer_new (NULL);
701    gtk_text_buffer_set_text (b, info->comment, -1);
702    w = gtk_text_view_new_with_buffer (b);
703    gtk_widget_set_size_request (w, 0u, 100u);
704    gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW(w), GTK_WRAP_WORD); 
705    gtk_text_view_set_editable (GTK_TEXT_VIEW(w), FALSE);
706    fr = gtk_frame_new (NULL);
707    gtk_frame_set_shadow_type (GTK_FRAME(fr), GTK_SHADOW_IN);
708    gtk_container_add (GTK_CONTAINER(fr), w);
709    w = hig_workarea_add_row (t, &row, name, fr, NULL);
710    gtk_misc_set_alignment (GTK_MISC(w), 0.0f, 0.0f);
711
712  hig_workarea_add_section_divider (t, &row);
713  hig_workarea_add_section_title (t, &row, _("Created by"));
714 
715    g_snprintf (name, sizeof(name), namefmt, _("Creator"));
716    l = gtk_label_new (*info->creator ? info->creator : _("N/A"));
717    hig_workarea_add_row (t, &row, name, l, NULL);
718
719    g_snprintf (name, sizeof(name), namefmt, _("Date"));
720    pch = rfc822date ((guint64)info->dateCreated * 1000u);
721    l = gtk_label_new (pch);
722    hig_workarea_add_row (t, &row, name, l, NULL); 
723    g_free (pch);
724
725  hig_workarea_add_section_divider (t, &row);
726  hig_workarea_add_section_title (t, &row, _("Location"));
727
728    g_snprintf (name, sizeof(name), namefmt, _("Downloaded data"));
729    l = gtk_label_new (tr_torrentGetFolder (tor));
730    gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END );
731    hig_workarea_add_row (t, &row, name, l, NULL); 
732
733    g_snprintf (name, sizeof(name), namefmt, _("Torrent file path"));
734    dname = g_path_get_dirname (info->torrent);
735    l = gtk_label_new ( dname );
736    gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END );
737    hig_workarea_add_row (t, &row, name, l, NULL); 
738    g_free (dname);
739
740    g_snprintf (name, sizeof(name), namefmt, _("Torrent file name"));
741    bname = g_path_get_basename (info->torrent);
742    l = gtk_label_new (bname);
743    gtk_label_set_ellipsize( GTK_LABEL( l ), PANGO_ELLIPSIZE_END );
744    hig_workarea_add_row (t, &row, name, l, NULL); 
745    g_free (bname);
746
747  hig_workarea_finish (t, &row);
748  return t;
749}
750
751/****
752*****  ACTIVITY TAB
753****/
754
755typedef struct
756{
757  GtkWidget * state_lb;
758  GtkWidget * progress_lb;
759  GtkWidget * have_lb;
760  GtkWidget * dl_lb;
761  GtkWidget * ul_lb;
762  GtkWidget * failed_lb;
763  GtkWidget * ratio_lb;
764  GtkWidget * err_lb;
765  GtkWidget * swarm_lb;
766  GtkWidget * date_added_lb;
767  GtkWidget * last_activity_lb;
768  GtkWidget * availability_da;
769  TrTorrent * gtor;
770}
771Activity;
772
773static void
774refresh_activity (GtkWidget * top)
775{
776  Activity * a = (Activity*) g_object_get_data (G_OBJECT(top), "activity-data");
777  const tr_stat * stat = tr_torrent_stat( a->gtor );
778  char *pch;
779  char sizeStr[64];
780  char sizeStr2[64];
781  char buf[128];
782
783  pch = tr_torrent_status_str( a->gtor );
784  gtk_label_set_text (GTK_LABEL(a->state_lb), pch);
785  g_free (pch);
786
787  pch = g_strdup_printf( _( "%.1f%% (%.1f%% selected)" ), stat->percentComplete*100.0, stat->percentDone*100.0 );
788  gtk_label_set_text (GTK_LABEL(a->progress_lb), pch);
789  g_free (pch);
790
791  tr_strlsize( sizeStr,  stat->haveValid + stat->haveUnchecked, sizeof(sizeStr) );
792  tr_strlsize( sizeStr2, stat->haveValid,                       sizeof(sizeStr2) );
793  g_snprintf( buf, sizeof(buf), _("%s (%s verified)"), sizeStr, sizeStr2 );
794  gtk_label_set_text( GTK_LABEL( a->have_lb ), buf );
795
796  tr_strlsize( sizeStr, stat->downloadedEver, sizeof(sizeStr) );
797  gtk_label_set_text( GTK_LABEL(a->dl_lb), sizeStr );
798
799  tr_strlsize( sizeStr, stat->uploadedEver, sizeof(sizeStr) );
800  gtk_label_set_text( GTK_LABEL(a->ul_lb), sizeStr );
801
802  tr_strlsize( sizeStr, stat->corruptEver, sizeof( sizeStr ) );
803  gtk_label_set_text( GTK_LABEL( a->failed_lb ), sizeStr );
804
805  tr_strlratio( buf, stat->ratio, sizeof( buf ) );
806  gtk_label_set_text( GTK_LABEL( a->ratio_lb ), buf );
807
808  tr_strlspeed( buf, stat->swarmspeed, sizeof(buf) );
809  gtk_label_set_text (GTK_LABEL(a->swarm_lb), buf );
810
811  gtk_label_set_text (GTK_LABEL(a->err_lb),
812                      *stat->errorString ? stat->errorString : _("None"));
813
814  pch = stat->startDate ? rfc822date (stat->startDate)
815                        : g_strdup_printf ("?");
816  gtk_label_set_text (GTK_LABEL(a->date_added_lb), pch);
817  g_free (pch);
818
819  pch = stat->activityDate ? rfc822date (stat->activityDate)
820                           : g_strdup_printf ("?");
821  gtk_label_set_text (GTK_LABEL(a->last_activity_lb), pch);
822  g_free (pch);
823
824  if (GDK_IS_DRAWABLE (a->availability_da->window))
825    refresh_pieces (a->availability_da, NULL, a->gtor);
826}
827 
828
829static GtkWidget*
830activity_page_new (TrTorrent * gtor)
831{
832  Activity * a = g_new (Activity, 1);
833  int row = 0;
834  GtkWidget *t = hig_workarea_create ();
835  GtkWidget *l, *w;
836
837  a->gtor = gtor;
838
839  hig_workarea_add_section_title (t, &row, _("Transfer"));
840
841    l = a->state_lb = gtk_label_new (NULL);
842    hig_workarea_add_row (t, &row, _("State:"), l, NULL);
843
844    l = a->progress_lb = gtk_label_new (NULL);
845    hig_workarea_add_row (t, &row, _("Progress:"), l, NULL);
846
847    l = a->have_lb = gtk_label_new (NULL);
848    hig_workarea_add_row (t, &row, _("Have:"), l, NULL);
849
850    l = a->dl_lb = gtk_label_new (NULL);
851    hig_workarea_add_row (t, &row, _("Downloaded:"), l, NULL);
852
853    l = a->ul_lb = gtk_label_new (NULL);
854    hig_workarea_add_row (t, &row, _("Uploaded:"), l, NULL);
855
856    l = a->failed_lb = gtk_label_new (NULL);
857    hig_workarea_add_row (t, &row, _("Failed DL:"), l, NULL);
858
859    l = a->ratio_lb = gtk_label_new (NULL);
860    hig_workarea_add_row (t, &row, _("Ratio:"), l, NULL);
861
862    l = a->swarm_lb = gtk_label_new (NULL);
863    hig_workarea_add_row (t, &row, _("Swarm rate:"), l, NULL);
864
865    l = a->err_lb = gtk_label_new (NULL);
866    hig_workarea_add_row (t, &row, _("Error:"), l, NULL);
867
868  hig_workarea_add_section_divider (t, &row);
869  hig_workarea_add_section_title (t, &row, _("Completeness"));
870
871    w = a->availability_da = gtk_drawing_area_new ();
872    gtk_widget_set_size_request (w, 0u, 100u);
873    g_object_set_data (G_OBJECT(w), "draw-mode", GINT_TO_POINTER(DRAW_PROG));
874    g_signal_connect (w, "expose-event", G_CALLBACK(refresh_pieces), gtor);
875    hig_workarea_add_wide_control( t, &row, w );
876
877  hig_workarea_add_section_divider (t, &row);
878  hig_workarea_add_section_title (t, &row, _("Dates"));
879
880    l = a->date_added_lb = gtk_label_new (NULL);
881    hig_workarea_add_row (t, &row, _("Date added:"), l, NULL);
882
883    l = a->last_activity_lb = gtk_label_new (NULL);
884    hig_workarea_add_row (t, &row, _("Last activity"), l, NULL);
885
886  hig_workarea_add_section_divider (t, &row);
887  hig_workarea_finish (t, &row);
888  g_object_set_data_full (G_OBJECT(t), "activity-data", a, g_free);
889  return t;
890}
891
892
893/****
894*****  OPTIONS
895****/
896
897static void
898speed_toggled_cb( GtkToggleButton * tb, gpointer gtor, int up_or_down )
899{
900  tr_torrent * tor = tr_torrent_handle (gtor);
901  gboolean b = gtk_toggle_button_get_active(tb);
902  tr_torrentSetSpeedMode( tor, up_or_down, b ? TR_SPEEDLIMIT_SINGLE
903                                             : TR_SPEEDLIMIT_GLOBAL );
904}
905static void
906ul_speed_toggled_cb (GtkToggleButton *tb, gpointer gtor)
907{
908  speed_toggled_cb( tb, gtor, TR_UP );
909}
910static void
911dl_speed_toggled_cb (GtkToggleButton *tb, gpointer gtor)
912{
913  speed_toggled_cb( tb, gtor, TR_DOWN );
914}
915
916
917#if 0
918static void
919seeding_cap_toggled_cb (GtkToggleButton *tb, gpointer gtor)
920{
921  tr_torrent_set_seeding_cap_enabled (TR_TORRENT(gtor),
922                                      gtk_toggle_button_get_active(tb));
923}
924#endif
925
926static void
927sensitize_from_check_cb (GtkToggleButton *toggle, gpointer w)
928{
929  gtk_widget_set_sensitive (GTK_WIDGET(w),
930                            gtk_toggle_button_get_active(toggle));
931}
932
933static void
934setSpeedLimit( GtkSpinButton* spin, gpointer gtor, int up_or_down )
935{
936  tr_torrent * tor = tr_torrent_handle (gtor);
937  int KiB_sec = gtk_spin_button_get_value_as_int (spin);
938  tr_torrentSetSpeedLimit( tor, up_or_down, KiB_sec );
939}
940static void
941ul_speed_spun_cb (GtkSpinButton *spin, gpointer gtor)
942{
943  setSpeedLimit( spin, gtor, TR_UP );
944}
945static void
946dl_speed_spun_cb (GtkSpinButton *spin, gpointer gtor)
947{
948  setSpeedLimit( spin, gtor, TR_DOWN );
949}
950
951#if 0
952static void
953seeding_ratio_spun_cb (GtkSpinButton *spin, gpointer gtor)
954{
955  tr_torrent_set_seeding_cap_ratio (TR_TORRENT(gtor),
956                                    gtk_spin_button_get_value(spin));
957}
958#endif
959
960static void
961max_peers_spun_cb( GtkSpinButton * spin, gpointer gtor )
962{
963  const uint16_t n = gtk_spin_button_get_value( spin );
964  tr_torrentSetMaxConnectedPeers( tr_torrent_handle( gtor ), n );
965}
966
967static GtkWidget*
968options_page_new ( TrTorrent * gtor )
969{
970  uint16_t maxConnectedPeers;
971  int i, row;
972  gboolean b;
973  GtkAdjustment *a;
974  GtkWidget *t, *w, *tb, *hb, *mis;
975  tr_torrent * tor = tr_torrent_handle (gtor);
976
977  row = 0;
978  t = hig_workarea_create ();
979  hig_workarea_add_section_title (t, &row, _("Speed Limits") );
980
981    tb = gtk_check_button_new_with_mnemonic (_("Limit _download speed to:"));
982    b = tr_torrentGetSpeedMode(tor,TR_DOWN) == TR_SPEEDLIMIT_SINGLE;
983    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(tb), b );
984    g_signal_connect (tb, "toggled", G_CALLBACK(dl_speed_toggled_cb), gtor);
985
986    i = tr_torrentGetSpeedLimit( tor, TR_DOWN );
987    hb = gtk_hbox_new ( FALSE, 6 );
988    a = (GtkAdjustment*) gtk_adjustment_new (i, 0.0, G_MAXDOUBLE, 1, 1, 1);
989    w = gtk_spin_button_new (a, 1, 0);
990    g_signal_connect (w, "value-changed", G_CALLBACK(dl_speed_spun_cb), gtor);
991    g_signal_connect (tb, "toggled", G_CALLBACK(sensitize_from_check_cb), w);
992    sensitize_from_check_cb (GTK_TOGGLE_BUTTON(tb), w);
993    gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (w), FALSE, TRUE, 0);
994    mis = gtk_label_new (_("KiB/s"));
995    gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (mis), FALSE, TRUE, 0);
996    hig_workarea_add_row_w (t, &row, tb, hb, NULL);
997
998    tb = gtk_check_button_new_with_mnemonic (_("Limit _upload speed to:"));
999    b = tr_torrentGetSpeedMode(tor,TR_UP) == TR_SPEEDLIMIT_SINGLE;
1000    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(tb), b );
1001    g_signal_connect (tb, "toggled", G_CALLBACK(ul_speed_toggled_cb), gtor);
1002
1003    i = tr_torrentGetSpeedLimit( tor, TR_UP );
1004    hb = gtk_hbox_new ( FALSE, 6 );
1005    a = (GtkAdjustment*) gtk_adjustment_new (i, 0.0, G_MAXDOUBLE, 1, 1, 1);
1006    w = gtk_spin_button_new (a, 1, 0);
1007    g_signal_connect (w, "value-changed", G_CALLBACK(ul_speed_spun_cb), gtor);
1008    g_signal_connect (tb, "toggled", G_CALLBACK(sensitize_from_check_cb), w);
1009    sensitize_from_check_cb (GTK_TOGGLE_BUTTON(tb), w);
1010    gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (w), FALSE, TRUE, 0);
1011    mis = gtk_label_new (_("KiB/s"));
1012    gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (mis), FALSE, TRUE, 0);   
1013    hig_workarea_add_row_w (t, &row, tb, hb, NULL);
1014
1015  hig_workarea_add_section_divider (t, &row);
1016  hig_workarea_add_section_title (t, &row, _("Peer Connections"));
1017
1018    maxConnectedPeers = tr_torrentGetMaxConnectedPeers( tor );
1019    w = gtk_spin_button_new_with_range( 1, 3000, 5 );
1020    gtk_spin_button_set_value( GTK_SPIN_BUTTON( w ), maxConnectedPeers );
1021    hb = gtk_hbox_new ( FALSE, 6 );
1022    gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (w), FALSE, TRUE, 0);
1023    mis = gtk_label_new (_("peers"));
1024    gtk_box_pack_start ( GTK_BOX (hb), GTK_WIDGET (mis), FALSE, TRUE, 0);   
1025    hig_workarea_add_row( t, &row, _( "Connect at _maximum to:" ), hb, w );
1026    g_signal_connect( w, "value-changed", G_CALLBACK( max_peers_spun_cb ), gtor );
1027
1028#if 0
1029    tb = gtk_check_button_new_with_mnemonic (_("_Stop seeding at ratio:"));
1030    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(tb), gtor->seeding_cap_enabled);
1031    g_signal_connect (tb, "toggled", G_CALLBACK(seeding_cap_toggled_cb), gtor);
1032    a = (GtkAdjustment*) gtk_adjustment_new (gtor->seeding_cap, 0.0, G_MAXDOUBLE, 1, 1, 1);
1033    w = gtk_spin_button_new (a, 1, 1);
1034    g_signal_connect (w, "value-changed", G_CALLBACK(seeding_ratio_spun_cb), gtor);
1035    g_signal_connect (tb, "toggled", G_CALLBACK(sensitize_from_check_cb), w);
1036    sensitize_from_check_cb (GTK_TOGGLE_BUTTON(tb), w);
1037    hig_workarea_add_row_w (t, &row, tb, w, NULL);
1038#endif
1039
1040  hig_workarea_finish (t, &row);
1041  return t;
1042}
1043
1044static void
1045refresh_options (GtkWidget * top UNUSED)
1046{
1047}
1048
1049/****
1050*****  DIALOG
1051****/
1052
1053static void
1054torrent_destroyed (gpointer dialog, GObject * dead_torrent UNUSED)
1055{
1056  gtk_widget_destroy (GTK_WIDGET(dialog));
1057}
1058
1059static void
1060remove_tag (gpointer tag)
1061{
1062  g_source_remove (GPOINTER_TO_UINT(tag)); /* stop the periodic refresh */
1063}
1064
1065static void
1066response_cb (GtkDialog *dialog, int response UNUSED, gpointer gtor)
1067{
1068  g_object_weak_unref (G_OBJECT(gtor), torrent_destroyed, dialog);
1069  gtk_widget_destroy (GTK_WIDGET(dialog));
1070}
1071
1072static gboolean
1073periodic_refresh (gpointer data)
1074{
1075  refresh_peers     (g_object_get_data (G_OBJECT(data), "peers-top"));
1076  refresh_activity  (g_object_get_data (G_OBJECT(data), "activity-top"));
1077  refresh_options   (g_object_get_data (G_OBJECT(data), "options-top"));
1078  file_list_refresh (g_object_get_data (G_OBJECT(data), "files-top"));
1079
1080  return TRUE;
1081}
1082
1083GtkWidget*
1084torrent_inspector_new ( GtkWindow * parent, TrTorrent * gtor )
1085{
1086  guint tag;
1087  GtkWidget *d, *n, *w;
1088  tr_torrent * tor = tr_torrent_handle (gtor);
1089  char sizeStr[64];
1090  char title[512];
1091  const tr_info * info = tr_torrent_info (gtor);
1092
1093  /* create the dialog */
1094  tr_strlsize( sizeStr, info->totalSize, sizeof(sizeStr) );
1095  g_snprintf( title, sizeof(title), _( "Details for %s (%s)" ), info->name, sizeStr );
1096  d = gtk_dialog_new_with_buttons (title, parent, 0,
1097                                   GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1098                                   NULL);
1099  gtk_window_set_role (GTK_WINDOW(d), "tr-info" );
1100  g_signal_connect (d, "response", G_CALLBACK (response_cb), gtor);
1101  gtk_dialog_set_has_separator( GTK_DIALOG( d ), FALSE );
1102  gtk_container_set_border_width (GTK_CONTAINER (d), 5);
1103  gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (d)->vbox), 2);
1104  g_object_weak_ref (G_OBJECT(gtor), torrent_destroyed, d);
1105 
1106
1107  /* add the notebook */
1108  n = gtk_notebook_new ();
1109  gtk_container_set_border_width ( GTK_CONTAINER ( n ), 5 );
1110
1111  w = activity_page_new (gtor);
1112  g_object_set_data (G_OBJECT(d), "activity-top", w);
1113  gtk_notebook_append_page (GTK_NOTEBOOK(n), w, 
1114                            gtk_label_new (_("Activity")));
1115
1116  w = peer_page_new (gtor);
1117  g_object_set_data (G_OBJECT(d), "peers-top", w);
1118  gtk_notebook_append_page (GTK_NOTEBOOK(n),  w,
1119                            gtk_label_new (_("Peers")));
1120
1121  gtk_notebook_append_page (GTK_NOTEBOOK(n),
1122                            info_page_new (tor),
1123                            gtk_label_new (_("Info")));
1124
1125  w = file_list_new( gtor );
1126  g_object_set_data (G_OBJECT(d), "files-top", w);
1127  gtk_notebook_append_page (GTK_NOTEBOOK(n), w,
1128                            gtk_label_new (_("Files")));
1129
1130  w = options_page_new (gtor);
1131  g_object_set_data (G_OBJECT(d), "options-top", w);
1132  gtk_notebook_append_page (GTK_NOTEBOOK(n), w,
1133                            gtk_label_new (_("Options")));
1134
1135  gtk_box_pack_start_defaults (GTK_BOX(GTK_DIALOG(d)->vbox), n);
1136
1137  tag = g_timeout_add (UPDATE_INTERVAL_MSEC, periodic_refresh, d);
1138  g_object_set_data_full (G_OBJECT(d), "tag",
1139                          GUINT_TO_POINTER(tag), remove_tag);
1140
1141  /* return the results */
1142  periodic_refresh (d);
1143  gtk_widget_show_all (GTK_DIALOG(d)->vbox);
1144  return d;
1145}
Note: See TracBrowser for help on using the repository browser.