source: trunk/gtk/dialogs.c @ 920

Last change on this file since 920 was 920, checked in by joshe, 15 years ago

Merge nat-traversal branch to trunk.

  • Property svn:keywords set to Date Rev Author Id
File size: 21.0 KB
Line 
1/******************************************************************************
2 * $Id: dialogs.c 920 2006-09-25 18:37:45Z joshe $
3 *
4 * Copyright (c) 2005-2006 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <errno.h>
26#include <stdlib.h>
27#include <string.h>
28
29#include <gtk/gtk.h>
30#include <glib/gi18n.h>
31
32#include "conf.h"
33#include "dialogs.h"
34#include "transmission.h"
35#include "util.h"
36
37#define PREFNAME                "transmission-dialog-pref-name"
38
39/* default values for a couple prefs */
40#define DEF_DOWNLIMIT           100
41#define DEF_USEDOWNLIMIT        FALSE
42#define DEF_UPLIMIT             20
43#define DEF_USEUPLIMIT          TRUE
44#define DEF_NAT                 TRUE
45
46struct prefdata {
47  GList *prefwidgets;
48  GtkWindow *parent;
49  TrBackend *back;
50};
51
52struct addcb {
53  add_torrents_func_t addfunc;
54  GtkWindow *parent;
55  void *data;
56  gboolean autostart;
57  gboolean usingaltdir;
58  GtkFileChooser *altdir;
59  GtkButtonBox *altbox;
60};
61
62static void
63clicklimitbox(GtkWidget *widget, gpointer gdata);
64static void
65freedata(gpointer gdata, GClosure *closure);
66static void
67clickdialog(GtkWidget *widget, int resp, gpointer gdata);
68static void
69autoclick(GtkWidget *widget, gpointer gdata);
70static void
71dirclick(GtkWidget *widget, gpointer gdata);
72static void
73addresp(GtkWidget *widget, gint resp, gpointer gdata);
74
75static void
76setupprefwidget(GtkWidget *widget, const char *prefname, ...) {
77  const char *pref = cf_getpref(prefname);
78  GtkTreeModel *model;
79  GtkTreeIter iter;
80  guint prefsflag, modelflag;
81  va_list ap;
82
83  g_object_set_data_full(G_OBJECT(widget), PREFNAME,
84                         g_strdup(prefname), (GDestroyNotify)g_free);
85
86  va_start(ap, prefname);
87
88  if(ISA(widget, GTK_TYPE_FILE_CHOOSER)) {
89    if(NULL != pref)
90      gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(widget), pref);
91  }
92  else if(ISA(widget, GTK_TYPE_SPIN_BUTTON))
93    gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),
94      (NULL == pref ? va_arg(ap, long) : strtol(pref, NULL, 10)));
95  else if(ISA(widget, GTK_TYPE_TOGGLE_BUTTON))
96    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
97      (NULL == pref ? va_arg(ap, gboolean) : strbool(pref)));
98  else if(ISA(widget, GTK_TYPE_COMBO_BOX)) {
99    model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
100    prefsflag = addactionflag(pref);
101    if(gtk_tree_model_get_iter_first(model, &iter))
102      do {
103        gtk_tree_model_get(model, &iter, 1, &modelflag, -1);
104        if(modelflag == prefsflag) {
105          gtk_combo_box_set_active_iter(GTK_COMBO_BOX(widget), &iter);
106          break;
107        }
108      } while(gtk_tree_model_iter_next(model, &iter));
109  }
110  else {
111    g_assert_not_reached();
112  }
113
114  va_end(ap);
115}
116
117static void
118saveprefwidget(GtkWindow *parent, GtkWidget *widget) {
119  char *prefname;
120  const char *strval;
121  char *freeablestr;
122  GtkTreeModel *model;
123  GtkTreeIter iter;
124  guint uintval;
125
126  strval = NULL;
127  freeablestr = NULL;
128  if(ISA(widget, GTK_TYPE_FILE_CHOOSER)) {
129    strval = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(widget));
130    if(NULL != strval) {
131      if(!mkdir_p(strval, 0777)) {
132        errmsg(parent, _("Failed to create the directory %s:\n%s"),
133               strval, strerror(errno));
134        return;
135      }
136    }
137  }
138  else if(ISA(widget, GTK_TYPE_SPIN_BUTTON))
139    freeablestr = g_strdup_printf("%i",
140      gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)));
141  else if(ISA(widget, GTK_TYPE_TOGGLE_BUTTON))
142    strval = (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)) ?
143              "yes" : "no");
144  else if(ISA(widget, GTK_TYPE_COMBO_BOX)) {
145    if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter)) {
146      model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
147      gtk_tree_model_get(model, &iter, 1, &uintval, -1);
148      strval = addactionname(uintval);
149    }
150  }
151  else {
152    g_assert_not_reached();
153    return;
154  }
155
156  prefname = g_object_get_data(G_OBJECT(widget), PREFNAME);
157  g_assert(NULL != prefname);
158
159  if(NULL != strval)
160    cf_setpref(prefname, strval);
161  else if(NULL != freeablestr) {
162    cf_setpref(prefname, freeablestr);
163    g_free(freeablestr);
164  }
165}
166
167GtkWidget *
168makeprefwindow(GtkWindow *parent, TrBackend *back) {
169  char *title = g_strdup_printf(_("%s Preferences"), g_get_application_name());
170  GtkWidget *wind = gtk_dialog_new_with_buttons(title, parent,
171   GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
172   GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
173   GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
174  const unsigned int rowcount = 9;
175  GtkWidget *table = gtk_table_new(rowcount, 2, FALSE);
176  GtkWidget *portnum = gtk_spin_button_new_with_range(1, 0xffff, 1);
177  GtkWidget *natcheck = gtk_check_button_new_with_mnemonic(
178    _("Use NAT _Traversal (NAT-PMP and UPnP)"));
179  GtkWidget *dirstr = gtk_file_chooser_button_new(
180    _("Choose a download directory"),
181    GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
182  GtkWidget *addstd = gtk_combo_box_new();
183  GtkWidget *addipc = gtk_combo_box_new();
184  GtkWidget *label;
185  GtkWidget **array;
186  struct prefdata *data = g_new0(struct prefdata, 1);
187  struct { GtkWidget *on; GtkWidget *num; GtkWidget *label; gboolean defuse;
188    const char *usepref; const char *numpref; long def; } lim[] = {
189    { gtk_check_button_new_with_mnemonic(_("_Limit download speed")),
190      gtk_spin_button_new_with_range(0, G_MAXLONG, 1),
191      gtk_label_new_with_mnemonic(_("Maximum _download speed:")),
192      DEF_USEDOWNLIMIT, PREF_USEDOWNLIMIT, PREF_DOWNLIMIT, DEF_DOWNLIMIT, },
193    { gtk_check_button_new_with_mnemonic(_("Li_mit upload speed")),
194      gtk_spin_button_new_with_range(0, G_MAXLONG, 1),
195      gtk_label_new_with_mnemonic(_("Maximum _upload speed:")),
196      DEF_USEUPLIMIT, PREF_USEUPLIMIT, PREF_UPLIMIT, DEF_UPLIMIT, },
197  };
198  unsigned int ii;
199  GtkTreeModel *model;
200  GtkTreeIter iter;
201  GtkCellRenderer *rend;
202  gboolean boolval;
203  int intval;
204
205  g_free(title);
206  gtk_widget_set_name(wind, "TransmissionDialog");
207  gtk_table_set_col_spacings(GTK_TABLE(table), 8);
208  gtk_table_set_row_spacings(GTK_TABLE(table), 8);
209  gtk_dialog_set_default_response(GTK_DIALOG(wind), GTK_RESPONSE_OK);
210  gtk_container_set_border_width(GTK_CONTAINER(table), 6);
211  gtk_window_set_resizable(GTK_WINDOW(wind), FALSE);
212
213  data->prefwidgets = makeglist(portnum, lim[0].on, lim[0].num, lim[1].on,
214    lim[1].num, dirstr, addstd, addipc, natcheck, NULL);
215  data->parent = parent;
216  data->back = back;
217  g_object_ref(G_OBJECT(back));
218
219#define RN(n) (n), (n) + 1
220
221  for(ii = 0; ii < ALEN(lim); ii++) {
222    /* limit checkbox */
223    setupprefwidget(lim[ii].on, lim[ii].usepref, (gboolean)lim[ii].defuse);
224    array = g_new(GtkWidget*, 2);
225    g_signal_connect_data(lim[ii].on, "clicked", G_CALLBACK(clicklimitbox),
226                          array, (GClosureNotify)g_free, 0);
227    gtk_table_attach_defaults(GTK_TABLE(table), lim[ii].on,    0, 2, RN(ii*2));
228
229    /* limit label and entry */
230    gtk_label_set_mnemonic_widget(GTK_LABEL(lim[ii].label), lim[ii].num);
231    gtk_misc_set_alignment(GTK_MISC(lim[ii].label), 0, .5);
232    gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(lim[ii].num), TRUE);
233    setupprefwidget(lim[ii].num, lim[ii].numpref, (long)lim[ii].def);
234    gtk_table_attach_defaults(GTK_TABLE(table), lim[ii].label, 0,1,RN(ii*2+1));
235    gtk_table_attach_defaults(GTK_TABLE(table), lim[ii].num,   1,2,RN(ii*2+1));
236    array[0] = lim[ii].label;
237    array[1] = lim[ii].num;
238    clicklimitbox(lim[ii].on, array);
239  }
240  ii *= 2;
241
242  /* directory label and chooser */
243  label = gtk_label_new_with_mnemonic(_("Download di_rectory:"));
244  gtk_label_set_mnemonic_widget(GTK_LABEL(label), dirstr);
245  gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
246  setupprefwidget(dirstr, PREF_DIR);
247  gtk_table_attach_defaults(GTK_TABLE(table), label,           0, 1, RN(ii));
248  gtk_table_attach_defaults(GTK_TABLE(table), dirstr,          1, 2, RN(ii));
249  ii++;
250
251  /* port label and entry */
252  label = gtk_label_new_with_mnemonic(_("Listening _port:"));
253  gtk_label_set_mnemonic_widget(GTK_LABEL(label), portnum);
254  gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
255  gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(portnum), TRUE);
256  setupprefwidget(portnum, PREF_PORT, (long)TR_DEFAULT_PORT);
257  gtk_table_attach_defaults(GTK_TABLE(table), label,           0, 1, RN(ii));
258  gtk_table_attach_defaults(GTK_TABLE(table), portnum,         1, 2, RN(ii));
259  ii++;
260
261  /* NAT traversal checkbox */
262  intval = tr_natTraversalStatus(tr_backend_handle(back));
263  boolval = !TR_NAT_TRAVERSAL_IS_DISABLED( intval );
264  setupprefwidget(natcheck, PREF_NAT, boolval);
265  gtk_table_attach_defaults(GTK_TABLE(table), natcheck,        0, 2, RN(ii));
266  ii++;
267
268  /* create the model used by the three popup menus */
269  model = GTK_TREE_MODEL(gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT));
270  gtk_list_store_append(GTK_LIST_STORE(model), &iter);
271  gtk_list_store_set(GTK_LIST_STORE(model), &iter, 1, 0, 0,
272    _("Use the torrent file where it is"), -1);
273  gtk_list_store_append(GTK_LIST_STORE(model), &iter);
274  gtk_list_store_set(GTK_LIST_STORE(model), &iter, 1, TR_TORNEW_SAVE_COPY, 0,
275    _("Keep a copy of the torrent file"), -1);
276  gtk_list_store_append(GTK_LIST_STORE(model), &iter);
277  gtk_list_store_set(GTK_LIST_STORE(model), &iter, 1, TR_TORNEW_SAVE_MOVE, 0,
278    _("Keep a copy and remove the original"), -1);
279
280  /* std */
281  label = gtk_label_new_with_mnemonic(_("For torrents added _normally:"));
282  gtk_label_set_mnemonic_widget(GTK_LABEL(label), addstd);
283  gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
284  gtk_combo_box_set_model(GTK_COMBO_BOX(addstd), model);
285  rend = gtk_cell_renderer_text_new();
286  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(addstd), rend, TRUE);
287  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(addstd), rend, "text", 0);
288  setupprefwidget(addstd, PREF_ADDSTD);
289  gtk_table_attach_defaults(GTK_TABLE(table), label,           0, 1, RN(ii));
290  gtk_table_attach_defaults(GTK_TABLE(table), addstd,          1, 2, RN(ii));
291  ii++;
292
293  /* ipc */
294  label = gtk_label_new_with_mnemonic(
295    _("For torrents added e_xternally\n(via the command-line):"));
296  gtk_label_set_mnemonic_widget(GTK_LABEL(label), addipc);
297  gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
298  gtk_combo_box_set_model(GTK_COMBO_BOX(addipc), model);
299  rend = gtk_cell_renderer_text_new();
300  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(addipc), rend, TRUE);
301  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(addipc), rend, "text", 0);
302  setupprefwidget(addipc, PREF_ADDIPC);
303  gtk_table_attach_defaults(GTK_TABLE(table), label,           0, 1, RN(ii));
304  gtk_table_attach_defaults(GTK_TABLE(table), addipc,          1, 2, RN(ii));
305  ii++;
306
307#undef RN
308  g_assert(rowcount == ii);
309
310  gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(wind)->vbox), table);
311  g_signal_connect_data(wind, "response", G_CALLBACK(clickdialog),
312                        data, freedata, 0);
313  gtk_widget_show_all(wind);
314
315  return wind;
316}
317
318static void
319clicklimitbox(GtkWidget *widget, gpointer gdata) {
320  GtkWidget **widgets = gdata;
321  int ii;
322
323  for(ii = 0; 2 > ii; ii++)
324    gtk_widget_set_sensitive(widgets[ii],
325      gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
326}
327
328static void
329freedata(gpointer gdata, GClosure *closure SHUTUP) {
330  struct prefdata *data = gdata;
331
332  g_list_free(data->prefwidgets);
333  g_object_unref(G_OBJECT(data->back));
334  g_free(data);
335}
336
337static void
338clickdialog(GtkWidget *widget, int resp, gpointer gdata) {
339  struct prefdata *data = gdata;
340  char *errstr;
341  GList *ii;
342
343  if(GTK_RESPONSE_APPLY == resp || GTK_RESPONSE_OK == resp) {
344    /* save all the prefs */
345    for(ii = g_list_first(data->prefwidgets); NULL != ii; ii = ii->next)
346      saveprefwidget(data->parent, ii->data);
347
348    /* write prefs to disk */
349    cf_saveprefs(&errstr);
350    if(NULL != errstr) {
351      errmsg(data->parent, "%s", errstr);
352      g_free(errstr);
353    }
354
355    applyprefs(data->back);
356    /* XXX would be nice to have errno strings, are they printed to stdout? */
357  }
358
359  if(GTK_RESPONSE_APPLY != resp)
360    gtk_widget_destroy(widget);
361}
362
363void
364applyprefs(TrBackend *back) {
365  struct { void (*func)(tr_handle_t*, int);
366    const char *use; const char *num; gboolean defuse; long def; } lim[] = {
367    {tr_setDownloadLimit, PREF_USEDOWNLIMIT, PREF_DOWNLIMIT,
368                          DEF_USEDOWNLIMIT,  DEF_DOWNLIMIT},
369    {tr_setUploadLimit,   PREF_USEUPLIMIT,   PREF_UPLIMIT,
370                          DEF_USEUPLIMIT,    DEF_UPLIMIT},
371  };
372  const char *pref;
373  int ii;
374  tr_handle_t *tr = tr_backend_handle(back);
375  gboolean boolval;
376
377  /* set upload and download limits */
378  for(ii = 0; ii < (int)ALEN(lim); ii++) {
379    pref = cf_getpref(lim[ii].use);
380    if(!(NULL == pref ? lim[ii].defuse : strbool(pref)))
381      lim[ii].func(tr, -1);
382    else {
383      pref = cf_getpref(lim[ii].num);
384      lim[ii].func(tr, (NULL == pref ? lim[ii].def : strtol(pref, NULL, 10)));
385    }
386  }
387
388  /* set the listening port */
389  if(NULL != (pref = cf_getpref(PREF_PORT)) &&
390     0 < (ii = strtol(pref, NULL, 10)) && 0xffff >= ii)
391    tr_setBindPort(tr, ii);
392
393  /* enable/disable NAT traversal */
394  boolval = (NULL == (pref = cf_getpref(PREF_NAT)) ? DEF_NAT : strbool(pref));
395  if( boolval )
396    tr_natTraversalEnable(tr);
397  else
398    tr_natTraversalDisable(tr);
399}
400
401void
402makeaddwind(GtkWindow *parent, add_torrents_func_t addfunc, void *cbdata) {
403  GtkWidget *wind = gtk_file_chooser_dialog_new(_("Add a Torrent"), parent,
404    GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
405    GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
406  struct addcb *data = g_new(struct addcb, 1);
407  GtkWidget *vbox = gtk_vbox_new(FALSE, 3);
408  GtkWidget *bbox = gtk_hbutton_box_new();
409  GtkWidget *autocheck = gtk_check_button_new_with_mnemonic(
410    _("Automatically _start torrent"));
411  GtkWidget *dircheck = gtk_check_button_new_with_mnemonic(
412    _("Use alternate _download directory"));
413  GtkFileFilter *filter = gtk_file_filter_new();
414  GtkFileFilter *unfilter = gtk_file_filter_new();
415  GtkWidget *getdir = gtk_file_chooser_button_new(
416    _("Choose a download directory"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
417  const char *pref;
418
419  data->addfunc = addfunc;
420  data->parent = parent;
421  data->data = cbdata;
422  data->autostart = TRUE;
423  data->usingaltdir = FALSE;
424  data->altdir = GTK_FILE_CHOOSER(getdir);
425  data->altbox = GTK_BUTTON_BOX(bbox);
426
427  gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_EDGE);
428  gtk_box_pack_start_defaults(GTK_BOX(bbox), dircheck);
429  gtk_box_pack_start_defaults(GTK_BOX(bbox), getdir);
430
431  gtk_box_pack_start_defaults(GTK_BOX(vbox), autocheck);
432  gtk_box_pack_start_defaults(GTK_BOX(vbox), bbox);
433
434  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(autocheck), TRUE);
435  if(NULL != (pref = cf_getpref(PREF_DIR)))
436    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(getdir), pref);
437
438  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dircheck), FALSE);
439  gtk_widget_set_sensitive(getdir, FALSE);
440
441  gtk_file_filter_set_name(filter, _("Torrent files"));
442  gtk_file_filter_add_pattern(filter, "*.torrent");
443  gtk_file_filter_set_name(unfilter, _("All files"));
444  gtk_file_filter_add_pattern(unfilter, "*");
445
446  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(wind), filter);
447  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(wind), unfilter);
448  gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(wind), TRUE);
449  gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(wind), vbox);
450
451  g_signal_connect(G_OBJECT(autocheck), "clicked", G_CALLBACK(autoclick),data);
452  g_signal_connect(G_OBJECT(dircheck), "clicked", G_CALLBACK(dirclick), data);
453  g_signal_connect(G_OBJECT(wind), "response", G_CALLBACK(addresp), data);
454
455  gtk_widget_show_all(wind);
456}
457
458static void
459autoclick(GtkWidget *widget, gpointer gdata) {
460  struct addcb *data = gdata;
461
462  data->autostart = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
463}
464
465static void
466dirclick(GtkWidget *widget, gpointer gdata) {
467  struct addcb *data = gdata;
468
469  data->usingaltdir = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
470  gtk_widget_set_sensitive(GTK_WIDGET(data->altdir), data->usingaltdir);
471}
472
473static void
474addresp(GtkWidget *widget, gint resp, gpointer gdata) {
475  struct addcb *data = gdata;
476  GSList *files, *ii;
477  GList *stupidgtk;
478  gboolean paused;
479  char *dir;
480
481  if(GTK_RESPONSE_ACCEPT == resp) {
482    dir = NULL;
483    if(data->usingaltdir)
484      dir = gtk_file_chooser_get_filename(data->altdir);
485    files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget));
486    stupidgtk = NULL;
487    for(ii = files; NULL != ii; ii = ii->next)
488      stupidgtk = g_list_append(stupidgtk, ii->data);
489    paused = !data->autostart;
490    data->addfunc(data->data, NULL, stupidgtk, dir,
491                  addactionflag(cf_getpref(PREF_ADDSTD)));
492    if(NULL != dir)
493      g_free(dir);
494    g_slist_free(files);
495    freestrlist(stupidgtk);
496  }
497
498  gtk_widget_destroy(widget);
499}
500
501#define INFOLINE(tab, ii, nam, val) \
502  do { \
503    char *txt = g_markup_printf_escaped("<b>%s</b>", nam); \
504    GtkWidget *wid = gtk_label_new(NULL); \
505    gtk_misc_set_alignment(GTK_MISC(wid), 0, .5); \
506    gtk_label_set_markup(GTK_LABEL(wid), txt); \
507    gtk_table_attach_defaults(GTK_TABLE(tab), wid, 0, 1, ii, ii + 1); \
508    wid = gtk_label_new(val); \
509    gtk_label_set_selectable(GTK_LABEL(wid), TRUE); \
510    gtk_misc_set_alignment(GTK_MISC(wid), 0, .5); \
511    gtk_table_attach_defaults(GTK_TABLE(tab), wid, 1, 2, ii, ii + 1); \
512    ii++; \
513    g_free(txt); \
514  } while(0)
515
516#define INFOLINEF(tab, ii, fmt, nam, val) \
517  do { \
518    char *buf = g_strdup_printf(fmt, val); \
519    INFOLINE(tab, ii, nam, buf); \
520    g_free(buf); \
521  } while(0)
522
523#define INFOLINEA(tab, ii, nam, val) \
524  do { \
525    char *buf = val; \
526    INFOLINE(tab, ii, nam, buf); \
527    g_free(buf); \
528  } while(0)
529
530#define INFOSEP(tab, ii) \
531  do { \
532    GtkWidget *wid = gtk_hseparator_new(); \
533    gtk_table_attach_defaults(GTK_TABLE(tab), wid, 0, 2, ii, ii + 1); \
534    ii++; \
535  } while(0)
536
537void
538makeinfowind(GtkWindow *parent, TrTorrent *tor) {
539  tr_stat_t *sb;
540  tr_info_t *in;
541  GtkWidget *wind, *label;
542  int ii;
543  char *str;
544  const int rowcount = 14;
545  GtkWidget *table = gtk_table_new(rowcount, 2, FALSE);
546
547  /* XXX should use model and update this window regularly */
548
549  sb = tr_torrent_stat(tor);
550  in = tr_torrent_info(tor);
551  str = g_strdup_printf(_("%s Properties"), in->name);
552  wind = gtk_dialog_new_with_buttons(str, parent,
553    GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
554    GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
555  g_free(str);
556
557  gtk_widget_set_name(wind, "TransmissionDialog");
558  gtk_table_set_col_spacings(GTK_TABLE(table), 12);
559  gtk_table_set_row_spacings(GTK_TABLE(table), 12);
560  gtk_dialog_set_default_response(GTK_DIALOG(wind), GTK_RESPONSE_ACCEPT);
561  gtk_container_set_border_width(GTK_CONTAINER(table), 6);
562  gtk_window_set_resizable(GTK_WINDOW(wind), FALSE);
563
564  label = gtk_label_new(NULL);
565  gtk_label_set_selectable(GTK_LABEL(label), TRUE);
566  str = g_markup_printf_escaped("<big>%s</big>", in->name);
567  gtk_label_set_markup(GTK_LABEL(label), str);
568  g_free(str);
569  gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 2, 0, 1);
570
571  ii = 1;
572
573  INFOSEP(table, ii);
574
575  if(80 == in->trackerPort)
576    INFOLINEA(table, ii, _("Tracker:"), g_strdup_printf("http://%s",
577              in->trackerAddress));
578  else
579    INFOLINEA(table, ii, _("Tracker:"), g_strdup_printf("http://%s:%i",
580              in->trackerAddress, in->trackerPort));
581  INFOLINE(table, ii, _("Announce:"), in->trackerAnnounce);
582  INFOLINEA(table, ii, _("Piece Size:"), readablesize(in->pieceSize));
583  INFOLINEF(table, ii, "%i", _("Pieces:"), in->pieceCount);
584  INFOLINEA(table, ii, _("Total Size:"), readablesize(in->totalSize));
585  if(0 > sb->seeders)
586    INFOLINE(table, ii, _("Seeders:"), _("?"));
587  else
588    INFOLINEF(table, ii, "%i", _("Seeders:"), sb->seeders);
589  if(0 > sb->leechers)
590    INFOLINE(table, ii, _("Leechers:"), _("?"));
591  else
592    INFOLINEF(table, ii, "%i", _("Leechers:"), sb->leechers);
593
594  INFOSEP(table, ii);
595
596  INFOLINE(table, ii, _("Directory:"), tr_torrentGetFolder(tr_torrent_handle(tor)));
597  INFOLINEA(table, ii, _("Downloaded:"), readablesize(sb->downloaded));
598  INFOLINEA(table, ii, _("Uploaded:"), readablesize(sb->uploaded));
599
600  INFOSEP(table, ii);
601
602  g_assert(rowcount == ii);
603
604  gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(wind)->vbox), table);
605  g_signal_connect(G_OBJECT(wind), "response",
606                   G_CALLBACK(gtk_widget_destroy), NULL);
607  gtk_widget_show_all(wind);
608}
Note: See TracBrowser for help on using the repository browser.