2 * This file Copyright (C) 2007-2010 Mnemosyne LLC
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
10 * $Id: tr-prefs.c 11401 2010-11-13 17:05:22Z charles $
13 #include <ctype.h> /* isspace */
14 #include <limits.h> /* USHRT_MAX */
15 #include <stdlib.h> /* free() */
17 #include <glib/gi18n.h>
19 #include <libtransmission/transmission.h>
20 #include <libtransmission/utils.h>
21 #include <libtransmission/version.h>
22 #include <libtransmission/web.h>
33 #define PREF_KEY "pref-key"
36 response_cb( GtkDialog
* dialog
,
38 gpointer unused UNUSED
)
40 if( response
== GTK_RESPONSE_HELP
)
42 char * uri
= g_strconcat( gtr_get_help_uri(), "/html/preferences.html", NULL
);
47 if( response
== GTK_RESPONSE_CLOSE
)
48 gtk_widget_destroy( GTK_WIDGET( dialog
) );
52 toggled_cb( GtkToggleButton
* w
,
55 const char * key
= g_object_get_data( G_OBJECT( w
), PREF_KEY
);
56 const gboolean flag
= gtk_toggle_button_get_active( w
);
58 tr_core_set_pref_bool( TR_CORE( core
), key
, flag
);
62 new_check_button( const char * mnemonic
,
66 GtkWidget
* w
= gtk_check_button_new_with_mnemonic( mnemonic
);
68 g_object_set_data_full( G_OBJECT( w
), PREF_KEY
, g_strdup(
70 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w
),
71 pref_flag_get( key
) );
72 g_signal_connect( w
, "toggled", G_CALLBACK( toggled_cb
), core
);
76 #define IDLE_DATA "idle-data"
86 spin_idle_data_free( gpointer gdata
)
88 struct spin_idle_data
* data
= gdata
;
90 g_timer_destroy( data
->last_change
);
95 spun_cb_idle( gpointer spin
)
97 gboolean keep_waiting
= TRUE
;
98 GObject
* o
= G_OBJECT( spin
);
99 struct spin_idle_data
* data
= g_object_get_data( o
, IDLE_DATA
);
101 /* has the user stopped making changes? */
102 if( g_timer_elapsed( data
->last_change
, NULL
) > 0.33f
)
104 /* update the core */
105 const char * key
= g_object_get_data( o
, PREF_KEY
);
109 const double value
= gtk_spin_button_get_value( GTK_SPIN_BUTTON( spin
) );
110 tr_core_set_pref_double( TR_CORE( data
->core
), key
, value
);
114 const int value
= gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( spin
) );
115 tr_core_set_pref_int( TR_CORE( data
->core
), key
, value
);
119 g_object_set_data( o
, IDLE_DATA
, NULL
);
120 keep_waiting
= FALSE
;
121 g_object_unref( G_OBJECT( o
) );
128 spun_cb( GtkSpinButton
* w
, gpointer core
, gboolean isDouble
)
130 /* user may be spinning through many values, so let's hold off
131 for a moment to keep from flooding the core with changes */
132 GObject
* o
= G_OBJECT( w
);
133 struct spin_idle_data
* data
= g_object_get_data( o
, IDLE_DATA
);
137 data
= g_new( struct spin_idle_data
, 1 );
139 data
->last_change
= g_timer_new( );
140 data
->isDouble
= isDouble
;
141 g_object_set_data_full( o
, IDLE_DATA
, data
, spin_idle_data_free
);
142 g_object_ref( G_OBJECT( o
) );
143 gtr_timeout_add_seconds( 1, spun_cb_idle
, w
);
145 g_timer_start( data
->last_change
);
149 spun_cb_int( GtkSpinButton
* w
, gpointer core
)
151 spun_cb( w
, core
, FALSE
);
155 spun_cb_double( GtkSpinButton
* w
, gpointer core
)
157 spun_cb( w
, core
, TRUE
);
161 new_spin_button( const char * key
,
167 GtkWidget
* w
= gtk_spin_button_new_with_range( low
, high
, step
);
168 g_object_set_data_full( G_OBJECT( w
), PREF_KEY
, g_strdup( key
), g_free
);
169 gtk_spin_button_set_digits( GTK_SPIN_BUTTON( w
), 0 );
170 gtk_spin_button_set_value( GTK_SPIN_BUTTON( w
), pref_int_get( key
) );
171 g_signal_connect( w
, "value-changed", G_CALLBACK( spun_cb_int
), core
);
176 new_spin_button_double( const char * key
,
182 GtkWidget
* w
= gtk_spin_button_new_with_range( low
, high
, step
);
183 g_object_set_data_full( G_OBJECT( w
), PREF_KEY
, g_strdup( key
), g_free
);
184 gtk_spin_button_set_digits( GTK_SPIN_BUTTON( w
), 2 );
185 gtk_spin_button_set_value( GTK_SPIN_BUTTON( w
), pref_double_get( key
) );
186 g_signal_connect( w
, "value-changed", G_CALLBACK( spun_cb_double
), core
);
191 entry_changed_cb( GtkEntry
* w
, gpointer core
)
193 const char * key
= g_object_get_data( G_OBJECT( w
), PREF_KEY
);
194 const char * value
= gtk_entry_get_text( w
);
196 tr_core_set_pref( TR_CORE( core
), key
, value
);
200 new_entry( const char * key
,
203 GtkWidget
* w
= gtk_entry_new( );
204 const char * value
= pref_string_get( key
);
207 gtk_entry_set_text( GTK_ENTRY( w
), value
);
208 g_object_set_data_full( G_OBJECT( w
), PREF_KEY
, g_strdup(
210 g_signal_connect( w
, "changed", G_CALLBACK( entry_changed_cb
), core
);
215 chosen_cb( GtkFileChooser
* w
, gpointer core
)
217 const char * key
= g_object_get_data( G_OBJECT( w
), PREF_KEY
);
218 char * value
= gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( w
) );
219 tr_core_set_pref( TR_CORE( core
), key
, value
);
224 new_path_chooser_button( const char * key
, gpointer core
)
226 GtkWidget
* w
= gtk_file_chooser_button_new( NULL
, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
);
227 const char * path
= pref_string_get( key
);
228 g_object_set_data_full( G_OBJECT( w
), PREF_KEY
, g_strdup( key
), g_free
);
229 g_signal_connect( w
, "selection-changed", G_CALLBACK( chosen_cb
), core
);
231 gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( w
), path
);
236 new_file_chooser_button( const char * key
, gpointer core
)
238 GtkWidget
* w
= gtk_file_chooser_button_new( NULL
, GTK_FILE_CHOOSER_ACTION_OPEN
);
239 const char * path
= pref_string_get( key
);
240 g_object_set_data_full( G_OBJECT( w
), PREF_KEY
, g_strdup( key
), g_free
);
242 gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( w
), path
);
243 g_signal_connect( w
, "selection-changed", G_CALLBACK( chosen_cb
), core
);
248 target_cb( GtkWidget
* tb
, gpointer target
)
250 const gboolean b
= gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( tb
) );
252 gtk_widget_set_sensitive( GTK_WIDGET( target
), b
);
260 torrentPage( GObject
* core
)
269 t
= hig_workarea_create( );
270 hig_workarea_add_section_title( t
, &row
, _( "Adding" ) );
273 s
= _( "Automatically _add torrents from:" );
274 l
= new_check_button( s
, PREF_KEY_DIR_WATCH_ENABLED
, core
);
275 w
= new_path_chooser_button( PREF_KEY_DIR_WATCH
, core
);
276 gtk_widget_set_sensitive( GTK_WIDGET( w
),
277 pref_flag_get( PREF_KEY_DIR_WATCH_ENABLED
) );
278 g_signal_connect( l
, "toggled", G_CALLBACK( target_cb
), w
);
279 hig_workarea_add_row_w( t
, &row
, l
, w
, NULL
);
282 s
= _( "Show _options dialog" );
283 w
= new_check_button( s
, PREF_KEY_OPTIONS_PROMPT
, core
);
284 hig_workarea_add_wide_control( t
, &row
, w
);
286 s
= _( "_Start when added" );
287 w
= new_check_button( s
, TR_PREFS_KEY_START
, core
);
288 hig_workarea_add_wide_control( t
, &row
, w
);
290 s
= _( "Mo_ve .torrent file to the trash" );
291 w
= new_check_button( s
, TR_PREFS_KEY_TRASH_ORIGINAL
, core
);
292 hig_workarea_add_wide_control( t
, &row
, w
);
294 hig_workarea_add_section_divider( t
, &row
);
295 hig_workarea_add_section_title( t
, &row
, _( "Downloading" ) );
297 s
= _( "Append \"._part\" to incomplete files' names" );
298 w
= new_check_button( s
, TR_PREFS_KEY_RENAME_PARTIAL_FILES
, core
);
299 hig_workarea_add_wide_control( t
, &row
, w
);
301 w
= new_path_chooser_button( TR_PREFS_KEY_DOWNLOAD_DIR
, core
);
302 hig_workarea_add_row( t
, &row
, _( "Save to _Location:" ), w
, NULL
);
304 s
= _( "Keep _incomplete torrents in:" );
305 l
= new_check_button( s
, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED
, core
);
306 w
= new_path_chooser_button( TR_PREFS_KEY_INCOMPLETE_DIR
, core
);
307 gtk_widget_set_sensitive( GTK_WIDGET( w
), pref_flag_get( TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED
) );
308 g_signal_connect( l
, "toggled", G_CALLBACK( target_cb
), w
);
309 hig_workarea_add_row_w( t
, &row
, l
, w
, NULL
);
311 s
= _( "Call scrip_t when torrent is completed:" );
312 l
= new_check_button( s
, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED
, core
);
313 w
= new_file_chooser_button( TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME
, core
);
314 gtk_widget_set_sensitive( GTK_WIDGET( w
), pref_flag_get( TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED
) );
315 g_signal_connect( l
, "toggled", G_CALLBACK( target_cb
), w
);
316 hig_workarea_add_row_w( t
, &row
, l
, w
, NULL
);
318 hig_workarea_add_section_divider( t
, &row
);
319 hig_workarea_add_section_title( t
, &row
, _( "Seeding Limits" ) );
321 s
= _( "Stop seeding at _ratio:" );
322 w
= new_check_button( s
, TR_PREFS_KEY_RATIO_ENABLED
, core
);
323 w2
= new_spin_button_double( TR_PREFS_KEY_RATIO
, core
, 0, 1000, .05 );
324 gtk_widget_set_sensitive( GTK_WIDGET( w2
), pref_flag_get( TR_PREFS_KEY_RATIO_ENABLED
) );
325 g_signal_connect( w
, "toggled", G_CALLBACK( target_cb
), w2
);
326 hig_workarea_add_row_w( t
, &row
, w
, w2
, NULL
);
328 s
= _( "Stop seeding if idle for _N minutes:" );
329 w
= new_check_button( s
, TR_PREFS_KEY_IDLE_LIMIT_ENABLED
, core
);
330 w2
= new_spin_button( TR_PREFS_KEY_IDLE_LIMIT
, core
, 1, 9999, 5 );
331 gtk_widget_set_sensitive( GTK_WIDGET( w2
), pref_flag_get( TR_PREFS_KEY_IDLE_LIMIT_ENABLED
) );
332 g_signal_connect( w
, "toggled", G_CALLBACK( target_cb
), w2
);
333 hig_workarea_add_row_w( t
, &row
, w
, w2
, NULL
);
335 hig_workarea_finish( t
, &row
);
344 desktopPage( GObject
* core
)
351 t
= hig_workarea_create( );
352 hig_workarea_add_section_title( t
, &row
, _( "Desktop" ) );
354 s
= _( "Inhibit _hibernation when torrents are active" );
355 w
= new_check_button( s
, PREF_KEY_INHIBIT_HIBERNATION
, core
);
356 hig_workarea_add_wide_control( t
, &row
, w
);
358 s
= _( "Show Transmission icon in the _notification area" );
359 w
= new_check_button( s
, PREF_KEY_SHOW_TRAY_ICON
, core
);
360 hig_workarea_add_wide_control( t
, &row
, w
);
362 s
= _( "Show _popup notifications" );
363 w
= new_check_button( s
, PREF_KEY_SHOW_DESKTOP_NOTIFICATION
, core
);
364 hig_workarea_add_wide_control( t
, &row
, w
);
366 #ifdef HAVE_LIBCANBERRA
367 s
= _( "Play _sound when downloads are complete" );
368 w
= new_check_button( s
, PREF_KEY_PLAY_DOWNLOAD_COMPLETE_SOUND
, core
);
369 hig_workarea_add_wide_control( t
, &row
, w
);
372 hig_workarea_finish( t
, &row
);
380 struct blocklist_data
382 gulong updateBlocklistTag
;
383 GtkWidget
* updateBlocklistButton
;
384 GtkWidget
* updateBlocklistDialog
;
391 updateBlocklistText( GtkWidget
* w
, TrCore
* core
)
395 const int n
= tr_blocklistGetRuleCount( tr_core_session( core
) );
396 g_snprintf( buf1
, sizeof( buf1
),
397 gtr_ngettext( "Blocklist contains %'d rule",
398 "Blocklist contains %'d rules", n
), n
);
399 g_snprintf( buf2
, sizeof( buf2
), "<i>%s</i>", buf1
);
400 gtk_label_set_markup( GTK_LABEL( w
), buf2
);
403 /* prefs dialog is being destroyed, so stop listening to blocklist updates */
405 privacyPageDestroyed( gpointer gdata
, GObject
* dead UNUSED
)
407 struct blocklist_data
* data
= gdata
;
408 if( data
->updateBlocklistTag
> 0 )
409 g_signal_handler_disconnect( data
->core
, data
->updateBlocklistTag
);
413 /* user hit "close" in the blocklist-update dialog */
415 onBlocklistUpdateResponse( GtkDialog
* dialog
, gint response UNUSED
, gpointer gdata
)
417 struct blocklist_data
* data
= gdata
;
418 gtk_widget_destroy( GTK_WIDGET( dialog
) );
419 gtk_widget_set_sensitive( data
->updateBlocklistButton
, TRUE
);
420 data
->updateBlocklistDialog
= NULL
;
421 g_signal_handler_disconnect( data
->core
, data
->updateBlocklistTag
);
424 /* core says the blocklist was updated */
426 onBlocklistUpdated( TrCore
* core
, int n
, gpointer gdata
)
428 const tr_bool success
= n
>= 0;
429 const int count
= n
>=0 ? n
: tr_blocklistGetRuleCount( tr_core_session( core
) );
430 const char * s
= gtr_ngettext( "Blocklist has %'d rule.", "Blocklist has %'d rules.", count
);
431 struct blocklist_data
* data
= gdata
;
432 GtkMessageDialog
* d
= GTK_MESSAGE_DIALOG( data
->updateBlocklistDialog
);
433 gtk_widget_set_sensitive( data
->updateBlocklistButton
, TRUE
);
434 gtk_message_dialog_set_markup( d
, success
? _( "<b>Update succeeded!</b>" ) : _( "<b>Unable to update.</b>" ) );
435 gtk_message_dialog_format_secondary_text( d
, s
, count
);
436 updateBlocklistText( data
->label
, core
);
439 /* user pushed a button to update the blocklist */
441 onBlocklistUpdate( GtkButton
* w
, gpointer gdata
)
444 struct blocklist_data
* data
= gdata
;
445 d
= gtk_message_dialog_new( GTK_WINDOW( gtk_widget_get_toplevel( GTK_WIDGET( w
) ) ),
446 GTK_DIALOG_DESTROY_WITH_PARENT
,
449 "%s", _( "Update Blocklist" ) );
450 gtk_widget_set_sensitive( data
->updateBlocklistButton
, FALSE
);
451 gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( d
), "%s", _( "Getting new blocklist..." ) );
452 data
->updateBlocklistDialog
= d
;
453 g_signal_connect( d
, "response", G_CALLBACK(onBlocklistUpdateResponse
), data
);
454 gtk_widget_show( d
);
455 tr_core_blocklist_update( data
->core
);
456 data
->updateBlocklistTag
= g_signal_connect( data
->core
, "blocklist-updated", G_CALLBACK( onBlocklistUpdated
), data
);
460 onIntComboChanged( GtkComboBox
* combo_box
, gpointer core
)
462 const int val
= gtr_combo_box_get_active_enum( combo_box
);
463 const char * key
= g_object_get_data( G_OBJECT( combo_box
), PREF_KEY
);
464 tr_core_set_pref_int( TR_CORE( core
), key
, val
);
468 new_encryption_combo( GObject
* core
, const char * key
)
470 GtkWidget
* w
= gtr_combo_box_new_enum( _( "Allow encryption" ), TR_CLEAR_PREFERRED
,
471 _( "Prefer encryption" ), TR_ENCRYPTION_PREFERRED
,
472 _( "Require encryption" ), TR_ENCRYPTION_REQUIRED
,
474 gtr_combo_box_set_active_enum( GTK_COMBO_BOX( w
), pref_int_get( key
) );
475 g_object_set_data_full( G_OBJECT( w
), PREF_KEY
, tr_strdup( key
), g_free
);
476 g_signal_connect( w
, "changed", G_CALLBACK( onIntComboChanged
), core
);
481 privacyPage( GObject
* core
)
490 struct blocklist_data
* data
;
492 data
= g_new0( struct blocklist_data
, 1 );
493 data
->core
= TR_CORE( core
);
495 t
= hig_workarea_create( );
496 hig_workarea_add_section_title( t
, &row
, _( "Blocklist" ) );
498 b
= new_check_button( _( "Enable _blocklist:" ), TR_PREFS_KEY_BLOCKLIST_ENABLED
, core
);
499 e
= new_entry( TR_PREFS_KEY_BLOCKLIST_URL
, core
);
500 gtk_widget_set_size_request( e
, 300, -1 );
501 hig_workarea_add_row_w( t
, &row
, b
, e
, NULL
);
503 g_signal_connect( b
, "toggled", G_CALLBACK( target_cb
), e
);
506 w
= gtk_label_new( "" );
507 gtk_misc_set_alignment( GTK_MISC( w
), 0.0f
, 0.5f
);
508 updateBlocklistText( w
, TR_CORE( core
) );
510 h
= gtk_hbox_new( FALSE
, GUI_PAD_BIG
);
511 gtk_box_pack_start( GTK_BOX( h
), w
, TRUE
, TRUE
, 0 );
512 b
= data
->updateBlocklistButton
= gtk_button_new_with_mnemonic( _( "_Update" ) );
513 g_object_set_data( G_OBJECT( b
), "session", tr_core_session( TR_CORE( core
) ) );
514 g_signal_connect( b
, "clicked", G_CALLBACK( onBlocklistUpdate
), data
);
515 g_signal_connect( data
->check
, "toggled", G_CALLBACK( target_cb
), b
); target_cb( data
->check
, b
);
516 gtk_box_pack_start( GTK_BOX( h
), b
, FALSE
, FALSE
, 0 );
517 g_signal_connect( data
->check
, "toggled", G_CALLBACK( target_cb
), w
); target_cb( data
->check
, w
);
518 hig_workarea_add_wide_control( t
, &row
, h
);
520 s
= _( "Enable _automatic updates" );
521 w
= new_check_button( s
, PREF_KEY_BLOCKLIST_UPDATES_ENABLED
, core
);
522 hig_workarea_add_wide_control( t
, &row
, w
);
523 g_signal_connect( data
->check
, "toggled", G_CALLBACK( target_cb
), w
); target_cb( data
->check
, w
);
525 hig_workarea_add_section_divider( t
, &row
);
526 hig_workarea_add_section_title ( t
, &row
, _( "Privacy" ) );
528 s
= _( "_Encryption mode:" );
529 w
= new_encryption_combo( core
, "encryption" );
530 hig_workarea_add_row( t
, &row
, s
, w
, NULL
);
532 s
= _( "Use PE_X to find more peers" );
533 w
= new_check_button( s
, TR_PREFS_KEY_PEX_ENABLED
, core
);
534 s
= _( "PEX is a tool for exchanging peer lists with the peers you're connected to." );
535 gtr_widget_set_tooltip_text( w
, s
);
536 hig_workarea_add_wide_control( t
, &row
, w
);
538 s
= _( "Use _DHT to find more peers" );
539 w
= new_check_button( s
, TR_PREFS_KEY_DHT_ENABLED
, core
);
540 s
= _( "DHT is a tool for finding peers without a tracker." );
541 gtr_widget_set_tooltip_text( w
, s
);
542 hig_workarea_add_wide_control( t
, &row
, w
);
544 s
= _( "Use _Local Peer Discovery to find more peers" );
545 w
= new_check_button( s
, TR_PREFS_KEY_LPD_ENABLED
, core
);
546 s
= _( "LPD is a tool for finding peers on your local network." );
547 gtr_widget_set_tooltip_text( w
, s
);
548 hig_workarea_add_wide_control( t
, &row
, w
);
550 hig_workarea_finish( t
, &row
);
551 g_object_weak_ref( G_OBJECT( t
), privacyPageDestroyed
, data
);
566 whitelist_tree_model_new( const char * whitelist
)
570 GtkListStore
* store
= gtk_list_store_new( N_COLS
,
574 rules
= g_strsplit( whitelist
, ",", 0 );
576 for( i
= 0; rules
&& rules
[i
]; ++i
)
579 const char * s
= rules
[i
];
580 while( isspace( *s
) ) ++s
;
581 gtk_list_store_append( store
, &iter
);
582 gtk_list_store_set( store
, &iter
, COL_ADDRESS
, s
, -1 );
586 return GTK_TREE_MODEL( store
);
593 GtkListStore
* store
;
594 GtkWidget
* remove_button
;
596 GSList
* auth_widgets
;
597 GSList
* whitelist_widgets
;
598 GtkToggleButton
* rpc_tb
;
599 GtkToggleButton
* auth_tb
;
600 GtkToggleButton
* whitelist_tb
;
604 refreshWhitelist( struct remote_page
* page
)
607 GtkTreeModel
* model
= GTK_TREE_MODEL( page
->store
);
608 GString
* gstr
= g_string_new( NULL
);
610 if( gtk_tree_model_get_iter_first( model
, &iter
) ) do
613 gtk_tree_model_get( model
, &iter
,
614 COL_ADDRESS
, &address
,
616 g_string_append( gstr
, address
);
617 g_string_append( gstr
, "," );
620 while( gtk_tree_model_iter_next( model
, &iter
) );
622 g_string_truncate( gstr
, gstr
->len
- 1 ); /* remove the trailing comma */
624 tr_core_set_pref( page
->core
, TR_PREFS_KEY_RPC_WHITELIST
, gstr
->str
);
626 g_string_free( gstr
, TRUE
);
630 onAddressEdited( GtkCellRendererText
* r UNUSED
,
636 struct remote_page
* page
= gpage
;
637 GtkTreeModel
* model
= GTK_TREE_MODEL( page
->store
);
638 GtkTreePath
* path
= gtk_tree_path_new_from_string( path_string
);
640 if( gtk_tree_model_get_iter( model
, &iter
, path
) )
641 gtk_list_store_set( page
->store
, &iter
, COL_ADDRESS
, address
, -1 );
643 gtk_tree_path_free( path
);
644 refreshWhitelist( page
);
648 onAddWhitelistClicked( GtkButton
* b UNUSED
,
653 struct remote_page
* page
= gpage
;
655 gtk_list_store_append( page
->store
, &iter
);
656 gtk_list_store_set( page
->store
, &iter
,
657 COL_ADDRESS
, "0.0.0.0",
660 path
= gtk_tree_model_get_path( GTK_TREE_MODEL( page
->store
), &iter
);
661 gtk_tree_view_set_cursor(
663 gtk_tree_view_get_column( page
->view
, COL_ADDRESS
),
665 gtk_tree_path_free( path
);
669 onRemoveWhitelistClicked( GtkButton
* b UNUSED
,
672 struct remote_page
* page
= gpage
;
673 GtkTreeSelection
* sel
= gtk_tree_view_get_selection( page
->view
);
676 if( gtk_tree_selection_get_selected( sel
, NULL
, &iter
) )
678 gtk_list_store_remove( page
->store
, &iter
);
679 refreshWhitelist( page
);
684 refreshRPCSensitivity( struct remote_page
* page
)
687 const int rpc_active
= gtk_toggle_button_get_active(
689 const int auth_active
= gtk_toggle_button_get_active(
691 const int whitelist_active
= gtk_toggle_button_get_active(
692 page
->whitelist_tb
);
693 GtkTreeSelection
* sel
= gtk_tree_view_get_selection( page
->view
);
694 const int have_addr
=
695 gtk_tree_selection_get_selected( sel
, NULL
,
697 const int n_rules
= gtk_tree_model_iter_n_children(
698 GTK_TREE_MODEL( page
->store
), NULL
);
700 for( l
= page
->widgets
; l
!= NULL
; l
= l
->next
)
701 gtk_widget_set_sensitive( GTK_WIDGET( l
->data
), rpc_active
);
703 for( l
= page
->auth_widgets
; l
!= NULL
; l
= l
->next
)
704 gtk_widget_set_sensitive( GTK_WIDGET(
705 l
->data
), rpc_active
&& auth_active
);
707 for( l
= page
->whitelist_widgets
; l
!= NULL
; l
= l
->next
)
708 gtk_widget_set_sensitive( GTK_WIDGET( l
->data
),
709 rpc_active
&& whitelist_active
);
711 gtk_widget_set_sensitive( page
->remove_button
,
712 rpc_active
&& have_addr
&& n_rules
> 1 );
716 onRPCToggled( GtkToggleButton
* tb UNUSED
,
719 refreshRPCSensitivity( page
);
723 onWhitelistSelectionChanged( GtkTreeSelection
* sel UNUSED
,
726 refreshRPCSensitivity( page
);
730 onLaunchClutchCB( GtkButton
* w UNUSED
, gpointer data UNUSED
)
732 const int port
= pref_int_get( TR_PREFS_KEY_RPC_PORT
);
733 char * uri
= g_strdup_printf( "http://localhost:%d/transmission/web", port
);
740 remotePageFree( gpointer gpage
)
742 struct remote_page
* page
= gpage
;
744 g_slist_free( page
->widgets
);
745 g_slist_free( page
->auth_widgets
);
746 g_slist_free( page
->whitelist_widgets
);
751 webPage( GObject
* core
)
758 struct remote_page
* page
= g_new0( struct remote_page
, 1 );
760 page
->core
= TR_CORE( core
);
762 t
= hig_workarea_create( );
763 g_object_set_data_full( G_OBJECT( t
), "page", page
, remotePageFree
);
765 hig_workarea_add_section_title( t
, &row
, _( "Web Client" ) );
767 /* "enabled" checkbutton */
768 s
= _( "_Enable web client" );
769 w
= new_check_button( s
, TR_PREFS_KEY_RPC_ENABLED
, core
);
770 page
->rpc_tb
= GTK_TOGGLE_BUTTON( w
);
771 g_signal_connect( w
, "clicked", G_CALLBACK( onRPCToggled
), page
);
772 h
= gtk_hbox_new( FALSE
, GUI_PAD_BIG
);
773 gtk_box_pack_start( GTK_BOX( h
), w
, TRUE
, TRUE
, 0 );
774 w
= gtk_button_new_with_mnemonic( _( "_Open web client" ) );
775 page
->widgets
= g_slist_append( page
->widgets
, w
);
776 g_signal_connect( w
, "clicked", G_CALLBACK( onLaunchClutchCB
), NULL
);
777 gtk_box_pack_start( GTK_BOX( h
), w
, FALSE
, FALSE
, 0 );
778 hig_workarea_add_wide_control( t
, &row
, h
);
781 w
= new_spin_button( TR_PREFS_KEY_RPC_PORT
, core
, 0, USHRT_MAX
, 1 );
782 page
->widgets
= g_slist_append( page
->widgets
, w
);
783 w
= hig_workarea_add_row( t
, &row
, _( "Listening _port:" ), w
, NULL
);
784 page
->widgets
= g_slist_append( page
->widgets
, w
);
786 /* require authentication */
787 s
= _( "Use _authentication" );
788 w
= new_check_button( s
, TR_PREFS_KEY_RPC_AUTH_REQUIRED
, core
);
789 hig_workarea_add_wide_control( t
, &row
, w
);
790 page
->auth_tb
= GTK_TOGGLE_BUTTON( w
);
791 page
->widgets
= g_slist_append( page
->widgets
, w
);
792 g_signal_connect( w
, "clicked", G_CALLBACK( onRPCToggled
), page
);
795 s
= _( "_Username:" );
796 w
= new_entry( TR_PREFS_KEY_RPC_USERNAME
, core
);
797 page
->auth_widgets
= g_slist_append( page
->auth_widgets
, w
);
798 w
= hig_workarea_add_row( t
, &row
, s
, w
, NULL
);
799 page
->auth_widgets
= g_slist_append( page
->auth_widgets
, w
);
802 s
= _( "Pass_word:" );
803 w
= new_entry( TR_PREFS_KEY_RPC_PASSWORD
, core
);
804 gtk_entry_set_visibility( GTK_ENTRY( w
), FALSE
);
805 page
->auth_widgets
= g_slist_append( page
->auth_widgets
, w
);
806 w
= hig_workarea_add_row( t
, &row
, s
, w
, NULL
);
807 page
->auth_widgets
= g_slist_append( page
->auth_widgets
, w
);
809 /* require authentication */
810 s
= _( "Only allow these IP a_ddresses to connect:" );
811 w
= new_check_button( s
, TR_PREFS_KEY_RPC_WHITELIST_ENABLED
, core
);
812 hig_workarea_add_wide_control( t
, &row
, w
);
813 page
->whitelist_tb
= GTK_TOGGLE_BUTTON( w
);
814 page
->widgets
= g_slist_append( page
->widgets
, w
);
815 g_signal_connect( w
, "clicked", G_CALLBACK( onRPCToggled
), page
);
817 /* access control list */
819 const char * val
= pref_string_get( TR_PREFS_KEY_RPC_WHITELIST
);
820 GtkTreeModel
* m
= whitelist_tree_model_new( val
);
821 GtkTreeViewColumn
* c
;
823 GtkTreeSelection
* sel
;
828 page
->store
= GTK_LIST_STORE( m
);
829 w
= gtk_tree_view_new_with_model( m
);
830 g_signal_connect( w
, "button-release-event",
831 G_CALLBACK( on_tree_view_button_released
), NULL
);
833 page
->whitelist_widgets
= g_slist_append( page
->whitelist_widgets
, w
);
834 v
= page
->view
= GTK_TREE_VIEW( w
);
835 gtr_widget_set_tooltip_text( w
, _( "IP addresses may use wildcards, such as 192.168.*.*" ) );
836 sel
= gtk_tree_view_get_selection( v
);
837 g_signal_connect( sel
, "changed",
838 G_CALLBACK( onWhitelistSelectionChanged
), page
);
839 g_object_unref( G_OBJECT( m
) );
840 gtk_tree_view_set_headers_visible( v
, TRUE
);
841 w
= gtk_frame_new( NULL
);
842 gtk_frame_set_shadow_type( GTK_FRAME( w
), GTK_SHADOW_IN
);
843 gtk_container_add( GTK_CONTAINER( w
), GTK_WIDGET( v
) );
845 /* ip address column */
846 r
= gtk_cell_renderer_text_new( );
847 g_signal_connect( r
, "edited",
848 G_CALLBACK( onAddressEdited
), page
);
849 g_object_set( G_OBJECT( r
), "editable", TRUE
, NULL
);
850 c
= gtk_tree_view_column_new_with_attributes( NULL
, r
,
853 gtk_tree_view_column_set_expand( c
, TRUE
);
854 gtk_tree_view_append_column( v
, c
);
855 gtk_tree_view_set_headers_visible( v
, FALSE
);
857 s
= _( "Addresses:" );
858 w
= hig_workarea_add_row( t
, &row
, s
, w
, NULL
);
859 gtk_misc_set_alignment( GTK_MISC( w
), 0.0f
, 0.0f
);
860 gtk_misc_set_padding( GTK_MISC( w
), 0, GUI_PAD
);
861 page
->whitelist_widgets
= g_slist_append( page
->whitelist_widgets
, w
);
863 h
= gtk_hbox_new( TRUE
, GUI_PAD
);
864 w
= gtk_button_new_from_stock( GTK_STOCK_REMOVE
);
865 g_signal_connect( w
, "clicked", G_CALLBACK(
866 onRemoveWhitelistClicked
), page
);
867 page
->remove_button
= w
;
868 onWhitelistSelectionChanged( sel
, page
);
869 gtk_box_pack_start( GTK_BOX( h
), w
, TRUE
, TRUE
, 0 );
870 w
= gtk_button_new_from_stock( GTK_STOCK_ADD
);
871 page
->whitelist_widgets
= g_slist_append( page
->whitelist_widgets
, w
);
872 g_signal_connect( w
, "clicked", G_CALLBACK( onAddWhitelistClicked
), page
);
873 gtk_box_pack_start( GTK_BOX( h
), w
, TRUE
, TRUE
, 0 );
874 w
= gtk_hbox_new( FALSE
, 0 );
875 gtk_box_pack_start( GTK_BOX( w
), gtk_alignment_new( 0, 0, 0, 0 ),
877 gtk_box_pack_start( GTK_BOX( w
), h
, FALSE
, FALSE
, 0 );
878 hig_workarea_add_wide_control( t
, &row
, w
);
881 refreshRPCSensitivity( page
);
882 hig_workarea_finish( t
, &row
);
893 GSList
* sched_widgets
;
897 refreshSchedSensitivity( struct BandwidthPage
* p
)
900 const gboolean sched_enabled
= pref_flag_get( TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED
);
902 for( l
= p
->sched_widgets
; l
!= NULL
; l
= l
->next
)
903 gtk_widget_set_sensitive( GTK_WIDGET( l
->data
), sched_enabled
);
907 onSchedToggled( GtkToggleButton
* tb UNUSED
,
910 refreshSchedSensitivity( user_data
);
914 onTimeComboChanged( GtkComboBox
* w
,
919 if( gtk_combo_box_get_active_iter( w
, &iter
) )
921 const char * key
= g_object_get_data( G_OBJECT( w
), PREF_KEY
);
923 gtk_tree_model_get( gtk_combo_box_get_model(
924 w
), &iter
, 0, &val
, -1 );
925 tr_core_set_pref_int( TR_CORE( core
), key
, val
);
930 new_time_combo( GObject
* core
,
937 GtkListStore
* store
;
939 /* build a store at 15 minute intervals */
940 store
= gtk_list_store_new( 2, G_TYPE_INT
, G_TYPE_STRING
);
941 for( i
= 0; i
< 60 * 24; i
+= 15 )
949 strftime( buf
, sizeof( buf
), "%H:%M", &tm
);
950 gtk_list_store_append( store
, &iter
);
951 gtk_list_store_set( store
, &iter
, 0, i
, 1, buf
, -1 );
954 /* build the widget */
955 w
= gtk_combo_box_new_with_model( GTK_TREE_MODEL( store
) );
956 gtk_combo_box_set_wrap_width( GTK_COMBO_BOX( w
), 4 );
957 r
= gtk_cell_renderer_text_new( );
958 gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( w
), r
, TRUE
);
959 gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT(
960 w
), r
, "text", 1, NULL
);
961 g_object_set_data_full( G_OBJECT( w
), PREF_KEY
, tr_strdup(
963 val
= pref_int_get( key
);
964 gtk_combo_box_set_active( GTK_COMBO_BOX( w
), val
/ ( 15 ) );
965 g_signal_connect( w
, "changed", G_CALLBACK( onTimeComboChanged
), core
);
968 g_object_unref( G_OBJECT( store
) );
973 new_week_combo( GObject
* core
, const char * key
)
975 GtkWidget
* w
= gtr_combo_box_new_enum( _( "Every Day" ), TR_SCHED_ALL
,
976 _( "Weekdays" ), TR_SCHED_WEEKDAY
,
977 _( "Weekends" ), TR_SCHED_WEEKEND
,
978 _( "Sunday" ), TR_SCHED_SUN
,
979 _( "Monday" ), TR_SCHED_MON
,
980 _( "Tuesday" ), TR_SCHED_TUES
,
981 _( "Wednesday" ), TR_SCHED_WED
,
982 _( "Thursday" ), TR_SCHED_THURS
,
983 _( "Friday" ), TR_SCHED_FRI
,
984 _( "Saturday" ), TR_SCHED_SAT
,
986 gtr_combo_box_set_active_enum( GTK_COMBO_BOX( w
), pref_int_get( key
) );
987 g_object_set_data_full( G_OBJECT( w
), PREF_KEY
, tr_strdup( key
), g_free
);
988 g_signal_connect( w
, "changed", G_CALLBACK( onIntComboChanged
), core
);
993 bandwidthPageFree( gpointer gpage
)
995 struct BandwidthPage
* page
= gpage
;
997 g_slist_free( page
->sched_widgets
);
1002 bandwidthPage( GObject
* core
)
1008 GtkWidget
* w
, * w2
, * h
;
1010 struct BandwidthPage
* page
= tr_new0( struct BandwidthPage
, 1 );
1012 page
->core
= TR_CORE( core
);
1014 t
= hig_workarea_create( );
1015 hig_workarea_add_section_title( t
, &row
, _( "Speed Limits" ) );
1017 g_snprintf( buf
, sizeof( buf
), _( "Limit _download speed (%s):" ), _(speed_K_str
) );
1018 w
= new_check_button( buf
, TR_PREFS_KEY_DSPEED_ENABLED
, core
);
1019 w2
= new_spin_button( TR_PREFS_KEY_DSPEED_KBps
, core
, 0, INT_MAX
, 5 );
1020 gtk_widget_set_sensitive( GTK_WIDGET( w2
), pref_flag_get( TR_PREFS_KEY_DSPEED_ENABLED
) );
1021 g_signal_connect( w
, "toggled", G_CALLBACK( target_cb
), w2
);
1022 hig_workarea_add_row_w( t
, &row
, w
, w2
, NULL
);
1024 g_snprintf( buf
, sizeof( buf
), _( "Limit _upload speed (%s):" ), _(speed_K_str
) );
1025 w
= new_check_button( buf
, TR_PREFS_KEY_USPEED_ENABLED
, core
);
1026 w2
= new_spin_button( TR_PREFS_KEY_USPEED_KBps
, core
, 0, INT_MAX
, 5 );
1027 gtk_widget_set_sensitive( GTK_WIDGET( w2
), pref_flag_get( TR_PREFS_KEY_USPEED_ENABLED
) );
1028 g_signal_connect( w
, "toggled", G_CALLBACK( target_cb
), w2
);
1029 hig_workarea_add_row_w( t
, &row
, w
, w2
, NULL
);
1031 hig_workarea_add_section_divider( t
, &row
);
1032 h
= gtk_hbox_new( FALSE
, GUI_PAD
);
1033 w
= gtk_image_new_from_stock( "alt-speed-off", -1 );
1034 gtk_box_pack_start( GTK_BOX( h
), w
, FALSE
, FALSE
, 0 );
1035 g_snprintf( buf
, sizeof( buf
), "<b>%s</b>", _( "Temporary Speed Limits" ) );
1036 w
= gtk_label_new( buf
);
1037 gtk_misc_set_alignment( GTK_MISC( w
), 0.0f
, 0.5f
);
1038 gtk_label_set_use_markup( GTK_LABEL( w
), TRUE
);
1039 gtk_box_pack_start( GTK_BOX( h
), w
, FALSE
, FALSE
, 0 );
1040 hig_workarea_add_section_title_widget( t
, &row
, h
);
1042 s
= _( "Override normal speed limits manually or at scheduled times" );
1043 g_snprintf( buf
, sizeof( buf
), "<small>%s</small>", s
);
1044 w
= gtk_label_new( buf
);
1045 gtk_label_set_use_markup( GTK_LABEL( w
), TRUE
);
1046 gtk_misc_set_alignment( GTK_MISC( w
), 0.5f
, 0.5f
);
1047 hig_workarea_add_wide_control( t
, &row
, w
);
1049 g_snprintf( buf
, sizeof( buf
), _( "Limit do_wnload speed (%s):" ), _(speed_K_str
) );
1050 w
= new_spin_button( TR_PREFS_KEY_ALT_SPEED_DOWN_KBps
, core
, 0, INT_MAX
, 5 );
1051 hig_workarea_add_row( t
, &row
, buf
, w
, NULL
);
1053 g_snprintf( buf
, sizeof( buf
), _( "Limit u_pload speed (%s):" ), _(speed_K_str
) );
1054 w
= new_spin_button( TR_PREFS_KEY_ALT_SPEED_UP_KBps
, core
, 0, INT_MAX
, 5 );
1055 hig_workarea_add_row( t
, &row
, buf
, w
, NULL
);
1057 s
= _( "_Scheduled times:" );
1058 h
= gtk_hbox_new( FALSE
, 0 );
1059 w2
= new_time_combo( core
, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN
);
1060 page
->sched_widgets
= g_slist_append( page
->sched_widgets
, w2
);
1061 gtk_box_pack_start( GTK_BOX( h
), w2
, TRUE
, TRUE
, 0 );
1062 w2
= l
= gtk_label_new_with_mnemonic ( _( " _to " ) );
1063 page
->sched_widgets
= g_slist_append( page
->sched_widgets
, w2
);
1064 gtk_box_pack_start( GTK_BOX( h
), w2
, FALSE
, FALSE
, 0 );
1065 w2
= new_time_combo( core
, TR_PREFS_KEY_ALT_SPEED_TIME_END
);
1066 gtk_label_set_mnemonic_widget( GTK_LABEL( l
), w2
);
1067 page
->sched_widgets
= g_slist_append( page
->sched_widgets
, w2
);
1068 gtk_box_pack_start( GTK_BOX( h
), w2
, TRUE
, TRUE
, 0 );
1069 w
= new_check_button( s
, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED
, core
);
1070 g_signal_connect( w
, "toggled", G_CALLBACK( onSchedToggled
), page
);
1071 hig_workarea_add_row_w( t
, &row
, w
, h
, NULL
);
1073 s
= _( "_On days:" );
1074 w
= new_week_combo( core
, TR_PREFS_KEY_ALT_SPEED_TIME_DAY
);
1075 page
->sched_widgets
= g_slist_append( page
->sched_widgets
, w
);
1076 w
= hig_workarea_add_row( t
, &row
, s
, w
, NULL
);
1077 page
->sched_widgets
= g_slist_append( page
->sched_widgets
, w
);
1079 hig_workarea_finish( t
, &row
);
1080 g_object_set_data_full( G_OBJECT( t
), "page", page
, bandwidthPageFree
);
1082 refreshSchedSensitivity( page
);
1090 struct network_page_data
1093 GtkWidget
* portLabel
;
1094 GtkWidget
* portButton
;
1095 GtkWidget
* portSpin
;
1101 onCorePrefsChanged( TrCore
* core UNUSED
, const char * key
, gpointer gdata
)
1103 if( !strcmp( key
, TR_PREFS_KEY_PEER_PORT
) )
1105 struct network_page_data
* data
= gdata
;
1106 gdk_threads_enter();
1107 gtk_label_set_text( GTK_LABEL( data
->portLabel
), _( "Status unknown" ) );
1108 gtk_widget_set_sensitive( data
->portButton
, TRUE
);
1109 gtk_widget_set_sensitive( data
->portSpin
, TRUE
);
1110 gdk_threads_leave();
1115 peerPageDestroyed( gpointer gdata
, GObject
* dead UNUSED
)
1117 struct network_page_data
* data
= gdata
;
1118 if( data
->prefsTag
> 0 )
1119 g_signal_handler_disconnect( data
->core
, data
->prefsTag
);
1120 if( data
->portTag
> 0 )
1121 g_signal_handler_disconnect( data
->core
, data
->portTag
);
1126 onPortTested( TrCore
* core UNUSED
, gboolean isOpen
, gpointer vdata
)
1128 struct network_page_data
* data
= vdata
;
1129 const char * markup
= isOpen
? _( "Port is <b>open</b>" ) : _( "Port is <b>closed</b>" );
1130 gdk_threads_enter();
1131 gtk_label_set_markup( GTK_LABEL( data
->portLabel
), markup
);
1132 gtk_widget_set_sensitive( data
->portButton
, TRUE
);
1133 gtk_widget_set_sensitive( data
->portSpin
, TRUE
);
1134 gdk_threads_leave();
1138 onPortTest( GtkButton
* button UNUSED
, gpointer vdata
)
1140 struct network_page_data
* data
= vdata
;
1141 gtk_widget_set_sensitive( data
->portButton
, FALSE
);
1142 gtk_widget_set_sensitive( data
->portSpin
, FALSE
);
1143 gtk_label_set_markup( GTK_LABEL( data
->portLabel
), _( "<i>Testing...</i>" ) );
1144 if( !data
->portTag
)
1145 data
->portTag
= g_signal_connect( data
->core
, "port-tested", G_CALLBACK(onPortTested
), data
);
1146 tr_core_port_test( data
->core
);
1150 peerPage( GObject
* core
)
1158 struct network_page_data
* data
;
1160 /* register to stop listening to core prefs changes when the page is destroyed */
1161 data
= g_new0( struct network_page_data
, 1 );
1162 data
->core
= TR_CORE( core
);
1164 /* build the page */
1165 t
= hig_workarea_create( );
1166 hig_workarea_add_section_title( t
, &row
, _( "Incoming Peers" ) );
1168 s
= _( "_Port for incoming connections:" );
1169 w
= data
->portSpin
= new_spin_button( TR_PREFS_KEY_PEER_PORT
, core
, 1, USHRT_MAX
, 1 );
1170 hig_workarea_add_row( t
, &row
, s
, w
, NULL
);
1172 h
= gtk_hbox_new( FALSE
, GUI_PAD_BIG
);
1173 l
= data
->portLabel
= gtk_label_new( _( "Status unknown" ) );
1174 gtk_misc_set_alignment( GTK_MISC( l
), 0.0f
, 0.5f
);
1175 gtk_box_pack_start( GTK_BOX( h
), l
, TRUE
, TRUE
, 0 );
1176 w
= data
->portButton
= gtk_button_new_with_mnemonic( _( "Te_st Port" ) );
1177 gtk_box_pack_end( GTK_BOX( h
), w
, FALSE
, FALSE
, 0 );
1178 g_signal_connect( w
, "clicked", G_CALLBACK(onPortTest
), data
);
1179 hig_workarea_add_row( t
, &row
, NULL
, h
, NULL
);
1180 data
->prefsTag
= g_signal_connect( TR_CORE( core
), "prefs-changed", G_CALLBACK( onCorePrefsChanged
), data
);
1181 g_object_weak_ref( G_OBJECT( t
), peerPageDestroyed
, data
);
1183 s
= _( "Pick a _random port every time Transmission is started" );
1184 w
= new_check_button( s
, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START
, core
);
1185 hig_workarea_add_wide_control( t
, &row
, w
);
1187 s
= _( "Use UPnP or NAT-PMP port _forwarding from my router" );
1188 w
= new_check_button( s
, TR_PREFS_KEY_PORT_FORWARDING
, core
);
1189 hig_workarea_add_wide_control( t
, &row
, w
);
1191 hig_workarea_add_section_divider( t
, &row
);
1192 hig_workarea_add_section_title( t
, &row
, _( "Limits" ) );
1194 w
= new_spin_button( TR_PREFS_KEY_PEER_LIMIT_TORRENT
, core
, 1, 300, 5 );
1195 hig_workarea_add_row( t
, &row
, _( "Maximum peers per _torrent:" ), w
, NULL
);
1196 w
= new_spin_button( TR_PREFS_KEY_PEER_LIMIT_GLOBAL
, core
, 1, 3000, 5 );
1197 hig_workarea_add_row( t
, &row
, _( "Maximum peers _overall:" ), w
, NULL
);
1199 hig_workarea_finish( t
, &row
);
1208 tr_prefs_dialog_new( GObject
* core
,
1209 GtkWindow
* parent
)
1214 d
= gtk_dialog_new_with_buttons( _(
1215 "Transmission Preferences" ),
1217 GTK_DIALOG_DESTROY_WITH_PARENT
,
1218 GTK_STOCK_HELP
, GTK_RESPONSE_HELP
,
1219 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
,
1221 gtk_window_set_role( GTK_WINDOW( d
), "transmission-preferences-dialog" );
1222 gtk_container_set_border_width( GTK_CONTAINER( d
), GUI_PAD
);
1224 n
= gtk_notebook_new( );
1225 gtk_container_set_border_width ( GTK_CONTAINER ( n
), GUI_PAD
);
1227 gtk_notebook_append_page( GTK_NOTEBOOK( n
),
1228 torrentPage( core
),
1229 gtk_label_new ( _( "Torrents" ) ) );
1230 gtk_notebook_append_page( GTK_NOTEBOOK( n
),
1231 bandwidthPage( core
),
1232 gtk_label_new ( _( "Speed" ) ) );
1233 gtk_notebook_append_page( GTK_NOTEBOOK( n
),
1234 privacyPage( core
),
1235 gtk_label_new ( _( "Privacy" ) ) );
1236 gtk_notebook_append_page( GTK_NOTEBOOK( n
),
1238 gtk_label_new ( _( "Network" ) ) );
1239 gtk_notebook_append_page( GTK_NOTEBOOK( n
),
1240 desktopPage( core
),
1241 gtk_label_new ( _( "Desktop" ) ) );
1242 gtk_notebook_append_page( GTK_NOTEBOOK( n
),
1244 gtk_label_new ( _( "Web" ) ) );
1246 g_signal_connect( d
, "response", G_CALLBACK( response_cb
), core
);
1247 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( d
)->vbox
), n
, TRUE
, TRUE
, 0 );
1248 gtk_widget_show_all( GTK_DIALOG( d
)->vbox
);