Changeset 1504


Ignore:
Timestamp:
Feb 19, 2007, 10:09:05 PM (16 years ago)
Author:
joshe
Message:

Merge gtkmisc branch.

Location:
trunk
Files:
17 edited
4 copied

Legend:

Unmodified
Added
Removed
  • trunk/gtk/conf.c

    r920 r1504  
    22 * $Id$
    33 *
    4  * Copyright (c) 2005-2006 Transmission authors and contributors
     4 * Copyright (c) 2005-2007 Transmission authors and contributors
    55 *
    66 * Permission is hereby granted, free of charge, to any person obtaining a
     
    501501
    502502void
    503 cf_freestate(benc_val_t *state) {
    504   tr_bencFree(state);
    505   g_free(state);
    506 }
     503cf_freestate( benc_val_t * state )
     504{
     505    if( NULL != state )
     506    {
     507        tr_bencFree( state );
     508        g_free( state );
     509    }
     510}
  • trunk/gtk/conf.h

    r1475 r1504  
    5050cf_freestate(benc_val_t *state);
    5151
    52 /* macros for names of prefs we use */
    53 #define PREF_PORT               "listening-port"
    54 #define PREF_USEDOWNLIMIT       "use-download-limit"
    55 #define PREF_DOWNLIMIT          "download-limit"
    56 #define PREF_USEUPLIMIT         "use-upload-limit"
    57 #define PREF_UPLIMIT            "upload-limit"
    58 #define PREF_DIR                "download-directory"
    59 #define PREF_ASKDIR             "ask-download-directory"
    60 #define PREF_ADDSTD             "add-behavior-standard"
    61 #define PREF_ADDIPC             "add-behavior-ipc"
    62 #define PREF_MSGLEVEL           "message-level"
    63 #define PREF_NAT                "use-nat-traversal"
    64 
    6552#endif /* TG_CONF_H */
  • trunk/gtk/dialogs.c

    r1468 r1504  
    22 * $Id$
    33 *
    4  * Copyright (c) 2005-2006 Transmission authors and contributors
     4 * Copyright (c) 2005-2007 Transmission authors and contributors
    55 *
    66 * Permission is hereby granted, free of charge, to any person obtaining a
     
    3232#include "conf.h"
    3333#include "dialogs.h"
     34#include "tr_icon.h"
     35#include "tr_prefs.h"
     36#include "util.h"
     37
    3438#include "transmission.h"
    35 #include "util.h"
    3639
    3740#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_ASKDIR              FALSE
    45 #define DEF_NAT                 TRUE
    46 
    47 struct prefdata {
    48   GList *prefwidgets;
    49   GtkWindow *parent;
    50   TrBackend *back;
    51   GtkTooltips * tips;
    52 };
    5341
    5442struct addcb {
     
    6957};
    7058
    71 static void
    72 clicklimitbox(GtkWidget *widget, gpointer gdata);
    73 static void
    74 freedata(gpointer gdata, GClosure *closure);
    75 static void
    76 clickdialog(GtkWidget *widget, int resp, gpointer gdata);
     59struct quitdata
     60{
     61    callbackfunc_t func;
     62    void         * cbdata;
     63};
     64
    7765static void
    7866autoclick(GtkWidget *widget, gpointer gdata);
     
    8371static void
    8472promptresp( GtkWidget * widget, gint resp, gpointer data );
    85 
    86 static void
    87 setupprefwidget(GtkWidget *widget, const char *prefname, ...) {
    88   const char *pref = cf_getpref(prefname);
    89   GtkTreeModel *model;
    90   GtkTreeIter iter;
    91   guint prefsflag, modelflag;
    92   va_list ap;
    93 
    94   g_object_set_data_full(G_OBJECT(widget), PREFNAME,
    95                          g_strdup(prefname), (GDestroyNotify)g_free);
    96 
    97   va_start(ap, prefname);
    98 
    99   if(ISA(widget, GTK_TYPE_FILE_CHOOSER)) {
    100     if(NULL != pref)
    101       gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(widget), pref);
    102   }
    103   else if(ISA(widget, GTK_TYPE_SPIN_BUTTON))
    104     gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),
    105       (NULL == pref ? va_arg(ap, long) : strtol(pref, NULL, 10)));
    106   else if(ISA(widget, GTK_TYPE_TOGGLE_BUTTON))
    107     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
    108       (NULL == pref ? va_arg(ap, gboolean) : strbool(pref)));
    109   else if(ISA(widget, GTK_TYPE_COMBO_BOX)) {
    110     model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
    111     prefsflag = addactionflag(pref);
    112     if(gtk_tree_model_get_iter_first(model, &iter))
    113       do {
    114         gtk_tree_model_get(model, &iter, 1, &modelflag, -1);
    115         if(modelflag == prefsflag) {
    116           gtk_combo_box_set_active_iter(GTK_COMBO_BOX(widget), &iter);
    117           break;
    118         }
    119       } while(gtk_tree_model_iter_next(model, &iter));
    120   }
    121   else {
    122     g_assert_not_reached();
    123   }
    124 
    125   va_end(ap);
    126 }
    127 
    128 static void
    129 saveprefwidget(GtkWindow *parent, GtkWidget *widget) {
    130   char *prefname;
    131   const char *strval;
    132   char *freeablestr;
    133   GtkTreeModel *model;
    134   GtkTreeIter iter;
    135   guint uintval;
    136 
    137   strval = NULL;
    138   freeablestr = NULL;
    139   if(ISA(widget, GTK_TYPE_FILE_CHOOSER)) {
    140     strval = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(widget));
    141     if(NULL != strval) {
    142       if(!mkdir_p(strval, 0777)) {
    143         errmsg(parent, _("Failed to create the directory %s:\n%s"),
    144                strval, strerror(errno));
    145         return;
    146       }
    147     }
    148   }
    149   else if(ISA(widget, GTK_TYPE_SPIN_BUTTON))
    150     freeablestr = g_strdup_printf("%i",
    151       gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)));
    152   else if(ISA(widget, GTK_TYPE_TOGGLE_BUTTON))
    153     strval = (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)) ?
    154               "yes" : "no");
    155   else if(ISA(widget, GTK_TYPE_COMBO_BOX)) {
    156     if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter)) {
    157       model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
    158       gtk_tree_model_get(model, &iter, 1, &uintval, -1);
    159       strval = addactionname(uintval);
    160     }
    161   }
    162   else {
    163     g_assert_not_reached();
    164     return;
    165   }
    166 
    167   prefname = g_object_get_data(G_OBJECT(widget), PREFNAME);
    168   g_assert(NULL != prefname);
    169 
    170   if(NULL != strval)
    171     cf_setpref(prefname, strval);
    172   else if(NULL != freeablestr) {
    173     cf_setpref(prefname, freeablestr);
    174     g_free(freeablestr);
    175   }
    176 }
    177 
    178 /* wrap a widget in an event box with a tooltip */
    179 static GtkWidget *
    180 tipbox( GtkWidget * widget, GtkTooltips * tips, const char * tip )
    181 {
    182     GtkWidget * box;
    183 
    184     box = gtk_event_box_new();
    185     gtk_container_add( GTK_CONTAINER( box ), widget );
    186     gtk_tooltips_set_tip( tips, box, tip, "" );
    187 
    188     return box;
    189 }
    190 
    191 GtkWidget *
    192 makeprefwindow(GtkWindow *parent, TrBackend *back) {
    193   char *title = g_strdup_printf(_("%s Preferences"), g_get_application_name());
    194   GtkWidget *wind = gtk_dialog_new_with_buttons(title, parent,
    195    GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
    196    GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
    197    GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
    198   const unsigned int rowcount = 10;
    199   GtkWidget *table = gtk_table_new(rowcount, 2, FALSE);
    200   GtkWidget *portnum = gtk_spin_button_new_with_range(1, 0xffff, 1);
    201   GtkWidget *natcheck = gtk_check_button_new_with_mnemonic(
    202     _("Au_tomatic port mapping via NAT-PMP or UPnP"));
    203   GtkWidget *askdir = gtk_check_button_new_with_mnemonic(
    204     _("Al_ways prompt for download directory"));
    205   GtkWidget *dirstr = gtk_file_chooser_button_new(
    206     _("Choose a download directory"),
    207     GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
    208   GtkWidget *addstd = gtk_combo_box_new();
    209   GtkWidget *addipc = gtk_combo_box_new();
    210   GtkWidget *label;
    211   GtkWidget **array;
    212   struct prefdata *data = g_new0(struct prefdata, 1);
    213   struct { GtkWidget *on; GtkWidget *num; GtkWidget *label; gboolean defuse;
    214     const char *usepref; const char *numpref; long def;
    215       const char *ontip; const char *numtip; } lim[] = {
    216     { gtk_check_button_new_with_mnemonic(_("_Limit download speed")),
    217       gtk_spin_button_new_with_range(0, G_MAXLONG, 1),
    218       gtk_label_new_with_mnemonic(_("Maximum _download speed:")),
    219       DEF_USEDOWNLIMIT, PREF_USEDOWNLIMIT, PREF_DOWNLIMIT, DEF_DOWNLIMIT,
    220       N_("Restrict the download rate"),
    221       N_("Speed in KiB/sec for restricted download rate")},
    222     { gtk_check_button_new_with_mnemonic(_("Li_mit upload speed")),
    223       gtk_spin_button_new_with_range(0, G_MAXLONG, 1),
    224       gtk_label_new_with_mnemonic(_("Maximum _upload speed:")),
    225       DEF_USEUPLIMIT, PREF_USEUPLIMIT, PREF_UPLIMIT, DEF_UPLIMIT,
    226       N_("Restrict the upload rate"),
    227       N_("Speed in KiB/sec for restricted upload rate")},
    228   };
    229   unsigned int ii;
    230   GtkTreeModel *model;
    231   GtkTreeIter iter;
    232   GtkCellRenderer *rend;
    233   gboolean boolval;
    234   int intval;
    235   GtkTooltips * tips;
    236   GtkWidget   * event;
    237 
    238   g_free(title);
    239   gtk_widget_set_name(wind, "TransmissionDialog");
    240   gtk_table_set_col_spacings(GTK_TABLE(table), 8);
    241   gtk_table_set_row_spacings(GTK_TABLE(table), 8);
    242   gtk_dialog_set_default_response(GTK_DIALOG(wind), GTK_RESPONSE_OK);
    243   gtk_container_set_border_width(GTK_CONTAINER(table), 6);
    244   gtk_window_set_resizable(GTK_WINDOW(wind), FALSE);
    245 
    246   tips = gtk_tooltips_new();
    247   g_object_ref( tips );
    248   gtk_object_sink( GTK_OBJECT( tips ) );
    249   gtk_tooltips_enable( tips );
    250 
    251   data->prefwidgets = makeglist(portnum, lim[0].on, lim[0].num, lim[1].on,
    252     lim[1].num, askdir, dirstr, addstd, addipc, natcheck, NULL);
    253   data->parent = parent;
    254   data->back = back;
    255   data->tips = tips;
    256   g_object_ref(G_OBJECT(back));
    257 
    258 #define RN(n) (n), (n) + 1
    259 
    260   for(ii = 0; ii < ALEN(lim); ii++) {
    261     /* limit checkbox */
    262     setupprefwidget(lim[ii].on, lim[ii].usepref, (gboolean)lim[ii].defuse);
    263     array = g_new(GtkWidget*, 3);
    264     g_signal_connect_data(lim[ii].on, "clicked", G_CALLBACK(clicklimitbox),
    265                           array, (GClosureNotify)g_free, 0);
    266     gtk_table_attach_defaults(GTK_TABLE(table), lim[ii].on,    0, 2, RN(ii*2));
    267     gtk_tooltips_set_tip( tips, lim[ii].on, gettext( lim[ii].ontip ), "" );
    268 
    269     /* limit label and entry */
    270     gtk_label_set_mnemonic_widget(GTK_LABEL(lim[ii].label), lim[ii].num);
    271     gtk_misc_set_alignment(GTK_MISC(lim[ii].label), 0, .5);
    272     gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(lim[ii].num), TRUE);
    273     setupprefwidget(lim[ii].num, lim[ii].numpref, (long)lim[ii].def);
    274     event = tipbox( lim[ii].label, tips, gettext( lim[ii].numtip ) );
    275     gtk_table_attach_defaults(GTK_TABLE(table), event,         0,1,RN(ii*2+1));
    276     gtk_table_attach_defaults(GTK_TABLE(table), lim[ii].num,   1,2,RN(ii*2+1));
    277     array[0] = lim[ii].label;
    278     array[1] = lim[ii].num;
    279     array[2] = GINT_TO_POINTER( TRUE );
    280     clicklimitbox(lim[ii].on, array);
    281     gtk_tooltips_set_tip( tips, lim[ii].num, gettext( lim[ii].numtip ), "" );
    282   }
    283   ii *= 2;
    284 
    285   /* always ask for download dir */
    286   setupprefwidget( askdir, PREF_ASKDIR, ( gboolean )DEF_ASKDIR );
    287   array = g_new( GtkWidget *, 3 );
    288   g_signal_connect_data( askdir, "clicked", G_CALLBACK( clicklimitbox ),
    289                          array, ( GClosureNotify )g_free, 0 );
    290   gtk_table_attach_defaults(GTK_TABLE(table), askdir,        0, 2, RN(ii));
    291   gtk_tooltips_set_tip( tips, askdir,
    292       _("When adding a torrent, always prompt for a directory to download data files into"), "" );
    293   ii++;
    294 
    295   /* directory label and chooser */
    296   label = gtk_label_new_with_mnemonic(_("Download di_rectory:"));
    297   gtk_label_set_mnemonic_widget(GTK_LABEL(label), dirstr);
    298   gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
    299   setupprefwidget(dirstr, PREF_DIR);
    300   event = tipbox( label, tips,
    301                   _("Destination directory for downloaded data files") );
    302   gtk_table_attach_defaults(GTK_TABLE(table), event,           0, 1, RN(ii));
    303   event = tipbox( dirstr, tips,
    304                   _("Destination directory for downloaded data files") );
    305   gtk_table_attach_defaults(GTK_TABLE(table), event,           1, 2, RN(ii));
    306   array[0] = label;
    307   array[1] = dirstr;
    308   array[2] = GINT_TO_POINTER( FALSE );
    309   clicklimitbox( askdir, array );
    310   ii++;
    311 
    312   /* port label and entry */
    313   label = gtk_label_new_with_mnemonic(_("Listening _port:"));
    314   gtk_label_set_mnemonic_widget(GTK_LABEL(label), portnum);
    315   gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
    316   gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(portnum), TRUE);
    317   setupprefwidget(portnum, PREF_PORT, (long)TR_DEFAULT_PORT);
    318   event = tipbox( label, tips,
    319       _("TCP port number to listen for peer connections") );
    320   gtk_table_attach_defaults(GTK_TABLE(table), event,           0, 1, RN(ii));
    321   gtk_table_attach_defaults(GTK_TABLE(table), portnum,         1, 2, RN(ii));
    322   gtk_tooltips_set_tip( tips, portnum,
    323       _("TCP port number to listen for peer connections"), "" );
    324   ii++;
    325 
    326   /* NAT traversal checkbox */
    327   intval = tr_handleStatus(tr_backend_handle(back))->natTraversalStatus;
    328   boolval = !TR_NAT_TRAVERSAL_IS_DISABLED( intval );
    329   setupprefwidget(natcheck, PREF_NAT, boolval);
    330   gtk_table_attach_defaults(GTK_TABLE(table), natcheck,        0, 2, RN(ii));
    331   gtk_tooltips_set_tip( tips, natcheck,
    332       _("Attempt to bypass NAT or firewall to allow incoming peer connections"), "" );
    333   ii++;
    334 
    335   /* create the model used by the two popup menus */
    336   model = GTK_TREE_MODEL(gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT));
    337   gtk_list_store_append(GTK_LIST_STORE(model), &iter);
    338   gtk_list_store_set(GTK_LIST_STORE(model), &iter, 1, 0, 0,
    339     _("Use the torrent file where it is"), -1);
    340   gtk_list_store_append(GTK_LIST_STORE(model), &iter);
    341   gtk_list_store_set(GTK_LIST_STORE(model), &iter, 1, TR_TORNEW_SAVE_COPY, 0,
    342     _("Keep a copy of the torrent file"), -1);
    343   gtk_list_store_append(GTK_LIST_STORE(model), &iter);
    344   gtk_list_store_set(GTK_LIST_STORE(model), &iter, 1, TR_TORNEW_SAVE_MOVE, 0,
    345     _("Keep a copy and remove the original"), -1);
    346 
    347   /* std */
    348   label = gtk_label_new_with_mnemonic(_("For torrents added _normally:"));
    349   gtk_label_set_mnemonic_widget(GTK_LABEL(label), addstd);
    350   gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
    351   gtk_combo_box_set_model(GTK_COMBO_BOX(addstd), model);
    352   rend = gtk_cell_renderer_text_new();
    353   gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(addstd), rend, TRUE);
    354   gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(addstd), rend, "text", 0);
    355   setupprefwidget(addstd, PREF_ADDSTD);
    356   event = tipbox( label, tips,
    357       _("Torrent files added via the toolbar, popup menu, and drag-and-drop") );
    358   gtk_table_attach_defaults(GTK_TABLE(table), event,           0, 1, RN(ii));
    359   event = tipbox( addstd, tips,
    360       _("Torrent files added via the toolbar, popup menu, and drag-and-drop") );
    361   gtk_table_attach_defaults(GTK_TABLE(table), event,           1, 2, RN(ii));
    362   ii++;
    363 
    364   /* ipc */
    365   label = gtk_label_new_with_mnemonic(
    366     _("For torrents added e_xternally\n(via the command-line):"));
    367   gtk_label_set_mnemonic_widget(GTK_LABEL(label), addipc);
    368   gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
    369   gtk_combo_box_set_model(GTK_COMBO_BOX(addipc), model);
    370   rend = gtk_cell_renderer_text_new();
    371   gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(addipc), rend, TRUE);
    372   gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(addipc), rend, "text", 0);
    373   setupprefwidget(addipc, PREF_ADDIPC);
    374   event = tipbox( label, tips,
    375       _("For torrents added via the command-line only") );
    376   gtk_table_attach_defaults(GTK_TABLE(table), event,           0, 1, RN(ii));
    377   event = tipbox( addipc, tips,
    378       _("For torrents added via the command-line only") );
    379   gtk_table_attach_defaults(GTK_TABLE(table), event,           1, 2, RN(ii));
    380   ii++;
    381 
    382 #undef RN
    383   g_assert(rowcount == ii);
    384 
    385   gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(wind)->vbox), table);
    386   g_signal_connect_data(wind, "response", G_CALLBACK(clickdialog),
    387                         data, freedata, 0);
    388   gtk_widget_show_all(wind);
    389 
    390   return wind;
    391 }
    392 
    393 static void
    394 clicklimitbox( GtkWidget * widget, gpointer gdata )
    395 {
    396     GtkWidget ** widgets;
    397     gboolean     with, active;
    398     int          ii;
    399 
    400     widgets = gdata;
    401     with = ( gboolean )GPOINTER_TO_INT( widgets[2] );
    402 
    403     for(ii = 0; 2 > ii; ii++)
    404     {
    405         active = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( widget ) );
    406         gtk_widget_set_sensitive( widgets[ii], ( with ? active : !active ) );
    407     }
    408 }
    409 
    410 static void
    411 freedata(gpointer gdata, GClosure *closure SHUTUP) {
    412   struct prefdata *data = gdata;
    413 
    414   g_list_free(data->prefwidgets);
    415   g_object_unref(G_OBJECT(data->back));
    416   g_object_unref( data->tips );
    417   g_free(data);
    418 }
    419 
    420 static void
    421 clickdialog(GtkWidget *widget, int resp, gpointer gdata) {
    422   struct prefdata *data = gdata;
    423   char *errstr;
    424   GList *ii;
    425 
    426   if(GTK_RESPONSE_APPLY == resp || GTK_RESPONSE_OK == resp) {
    427     /* save all the prefs */
    428     for(ii = g_list_first(data->prefwidgets); NULL != ii; ii = ii->next)
    429       saveprefwidget(data->parent, ii->data);
    430 
    431     /* write prefs to disk */
    432     cf_saveprefs(&errstr);
    433     if(NULL != errstr) {
    434       errmsg(data->parent, "%s", errstr);
    435       g_free(errstr);
    436     }
    437 
    438     applyprefs(data->back);
    439     /* XXX would be nice to have errno strings, are they printed to stdout? */
    440   }
    441 
    442   if(GTK_RESPONSE_APPLY != resp)
    443     gtk_widget_destroy(widget);
    444 }
    445 
    446 void
    447 applyprefs(TrBackend *back) {
    448   struct { void (*func)(tr_handle_t*, int);
    449     const char *use; const char *num; gboolean defuse; long def; } lim[] = {
    450     {tr_setGlobalDownloadLimit, PREF_USEDOWNLIMIT, PREF_DOWNLIMIT,
    451                                 DEF_USEDOWNLIMIT,  DEF_DOWNLIMIT},
    452     {tr_setGlobalUploadLimit,   PREF_USEUPLIMIT,   PREF_UPLIMIT,
    453                                 DEF_USEUPLIMIT,    DEF_UPLIMIT},
    454   };
    455   const char *pref;
    456   int ii;
    457   tr_handle_t *tr = tr_backend_handle(back);
    458   gboolean boolval;
    459 
    460   /* set upload and download limits */
    461   for(ii = 0; ii < (int)ALEN(lim); ii++) {
    462     pref = cf_getpref(lim[ii].use);
    463     if(!(NULL == pref ? lim[ii].defuse : strbool(pref)))
    464       lim[ii].func(tr, -1);
    465     else {
    466       pref = cf_getpref(lim[ii].num);
    467       lim[ii].func(tr, (NULL == pref ? lim[ii].def : strtol(pref, NULL, 10)));
    468     }
    469   }
    470 
    471   /* set the listening port */
    472   if(NULL != (pref = cf_getpref(PREF_PORT)) &&
    473      0 < (ii = strtol(pref, NULL, 10)) && 0xffff >= ii)
    474     tr_setBindPort(tr, ii);
    475 
    476   /* enable/disable NAT traversal */
    477   boolval = (NULL == (pref = cf_getpref(PREF_NAT)) ? DEF_NAT : strbool(pref));
    478   tr_natTraversalEnable(tr, boolval);
    479 }
     73static void
     74quitresp( GtkWidget * widget, gint resp, gpointer data );
    48075
    48176void
     
    49590  GtkWidget *getdir = gtk_file_chooser_button_new(
    49691    _("Choose a download directory"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
    497   const char *pref;
    49892
    49993  data->addfunc = addfunc;
     
    512106
    513107  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(autocheck), TRUE);
    514   if(NULL != (pref = cf_getpref(PREF_DIR)))
    515     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(getdir), pref);
     108  gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( getdir ),
     109                                       getdownloaddir() );
    516110
    517111  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dircheck), FALSE);
     
    567161      stupidgtk = g_list_append(stupidgtk, ii->data);
    568162    flags = ( data->autostart ? TR_TORNEW_RUNNING : TR_TORNEW_PAUSED );
    569     flags |= addactionflag( cf_getpref( PREF_ADDSTD ) );
     163    flags |= addactionflag( tr_prefs_get( PREF_ID_ADDSTD ) );
    570164    data->addfunc( data->data, NULL, stupidgtk, dir, flags );
    571165    if(NULL != dir)
     
    694288void
    695289promptfordir( GtkWindow * parent, add_torrents_func_t addfunc, void *cbdata,
    696               GList * files, guint flags, const char * defaultdir )
     290              GList * files, guint flags )
    697291{
    698292    struct dirdata * stuff;
     
    712306    gtk_file_chooser_set_local_only( GTK_FILE_CHOOSER( wind ), TRUE );
    713307    gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER( wind ), FALSE );
    714     gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( wind ), defaultdir );
     308    gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( wind ),
     309                                   getdownloaddir() );
    715310
    716311    g_signal_connect( G_OBJECT( wind ), "response",
     
    741336    gtk_widget_destroy( widget );
    742337}
     338
     339void
     340askquit( GtkWindow * parent, callbackfunc_t func, void * cbdata )
     341{
     342    struct quitdata * stuff;
     343    GtkWidget * wind;
     344
     345    stuff          = g_new( struct quitdata, 1 );
     346    stuff->func    = func;
     347    stuff->cbdata  = cbdata;
     348
     349    wind = gtk_message_dialog_new( parent, GTK_DIALOG_DESTROY_WITH_PARENT,
     350                                   GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
     351                                   _("Are you sure you want to quit %s?"),
     352                                   g_get_application_name() );
     353    gtk_dialog_set_default_response( GTK_DIALOG( wind ), GTK_RESPONSE_YES );
     354    g_signal_connect( G_OBJECT( wind ), "response",
     355                      G_CALLBACK( quitresp ), stuff );
     356
     357    gtk_widget_show_all( wind );
     358}
     359
     360static void
     361quitresp( GtkWidget * widget, gint resp, gpointer data )
     362{
     363    struct quitdata * stuff;
     364
     365    stuff = data;
     366
     367    if( GTK_RESPONSE_YES == resp )
     368    {
     369        stuff->func( stuff->cbdata );
     370    }
     371
     372    g_free( stuff );
     373    gtk_widget_destroy( widget );
     374}
  • trunk/gtk/dialogs.h

    r1475 r1504  
    4848void
    4949promptfordir( GtkWindow * parent, add_torrents_func_t addfunc, void *cbdata,
    50               GList * files, guint flags, const char * defaultdir );
     50              GList * files, guint flags );
     51
     52/* prompt if the user wants to quit, calls func with cbdata if they do */
     53void
     54askquit( GtkWindow * parent, callbackfunc_t func, void * cbdata );
    5155
    5256#endif /* TG_PREFS_H */
  • trunk/gtk/ipc.c

    r920 r1504  
    22 * $Id$
    33 *
    4  * Copyright (c) 2006 Transmission authors and contributors
     4 * Copyright (c) 2006-2007 Transmission authors and contributors
    55 *
    66 * Permission is hereby granted, free of charge, to any person obtaining a
     
    4141#include "io.h"
    4242#include "ipc.h"
     43#include "tr_prefs.h"
    4344#include "util.h"
    4445
     
    4950/* list of strings, full paths to torrent files to load */
    5051#define MSG_ADDFILES            ("addfiles")
    51 
    52 enum contype { CON_SERV, CON_ADDFILE };
     52/* request that the server quit */
     53#define MSG_QUIT                ("quit")
     54
     55enum contype { CON_SERV, CON_CLIENT };
    5356
    5457struct constate_serv {
    5558  void *wind;
    5659  add_torrents_func_t addfunc;
     60  callbackfunc_t quitfunc;
    5761  void *cbdata;
    5862};
    5963
    60 struct constate_addfile {
     64enum client_cmd { CCMD_ADD, CCMD_QUIT };
     65
     66struct constate_client {
    6167  GMainLoop *loop;
     68  enum client_cmd cmd;
    6269  GList *files;
    6370  gboolean *succeeded;
    64   unsigned int addid;
     71  unsigned int msgid;
    6572};
    6673
     
    7683  union {
    7784    struct constate_serv serv;
    78     struct constate_addfile addfile;
     85    struct constate_client client;
    7986  } u;
    8087};
    8188
    82 void
    83 ipc_socket_setup(void *parent, add_torrents_func_t addfunc, void *cbdata);
    84 gboolean
    85 ipc_sendfiles_blocking(GList *files);
    8689static void
    8790serv_bind(struct constate *con);
     
    106109srv_addfile(struct constate *con, const char *name, benc_val_t *val);
    107110static void
     111srv_quit( struct constate * con, const char * name, benc_val_t * val );
     112static void
    108113afc_version(struct constate *con, const char *name, benc_val_t *val);
    109114static void
     
    112117static const struct handlerdef gl_funcs_serv[] = {
    113118  {MSG_ADDFILES, srv_addfile},
     119  {MSG_QUIT,     srv_quit},
    114120  {NULL, NULL}
    115121};
    116122
    117 static const struct handlerdef gl_funcs_addfile[] = {
     123static const struct handlerdef gl_funcs_client[] = {
    118124  {MSG_VERSION, afc_version},
    119125  {NULL, NULL}
     
    124130
    125131void
    126 ipc_socket_setup(void *parent, add_torrents_func_t addfunc, void *cbdata) {
     132ipc_socket_setup( void * parent, add_torrents_func_t addfunc,
     133                  callbackfunc_t quitfunc, void * cbdata )
     134{
    127135  struct constate *con;
    128136
     
    134142  con->u.serv.wind = parent;
    135143  con->u.serv.addfunc = addfunc;
     144  con->u.serv.quitfunc = quitfunc;
    136145  con->u.serv.cbdata = cbdata;
    137146 
     
    139148}
    140149
    141 gboolean
    142 ipc_sendfiles_blocking(GList *files) {
     150static gboolean
     151blocking_client( enum client_cmd cmd, GList * files )
     152{
     153
    143154  struct constate *con;
    144155  char *path;
     
    148159  con->source = NULL;
    149160  con->fd = -1;
    150   con->funcs = gl_funcs_addfile;
    151   con->type = CON_ADDFILE;
    152   con->u.addfile.loop = g_main_loop_new(NULL, TRUE);
    153   con->u.addfile.files = files;
    154   con->u.addfile.succeeded = &ret;
    155   con->u.addfile.addid = 0;
     161  con->funcs = gl_funcs_client;
     162  con->type = CON_CLIENT;
     163  con->u.client.loop = g_main_loop_new(NULL, TRUE);
     164  con->u.client.cmd = cmd;
     165  con->u.client.files = files;
     166  con->u.client.succeeded = &ret;
     167  con->u.client.msgid = 0;
    156168
    157169  path = cf_sockname();
     
    162174  }
    163175
    164   g_main_loop_run(con->u.addfile.loop);
     176  g_main_loop_run(con->u.client.loop);
    165177
    166178  return ret;
     179}
     180
     181gboolean
     182ipc_sendfiles_blocking( GList * files )
     183{
     184    return blocking_client( CCMD_ADD, files );
     185}
     186
     187gboolean
     188ipc_sendquit_blocking( void )
     189{
     190    return blocking_client( CCMD_QUIT, NULL );
    167191}
    168192
     
    357381    case CON_SERV:
    358382      break;
    359     case CON_ADDFILE:
    360       freestrlist(con->u.addfile.files);
    361       g_main_loop_quit(con->u.addfile.loop);
     383    case CON_CLIENT:
     384      freestrlist(con->u.client.files);
     385      g_main_loop_quit(con->u.client.loop);
    362386      break;
    363387  }
     
    376400  GList *files;
    377401  int ii;
     402  guint flags;
    378403
    379404  if(TYPE_LIST == val->type) {
     
    384409         g_utf8_validate(val->val.l.vals[ii].val.s.s, -1, NULL))
    385410        files = g_list_append(files, val->val.l.vals[ii].val.s.s);
    386     srv->addfunc(srv->cbdata, NULL, files, NULL,
    387                  addactionflag(cf_getpref(PREF_ADDIPC)));
     411    flags = addactionflag( tr_prefs_get( PREF_ID_ADDIPC ) );
     412    srv->addfunc( srv->cbdata, NULL, files, NULL, flags );
    388413    g_list_free(files);
    389414  }
     
    391416
    392417static void
     418srv_quit( struct constate * con, const char * name SHUTUP,
     419          benc_val_t * val SHUTUP )
     420{
     421    struct constate_serv * srv;
     422
     423    srv = &con->u.serv;
     424    srv->quitfunc( srv->cbdata );
     425}
     426
     427static void
    393428afc_version(struct constate *con, const char *name SHUTUP, benc_val_t *val) {
    394   struct constate_addfile *afc = &con->u.addfile;
     429  struct constate_client *afc = &con->u.client;
    395430  GList *file;
    396431  benc_val_t list, *str;
     
    399434    fprintf(stderr, _("bad IPC protocol version\n"));
    400435    destroycon(con);
    401   } else {
    402     /* XXX handle getting a non-version tag, invalid data,
    403            or nothing (read timeout) */
    404     bzero(&list, sizeof(list));
    405     list.type = TYPE_LIST;
    406     list.val.l.alloc = g_list_length(afc->files);
    407     list.val.l.vals = g_new0(benc_val_t, list.val.l.alloc);
    408     for(file = afc->files; NULL != file; file = file->next) {
    409       str = list.val.l.vals + list.val.l.count;
    410       str->type = TYPE_STR;
    411       str->val.s.i = strlen(file->data);
    412       str->val.s.s = file->data;
    413       list.val.l.count++;
    414     }
    415     g_list_free(afc->files);
    416     afc->files = NULL;
    417     afc->addid = send_msg(con, MSG_ADDFILES, &list);
    418     tr_bencFree(&list);
     436    return;
     437  }
     438
     439  /* XXX handle getting a non-version tag, invalid data,
     440     or nothing (read timeout) */
     441  switch( afc->cmd )
     442  {
     443      case CCMD_ADD:
     444          list.type = TYPE_LIST;
     445          list.val.l.alloc = g_list_length(afc->files);
     446          list.val.l.count = 0;
     447          list.val.l.vals = g_new0(benc_val_t, list.val.l.alloc);
     448          for(file = afc->files; NULL != file; file = file->next) {
     449              str = list.val.l.vals + list.val.l.count;
     450              str->type = TYPE_STR;
     451              str->val.s.i = strlen(file->data);
     452              str->val.s.s = file->data;
     453              list.val.l.count++;
     454          }
     455          g_list_free(afc->files);
     456          afc->files = NULL;
     457          afc->msgid = send_msg(con, MSG_ADDFILES, &list);
     458          tr_bencFree(&list);
     459          break;
     460      case CCMD_QUIT:
     461          bzero( &list, sizeof( list ) );
     462          list.type  = TYPE_STR;
     463          afc->msgid = send_msg( con, MSG_QUIT, &list );
     464          break;
    419465  }
    420466}
     
    422468static void
    423469afc_io_sent(GSource *source SHUTUP, unsigned int id, void *vdata) {
    424   struct constate_addfile *afc = &((struct constate*)vdata)->u.addfile;
    425 
    426   if(0 < id && afc->addid == id) {
     470  struct constate_client *afc = &((struct constate*)vdata)->u.client;
     471
     472  if(0 < id && afc->msgid == id) {
    427473    *(afc->succeeded) = TRUE;
    428474    destroycon(vdata);
  • trunk/gtk/ipc.h

    r760 r1504  
    22 * $Id$
    33 *
    4  * Copyright (c) 2006 Transmission authors and contributors
     4 * Copyright (c) 2006-2007 Transmission authors and contributors
    55 *
    66 * Permission is hereby granted, free of charge, to any person obtaining a
     
    2929
    3030void
    31 ipc_socket_setup(void *wind, add_torrents_func_t addfunc, void *cbdata);
     31ipc_socket_setup( void * wind, add_torrents_func_t addfunc,
     32                  callbackfunc_t quitfunc, void * cbdata );
    3233
    3334gboolean
    34 ipc_sendfiles_blocking(GList *files);
     35ipc_sendfiles_blocking( GList * files );
     36
     37gboolean
     38ipc_sendquit_blocking( void );
    3539
    3640#endif /* TG_IPC_H */
  • trunk/gtk/main.c

    r1475 r1504  
    2525#include <sys/param.h>
    2626#include <errno.h>
     27#include <getopt.h>
    2728#include <signal.h>
    2829#include <string.h>
     
    4142#include "msgwin.h"
    4243#include "tr_backend.h"
     44#include "tr_cell_renderer_progress.h"
     45#include "tr_icon.h"
     46#include "tr_prefs.h"
    4347#include "tr_torrent.h"
    44 #include "tr_cell_renderer_progress.h"
    4548#include "tr_window.h"
     49#include "util.h"
     50
    4651#include "transmission.h"
    47 #include "util.h"
    4852
    4953#include "img_icon_full.h"
     
    5862#define EXIT_CHECK_INTERVAL     500
    5963
     64/* number of fatal signals required to cause an immediate exit */
     65#define SIGCOUNT_MAX            3
     66
    6067struct cbdata {
    61   TrBackend *back;
    62   GtkWindow *wind;
    63   GtkTreeModel *model;
    64   guint timer;
    65   gboolean prefsopen;
    66   gboolean msgwinopen;
    67   gboolean closing;
     68    TrBackend    * back;
     69    GtkWindow    * wind;
     70    GtkTreeModel * model;
     71    TrIcon       * icon;
     72    TrPrefs      * prefs;
     73    guint          timer;
     74    gboolean       msgwinopen;
     75    gboolean       closing;
    6876};
    6977
    7078struct exitdata {
    71   struct cbdata *cbdata;
    72   time_t started;
    73   guint timer;
     79    struct cbdata * cbdata;
     80    time_t          started;
     81    guint          timer;
    7482};
    7583
     
    8391    ACT_PREF,
    8492    ACT_DEBUG,
     93    ACT_ICON,
    8594    ACTION_COUNT,
    8695};
     
    109118    { N_("Open debug window"), NULL,            ACTF_MENU     | ACTF_ALWAYS,
    110119      NULL },
     120    /* this isn't a terminator for the list, it's ACT_ICON */
     121    { NULL,              NULL,                  0, NULL },
    111122};
    112123
    113124#define CBDATA_PTR              "callback-data-pointer"
    114125
    115 #define SIGCOUNT_MAX            3
    116 
    117126static sig_atomic_t global_sigcount = 0;
    118127
    119128static GList *
    120 readargs(int argc, char **argv);
    121 
    122 static void
    123 makewind(TrWindow *wind, TrBackend *back, benc_val_t *state, GList *args);
    124 static void
    125 quittransmission(struct cbdata *data);
     129readargs( int argc, char ** argv, gboolean * sendquit, gboolean * paused );
    126130static gboolean
    127 winclose(GtkWidget *widget, GdkEvent *event, gpointer gdata);
     131sendremote( GList * files, gboolean sendquit );
     132static void
     133gtksetup( int * argc, char *** argv );
     134static void
     135appsetup( TrWindow * wind, benc_val_t * state, GList * args, gboolean paused );
     136static void
     137winsetup( struct cbdata * cbdata, TrWindow * wind );
     138static void
     139remakewind( struct cbdata * cbdata );
     140static void
     141makeicon( struct cbdata * cbdata );
     142static gboolean
     143winclose( GtkWidget * widget, GdkEvent * event, gpointer gdata );
     144static void
     145wannaquit( void * vdata );
    128146static gboolean
    129147exitcheck(gpointer gdata);
     
    134152        GtkSelectionData *sel, guint info, guint time, gpointer gdata);
    135153
     154static void
     155readinitialprefs( struct cbdata * cbdata );
     156static void
     157prefschanged( GtkWidget * widget, int id, gpointer data );
    136158static gboolean
    137159updatemodel(gpointer gdata);
    138160static void
    139 boolwindclosed(GtkWidget *widget SHUTUP, gpointer gdata);
     161boolwindclosed(GtkWidget *widget, gpointer gdata);
    140162static void
    141163windact(GtkWidget *widget, int action, gpointer gdata);
     164static GList *
     165getselection( struct cbdata * cbdata );
    142166static void
    143167handleaction(struct cbdata *data, enum action action);
     
    156180
    157181int
    158 main(int argc, char **argv) {
    159   GtkWidget *mainwind, *preferr, *stateerr;
    160   char *err;
    161   TrBackend *back;
    162   benc_val_t *state;
    163   GList *argfiles;
    164   gboolean didinit, didlock;
    165   GdkPixbuf * icon;
    166 
    167   safepipe();
    168 
    169   argfiles = readargs(argc, argv);
    170 
    171   didinit = cf_init(tr_getPrefsDirectory(), NULL);
    172   didlock = FALSE;
    173   if(NULL != argfiles && didinit && !(didlock = cf_lock(NULL)))
    174     return !ipc_sendfiles_blocking(argfiles);
    175 
    176   setupsighandlers();
    177 
    178   gtk_init(&argc, &argv);
    179 
    180   bindtextdomain("transmission-gtk", LOCALEDIR);
    181   bind_textdomain_codeset("transmission-gtk", "UTF-8");
    182   textdomain("transmission-gtk");
    183 
    184   g_set_application_name(_("Transmission"));
    185 #if 0
    186   /* this isn't used in transmission-gtk itself, it's for the .desktop file */
    187   N_("BitTorrent Client");
    188   /* this too */
    189   N_("A free, lightweight client with a simple, intuitive interface");
    190 #endif
    191 
    192   gtk_rc_parse_string(
    193     "style \"transmission-standard\" {\n"
    194     " GtkDialog::action-area-border = 6\n"
    195     " GtkDialog::button-spacing = 12\n"
    196     " GtkDialog::content-area-border = 6\n"
    197     "}\n"
    198     "widget \"TransmissionDialog\" style \"transmission-standard\"\n");
    199 
    200   icon = gdk_pixbuf_new_from_inline( -1, tr_icon_full, FALSE, NULL );
    201   gtk_window_set_default_icon( icon );
    202   g_object_unref( icon );
    203 
    204   if(didinit || cf_init(tr_getPrefsDirectory(), &err)) {
    205     if(didlock || cf_lock(&err)) {
    206 
    207       /* create main window now so any error dialogs can be it's children */
    208       mainwind = tr_window_new();
    209       preferr = NULL;
    210       stateerr = NULL;
    211 
    212       cf_loadprefs(&err);
    213       if(NULL != err) {
    214         preferr = errmsg(GTK_WINDOW(mainwind), "%s", err);
    215         g_free(err);
    216       }
    217       state = cf_loadstate(&err);
    218       if(NULL != err) {
    219         stateerr = errmsg(GTK_WINDOW(mainwind), "%s", err);
    220         g_free(err);
    221       }
    222 
    223       /* set libT message level */
    224       msgwin_loadpref();
    225 
    226       back = tr_backend_new();
    227 
    228       /* apply a few prefs */
    229       applyprefs(back);
    230 
    231       makewind( TR_WINDOW( mainwind ), back, state, argfiles );
    232 
    233       if(NULL != state)
    234         cf_freestate(state);
    235       g_object_unref(back);
    236 
    237       if(NULL != preferr)
    238         gtk_widget_show_all(preferr);
    239       if(NULL != stateerr)
    240         gtk_widget_show_all(stateerr);
    241     } else {
    242       gtk_widget_show(errmsg_full(NULL, (callbackfunc_t)gtk_main_quit,
    243                                   NULL, "%s", err));
    244       g_free(err);
    245     }
    246   } else {
    247     gtk_widget_show(errmsg_full(NULL, (callbackfunc_t)gtk_main_quit,
    248                                 NULL, "%s", err));
    249     g_free(err);
    250   }
    251 
    252   if(NULL != argfiles)
     182main( int argc, char ** argv )
     183{
     184    GtkWindow  * mainwind;
     185    char       * err;
     186    benc_val_t * state;
     187    GList      * argfiles;
     188    gboolean     didinit, didlock, sendquit, startpaused;
     189
     190    safepipe();                 /* ignore SIGPIPE */
     191    argfiles = readargs( argc, argv, &sendquit, &startpaused );
     192    didinit = cf_init( tr_getPrefsDirectory(), NULL );
     193    didlock = FALSE;
     194    if( didinit )
     195    {
     196        /* maybe send remote commands, also try cf_lock() */
     197        didlock = sendremote( argfiles, sendquit );
     198    }
     199    setupsighandlers();         /* set up handlers for fatal signals */
     200    gtksetup( &argc, &argv );   /* set up gtk and gettext */
     201
     202    if( ( didinit || cf_init( tr_getPrefsDirectory(), &err ) ) &&
     203        ( didlock || cf_lock( &err ) ) )
     204    {
     205        /* create main window now to be a parent to any error dialogs */
     206        mainwind = GTK_WINDOW( tr_window_new() );
     207
     208        /* try to load prefs and saved state */
     209        cf_loadprefs( &err );
     210        if( NULL != err )
     211        {
     212            errmsg( mainwind, "%s", err );
     213            g_free( err );
     214        }
     215        state = cf_loadstate( &err );
     216        if( NULL != err )
     217        {
     218            errmsg( mainwind, "%s", err );
     219            g_free( err );
     220        }
     221
     222        msgwin_loadpref();      /* set message level here before tr_init() */
     223        appsetup( TR_WINDOW( mainwind ), state, argfiles, startpaused );
     224        cf_freestate( state );
     225    }
     226    else
     227    {
     228        gtk_widget_show( errmsg_full( NULL, (callbackfunc_t)gtk_main_quit,
     229                                      NULL, "%s", err ) );
     230        g_free( err );
     231    }
     232
    253233    freestrlist(argfiles);
    254234
    255   gtk_main();
    256 
    257   return 0;
     235    gtk_main();
     236
     237    return 0;
    258238}
    259239
    260240GList *
    261 readargs(int argc, char **argv) {
    262   char *name;
    263 
    264   if(NULL == (name = strrchr(argv[0], '/')) || '\0' == *(++name))
    265     name = argv[0];
    266 
    267   while(0 < --argc) {
    268     argv++;
    269     if(0 == strcmp("--", *argv))
    270       return checkfilenames(argc - 1, argv + 1);
    271     else if('-' != argv[0][0])
    272       return checkfilenames(argc, argv);
    273     else if(0 == strcmp("-v", *argv) || 0 == strcmp("--version", *argv)) {
    274       printf("%s %s (%d) http://transmission.m0k.org/\n",
    275              name, VERSION_STRING, VERSION_REVISION);
    276       exit(0);
    277     }
    278     else if(0 == strcmp("-h", *argv) || 0 == strcmp("--help", *argv)) {
    279       printf("usage: %1$s [-hv] [files...]\n\n"
    280 "If %1$s is already running then a second copy will not be\n"
    281 "started, any torrents on the command-line will be opened in the first.\n",
    282              name);
    283       exit(0);
    284     }
    285   }
    286 
    287   return NULL;
    288 }
    289 
    290 static void
    291 makewind( TrWindow * wind, TrBackend * back, benc_val_t * state, GList * args)
    292 {
    293   GType types[] = {
    294     /* info->name, info->totalSize, status,     error,      errorString, */
    295     G_TYPE_STRING, G_TYPE_UINT64,   G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING,
    296     /* progress,  rateDownload, rateUpload,   eta,        peersTotal, */
    297     G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT,
    298     /* peersUploading, peersDownloading, downloaded,    uploaded */
    299     G_TYPE_INT,        G_TYPE_INT,       G_TYPE_UINT64, G_TYPE_UINT64,
    300     /* the torrent object */
    301     TR_TORRENT_TYPE};
    302   struct cbdata *data = g_new0(struct cbdata, 1);
    303   GtkListStore *store;
    304   unsigned int ii;
    305   GtkWidget *drag;
    306 
    307   g_assert(MC_ROW_COUNT == ALEN(types));
    308   store = gtk_list_store_newv(MC_ROW_COUNT, types);
    309 
    310   g_object_ref(G_OBJECT(back));
    311   data->back = back;
    312   data->wind = GTK_WINDOW(wind);
    313   data->timer = 0;
    314   data->model = GTK_TREE_MODEL(store);
    315   data->prefsopen = FALSE;
    316   data->msgwinopen = FALSE;
    317   data->closing = FALSE;
    318 
    319   g_assert( ACTION_COUNT == ALEN( actions ) );
    320   for( ii = 0; ii < ALEN( actions ); ii++ )
    321   {
    322       tr_window_action_add( wind, ii, actions[ii].flags,
    323                             gettext( actions[ii].label ),
    324                             actions[ii].icon,
    325                             gettext( actions[ii].tooltip ) );
    326   }
    327   g_object_set( wind, "model", data->model,
    328                       "double-click-action", ACT_INFO, NULL);
    329 
    330   g_signal_connect( wind, "action",       G_CALLBACK( windact  ), data );
    331   g_signal_connect( wind, "delete_event", G_CALLBACK( winclose ), data );
    332 
    333   g_object_get( wind, "drag-widget", &drag, NULL );
    334   setupdrag( drag, data );
    335 
    336   addtorrents(data, state, args, NULL, addactionflag(cf_getpref(PREF_ADDIPC)));
    337 
    338   data->timer = g_timeout_add(UPDATE_INTERVAL, updatemodel, data);
    339   updatemodel(data);
    340 
    341   /* this shows the window */
    342   tr_window_size_hack( wind );
    343 
    344   /* set up the ipc socket now that we're ready to get torrents from it */
    345   ipc_socket_setup(GTK_WINDOW(wind), addtorrents, data);
    346 }
    347 
    348 static void
    349 quittransmission( struct cbdata * data )
    350 {
    351     g_object_unref( G_OBJECT( data->back ) );
    352     if( NULL != data->wind )
    353     {
    354         gtk_widget_destroy( GTK_WIDGET( data->wind ) );
    355     }
    356     g_object_unref( data->model );
    357     if( 0 < data->timer )
    358     {
    359         g_source_remove( data->timer );
    360     }
    361     g_free( data );
    362     gtk_main_quit();
    363 }
    364 
    365 gboolean
    366 winclose(GtkWidget *widget SHUTUP, GdkEvent *event SHUTUP, gpointer gdata) {
    367   struct cbdata *data = gdata;
     241readargs( int argc, char ** argv, gboolean * sendquit, gboolean * startpaused )
     242{
     243    struct option opts[] =
     244    {
     245        { "help",    no_argument, NULL, 'h' },
     246        { "paused",  no_argument, NULL, 'p' },
     247        { "quit",    no_argument, NULL, 'q' },
     248        { "version", no_argument, NULL, 'v' },
     249        { NULL, 0, NULL, 0 }
     250    };
     251    int opt;
     252
     253    *sendquit    = FALSE;
     254    *startpaused = FALSE;
     255
     256    gtk_parse_args( &argc, &argv );
     257
     258    while( 0 <= ( opt = getopt_long( argc, argv, "hpqv", opts, NULL ) ) )
     259    {
     260        switch( opt )
     261        {
     262            case 'p':
     263                *startpaused = TRUE;
     264                break;
     265            case 'q':
     266                *sendquit = TRUE;
     267                break;
     268            case 'v':
     269            case 'h':
     270                printf(
     271_("usage: %1$s [-hpq] [files...]\n"
     272  "\n"
     273  "Transmission %2$s (r%3$d) http://transmission.m0k.org/\n"
     274  "A free, lightweight BitTorrent client with a simple, intuitive interface\n"
     275  "\n"
     276  "  -h --help    display this message and exit\n"
     277  "  -p --paused  start with all torrents paused\n"
     278  "  -q --quit    request that the running %1$s instance quit\n"
     279  "\n"
     280  "Only one instance of %1$s may run at one time. Multiple\n"
     281  "torrent files may be loaded at startup by adding them to the command\n"
     282  "line. If %1$s is already running, those torrents will be\n"
     283  "opened in the running instance.\n"),
     284                        g_get_prgname(), VERSION_STRING, VERSION_REVISION );
     285                exit(0);
     286                break;
     287        }
     288    }
     289
     290    argc -= optind;
     291    argv += optind;
     292
     293    return checkfilenames( argc, argv );
     294}
     295
     296static gboolean
     297sendremote( GList * files, gboolean sendquit )
     298{
     299    gboolean didlock;
     300
     301    didlock = cf_lock( NULL );
     302
     303    if( NULL != files )
     304    {
     305        /* send files if there's another instance, otherwise start normally */
     306        if( !didlock )
     307        {
     308            exit( ipc_sendfiles_blocking( files ) ? 0 : 1 );
     309        }
     310    }
     311
     312    if( sendquit )
     313    {
     314        /* either send a quit message or exit if no other instance */
     315        if( !didlock )
     316        {
     317            exit( ipc_sendquit_blocking() ? 0 : 1 );
     318        }
     319        exit( 0 );
     320    }
     321
     322    return didlock;
     323}
     324
     325static void
     326gtksetup( int * argc, char *** argv )
     327{
     328    GdkPixbuf * icon;
     329
     330    gtk_init( argc, argv );
     331
     332    bindtextdomain( "transmission-gtk", LOCALEDIR );
     333    bind_textdomain_codeset( "transmission-gtk", "UTF-8" );
     334    textdomain( "transmission-gtk" );
     335
     336    g_set_application_name( _("Transmission") );
     337
     338    /* tweak some style properties in dialogs to get closer to the GNOME HiG */
     339    gtk_rc_parse_string(
     340        "style \"transmission-standard\"\n"
     341        "{\n"
     342        "    GtkDialog::action-area-border  = 6\n"
     343        "    GtkDialog::button-spacing      = 12\n"
     344        "    GtkDialog::content-area-border = 6\n"
     345        "}\n"
     346        "widget \"TransmissionDialog\" style \"transmission-standard\"\n" );
     347
     348    icon = gdk_pixbuf_new_from_inline( -1, tr_icon_full, FALSE, NULL );
     349    gtk_window_set_default_icon( icon );
     350    g_object_unref( icon );
     351}
     352
     353static void
     354appsetup( TrWindow * wind, benc_val_t * state, GList * args, gboolean paused )
     355{
     356    GType types[] =
     357    {
     358        /* info->name, info->totalSize, status,     error,      errorString, */
     359        G_TYPE_STRING, G_TYPE_UINT64,   G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING,
     360        /* progress,  rateDownload, rateUpload,   eta,        peersTotal, */
     361        G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT,
     362        /* peersUploading, peersDownloading, downloaded,    uploaded */
     363        G_TYPE_INT,        G_TYPE_INT,       G_TYPE_UINT64, G_TYPE_UINT64,
     364        /* the TrTorrent object */
     365        TR_TORRENT_TYPE,
     366    };
     367    struct cbdata * cbdata;
     368    GtkListStore  * store;
     369    guint           flags;
     370
     371    /* create the model used to store torrent data */
     372    g_assert( ALEN( types ) == MC_ROW_COUNT );
     373    store = gtk_list_store_newv( MC_ROW_COUNT, types );
     374
     375    /* fill out cbdata */
     376    cbdata = g_new0( struct cbdata, 1 );
     377    cbdata->back       = tr_backend_new();
     378    cbdata->wind       = NULL;
     379    cbdata->model      = GTK_TREE_MODEL(store);
     380    cbdata->icon       = NULL;
     381    cbdata->prefs      = NULL;
     382    cbdata->timer      = 0;
     383    cbdata->msgwinopen = FALSE;
     384    cbdata->closing    = FALSE;
     385
     386    /* apply a few prefs */
     387    readinitialprefs( cbdata );
     388
     389    /* set up main window */
     390    winsetup( cbdata, wind );
     391
     392    /* add torrents from command-line and saved state */
     393    flags = addactionflag( tr_prefs_get( PREF_ID_ADDIPC ) );
     394    g_assert( !( flags & ( TR_TORNEW_PAUSED | TR_TORNEW_RUNNING ) ) );
     395    if( paused )
     396    {
     397        flags |= TR_TORNEW_PAUSED;
     398    }
     399    addtorrents( cbdata, state, args, NULL, flags );
     400
     401    /* start model update timer */
     402    cbdata->timer = g_timeout_add( UPDATE_INTERVAL, updatemodel, cbdata );
     403    updatemodel( cbdata );
     404
     405    /* this shows the window */
     406    tr_window_size_hack( wind );
     407
     408    /* set up the ipc socket now that we're ready to get torrents from it */
     409    ipc_socket_setup( GTK_WINDOW( wind ), addtorrents, wannaquit, cbdata );
     410}
     411
     412static void
     413winsetup( struct cbdata * cbdata, TrWindow * wind )
     414{
     415    int ii;
     416    GtkWidget  * drag;
     417
     418    g_assert( ACTION_COUNT == ALEN( actions ) );
     419    g_assert( NULL == cbdata->wind );
     420    cbdata->wind = GTK_WINDOW( wind );
     421    for( ii = 0; ii < ALEN( actions ); ii++ )
     422    {
     423        tr_window_action_add( wind, ii, actions[ii].flags,
     424                              gettext( actions[ii].label ), actions[ii].icon,
     425                              gettext( actions[ii].tooltip ) );
     426    }
     427    g_object_set( wind, "model", cbdata->model,
     428                        "double-click-action", ACT_INFO, NULL);
     429
     430    g_signal_connect( wind, "action",       G_CALLBACK( windact  ), cbdata );
     431    g_signal_connect( wind, "delete-event", G_CALLBACK( winclose ), cbdata );
     432   
     433    g_object_get( wind, "drag-widget", &drag, NULL );
     434    setupdrag( drag, cbdata );
     435}
     436
     437static void
     438remakewind( struct cbdata * cbdata )
     439{
     440    GtkWidget * win;
     441
     442    if( NULL != cbdata->wind )
     443    {
     444        return;
     445    }
     446
     447    /* create window */
     448    win = tr_window_new();
     449    winsetup( cbdata, TR_WINDOW( win ) );
     450
     451    /* this shows the window */
     452    tr_window_size_hack( TR_WINDOW( win ) );
     453}
     454
     455static void
     456makeicon( struct cbdata * cbdata )
     457{
     458    TrIcon * icon;
     459
     460    if( NULL != cbdata->icon )
     461    {
     462        return;
     463    }
     464
     465    icon = tr_icon_new();
     466    g_object_set( icon, "activate-action", ACT_ICON, NULL);
     467    g_signal_connect( icon, "action", G_CALLBACK( windact  ), cbdata );
     468
     469    cbdata->icon = icon;
     470}
     471
     472static gboolean
     473winclose( GtkWidget * widget SHUTUP, GdkEvent * event SHUTUP, gpointer gdata )
     474{
     475    struct cbdata * cbdata;
     476
     477    cbdata = gdata;
     478
     479    if( NULL != cbdata->icon && tr_icon_docked( cbdata->icon ) )
     480    {
     481        gtk_widget_destroy( GTK_WIDGET( cbdata->wind ) );
     482        cbdata->wind = NULL;
     483    }
     484    else
     485    {
     486        askquit( cbdata->wind, wannaquit, cbdata );
     487    }
     488
     489    /* don't propagate event further */
     490    return TRUE;
     491}
     492
     493static void
     494wannaquit( void * vdata )
     495{
     496  struct cbdata * data;
    368497  struct exitdata *edata;
    369498  GtkTreeIter iter;
    370499  TrTorrent *tor;
    371500
     501  data = vdata;
     502  if( data->closing )
     503  {
     504      return;
     505  }
    372506  data->closing = TRUE;
    373507
     
    403537    /* yes, start the exit timer and disable widgets */
    404538    edata->timer = g_timeout_add(EXIT_CHECK_INTERVAL, exitcheck, edata);
    405     gtk_widget_set_sensitive( GTK_WIDGET( data->wind ), FALSE );
    406   }
    407 
    408   /* returning FALSE means to destroy the window */
    409   return TRUE;
    410 }
    411 
    412 gboolean
    413 exitcheck(gpointer gdata) {
    414   struct exitdata *data = gdata;
    415   tr_handle_status_t * hstat;
    416 
    417   hstat = tr_handleStatus( tr_backend_handle( data->cbdata->back ) );
    418 
    419   /* keep going if we haven't hit the exit timeout and
    420      we either have torrents left or nat traversal is stopping */
    421   if( time( NULL ) - data->started < TRACKER_EXIT_TIMEOUT &&
    422       ( !tr_backend_torrents_stopped( data->cbdata->back ) ||
    423         TR_NAT_TRAVERSAL_DISABLED != hstat->natTraversalStatus ) ) {
    424     updatemodel(data->cbdata);
    425     return TRUE;
    426   }
    427 
    428   /* exit otherwise */
    429   if(0 < data->timer)
    430     g_source_remove(data->timer);
    431   quittransmission(data->cbdata);
    432   g_free(data);
    433 
    434   return FALSE;
     539    if( NULL != data->wind )
     540    {
     541        gtk_widget_set_sensitive( GTK_WIDGET( data->wind ), FALSE );
     542    }
     543  }
     544}
     545
     546static gboolean
     547exitcheck( gpointer gdata )
     548{
     549    struct exitdata    * edata;
     550    struct cbdata      * cbdata;
     551    tr_handle_status_t * hstat;
     552
     553    edata  = gdata;
     554    cbdata = edata->cbdata;
     555    hstat  = tr_handleStatus( tr_backend_handle( cbdata->back ) );
     556
     557    /* keep going if we haven't hit the exit timeout and
     558       we either have torrents left or nat traversal is active */
     559    if( time( NULL ) - edata->started < TRACKER_EXIT_TIMEOUT )
     560    {
     561        if( !tr_backend_torrents_stopped( cbdata->back, FALSE ) ||
     562            TR_NAT_TRAVERSAL_DISABLED != hstat->natTraversalStatus )
     563        {
     564            updatemodel( cbdata );
     565            return TRUE;
     566        }
     567    }
     568    else
     569    {
     570        /* time the remaining torrents out so they signal politely-stopped */
     571        tr_backend_torrents_stopped( cbdata->back, TRUE );
     572    }
     573
     574    /* exit otherwise */
     575    if( 0 < edata->timer )
     576    {
     577        g_source_remove( edata->timer );
     578    }
     579    g_free( edata );
     580    /* The prefs window need to be destroyed first as destroying it may
     581       trigger callbacks that use cbdata->back. Ick. */
     582    if( NULL != cbdata->prefs )
     583    {
     584        gtk_widget_destroy( GTK_WIDGET( cbdata->prefs ) );
     585    }
     586    g_object_unref( G_OBJECT( cbdata->back ) );
     587    if( NULL != cbdata->wind )
     588    {
     589        gtk_widget_destroy( GTK_WIDGET( cbdata->wind ) );
     590    }
     591    g_object_unref( cbdata->model );
     592    if( NULL != cbdata->icon )
     593    {
     594        g_object_unref( cbdata->icon );
     595    }
     596    g_assert( 0 == cbdata->timer );
     597    g_free( cbdata );
     598    gtk_main_quit();
     599
     600    return FALSE;
    435601}
    436602
     
    507673
    508674    /* try to add any torrents we found */
    509     if(NULL != paths)
    510       addtorrents(data, NULL, paths, NULL,
    511                   addactionflag(cf_getpref(PREF_ADDSTD)));
     675    if( NULL != paths )
     676    {
     677        addtorrents( data, NULL, paths, NULL,
     678                     addactionflag( tr_prefs_get( PREF_ID_ADDSTD ) ) );
     679    }
    512680    freestrlist(freeables);
    513681    g_free(files);
     
    529697  gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_ALL, targets,
    530698                    ALEN(targets), GDK_ACTION_COPY | GDK_ACTION_MOVE);
     699}
     700
     701static void
     702readinitialprefs( struct cbdata * cbdata )
     703{
     704    int prefs[] =
     705    {
     706        PREF_ID_PORT,
     707        PREF_ID_USEDOWNLIMIT,
     708        PREF_ID_USEUPLIMIT,
     709        PREF_ID_NAT,
     710        PREF_ID_ICON,
     711    };
     712    int ii;
     713
     714    for( ii = 0; ALEN( prefs ) > ii; ii++ )
     715    {
     716        prefschanged( NULL, prefs[ii], cbdata );
     717    }
     718}
     719
     720static void
     721prefschanged( GtkWidget * widget SHUTUP, int id, gpointer data )
     722{
     723    struct cbdata * cbdata;
     724    tr_handle_t   * tr;
     725    int             num;
     726
     727    cbdata = data;
     728    tr     = tr_backend_handle( cbdata->back );
     729
     730    switch( id )
     731    {
     732        case PREF_ID_PORT:
     733            tr_setBindPort( tr, tr_prefs_get_int_with_default( id ) );
     734            break;
     735
     736        case PREF_ID_USEDOWNLIMIT:
     737        case PREF_ID_DOWNLIMIT:
     738            num = -1;
     739            if( tr_prefs_get_bool_with_default( PREF_ID_USEDOWNLIMIT ) )
     740            {
     741                num = tr_prefs_get_int_with_default( PREF_ID_DOWNLIMIT );
     742            }
     743            tr_setGlobalDownloadLimit( tr, num );
     744            break;
     745
     746        case PREF_ID_USEUPLIMIT:
     747        case PREF_ID_UPLIMIT:
     748            num = -1;
     749            if( tr_prefs_get_bool_with_default( PREF_ID_USEUPLIMIT ) )
     750            {
     751                num = tr_prefs_get_int_with_default( PREF_ID_UPLIMIT );
     752            }
     753            tr_setGlobalUploadLimit( tr, num );
     754            break;
     755
     756        case PREF_ID_NAT:
     757            tr_natTraversalEnable( tr, tr_prefs_get_bool_with_default( id ) );
     758            break;
     759
     760        case PREF_ID_ICON:
     761            if( tr_prefs_get_bool_with_default( id ) )
     762            {
     763                makeicon( cbdata );
     764            }
     765            else if( NULL != cbdata->icon )
     766            {
     767                g_object_unref( cbdata->icon );
     768                cbdata->icon = NULL;
     769            }
     770            break;
     771
     772        case PREF_ID_DIR:
     773        case PREF_ID_ASKDIR:
     774        case PREF_ID_ADDSTD:
     775        case PREF_ID_ADDIPC:
     776        case PREF_ID_MSGLEVEL:
     777        case PREF_MAX_ID:
     778            break;
     779    }
    531780}
    532781
     
    540789  float up, down;
    541790
    542   if(0 < global_sigcount) {
    543     quittransmission(data);
    544     return FALSE;
     791  if( !data->closing && 0 < global_sigcount )
     792  {
     793      wannaquit( data );
     794      return FALSE;
    545795  }
    546796
     
    563813
    564814  /* update the main window's statusbar and toolbar buttons */
    565   tr_torrentRates( tr_backend_handle( data->back ), &down, &up );
    566   tr_window_update( TR_WINDOW(data->wind), down, up );
     815  if( NULL != data->wind )
     816  {
     817      tr_torrentRates( tr_backend_handle( data->back ), &down, &up );
     818      tr_window_update( TR_WINDOW( data->wind ), down, up );
     819  }
    567820
    568821  /* check for politely stopped torrents unless we're exiting */
    569   if(!data->closing)
    570     tr_backend_torrents_stopped(data->back);
     822  if( !data->closing )
     823  {
     824      tr_backend_torrents_stopped( data->back, FALSE );
     825  }
    571826
    572827  /* update the message window */
     
    590845}
    591846
     847/* returns a GList containing a GtkTreeRowReference to each selected row */
     848static GList *
     849getselection( struct cbdata * cbdata )
     850{
     851    GtkTreeSelection    * sel;
     852    GList               * rows, * ii;
     853    GtkTreeRowReference * ref;
     854
     855    if( NULL == cbdata->wind )
     856    {
     857        return NULL;
     858    }
     859    g_object_get( cbdata->wind, "selection", &sel, NULL );
     860    rows = gtk_tree_selection_get_selected_rows( sel, NULL );
     861    for( ii = rows; NULL != ii; ii = ii->next )
     862    {
     863        ref = gtk_tree_row_reference_new( cbdata->model, ii->data );
     864        gtk_tree_path_free( ii->data );
     865        ii->data = ref;
     866    }
     867
     868    return rows;
     869}
     870
    592871static void
    593872handleaction( struct cbdata * data, enum action act )
    594873{
    595   GtkTreeSelection *sel;
    596874  GList *rows, *ii;
    597   GtkTreeRowReference *ref;
    598875  GtkTreePath *path;
    599876  GtkTreeIter iter;
     
    611888          return;
    612889      case ACT_PREF:
    613           if( !data->prefsopen )
     890          if( NULL != data->prefs )
    614891          {
    615               data->prefsopen = TRUE;
    616               win = makeprefwindow( data->wind, data->back );
    617               g_signal_connect( win, "destroy", G_CALLBACK( boolwindclosed ),
    618                                 &data->prefsopen );
     892              return;
    619893          }
     894          data->prefs = tr_prefs_new_with_parent( data->wind );
     895          g_signal_connect( data->prefs, "prefs-changed",
     896                            G_CALLBACK( prefschanged ), data );
     897          g_signal_connect( data->prefs, "destroy",
     898                            G_CALLBACK( gtk_widget_destroyed ), &data->prefs );
     899          gtk_widget_show( GTK_WIDGET( data->prefs ) );
    620900          return;
    621901      case ACT_DEBUG:
     
    628908          }
    629909          return;
     910      case ACT_ICON:
     911          remakewind( data );
     912          return;
    630913      case ACT_START:
    631914      case ACT_STOP:
     
    637920
    638921  /* get a list of references to selected rows */
    639   g_object_get( data->wind, "selection", &sel, NULL );
    640   rows = gtk_tree_selection_get_selected_rows( sel, NULL );
    641   for(ii = rows; NULL != ii; ii = ii->next) {
    642     ref = gtk_tree_row_reference_new(data->model, ii->data);
    643     gtk_tree_path_free(ii->data);
    644     ii->data = ref;
    645   }
     922  rows = getselection( data );
    646923
    647924  changed = FALSE;
     
    682959              case ACT_PREF:
    683960              case ACT_DEBUG:
     961              case ACT_ICON:
    684962              case ACTION_COUNT:
    685963                  break;
     
    698976    updatemodel(data);
    699977  }
    700 }
    701 
    702 static const char *
    703 defaultdir( void )
    704 {
    705     static char * wd = NULL;
    706     const char  * dir;
    707 
    708     dir = cf_getpref( PREF_DIR );
    709     if( NULL == dir )
    710     {
    711         if( NULL == wd )
    712         {
    713             wd = g_new( char, MAX_PATH_LENGTH + 1 );
    714             if( NULL == getcwd( wd, MAX_PATH_LENGTH + 1 ) )
    715             {
    716                 strcpy( wd, "." );
    717             }
    718         }
    719         dir = wd;
    720     }
    721 
    722     return dir;
    723978}
    724979
     
    736991  torlist = NULL;
    737992
    738   if(NULL != state)
    739     torlist = tr_backend_load_state(data->back, state, &errlist);
     993  if( NULL != state )
     994  {
     995      torlist = tr_backend_load_state( data->back, state, flags, &errlist );
     996  }
    740997
    741998  if(NULL != files) {
    742999    if( NULL == dir )
    7431000    {
    744         pref = cf_getpref( PREF_ASKDIR );
     1001        pref = tr_prefs_get( PREF_ID_ASKDIR );
    7451002        if( NULL != pref && strbool( pref ) )
    7461003        {
    747             promptfordir( data->wind, addtorrents, data,
    748                           files, flags, defaultdir() );
     1004            promptfordir( data->wind, addtorrents, data, files, flags );
    7491005            files = NULL;
    7501006        }
    751         dir = defaultdir();
     1007        dir = getdownloaddir();
    7521008    }
    7531009    for(ii = g_list_first(files); NULL != ii; ii = ii->next) {
     
    7741030  if(NULL != errlist) {
    7751031    errstr = joinstrlist(errlist, "\n");
    776     errmsg(data->wind, ngettext("Failed to load torrent file:\n%s",
    777                                 "Failed to load torrent files:\n%s",
    778                                 g_list_length(errlist)), errstr);
     1032    errmsg( data->wind, ngettext( "Failed to load torrent file:\n%s",
     1033                                  "Failed to load torrent files:\n%s",
     1034                                  g_list_length( errlist ) ), errstr );
    7791035    g_list_foreach(errlist, (GFunc)g_free, NULL);
    7801036    g_list_free(errlist);
     
    8121068static void
    8131069setupsighandlers(void) {
    814   int sigs[] = {SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2};
     1070  int sigs[] = {SIGHUP, SIGINT, SIGQUIT, SIGTERM};
    8151071  struct sigaction sa;
    816   unsigned int ii;
     1072  int ii;
    8171073
    8181074  bzero(&sa, sizeof(sa));
  • trunk/gtk/msgwin.c

    r866 r1504  
    22 * $Id$
    33 *
    4  * Copyright (c) 2006 Transmission authors and contributors
     4 * Copyright (c) 2006-2007 Transmission authors and contributors
    55 *
    66 * Permission is hereby granted, free of charge, to any person obtaining a
     
    3131#include "conf.h"
    3232#include "msgwin.h"
     33#include "tr_prefs.h"
    3334#include "transmission.h"
    3435#include "util.h"
     
    6162  GtkWidget * frame, * bbox, * save, * clear, * menu;
    6263  PangoFontDescription * desc;
    63   unsigned int ii;
    64   int curlevel;
     64  int ii, curlevel;
    6565
    6666  if( NULL == textbuf )
     
    127127      tr_getMessageLevel() != levels[index].id ) {
    128128    tr_setMessageLevel( levels[index].id );
    129     cf_setpref( PREF_MSGLEVEL, levels[index].pref );
     129    cf_setpref( tr_prefs_name( PREF_ID_MSGLEVEL ), levels[index].pref );
    130130    cf_saveprefs( &ignored );
    131131    g_free( ignored );
     
    197197msgwin_loadpref( void ) {
    198198  const char * pref;
    199   unsigned int ii;
     199  int ii;
    200200
    201201  tr_setMessageQueuing( 1 );
    202   pref = cf_getpref( PREF_MSGLEVEL );
     202  pref = tr_prefs_get( PREF_ID_MSGLEVEL );
    203203  if( NULL == pref )
    204204    return;
     
    217217  GtkTextIter     iter, front;
    218218  char          * label, * line;
    219   int             count;
     219  int             count, jj;
    220220  struct tm     * tm;
    221   unsigned int    jj;
    222221
    223222  if( NULL == textbuf )
  • trunk/gtk/tr_backend.c

    r1190 r1504  
    22 * $Id$
    33 *
    4  * Copyright (c) 2006 Transmission authors and contributors
     4 * Copyright (c) 2006-2007 Transmission authors and contributors
    55 *
    66 * Permission is hereby granted, free of charge, to any person obtaining a
     
    221221
    222222GList *
    223 tr_backend_load_state(TrBackend *back, benc_val_t *state, GList **errors) {
     223tr_backend_load_state( TrBackend * back, benc_val_t * state,
     224                       guint flags, GList ** errors )
     225{
    224226  GList *ret = NULL;
    225227  int ii;
     
    234236  for(ii = 0; ii < state->val.l.count; ii++) {
    235237    errstr = NULL;
    236     tor = tr_torrent_new_with_state(G_OBJECT(back), state->val.l.vals + ii,
    237                                     &errstr);
     238    tor = tr_torrent_new_with_state( G_OBJECT( back ), state->val.l.vals + ii,
     239                                     flags, &errstr );
    238240    if(NULL != errstr)
    239241      *errors = g_list_append(*errors, errstr);
     
    274276
    275277gboolean
    276 tr_backend_torrents_stopped(TrBackend *back) {
    277   GList *ii, *list;
    278   tr_stat_t *st;
    279   gboolean ret = TRUE;
    280 
    281   TR_IS_BACKEND(back);
    282 
    283   list = g_list_copy(back->torrents);
    284   for(ii = list; NULL != ii; ii = ii->next) {
    285     st = tr_torrent_stat_polite(ii->data);
    286     if(NULL == st || !(TR_STATUS_PAUSE & st->status))
    287       ret = FALSE;
    288   }
    289   g_list_free(list);
    290 
    291   return ret;
    292 }
     278tr_backend_torrents_stopped( TrBackend * back, gboolean timeout )
     279{
     280    GList     * ii, * list;
     281    tr_stat_t * st;
     282    gboolean  ret;
     283
     284    TR_IS_BACKEND( back );
     285
     286    ret  = TRUE;
     287    list = g_list_copy( back->torrents );
     288    for( ii = list; NULL != ii; ii = ii->next )
     289    {
     290        st = tr_torrent_stat_polite( ii->data, timeout );
     291        if( NULL == st || !( TR_STATUS_PAUSE & st->status ) )
     292        {
     293            ret = FALSE;
     294        }
     295    }
     296    g_list_free( list );
     297
     298    return ret;
     299}
  • trunk/gtk/tr_backend.h

    r760 r1504  
    22 * $Id$
    33 *
    4  * Copyright (c) 2006 Transmission authors and contributors
     4 * Copyright (c) 2006-2007 Transmission authors and contributors
    55 *
    66 * Permission is hereby granted, free of charge, to any person obtaining a
     
    7171
    7272GList *
    73 tr_backend_load_state(TrBackend *back, benc_val_t *state, GList **errors);
     73tr_backend_load_state( TrBackend * back, benc_val_t * state,
     74                       guint flags, GList ** errors );
    7475
    7576void
     
    7778
    7879gboolean
    79 tr_backend_torrents_stopped(TrBackend *back);
     80tr_backend_torrents_stopped( TrBackend * back, gboolean timeout );
    8081
    8182#ifdef TR_WANT_BACKEND_PRIVATE
  • trunk/gtk/tr_torrent.c

    r1475 r1504  
    309309
    310310TrTorrent *
    311 tr_torrent_new_with_state(GObject *backend, benc_val_t *state, char **err) {
     311tr_torrent_new_with_state( GObject * backend, benc_val_t * state,
     312                           guint forcedflags, char ** err)
     313{
    312314  int ii;
    313315  benc_val_t *name, *data;
     
    354356    torrent = hash;
    355357  }
     358  forcedflags &= TR_TORNEW_PAUSED | TR_TORNEW_RUNNING;
     359  if( forcedflags )
     360  {
     361      flags &= ~( TR_TORNEW_PAUSED | TR_TORNEW_RUNNING );
     362      flags |= forcedflags;
     363  }
    356364
    357365  return tr_torrent_new(backend, torrent, dir, flags, err);
     
    449457
    450458tr_stat_t *
    451 tr_torrent_stat_polite(TrTorrent *tor) {
    452   TrTorrentClass *klass;
    453   tr_stat_t *st;
    454 
    455   if(tor->disposed)
    456     return NULL;
    457 
    458   st = tr_torrentStat(tor->handle);
    459   if(tor->closing && TR_STATUS_PAUSE & st->status) {
    460     tor->closing = FALSE;
    461     klass = g_type_class_peek(TR_TORRENT_TYPE);
    462     g_signal_emit(tor, klass->paused_signal_id, 0, NULL);
    463     return tr_torrent_stat_polite(tor);
    464   }
    465 
    466   return st;
    467 }
     459tr_torrent_stat_polite( TrTorrent * tor, gboolean timeout )
     460{
     461    TrTorrentClass * klass;
     462    tr_stat_t      * st;
     463
     464    TR_IS_TORRENT( tor );
     465
     466    if( tor->disposed )
     467    {
     468        return NULL;
     469    }
     470
     471    st = tr_torrentStat( tor->handle );
     472    if( tor->closing && ( TR_STATUS_PAUSE & st->status || timeout ) )
     473    {
     474        tor->closing = FALSE;
     475        klass = g_type_class_peek( TR_TORRENT_TYPE );
     476        g_signal_emit( tor, klass->paused_signal_id, 0, NULL );
     477        return tr_torrent_stat_polite( tor, FALSE );
     478    }
     479
     480    return st;
     481}
  • trunk/gtk/tr_torrent.h

    r760 r1504  
    22 * $Id$
    33 *
    4  * Copyright (c) 2006 Transmission authors and contributors
     4 * Copyright (c) 2006-2007 Transmission authors and contributors
    55 *
    66 * Permission is hereby granted, free of charge, to any person obtaining a
     
    8989
    9090TrTorrent *
    91 tr_torrent_new_with_state(GObject *backend, benc_val_t *state, char **err);
     91tr_torrent_new_with_state( GObject * backend, benc_val_t * state,
     92                           guint flags, char ** err );
    9293
    9394void
     
    9596
    9697tr_stat_t *
    97 tr_torrent_stat_polite(TrTorrent *tor);
     98tr_torrent_stat_polite( TrTorrent * tor, gboolean timeout );
    9899
    99100#ifdef TR_WANT_TORRENT_PRIVATE
  • trunk/gtk/tr_window.h

    r1475 r1504  
    9191
    9292/* some evil magic to show the window with a nice initial window size */
     93/* note that the gtk main loop runs in this function */
    9394void
    9495tr_window_size_hack( TrWindow * wind );
    9596
    96 /* XXX these should be somewhere else */
    97 #define ACTF_TOOL       ( 1 << 0 ) /* appear in the toolbar */
    98 #define ACTF_MENU       ( 1 << 1 ) /* appear in the popup menu */
    99 #define ACTF_ALWAYS     ( 1 << 2 ) /* available regardless of selection */
    100 #define ACTF_ACTIVE     ( 1 << 3 ) /* available for active torrent */
    101 #define ACTF_INACTIVE   ( 1 << 4 ) /* available for inactive torrent */
    102 /* appear in the toolbar and the popup menu */
    103 #define ACTF_WHEREVER   ( ACTF_TOOL | ACTF_MENU )
    104 /* available if there is something selected */
    105 #define ACTF_WHATEVER   ( ACTF_ACTIVE | ACTF_INACTIVE )
    106 
    107 /* XXX this too*/
    108 #define ACT_ISAVAIL( flags, status ) \
    109     ( ( ACTF_ACTIVE   & (flags) && TR_STATUS_ACTIVE   & (status) ) || \
    110       ( ACTF_INACTIVE & (flags) && TR_STATUS_INACTIVE & (status) ) || \
    111         ACTF_ALWAYS   & (flags) )
    112 
    113 /* XXX and this */
    114 /* model column names */
    115 enum {
    116   MC_NAME, MC_SIZE, MC_STAT, MC_ERR, MC_TERR,
    117   MC_PROG, MC_DRATE, MC_URATE, MC_ETA, MC_PEERS,
    118   MC_UPEERS, MC_DPEERS, MC_DOWN, MC_UP,
    119   MC_TORRENT, MC_ROW_COUNT,
    120 };
    121 
    12297#endif
  • trunk/gtk/transmission-gtk.desktop

    r1470 r1504  
    44Name=Transmission
    55Name[ru]=ПереЎача
    6 GenericName=BitTorrent Client
    76Type=Application
    8 Comment=A free, lightweight client with a simple, intuitive interface
     7Comment=A free, lightweight BitTorrent client with a simple, intuitive interface
    98Exec=transmission-gtk %F
    109TryExec=transmission-gtk
  • trunk/gtk/util.c

    r1468 r1504  
    22 * $Id$
    33 *
    4  * Copyright (c) 2005-2006 Transmission authors and contributors
     4 * Copyright (c) 2005-2007 Transmission authors and contributors
    55 *
    66 * Permission is hereby granted, free of charge, to any person obtaining a
     
    3434#include <glib/gi18n.h>
    3535
     36#include "tr_prefs.h"
    3637#include "tr_torrent.h"
    3738#include "util.h"
     
    6667char *
    6768readablesize(guint64 size) {
    68   unsigned int ii;
     69  int ii;
    6970  double small = size;
    7071
     
    309310}
    310311
     312const char *
     313getdownloaddir( void )
     314{
     315    static char * wd = NULL;
     316    const char  * dir;
     317
     318    dir = tr_prefs_get( PREF_ID_DIR );
     319    if( NULL == dir )
     320    {
     321        if( NULL == wd )
     322        {
     323            wd = g_new( char, MAX_PATH_LENGTH + 1 );
     324            if( NULL == getcwd( wd, MAX_PATH_LENGTH + 1 ) )
     325            {
     326                strcpy( wd, "." );
     327            }
     328        }
     329        dir = wd;
     330    }
     331
     332    return dir;
     333}
     334
     335void
     336errmsg( GtkWindow * wind, const char * format, ... )
     337{
     338    GtkWidget * dialog;
     339    va_list     ap;
     340
     341    va_start( ap, format );
     342    dialog = verrmsg_full( wind, NULL, NULL, format, ap );
     343    va_end( ap );
     344
     345    if( NULL != wind && !GTK_WIDGET_MAPPED( GTK_WIDGET( wind ) ) )
     346    {
     347        g_signal_connect_swapped( wind, "map",
     348                                  G_CALLBACK( gtk_widget_show ), dialog );
     349    }
     350    else
     351    {
     352        gtk_widget_show( dialog );
     353    }
     354}
     355
    311356GtkWidget *
    312 errmsg(GtkWindow *wind, const char *format, ...) {
    313   GtkWidget *dialog;
    314   va_list ap;
    315 
    316   va_start(ap, format);
    317   dialog = verrmsg(wind, NULL, NULL, format, ap);
    318   va_end(ap);
    319 
    320   return dialog;
     357errmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
     358             const char * format, ... )
     359{
     360    GtkWidget * dialog;
     361    va_list     ap;
     362
     363    va_start( ap, format );
     364    dialog = verrmsg_full( wind, func, data, format, ap );
     365    va_end( ap );
     366
     367    return dialog;
    321368}
    322369
    323370GtkWidget *
    324 errmsg_full(GtkWindow *wind, callbackfunc_t func, void *data,
    325             const char *format, ...) {
    326   GtkWidget *dialog;
    327   va_list ap;
    328 
    329   va_start(ap, format);
    330   dialog = verrmsg(wind, func, data, format, ap);
    331   va_end(ap);
    332 
    333   return dialog;
    334 }
    335 
    336 GtkWidget *
    337 verrmsg(GtkWindow *wind, callbackfunc_t func, void *data,
    338         const char *format, va_list ap) {
     371verrmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
     372              const char * format, va_list ap )
     373{
    339374  GtkWidget *dialog;
    340375  char *msg;
     
    356391    funcdata = g_list_append(g_list_append(NULL, func), data);
    357392  g_signal_connect(dialog, "response", G_CALLBACK(errcb), funcdata);
    358   if(NULL != wind)
    359     gtk_widget_show(dialog);
    360393  g_free(msg);
    361394
  • trunk/gtk/util.h

    r1475 r1504  
    3939
    4040/* return number of items in array */
    41 #define ALEN(a)                 (sizeof(a) / sizeof((a)[0]))
    42 
    43 #define ISA(o, t)               (g_type_is_a(G_OBJECT_TYPE(G_OBJECT(o)), (t)))
     41#define ALEN( a )               ( ( signed )( sizeof(a) / sizeof( (a)[0] ) ) )
    4442
    4543/* used for a callback function with a data parameter */
    4644typedef void (*callbackfunc_t)(void*);
     45
     46/* flags indicating where and when an action is valid */
     47#define ACTF_TOOL       ( 1 << 0 ) /* appear in the toolbar */
     48#define ACTF_MENU       ( 1 << 1 ) /* appear in the popup menu */
     49#define ACTF_ALWAYS     ( 1 << 2 ) /* available regardless of selection */
     50#define ACTF_ACTIVE     ( 1 << 3 ) /* available for active torrent */
     51#define ACTF_INACTIVE   ( 1 << 4 ) /* available for inactive torrent */
     52/* appear in the toolbar and the popup menu */
     53#define ACTF_WHEREVER   ( ACTF_TOOL | ACTF_MENU )
     54/* available if there is something selected */
     55#define ACTF_WHATEVER   ( ACTF_ACTIVE | ACTF_INACTIVE )
     56
     57/* checking action flags against torrent status */
     58#define ACT_ISAVAIL( flags, status ) \
     59    ( ( ACTF_ACTIVE   & (flags) && TR_STATUS_ACTIVE   & (status) ) || \
     60      ( ACTF_INACTIVE & (flags) && TR_STATUS_INACTIVE & (status) ) || \
     61        ACTF_ALWAYS   & (flags) )
     62
     63/* column names for the model used to store torrent information */
     64enum {
     65  MC_NAME, MC_SIZE, MC_STAT, MC_ERR, MC_TERR,
     66  MC_PROG, MC_DRATE, MC_URATE, MC_ETA, MC_PEERS,
     67  MC_UPEERS, MC_DPEERS, MC_DOWN, MC_UP,
     68  MC_TORRENT, MC_ROW_COUNT,
     69};
    4770
    4871/* try to interpret a string as a textual representation of a boolean */
     
    102125makeglist(void *ptr, ...);
    103126
     127/* retrieve the global download directory */
     128const char *
     129getdownloaddir( void );
     130
    104131#ifdef GTK_MAJOR_VERSION
    105132
    106 /* if wind is NULL then you must call gtk_widget_show on the returned widget */
     133/* create an error dialog, if wind is NULL or mapped then show dialog now,
     134   otherwise show it when wind becomes mapped */
     135void
     136errmsg( GtkWindow * wind, const char * format, ... )
     137#ifdef __GNUC__
     138    __attribute__ (( format ( printf, 2, 3 ) ))
     139#endif
     140    ;
    107141
     142/* create an error dialog but do not gtk_widget_show() it,
     143   calls func( data ) when the dialog is closed */
    108144GtkWidget *
    109 errmsg(GtkWindow *wind, const char *format, ...)
     145errmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
     146             const char * format, ... )
    110147#ifdef __GNUC__
    111   __attribute__ ((format (printf, 2, 3)))
     148    __attribute__ (( format ( printf, 4, 5 ) ))
    112149#endif
    113   ;
     150    ;
    114151
     152/* varargs version of errmsg_full() */
    115153GtkWidget *
    116 errmsg_full(GtkWindow *wind, callbackfunc_t func, void *data,
    117             const char *format, ...)
    118 #ifdef __GNUC__
    119   __attribute__ ((format (printf, 4, 5)))
    120 #endif
    121   ;
    122 
    123 GtkWidget *
    124 verrmsg(GtkWindow *wind, callbackfunc_t func, void *data,
    125         const char *format, va_list ap);
     154verrmsg_full( GtkWindow * wind, callbackfunc_t func, void * data,
     155              const char * format, va_list ap );
    126156
    127157#endif /* GTK_MAJOR_VERSION */
  • trunk/mk/gtk.mk

    r1475 r1504  
    44include ../mk/common.mk
    55
    6 SRCS = conf.c dialogs.c io.c ipc.c main.c msgwin.c tr_backend.c tr_torrent.c \
    7         tr_cell_renderer_progress.c tr_window.c util.c
     6SRCS = conf.c dialogs.c io.c ipc.c main.c msgwin.c tr_backend.c \
     7        tr_cell_renderer_progress.c tr_icon.c tr_prefs.c tr_torrent.c \
     8        tr_window.c util.c
    89OBJS = $(SRCS:%.c=%.o)
    910
Note: See TracChangeset for help on using the changeset viewer.