Changeset 1504
- Timestamp:
- Feb 19, 2007, 10:09:05 PM (16 years ago)
- Location:
- trunk
- Files:
-
- 17 edited
- 4 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/gtk/conf.c
r920 r1504 2 2 * $Id$ 3 3 * 4 * Copyright (c) 2005-200 6Transmission authors and contributors4 * Copyright (c) 2005-2007 Transmission authors and contributors 5 5 * 6 6 * Permission is hereby granted, free of charge, to any person obtaining a … … 501 501 502 502 void 503 cf_freestate(benc_val_t *state) { 504 tr_bencFree(state); 505 g_free(state); 506 } 503 cf_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 50 50 cf_freestate(benc_val_t *state); 51 51 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 65 52 #endif /* TG_CONF_H */ -
trunk/gtk/dialogs.c
r1468 r1504 2 2 * $Id$ 3 3 * 4 * Copyright (c) 2005-200 6Transmission authors and contributors4 * Copyright (c) 2005-2007 Transmission authors and contributors 5 5 * 6 6 * Permission is hereby granted, free of charge, to any person obtaining a … … 32 32 #include "conf.h" 33 33 #include "dialogs.h" 34 #include "tr_icon.h" 35 #include "tr_prefs.h" 36 #include "util.h" 37 34 38 #include "transmission.h" 35 #include "util.h"36 39 37 40 #define PREFNAME "transmission-dialog-pref-name" 38 39 /* default values for a couple prefs */40 #define DEF_DOWNLIMIT 10041 #define DEF_USEDOWNLIMIT FALSE42 #define DEF_UPLIMIT 2043 #define DEF_USEUPLIMIT TRUE44 #define DEF_ASKDIR FALSE45 #define DEF_NAT TRUE46 47 struct prefdata {48 GList *prefwidgets;49 GtkWindow *parent;50 TrBackend *back;51 GtkTooltips * tips;52 };53 41 54 42 struct addcb { … … 69 57 }; 70 58 71 st atic void72 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); 59 struct quitdata 60 { 61 callbackfunc_t func; 62 void * cbdata; 63 }; 64 77 65 static void 78 66 autoclick(GtkWidget *widget, gpointer gdata); … … 83 71 static void 84 72 promptresp( 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 } 73 static void 74 quitresp( GtkWidget * widget, gint resp, gpointer data ); 480 75 481 76 void … … 495 90 GtkWidget *getdir = gtk_file_chooser_button_new( 496 91 _("Choose a download directory"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); 497 const char *pref;498 92 499 93 data->addfunc = addfunc; … … 512 106 513 107 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() ); 516 110 517 111 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dircheck), FALSE); … … 567 161 stupidgtk = g_list_append(stupidgtk, ii->data); 568 162 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 ) ); 570 164 data->addfunc( data->data, NULL, stupidgtk, dir, flags ); 571 165 if(NULL != dir) … … 694 288 void 695 289 promptfordir( GtkWindow * parent, add_torrents_func_t addfunc, void *cbdata, 696 GList * files, guint flags , const char * defaultdir)290 GList * files, guint flags ) 697 291 { 698 292 struct dirdata * stuff; … … 712 306 gtk_file_chooser_set_local_only( GTK_FILE_CHOOSER( wind ), TRUE ); 713 307 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() ); 715 310 716 311 g_signal_connect( G_OBJECT( wind ), "response", … … 741 336 gtk_widget_destroy( widget ); 742 337 } 338 339 void 340 askquit( 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 360 static void 361 quitresp( 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 48 48 void 49 49 promptfordir( 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 */ 53 void 54 askquit( GtkWindow * parent, callbackfunc_t func, void * cbdata ); 51 55 52 56 #endif /* TG_PREFS_H */ -
trunk/gtk/ipc.c
r920 r1504 2 2 * $Id$ 3 3 * 4 * Copyright (c) 2006 Transmission authors and contributors4 * Copyright (c) 2006-2007 Transmission authors and contributors 5 5 * 6 6 * Permission is hereby granted, free of charge, to any person obtaining a … … 41 41 #include "io.h" 42 42 #include "ipc.h" 43 #include "tr_prefs.h" 43 44 #include "util.h" 44 45 … … 49 50 /* list of strings, full paths to torrent files to load */ 50 51 #define MSG_ADDFILES ("addfiles") 51 52 enum contype { CON_SERV, CON_ADDFILE }; 52 /* request that the server quit */ 53 #define MSG_QUIT ("quit") 54 55 enum contype { CON_SERV, CON_CLIENT }; 53 56 54 57 struct constate_serv { 55 58 void *wind; 56 59 add_torrents_func_t addfunc; 60 callbackfunc_t quitfunc; 57 61 void *cbdata; 58 62 }; 59 63 60 struct constate_addfile { 64 enum client_cmd { CCMD_ADD, CCMD_QUIT }; 65 66 struct constate_client { 61 67 GMainLoop *loop; 68 enum client_cmd cmd; 62 69 GList *files; 63 70 gboolean *succeeded; 64 unsigned int addid;71 unsigned int msgid; 65 72 }; 66 73 … … 76 83 union { 77 84 struct constate_serv serv; 78 struct constate_ addfile addfile;85 struct constate_client client; 79 86 } u; 80 87 }; 81 88 82 void83 ipc_socket_setup(void *parent, add_torrents_func_t addfunc, void *cbdata);84 gboolean85 ipc_sendfiles_blocking(GList *files);86 89 static void 87 90 serv_bind(struct constate *con); … … 106 109 srv_addfile(struct constate *con, const char *name, benc_val_t *val); 107 110 static void 111 srv_quit( struct constate * con, const char * name, benc_val_t * val ); 112 static void 108 113 afc_version(struct constate *con, const char *name, benc_val_t *val); 109 114 static void … … 112 117 static const struct handlerdef gl_funcs_serv[] = { 113 118 {MSG_ADDFILES, srv_addfile}, 119 {MSG_QUIT, srv_quit}, 114 120 {NULL, NULL} 115 121 }; 116 122 117 static const struct handlerdef gl_funcs_ addfile[] = {123 static const struct handlerdef gl_funcs_client[] = { 118 124 {MSG_VERSION, afc_version}, 119 125 {NULL, NULL} … … 124 130 125 131 void 126 ipc_socket_setup(void *parent, add_torrents_func_t addfunc, void *cbdata) { 132 ipc_socket_setup( void * parent, add_torrents_func_t addfunc, 133 callbackfunc_t quitfunc, void * cbdata ) 134 { 127 135 struct constate *con; 128 136 … … 134 142 con->u.serv.wind = parent; 135 143 con->u.serv.addfunc = addfunc; 144 con->u.serv.quitfunc = quitfunc; 136 145 con->u.serv.cbdata = cbdata; 137 146 … … 139 148 } 140 149 141 gboolean 142 ipc_sendfiles_blocking(GList *files) { 150 static gboolean 151 blocking_client( enum client_cmd cmd, GList * files ) 152 { 153 143 154 struct constate *con; 144 155 char *path; … … 148 159 con->source = NULL; 149 160 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; 156 168 157 169 path = cf_sockname(); … … 162 174 } 163 175 164 g_main_loop_run(con->u. addfile.loop);176 g_main_loop_run(con->u.client.loop); 165 177 166 178 return ret; 179 } 180 181 gboolean 182 ipc_sendfiles_blocking( GList * files ) 183 { 184 return blocking_client( CCMD_ADD, files ); 185 } 186 187 gboolean 188 ipc_sendquit_blocking( void ) 189 { 190 return blocking_client( CCMD_QUIT, NULL ); 167 191 } 168 192 … … 357 381 case CON_SERV: 358 382 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); 362 386 break; 363 387 } … … 376 400 GList *files; 377 401 int ii; 402 guint flags; 378 403 379 404 if(TYPE_LIST == val->type) { … … 384 409 g_utf8_validate(val->val.l.vals[ii].val.s.s, -1, NULL)) 385 410 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 ); 388 413 g_list_free(files); 389 414 } … … 391 416 392 417 static void 418 srv_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 427 static void 393 428 afc_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; 395 430 GList *file; 396 431 benc_val_t list, *str; … … 399 434 fprintf(stderr, _("bad IPC protocol version\n")); 400 435 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; 419 465 } 420 466 } … … 422 468 static void 423 469 afc_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) { 427 473 *(afc->succeeded) = TRUE; 428 474 destroycon(vdata); -
trunk/gtk/ipc.h
r760 r1504 2 2 * $Id$ 3 3 * 4 * Copyright (c) 2006 Transmission authors and contributors4 * Copyright (c) 2006-2007 Transmission authors and contributors 5 5 * 6 6 * Permission is hereby granted, free of charge, to any person obtaining a … … 29 29 30 30 void 31 ipc_socket_setup(void *wind, add_torrents_func_t addfunc, void *cbdata); 31 ipc_socket_setup( void * wind, add_torrents_func_t addfunc, 32 callbackfunc_t quitfunc, void * cbdata ); 32 33 33 34 gboolean 34 ipc_sendfiles_blocking(GList *files); 35 ipc_sendfiles_blocking( GList * files ); 36 37 gboolean 38 ipc_sendquit_blocking( void ); 35 39 36 40 #endif /* TG_IPC_H */ -
trunk/gtk/main.c
r1475 r1504 25 25 #include <sys/param.h> 26 26 #include <errno.h> 27 #include <getopt.h> 27 28 #include <signal.h> 28 29 #include <string.h> … … 41 42 #include "msgwin.h" 42 43 #include "tr_backend.h" 44 #include "tr_cell_renderer_progress.h" 45 #include "tr_icon.h" 46 #include "tr_prefs.h" 43 47 #include "tr_torrent.h" 44 #include "tr_cell_renderer_progress.h"45 48 #include "tr_window.h" 49 #include "util.h" 50 46 51 #include "transmission.h" 47 #include "util.h"48 52 49 53 #include "img_icon_full.h" … … 58 62 #define EXIT_CHECK_INTERVAL 500 59 63 64 /* number of fatal signals required to cause an immediate exit */ 65 #define SIGCOUNT_MAX 3 66 60 67 struct 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; 68 76 }; 69 77 70 78 struct exitdata { 71 struct cbdata *cbdata;72 time_tstarted;73 guinttimer;79 struct cbdata * cbdata; 80 time_t started; 81 guint timer; 74 82 }; 75 83 … … 83 91 ACT_PREF, 84 92 ACT_DEBUG, 93 ACT_ICON, 85 94 ACTION_COUNT, 86 95 }; … … 109 118 { N_("Open debug window"), NULL, ACTF_MENU | ACTF_ALWAYS, 110 119 NULL }, 120 /* this isn't a terminator for the list, it's ACT_ICON */ 121 { NULL, NULL, 0, NULL }, 111 122 }; 112 123 113 124 #define CBDATA_PTR "callback-data-pointer" 114 125 115 #define SIGCOUNT_MAX 3116 117 126 static sig_atomic_t global_sigcount = 0; 118 127 119 128 static 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); 129 readargs( int argc, char ** argv, gboolean * sendquit, gboolean * paused ); 126 130 static gboolean 127 winclose(GtkWidget *widget, GdkEvent *event, gpointer gdata); 131 sendremote( GList * files, gboolean sendquit ); 132 static void 133 gtksetup( int * argc, char *** argv ); 134 static void 135 appsetup( TrWindow * wind, benc_val_t * state, GList * args, gboolean paused ); 136 static void 137 winsetup( struct cbdata * cbdata, TrWindow * wind ); 138 static void 139 remakewind( struct cbdata * cbdata ); 140 static void 141 makeicon( struct cbdata * cbdata ); 142 static gboolean 143 winclose( GtkWidget * widget, GdkEvent * event, gpointer gdata ); 144 static void 145 wannaquit( void * vdata ); 128 146 static gboolean 129 147 exitcheck(gpointer gdata); … … 134 152 GtkSelectionData *sel, guint info, guint time, gpointer gdata); 135 153 154 static void 155 readinitialprefs( struct cbdata * cbdata ); 156 static void 157 prefschanged( GtkWidget * widget, int id, gpointer data ); 136 158 static gboolean 137 159 updatemodel(gpointer gdata); 138 160 static void 139 boolwindclosed(GtkWidget *widget SHUTUP, gpointer gdata);161 boolwindclosed(GtkWidget *widget, gpointer gdata); 140 162 static void 141 163 windact(GtkWidget *widget, int action, gpointer gdata); 164 static GList * 165 getselection( struct cbdata * cbdata ); 142 166 static void 143 167 handleaction(struct cbdata *data, enum action action); … … 156 180 157 181 int 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) 182 main( 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 253 233 freestrlist(argfiles); 254 234 255 gtk_main();256 257 return 0;235 gtk_main(); 236 237 return 0; 258 238 } 259 239 260 240 GList * 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; 241 readargs( 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 296 static gboolean 297 sendremote( 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 325 static void 326 gtksetup( 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 353 static void 354 appsetup( 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 412 static void 413 winsetup( 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 437 static void 438 remakewind( 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 455 static void 456 makeicon( 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 472 static gboolean 473 winclose( 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 493 static void 494 wannaquit( void * vdata ) 495 { 496 struct cbdata * data; 368 497 struct exitdata *edata; 369 498 GtkTreeIter iter; 370 499 TrTorrent *tor; 371 500 501 data = vdata; 502 if( data->closing ) 503 { 504 return; 505 } 372 506 data->closing = TRUE; 373 507 … … 403 537 /* yes, start the exit timer and disable widgets */ 404 538 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 546 static gboolean 547 exitcheck( 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; 435 601 } 436 602 … … 507 673 508 674 /* 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 } 512 680 freestrlist(freeables); 513 681 g_free(files); … … 529 697 gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_ALL, targets, 530 698 ALEN(targets), GDK_ACTION_COPY | GDK_ACTION_MOVE); 699 } 700 701 static void 702 readinitialprefs( 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 720 static void 721 prefschanged( 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 } 531 780 } 532 781 … … 540 789 float up, down; 541 790 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; 545 795 } 546 796 … … 563 813 564 814 /* 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 } 567 820 568 821 /* 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 } 571 826 572 827 /* update the message window */ … … 590 845 } 591 846 847 /* returns a GList containing a GtkTreeRowReference to each selected row */ 848 static GList * 849 getselection( 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 592 871 static void 593 872 handleaction( struct cbdata * data, enum action act ) 594 873 { 595 GtkTreeSelection *sel;596 874 GList *rows, *ii; 597 GtkTreeRowReference *ref;598 875 GtkTreePath *path; 599 876 GtkTreeIter iter; … … 611 888 return; 612 889 case ACT_PREF: 613 if( !data->prefsopen)890 if( NULL != data->prefs ) 614 891 { 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; 619 893 } 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 ) ); 620 900 return; 621 901 case ACT_DEBUG: … … 628 908 } 629 909 return; 910 case ACT_ICON: 911 remakewind( data ); 912 return; 630 913 case ACT_START: 631 914 case ACT_STOP: … … 637 920 638 921 /* 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 ); 646 923 647 924 changed = FALSE; … … 682 959 case ACT_PREF: 683 960 case ACT_DEBUG: 961 case ACT_ICON: 684 962 case ACTION_COUNT: 685 963 break; … … 698 976 updatemodel(data); 699 977 } 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;723 978 } 724 979 … … 736 991 torlist = NULL; 737 992 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 } 740 997 741 998 if(NULL != files) { 742 999 if( NULL == dir ) 743 1000 { 744 pref = cf_getpref( PREF_ASKDIR );1001 pref = tr_prefs_get( PREF_ID_ASKDIR ); 745 1002 if( NULL != pref && strbool( pref ) ) 746 1003 { 747 promptfordir( data->wind, addtorrents, data, 748 files, flags, defaultdir() ); 1004 promptfordir( data->wind, addtorrents, data, files, flags ); 749 1005 files = NULL; 750 1006 } 751 dir = defaultdir();1007 dir = getdownloaddir(); 752 1008 } 753 1009 for(ii = g_list_first(files); NULL != ii; ii = ii->next) { … … 774 1030 if(NULL != errlist) { 775 1031 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 ); 779 1035 g_list_foreach(errlist, (GFunc)g_free, NULL); 780 1036 g_list_free(errlist); … … 812 1068 static void 813 1069 setupsighandlers(void) { 814 int sigs[] = {SIGHUP, SIGINT, SIGQUIT, SIGTERM , SIGUSR1, SIGUSR2};1070 int sigs[] = {SIGHUP, SIGINT, SIGQUIT, SIGTERM}; 815 1071 struct sigaction sa; 816 unsignedint ii;1072 int ii; 817 1073 818 1074 bzero(&sa, sizeof(sa)); -
trunk/gtk/msgwin.c
r866 r1504 2 2 * $Id$ 3 3 * 4 * Copyright (c) 2006 Transmission authors and contributors4 * Copyright (c) 2006-2007 Transmission authors and contributors 5 5 * 6 6 * Permission is hereby granted, free of charge, to any person obtaining a … … 31 31 #include "conf.h" 32 32 #include "msgwin.h" 33 #include "tr_prefs.h" 33 34 #include "transmission.h" 34 35 #include "util.h" … … 61 62 GtkWidget * frame, * bbox, * save, * clear, * menu; 62 63 PangoFontDescription * desc; 63 unsigned int ii; 64 int curlevel; 64 int ii, curlevel; 65 65 66 66 if( NULL == textbuf ) … … 127 127 tr_getMessageLevel() != levels[index].id ) { 128 128 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 ); 130 130 cf_saveprefs( &ignored ); 131 131 g_free( ignored ); … … 197 197 msgwin_loadpref( void ) { 198 198 const char * pref; 199 unsignedint ii;199 int ii; 200 200 201 201 tr_setMessageQueuing( 1 ); 202 pref = cf_getpref( PREF_MSGLEVEL );202 pref = tr_prefs_get( PREF_ID_MSGLEVEL ); 203 203 if( NULL == pref ) 204 204 return; … … 217 217 GtkTextIter iter, front; 218 218 char * label, * line; 219 int count ;219 int count, jj; 220 220 struct tm * tm; 221 unsigned int jj;222 221 223 222 if( NULL == textbuf ) -
trunk/gtk/tr_backend.c
r1190 r1504 2 2 * $Id$ 3 3 * 4 * Copyright (c) 2006 Transmission authors and contributors4 * Copyright (c) 2006-2007 Transmission authors and contributors 5 5 * 6 6 * Permission is hereby granted, free of charge, to any person obtaining a … … 221 221 222 222 GList * 223 tr_backend_load_state(TrBackend *back, benc_val_t *state, GList **errors) { 223 tr_backend_load_state( TrBackend * back, benc_val_t * state, 224 guint flags, GList ** errors ) 225 { 224 226 GList *ret = NULL; 225 227 int ii; … … 234 236 for(ii = 0; ii < state->val.l.count; ii++) { 235 237 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 ); 238 240 if(NULL != errstr) 239 241 *errors = g_list_append(*errors, errstr); … … 274 276 275 277 gboolean 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 } 278 tr_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 2 2 * $Id$ 3 3 * 4 * Copyright (c) 2006 Transmission authors and contributors4 * Copyright (c) 2006-2007 Transmission authors and contributors 5 5 * 6 6 * Permission is hereby granted, free of charge, to any person obtaining a … … 71 71 72 72 GList * 73 tr_backend_load_state(TrBackend *back, benc_val_t *state, GList **errors); 73 tr_backend_load_state( TrBackend * back, benc_val_t * state, 74 guint flags, GList ** errors ); 74 75 75 76 void … … 77 78 78 79 gboolean 79 tr_backend_torrents_stopped( TrBackend *back);80 tr_backend_torrents_stopped( TrBackend * back, gboolean timeout ); 80 81 81 82 #ifdef TR_WANT_BACKEND_PRIVATE -
trunk/gtk/tr_torrent.c
r1475 r1504 309 309 310 310 TrTorrent * 311 tr_torrent_new_with_state(GObject *backend, benc_val_t *state, char **err) { 311 tr_torrent_new_with_state( GObject * backend, benc_val_t * state, 312 guint forcedflags, char ** err) 313 { 312 314 int ii; 313 315 benc_val_t *name, *data; … … 354 356 torrent = hash; 355 357 } 358 forcedflags &= TR_TORNEW_PAUSED | TR_TORNEW_RUNNING; 359 if( forcedflags ) 360 { 361 flags &= ~( TR_TORNEW_PAUSED | TR_TORNEW_RUNNING ); 362 flags |= forcedflags; 363 } 356 364 357 365 return tr_torrent_new(backend, torrent, dir, flags, err); … … 449 457 450 458 tr_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 } 459 tr_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 2 2 * $Id$ 3 3 * 4 * Copyright (c) 2006 Transmission authors and contributors4 * Copyright (c) 2006-2007 Transmission authors and contributors 5 5 * 6 6 * Permission is hereby granted, free of charge, to any person obtaining a … … 89 89 90 90 TrTorrent * 91 tr_torrent_new_with_state(GObject *backend, benc_val_t *state, char **err); 91 tr_torrent_new_with_state( GObject * backend, benc_val_t * state, 92 guint flags, char ** err ); 92 93 93 94 void … … 95 96 96 97 tr_stat_t * 97 tr_torrent_stat_polite( TrTorrent *tor);98 tr_torrent_stat_polite( TrTorrent * tor, gboolean timeout ); 98 99 99 100 #ifdef TR_WANT_TORRENT_PRIVATE -
trunk/gtk/tr_window.h
r1475 r1504 91 91 92 92 /* some evil magic to show the window with a nice initial window size */ 93 /* note that the gtk main loop runs in this function */ 93 94 void 94 95 tr_window_size_hack( TrWindow * wind ); 95 96 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 122 97 #endif -
trunk/gtk/transmission-gtk.desktop
r1470 r1504 4 4 Name=Transmission 5 5 Name[ru]=ÐеÑеЎаÑа 6 GenericName=BitTorrent Client7 6 Type=Application 8 Comment=A free, lightweight client with a simple, intuitive interface7 Comment=A free, lightweight BitTorrent client with a simple, intuitive interface 9 8 Exec=transmission-gtk %F 10 9 TryExec=transmission-gtk -
trunk/gtk/util.c
r1468 r1504 2 2 * $Id$ 3 3 * 4 * Copyright (c) 2005-200 6Transmission authors and contributors4 * Copyright (c) 2005-2007 Transmission authors and contributors 5 5 * 6 6 * Permission is hereby granted, free of charge, to any person obtaining a … … 34 34 #include <glib/gi18n.h> 35 35 36 #include "tr_prefs.h" 36 37 #include "tr_torrent.h" 37 38 #include "util.h" … … 66 67 char * 67 68 readablesize(guint64 size) { 68 unsignedint ii;69 int ii; 69 70 double small = size; 70 71 … … 309 310 } 310 311 312 const char * 313 getdownloaddir( 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 335 void 336 errmsg( 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 311 356 GtkWidget * 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; 357 errmsg_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; 321 368 } 322 369 323 370 GtkWidget * 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) { 371 verrmsg_full( GtkWindow * wind, callbackfunc_t func, void * data, 372 const char * format, va_list ap ) 373 { 339 374 GtkWidget *dialog; 340 375 char *msg; … … 356 391 funcdata = g_list_append(g_list_append(NULL, func), data); 357 392 g_signal_connect(dialog, "response", G_CALLBACK(errcb), funcdata); 358 if(NULL != wind)359 gtk_widget_show(dialog);360 393 g_free(msg); 361 394 -
trunk/gtk/util.h
r1475 r1504 39 39 40 40 /* 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] ) ) ) 44 42 45 43 /* used for a callback function with a data parameter */ 46 44 typedef 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 */ 64 enum { 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 }; 47 70 48 71 /* try to interpret a string as a textual representation of a boolean */ … … 102 125 makeglist(void *ptr, ...); 103 126 127 /* retrieve the global download directory */ 128 const char * 129 getdownloaddir( void ); 130 104 131 #ifdef GTK_MAJOR_VERSION 105 132 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 */ 135 void 136 errmsg( GtkWindow * wind, const char * format, ... ) 137 #ifdef __GNUC__ 138 __attribute__ (( format ( printf, 2, 3 ) )) 139 #endif 140 ; 107 141 142 /* create an error dialog but do not gtk_widget_show() it, 143 calls func( data ) when the dialog is closed */ 108 144 GtkWidget * 109 errmsg(GtkWindow *wind, const char *format, ...) 145 errmsg_full( GtkWindow * wind, callbackfunc_t func, void * data, 146 const char * format, ... ) 110 147 #ifdef __GNUC__ 111 __attribute__ ((format (printf, 2, 3)))148 __attribute__ (( format ( printf, 4, 5 ) )) 112 149 #endif 113 ;150 ; 114 151 152 /* varargs version of errmsg_full() */ 115 153 GtkWidget * 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); 154 verrmsg_full( GtkWindow * wind, callbackfunc_t func, void * data, 155 const char * format, va_list ap ); 126 156 127 157 #endif /* GTK_MAJOR_VERSION */ -
trunk/mk/gtk.mk
r1475 r1504 4 4 include ../mk/common.mk 5 5 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 6 SRCS = 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 8 9 OBJS = $(SRCS:%.c=%.o) 9 10
Note: See TracChangeset
for help on using the changeset viewer.