~sense/ubuntu/lucid/transmission/fix-497882

« back to all changes in this revision

Viewing changes to gtk/tr-prefs.c

  • Committer: Bazaar Package Importer
  • Author(s): Chris Coulson
  • Date: 2008-11-28 15:33:48 UTC
  • mfrom: (1.1.19 upstream)
  • Revision ID: james.westby@ubuntu.com-20081128153348-it70trfnxiroblmc
Tags: 1.40-0ubuntu1
* New upstream release (LP: #302672)
  - Tracker communication uses fewer resources
  - More accurate bandwidth limits
  - Reduce disk fragmentation by preallocating files (LP: #287726)
  - Stability, security and performance improvements to the RPC /
    Web UI server (closes LP: #290423)
  - Support compression when serving Web UI and RPC responses
  - Simplify the RPC whitelist
  - Fix bug that prevented handshakes with encrypted BitComet peers
  - Fix 1.3x bug that could re-download some data unnecessarily
    (LP: #295040)
  - Option to automatically update the blocklist weekly
  - Added off-hour bandwidth scheduling
  - Simplify file/priority selection in the details dialog
  - Fix a couple of crashes
  - New / updated translations
  - Don't inhibit hibernation by default (LP: #292929)
  - Use "close" animation when sending to notification area (LP: #130811)
  - Fix resize problems (LP: #269872)
  - Support "--version" option when launching from command line
    (LP: #292011)
  - Correctly parse announce URLs that have leading or trailing
    spaces (LP: #262411)
  - Display an error when "Open Torrent" fails (LP: #281463)
* Dropped 10_fix_crasher_from_upstream.dpatch: Fix is in this
  upstream release.
* debian/control: Don't just build-depend on libcurl-dev, which is
  a virtual package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
 *
4
4
 * This file is licensed by the GPL version 2.  Works owned by the
5
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. 
 
6
 * so that the bulk of its code can remain under the MIT license.
7
7
 * This exemption does not extend to derived works not owned by
8
8
 * the Transmission project.
9
 
 * 
10
 
 * $Id: tr-prefs.c 6492 2008-08-12 00:52:34Z charles $
 
9
 *
 
10
 * $Id: tr-prefs.c 7041 2008-11-04 20:53:37Z charles $
11
11
 */
12
12
 
13
13
#include <ctype.h> /* isspace */
21
21
#include <libtransmission/utils.h>
22
22
#include <libtransmission/version.h>
23
23
#include <libtransmission/web.h>
 
24
#include "blocklist.h"
24
25
#include "conf.h"
25
26
#include "hig.h"
26
27
#include "tr-core.h"
34
35
void
35
36
tr_prefs_init_global( void )
36
37
{
37
 
    int i;
38
 
    char pw[32];
 
38
    int          i;
 
39
    char         pw[32];
39
40
    const char * str;
40
41
    const char * pool = "abcdefghijklmnopqrstuvwxyz"
41
42
                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
42
43
                        "1234567890";
 
44
    GRand *      rand;
43
45
 
44
46
    cf_check_older_configs( );
45
47
 
51
53
    pref_flag_set_default   ( PREF_KEY_DIR_WATCH_ENABLED, FALSE );
52
54
#endif
53
55
 
54
 
    pref_int_set_default    ( PREF_KEY_PEER_SOCKET_TOS, TR_DEFAULT_PEER_SOCKET_TOS );
55
 
    pref_flag_set_default   ( PREF_KEY_ALLOW_HIBERNATION, FALSE );
56
 
    pref_flag_set_default   ( PREF_KEY_BLOCKLIST_ENABLED, TR_DEFAULT_BLOCKLIST_ENABLED );
 
56
    pref_int_set_default    ( PREF_KEY_PEER_SOCKET_TOS,
 
57
                              TR_DEFAULT_PEER_SOCKET_TOS );
 
58
    pref_flag_set_default   ( PREF_KEY_INHIBIT_HIBERNATION, FALSE );
 
59
    pref_flag_set_default   ( PREF_KEY_BLOCKLIST_ENABLED, TRUE );
 
60
    pref_flag_set_default   ( PREF_KEY_BLOCKLIST_UPDATES_ENABLED, TRUE );
57
61
 
58
62
    pref_string_set_default ( PREF_KEY_OPEN_DIALOG_FOLDER, g_get_home_dir( ) );
59
63
 
60
 
    pref_int_set_default    ( PREF_KEY_MAX_PEERS_GLOBAL, TR_DEFAULT_GLOBAL_PEER_LIMIT );
 
64
    pref_int_set_default    ( PREF_KEY_MAX_PEERS_GLOBAL,
 
65
                              TR_DEFAULT_GLOBAL_PEER_LIMIT );
61
66
    pref_int_set_default    ( PREF_KEY_MAX_PEERS_PER_TORRENT, 50 );
62
67
 
63
68
    pref_flag_set_default   ( PREF_KEY_TOOLBAR, TRUE );
70
75
    pref_int_set_default    ( PREF_KEY_DL_LIMIT, 100 );
71
76
    pref_flag_set_default   ( PREF_KEY_UL_LIMIT_ENABLED, FALSE );
72
77
    pref_int_set_default    ( PREF_KEY_UL_LIMIT, 50 );
 
78
    pref_flag_set_default   ( PREF_KEY_SCHED_LIMIT_ENABLED, FALSE );
 
79
    pref_int_set_default    ( PREF_KEY_SCHED_BEGIN,    60 * 23 ); /* 11pm */
 
80
    pref_int_set_default    ( PREF_KEY_SCHED_END,      60 * 7 );  /* 7am */
 
81
    pref_int_set_default    ( PREF_KEY_SCHED_DL_LIMIT, 200 );   /* 2x the other
 
82
                                                                  limit */
 
83
    pref_int_set_default    ( PREF_KEY_SCHED_UL_LIMIT, 100 );   /* 2x the other
 
84
                                                                  limit */
 
85
 
73
86
    pref_flag_set_default   ( PREF_KEY_OPTIONS_PROMPT, TRUE );
74
87
 
75
88
    pref_int_set_default    ( PREF_KEY_MAIN_WINDOW_HEIGHT, 500 );
80
93
    pref_string_set_default ( PREF_KEY_PROXY_SERVER, "" );
81
94
    pref_int_set_default    ( PREF_KEY_PROXY_PORT, TR_DEFAULT_PROXY_PORT );
82
95
    pref_int_set_default    ( PREF_KEY_PROXY_TYPE, TR_DEFAULT_PROXY_TYPE );
83
 
    pref_flag_set_default   ( PREF_KEY_PROXY_SERVER_ENABLED, TR_DEFAULT_PROXY_ENABLED );
84
 
    pref_flag_set_default   ( PREF_KEY_PROXY_AUTH_ENABLED, TR_DEFAULT_PROXY_AUTH_ENABLED );
 
96
    pref_flag_set_default   ( PREF_KEY_PROXY_SERVER_ENABLED,
 
97
                              TR_DEFAULT_PROXY_ENABLED );
 
98
    pref_flag_set_default   ( PREF_KEY_PROXY_AUTH_ENABLED,
 
99
                              TR_DEFAULT_PROXY_AUTH_ENABLED );
85
100
    pref_string_set_default ( PREF_KEY_PROXY_USERNAME, "" );
86
101
    pref_string_set_default ( PREF_KEY_PROXY_PASSWORD, "" );
87
102
 
88
103
    str = NULL;
89
 
#if GLIB_CHECK_VERSION(2,14,0)
 
104
#if GLIB_CHECK_VERSION( 2, 14, 0 )
90
105
    if( !str ) str = g_get_user_special_dir( G_USER_DIRECTORY_DOWNLOAD );
91
106
#endif
92
107
    if( !str ) str = g_get_home_dir( );
97
112
    pref_flag_set_default   ( PREF_KEY_PORT_FORWARDING, TRUE );
98
113
    pref_flag_set_default   ( PREF_KEY_PEX, TR_DEFAULT_PEX_ENABLED );
99
114
    pref_flag_set_default   ( PREF_KEY_ASKQUIT, TRUE );
100
 
    pref_flag_set_default   ( PREF_KEY_ENCRYPTION, TR_ENCRYPTION_PREFERRED );
 
115
    pref_flag_set_default   ( PREF_KEY_ENCRYPTION, TR_DEFAULT_ENCRYPTION );
 
116
    pref_flag_set_default   ( PREF_KEY_LAZY_BITFIELD,
 
117
                              TR_DEFAULT_LAZY_BITFIELD_ENABLED );
101
118
 
102
119
    pref_int_set_default    ( PREF_KEY_MSGLEVEL, TR_MSG_INF );
103
120
 
110
127
 
111
128
    pref_flag_set_default   ( PREF_KEY_RPC_ENABLED, TR_DEFAULT_RPC_ENABLED );
112
129
    pref_int_set_default    ( PREF_KEY_RPC_PORT, TR_DEFAULT_RPC_PORT );
113
 
    pref_string_set_default ( PREF_KEY_RPC_ACL, TR_DEFAULT_RPC_ACL );
114
 
 
115
 
    for( i=0; i<16; ++i )
116
 
        pw[i] = pool[ tr_rand( strlen( pool ) ) ];
 
130
    pref_string_set_default ( PREF_KEY_RPC_WHITELIST, TR_DEFAULT_RPC_WHITELIST );
 
131
    pref_flag_set_default   ( PREF_KEY_RPC_WHITELIST_ENABLED,
 
132
                              TR_DEFAULT_RPC_WHITELIST_ENABLED  );
 
133
 
 
134
    rand = g_rand_new ( );
 
135
    for( i = 0; i < 16; ++i )
 
136
        pw[i] = pool[g_rand_int_range ( rand, 0, strlen( pool ) )];
 
137
    g_rand_free ( rand );
 
138
 
117
139
    pw[16] = '\0';
118
140
    pref_string_set_default( PREF_KEY_RPC_USERNAME, "transmission" );
119
141
    pref_string_set_default( PREF_KEY_RPC_PASSWORD, pw );
129
151
#define PREF_KEY "pref-key"
130
152
 
131
153
static void
132
 
response_cb( GtkDialog * dialog, int response, gpointer unused UNUSED )
 
154
response_cb( GtkDialog *     dialog,
 
155
             int             response,
 
156
             gpointer unused UNUSED )
133
157
{
134
 
    if( response == GTK_RESPONSE_HELP ) {
 
158
    if( response == GTK_RESPONSE_HELP )
 
159
    {
135
160
        char * base = gtr_get_help_url( );
136
161
        char * url = g_strdup_printf( "%s/html/preferences.html", base );
137
162
        gtr_open_file( url );
140
165
    }
141
166
 
142
167
    if( response == GTK_RESPONSE_CLOSE )
143
 
        gtk_widget_destroy( GTK_WIDGET(dialog) );
 
168
        gtk_widget_destroy( GTK_WIDGET( dialog ) );
144
169
}
145
170
 
146
171
static void
147
 
toggled_cb( GtkToggleButton * w, gpointer core )
 
172
toggled_cb( GtkToggleButton * w,
 
173
            gpointer          core )
148
174
{
149
 
    const char * key = g_object_get_data( G_OBJECT(w), PREF_KEY );
 
175
    const char *   key = g_object_get_data( G_OBJECT( w ), PREF_KEY );
150
176
    const gboolean flag = gtk_toggle_button_get_active( w );
151
 
    tr_core_set_pref_bool( TR_CORE(core), key, flag );
 
177
 
 
178
    tr_core_set_pref_bool( TR_CORE( core ), key, flag );
152
179
}
153
180
 
154
181
static GtkWidget*
155
 
new_check_button( const char * mnemonic, const char * key, gpointer core )
 
182
new_check_button( const char * mnemonic,
 
183
                  const char * key,
 
184
                  gpointer     core )
156
185
{
157
186
    GtkWidget * w = gtk_check_button_new_with_mnemonic( mnemonic );
158
 
    g_object_set_data_full( G_OBJECT(w), PREF_KEY, g_strdup(key), g_free );
159
 
    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(w), pref_flag_get(key) );
160
 
    g_signal_connect( w, "toggled", G_CALLBACK(toggled_cb), core );
 
187
 
 
188
    g_object_set_data_full( G_OBJECT( w ), PREF_KEY, g_strdup(
 
189
                                key ), g_free );
 
190
    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ),
 
191
                                 pref_flag_get( key ) );
 
192
    g_signal_connect( w, "toggled", G_CALLBACK( toggled_cb ), core );
161
193
    return w;
162
194
}
163
195
 
164
 
static void
165
 
spun_cb( GtkSpinButton * w, gpointer core )
166
 
{
167
 
    const char * key = g_object_get_data( G_OBJECT(w), PREF_KEY );
168
 
    const int value = gtk_spin_button_get_value_as_int( w );
169
 
    tr_core_set_pref_int( TR_CORE(core), key, value );
 
196
#define IDLE_DATA "idle-data"
 
197
 
 
198
struct spin_idle_data
 
199
{
 
200
    gpointer    core;
 
201
    GTimer *    last_change;
 
202
};
 
203
 
 
204
static void
 
205
spin_idle_data_free( gpointer gdata )
 
206
{
 
207
    struct spin_idle_data * data = gdata;
 
208
 
 
209
    g_timer_destroy( data->last_change );
 
210
    g_free( data );
 
211
}
 
212
 
 
213
static gboolean
 
214
spun_cb_idle( gpointer spin )
 
215
{
 
216
    gboolean                keep_waiting = TRUE;
 
217
    GObject *               o = G_OBJECT( spin );
 
218
    struct spin_idle_data * data = g_object_get_data( o, IDLE_DATA );
 
219
 
 
220
    /* has the user stopped making changes? */
 
221
    if( g_timer_elapsed( data->last_change, NULL ) > 0.33f )
 
222
    {
 
223
        /* update the core */
 
224
        const char * key = g_object_get_data( o, PREF_KEY );
 
225
        const int    value = gtk_spin_button_get_value_as_int(
 
226
             GTK_SPIN_BUTTON( spin ) );
 
227
        tr_core_set_pref_int( TR_CORE( data->core ), key, value );
 
228
 
 
229
        /* cleanup */
 
230
        g_object_set_data( o, IDLE_DATA, NULL );
 
231
        keep_waiting = FALSE;
 
232
        g_object_unref( G_OBJECT( o ) );
 
233
    }
 
234
 
 
235
    return keep_waiting;
 
236
}
 
237
 
 
238
static void
 
239
spun_cb( GtkSpinButton * w,
 
240
         gpointer        core )
 
241
{
 
242
    /* user may be spinning through many values, so let's hold off
 
243
       for a moment to keep from flooding the core with changes */
 
244
    GObject *               o = G_OBJECT( w );
 
245
    struct spin_idle_data * data = g_object_get_data( o, IDLE_DATA );
 
246
 
 
247
    if( data == NULL )
 
248
    {
 
249
        data = g_new( struct spin_idle_data, 1 );
 
250
        data->core = core;
 
251
        data->last_change = g_timer_new( );
 
252
        g_object_set_data_full( o, IDLE_DATA, data, spin_idle_data_free );
 
253
        g_object_ref( G_OBJECT( o ) );
 
254
        g_timeout_add( 100, spun_cb_idle, w );
 
255
    }
 
256
    g_timer_start( data->last_change );
170
257
}
171
258
 
172
259
static GtkWidget*
173
 
new_spin_button( const char * key, gpointer core, int low, int high, int step )
 
260
new_spin_button( const char * key,
 
261
                 gpointer     core,
 
262
                 int          low,
 
263
                 int          high,
 
264
                 int          step )
174
265
{
175
266
    GtkWidget * w = gtk_spin_button_new_with_range( low, high, step );
176
 
    g_object_set_data_full( G_OBJECT(w), PREF_KEY, g_strdup(key), g_free );
177
 
    gtk_spin_button_set_digits( GTK_SPIN_BUTTON(w), 0 );
178
 
    gtk_spin_button_set_value( GTK_SPIN_BUTTON(w), pref_int_get(key) );
179
 
    g_signal_connect( w, "value-changed", G_CALLBACK(spun_cb), core );
 
267
 
 
268
    g_object_set_data_full( G_OBJECT( w ), PREF_KEY, g_strdup(
 
269
                                key ), g_free );
 
270
    gtk_spin_button_set_digits( GTK_SPIN_BUTTON( w ), 0 );
 
271
    gtk_spin_button_set_value( GTK_SPIN_BUTTON( w ), pref_int_get( key ) );
 
272
    g_signal_connect( w, "value-changed", G_CALLBACK( spun_cb ), core );
180
273
    return w;
181
274
}
182
275
 
183
276
static void
184
 
entry_changed_cb( GtkEntry * w, gpointer core )
 
277
entry_changed_cb( GtkEntry * w,
 
278
                  gpointer   core )
185
279
{
186
280
    const char * key = g_object_get_data( G_OBJECT( w ), PREF_KEY );
187
281
    const char * value = gtk_entry_get_text( w );
 
282
 
188
283
    tr_core_set_pref( TR_CORE( core ), key, value );
189
284
}
190
285
 
191
286
static GtkWidget*
192
 
new_entry( const char * key, gpointer core )
 
287
new_entry( const char * key,
 
288
           gpointer     core )
193
289
{
194
 
    GtkWidget * w = gtk_entry_new( );
 
290
    GtkWidget *  w = gtk_entry_new( );
195
291
    const char * value = pref_string_get( key );
 
292
 
196
293
    if( value )
197
294
        gtk_entry_set_text( GTK_ENTRY( w ), value );
198
 
    g_object_set_data_full( G_OBJECT(w), PREF_KEY, g_strdup(key), g_free );
199
 
    g_signal_connect( w, "changed", G_CALLBACK(entry_changed_cb), core );
 
295
    g_object_set_data_full( G_OBJECT( w ), PREF_KEY, g_strdup(
 
296
                                key ), g_free );
 
297
    g_signal_connect( w, "changed", G_CALLBACK( entry_changed_cb ), core );
200
298
    return w;
201
299
}
202
300
 
203
301
static void
204
 
chosen_cb( GtkFileChooser * w, gpointer core )
 
302
chosen_cb( GtkFileChooser * w,
 
303
           gpointer         core )
205
304
{
206
 
    const char * key = g_object_get_data( G_OBJECT(w), PREF_KEY );
207
 
    char * value = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(w) );
208
 
    tr_core_set_pref( TR_CORE(core), key, value );
 
305
    const char * key = g_object_get_data( G_OBJECT( w ), PREF_KEY );
 
306
    char *       value = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( w ) );
 
307
 
 
308
    tr_core_set_pref( TR_CORE( core ), key, value );
209
309
    g_free( value );
210
310
}
211
311
 
212
312
static GtkWidget*
213
 
new_path_chooser_button( const char * key, gpointer core )
 
313
new_path_chooser_button( const char * key,
 
314
                         gpointer     core )
214
315
{
215
 
    GtkWidget * w = gtk_file_chooser_button_new( NULL,
216
 
                                    GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
 
316
    GtkWidget *  w = gtk_file_chooser_button_new(
 
317
        NULL,
 
318
        GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
217
319
    const char * path = pref_string_get( key );
218
 
    g_object_set_data_full( G_OBJECT(w), PREF_KEY, g_strdup(key), g_free );
219
 
    g_signal_connect( w, "selection-changed", G_CALLBACK(chosen_cb), core );
220
 
    gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(w), path );
 
320
 
 
321
    g_object_set_data_full( G_OBJECT( w ), PREF_KEY, g_strdup(
 
322
                                key ), g_free );
 
323
    g_signal_connect( w, "selection-changed", G_CALLBACK( chosen_cb ), core );
 
324
    gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( w ), path );
221
325
    return w;
222
326
}
223
327
 
224
328
static void
225
 
target_cb( GtkWidget * tb, gpointer target )
 
329
target_cb( GtkWidget * tb,
 
330
           gpointer    target )
226
331
{
227
332
    const gboolean b = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( tb ) );
228
 
    gtk_widget_set_sensitive( GTK_WIDGET(target), b );
229
 
}
230
 
 
231
 
struct test_port_data
232
 
{
233
 
    GtkWidget * label;
234
 
    gboolean * alive;
235
 
};
236
 
 
237
 
static void
238
 
testing_port_done( tr_handle   * handle         UNUSED,
239
 
                   long          response_code  UNUSED,
240
 
                   const void  * response,
241
 
                   size_t        response_len,
242
 
                   void        * gdata )
243
 
{
244
 
    struct test_port_data * data = gdata;
245
 
    if( *data->alive )
246
 
    {
247
 
        const int isOpen = response_len && *(char*)response=='1';
248
 
        gtk_label_set_markup( GTK_LABEL( data->label ), isOpen
249
 
                              ? _("Port is <b>open</b>")
250
 
                              : _("Port is <b>closed</b>") );
251
 
    }
252
 
}
253
 
 
254
 
static gboolean
255
 
testing_port_begin( gpointer gdata )
256
 
{
257
 
    struct test_port_data * data = gdata;
258
 
    if( *data->alive )
259
 
    {
260
 
        GtkSpinButton * spin = g_object_get_data( G_OBJECT( data->label ), "tr-port-spin" );
261
 
        tr_handle * handle = g_object_get_data( G_OBJECT( data->label ), "handle" );
262
 
        const int port = gtk_spin_button_get_value_as_int( spin );
263
 
        char url[256];
264
 
        g_snprintf( url, sizeof(url), "http://portcheck.transmissionbt.com/%d", port );
265
 
        tr_webRun( handle, url, NULL, testing_port_done, data );
266
 
    }
267
 
    return FALSE;
268
 
}
269
 
 
270
 
static void
271
 
testing_port_cb( GtkWidget * unused UNUSED, gpointer l )
272
 
{
273
 
    struct test_port_data * data;
274
 
 
275
 
    gtk_label_set_markup( GTK_LABEL( l ), _( "<i>Testing port...</i>" ) );
276
 
 
277
 
    /* wait three seconds to give the port forwarding time to kick in */
278
 
    data = g_new0( struct test_port_data, 1 );
279
 
    data->label = l;
280
 
    data->alive = g_object_get_data( G_OBJECT( l ), "alive" );
281
 
    g_timeout_add( 3000, testing_port_begin, data );
282
 
}
283
 
 
284
 
static void
285
 
dialogDestroyed( gpointer alive, GObject * dialog UNUSED )
286
 
{
287
 
    *(gboolean*)alive = FALSE;
288
 
}
 
333
 
 
334
    gtk_widget_set_sensitive( GTK_WIDGET( target ), b );
 
335
}
 
336
 
 
337
/****
 
338
*****  Torrent Tab
 
339
****/
289
340
 
290
341
static GtkWidget*
291
342
torrentPage( GObject * core )
292
343
{
293
 
    int row = 0;
 
344
    int          row = 0;
294
345
    const char * s;
295
 
    GtkWidget * t;
296
 
    GtkWidget * w;
 
346
    GtkWidget *  t;
 
347
    GtkWidget *  w;
 
348
 
297
349
#ifdef HAVE_GIO
298
 
    GtkWidget * l;
 
350
    GtkWidget *  l;
299
351
#endif
300
352
 
301
353
    t = hig_workarea_create( );
302
354
    hig_workarea_add_section_title( t, &row, _( "Adding Torrents" ) );
303
355
 
304
356
#ifdef HAVE_GIO
305
 
        s = _( "Automatically _add torrents from:" );
306
 
        l = new_check_button( s, PREF_KEY_DIR_WATCH_ENABLED, core );
307
 
        w = new_path_chooser_button( PREF_KEY_DIR_WATCH, core );
308
 
        gtk_widget_set_sensitive( GTK_WIDGET(w), pref_flag_get( PREF_KEY_DIR_WATCH_ENABLED ) );
309
 
        g_signal_connect( l, "toggled", G_CALLBACK(target_cb), w );
310
 
        hig_workarea_add_row_w( t, &row, l, w, NULL );
 
357
    s = _( "Automatically _add torrents from:" );
 
358
    l = new_check_button( s, PREF_KEY_DIR_WATCH_ENABLED, core );
 
359
    w = new_path_chooser_button( PREF_KEY_DIR_WATCH, core );
 
360
    gtk_widget_set_sensitive( GTK_WIDGET( w ),
 
361
                             pref_flag_get( PREF_KEY_DIR_WATCH_ENABLED ) );
 
362
    g_signal_connect( l, "toggled", G_CALLBACK( target_cb ), w );
 
363
    hig_workarea_add_row_w( t, &row, l, w, NULL );
311
364
#endif
312
365
 
313
 
        s = _( "Display _options dialog" );
314
 
        w = new_check_button( s, PREF_KEY_OPTIONS_PROMPT, core );
315
 
        hig_workarea_add_wide_control( t, &row, w );
316
 
 
317
 
        s = _( "_Start when added" );
318
 
        w = new_check_button( s, PREF_KEY_START, core );
319
 
        hig_workarea_add_wide_control( t, &row, w );
320
 
 
321
 
        s = _( "Mo_ve source files to Trash" );
322
 
        w = new_check_button( s, PREF_KEY_TRASH_ORIGINAL, core ); 
323
 
        hig_workarea_add_wide_control( t, &row, w );
324
 
 
325
 
        w = new_path_chooser_button( PREF_KEY_DOWNLOAD_DIR, core );
326
 
        hig_workarea_add_row( t, &row, _( "_Destination folder:" ), w, NULL );
327
 
 
328
 
    hig_workarea_finish( t, &row );
329
 
    return t;
330
 
}
331
 
 
332
 
/***
333
 
****
334
 
***/
 
366
    s = _( "Display _options dialog" );
 
367
    w = new_check_button( s, PREF_KEY_OPTIONS_PROMPT, core );
 
368
    hig_workarea_add_wide_control( t, &row, w );
 
369
 
 
370
    s = _( "_Start when added" );
 
371
    w = new_check_button( s, PREF_KEY_START, core );
 
372
    hig_workarea_add_wide_control( t, &row, w );
 
373
 
 
374
    s = _( "Mo_ve source files to Trash" );
 
375
    w = new_check_button( s, PREF_KEY_TRASH_ORIGINAL, core );
 
376
    hig_workarea_add_wide_control( t, &row, w );
 
377
 
 
378
    w = new_path_chooser_button( PREF_KEY_DOWNLOAD_DIR, core );
 
379
    hig_workarea_add_row( t, &row, _( "_Destination folder:" ), w, NULL );
 
380
 
 
381
    hig_workarea_finish( t, &row );
 
382
    return t;
 
383
}
 
384
 
 
385
/****
 
386
*****  Desktop Tab
 
387
****/
 
388
 
 
389
static GtkWidget*
 
390
desktopPage( GObject * core )
 
391
{
 
392
    int          row = 0;
 
393
    const char * s;
 
394
    GtkWidget *  t;
 
395
    GtkWidget *  w;
 
396
 
 
397
    t = hig_workarea_create( );
 
398
    hig_workarea_add_section_title( t, &row, _( "Options" ) );
 
399
 
 
400
    s = _( "Inhibit desktop _hibernation when torrents are active" );
 
401
    w = new_check_button( s, PREF_KEY_INHIBIT_HIBERNATION, core );
 
402
    hig_workarea_add_wide_control( t, &row, w );
 
403
 
 
404
    s = _( "Show _icon in the desktop Notification Area" );
 
405
    w = new_check_button( s, PREF_KEY_SHOW_TRAY_ICON, core );
 
406
    hig_workarea_add_wide_control( t, &row, w );
 
407
 
 
408
    hig_workarea_finish( t, &row );
 
409
    return t;
 
410
}
 
411
 
 
412
/****
 
413
*****  Peer Tab
 
414
****/
335
415
 
336
416
struct blocklist_data
337
417
{
338
 
    GtkWidget * check;
339
 
    GtkWidget * dialog;
340
 
    TrCore * core;
341
 
    int abortFlag;
342
 
    char secondary[256];
 
418
    GtkWidget *  check;
 
419
    GtkWidget *  dialog;
 
420
    TrCore *     core;
 
421
    gulong       id;
 
422
    int          abortFlag;
 
423
    char         secondary[256];
343
424
};
344
425
 
345
426
static void
346
 
updateBlocklistText( GtkWidget * w, TrCore * core )
 
427
updateBlocklistText( GtkWidget * w,
 
428
                     TrCore *    core )
347
429
{
348
 
    const int n = tr_blocklistGetRuleCount( tr_core_handle( core ) );
349
 
    char buf[512];
 
430
    const int n = tr_blocklistGetRuleCount( tr_core_session( core ) );
 
431
    char      buf[512];
 
432
 
350
433
    g_snprintf( buf, sizeof( buf ),
351
 
                ngettext( "Ignore the %'d _blocklisted peer",
352
 
                          "Ignore the %'d _blocklisted peers", n ), n );
 
434
                ngettext( "Enable _blocklist (contains %'d rule)",
 
435
                          "Enable _blocklist (contains %'d rules)", n ), n );
353
436
    gtk_button_set_label( GTK_BUTTON( w ), buf );
354
437
}
355
 
static gboolean
356
 
updateBlocklistTextFromData( gpointer gdata )
357
 
{
358
 
    struct blocklist_data * data = gdata;
359
 
    updateBlocklistText( data->check, data->core );
360
 
    return FALSE;
361
 
}
362
 
 
363
 
static gboolean
364
 
blocklistDialogSetSecondary( gpointer gdata )
365
 
{
366
 
    struct blocklist_data * data = gdata;
367
 
    GtkMessageDialog * md = GTK_MESSAGE_DIALOG( data->dialog );
368
 
    gtk_message_dialog_format_secondary_text( md, data->secondary );
369
 
    return FALSE;
370
 
}
371
 
 
372
 
static gboolean
373
 
blocklistDialogAllowClose( gpointer dialog )
374
 
{
375
 
    GtkDialog * d = GTK_DIALOG( dialog );
376
 
    gtk_dialog_set_response_sensitive( GTK_DIALOG( d ), GTK_RESPONSE_CANCEL, FALSE );
377
 
    gtk_dialog_set_response_sensitive( GTK_DIALOG( d ), GTK_RESPONSE_CLOSE, TRUE );
378
 
    return FALSE;
379
 
}
380
 
 
381
 
static void
382
 
got_blocklist( tr_handle   * handle         UNUSED,
383
 
               long          response_code  UNUSED,
384
 
               const void  * response,
385
 
               size_t        response_len,
386
 
               void        * gdata )
387
 
{
388
 
    struct blocklist_data * data = gdata;
389
 
    const char * text = response;
390
 
    int size = response_len;
391
 
    int rules = 0;
392
 
    gchar * filename = NULL;
393
 
    gchar * filename2 = NULL;
394
 
    int fd = -1;
395
 
    int ok = 1;
396
 
 
397
 
    if( !data->abortFlag && ( !text || !size ) )
398
 
    {
399
 
        ok = FALSE;
400
 
        g_snprintf( data->secondary, sizeof( data->secondary ),
401
 
                    _( "Unable to get blocklist." ) );
402
 
        g_message( data->secondary );
403
 
        g_idle_add( blocklistDialogSetSecondary, data );
404
 
    }      
405
 
 
406
 
    if( ok && !data->abortFlag )
407
 
    {
408
 
        GError * err = NULL;
409
 
        fd = g_file_open_tmp( "transmission-blockfile-XXXXXX", &filename, &err );
410
 
        if( err ) {
411
 
            g_snprintf( data->secondary, sizeof( data->secondary ),
412
 
                        _( "Unable to get blocklist: %s" ), err->message );
413
 
            g_warning( data->secondary );
414
 
            g_idle_add( blocklistDialogSetSecondary, data );
415
 
            g_clear_error( &err );
416
 
            ok = FALSE;
417
 
        } else {
418
 
            write( fd, text, size );
419
 
            close( fd );
420
 
        }
421
 
    }
422
 
    if( ok && !data->abortFlag )
423
 
    {
424
 
        char * cmd;
425
 
        filename2 = g_strdup_printf( "%s.txt", filename );
426
 
        g_snprintf( data->secondary, sizeof( data->secondary ),
427
 
                    _( "Uncompressing blocklist..." ) );
428
 
        g_idle_add( blocklistDialogSetSecondary, data );
429
 
        cmd = g_strdup_printf( "zcat %s > %s ", filename, filename2 );
430
 
        tr_dbg( "%s", cmd );
431
 
        system( cmd );
432
 
        g_free( cmd );
433
 
    }
434
 
    if( ok && !data->abortFlag )
435
 
    {
436
 
        g_snprintf( data->secondary, sizeof( data->secondary ),
437
 
                    _( "Parsing blocklist..." ) );
438
 
        g_idle_add( blocklistDialogSetSecondary, data );
439
 
        rules = tr_blocklistSetContent( tr_core_handle( data->core ), filename2 );
440
 
    }
441
 
    if( ok && !data->abortFlag )
442
 
    {
443
 
        g_snprintf( data->secondary, sizeof( data->secondary ),
444
 
                    _( "Blocklist updated with %'d entries" ), rules );
445
 
        g_idle_add( blocklistDialogSetSecondary, data );
446
 
        g_idle_add( blocklistDialogAllowClose, data->dialog );
447
 
        g_idle_add( updateBlocklistTextFromData, data );
448
 
    }
449
 
 
450
 
    /* g_free( data ); */
451
 
    if( filename2 ) {
452
 
        unlink( filename2 );
453
 
        g_free( filename2 );
454
 
    }
455
 
    if( filename ) {
456
 
        unlink( filename );
457
 
        g_free( filename );
458
 
    }
459
 
}
460
 
 
461
 
static void
462
 
onUpdateBlocklistResponseCB( GtkDialog * dialog, int response, gpointer vdata )
463
 
{
464
 
    struct blocklist_data * data = vdata;
465
 
 
466
 
    if( response == GTK_RESPONSE_CANCEL )
467
 
        data->abortFlag = 1;
468
 
 
469
 
    data->dialog = NULL;
470
 
    gtk_widget_destroy( GTK_WIDGET( dialog ) );
471
 
}
472
 
 
473
 
static void
474
 
onUpdateBlocklistCB( GtkButton * w, gpointer gdata )
475
 
{
476
 
    GtkWidget * d;
477
 
    struct blocklist_data * data = gdata;
478
 
    tr_handle * handle = g_object_get_data( G_OBJECT( w ), "handle" );
479
 
    const char * url = "http://download.m0k.org/transmission/files/level1.gz";
480
 
    
481
 
    d = gtk_message_dialog_new( GTK_WINDOW( gtk_widget_get_toplevel( GTK_WIDGET( w ) ) ),
482
 
                                GTK_DIALOG_DESTROY_WITH_PARENT,
483
 
                                GTK_MESSAGE_INFO,
484
 
                                GTK_BUTTONS_NONE,
485
 
                                _( "Updating Blocklist" ) );
486
 
    gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( d ),
487
 
                                              _( "Retrieving blocklist..." ) );
 
438
 
 
439
static void
 
440
onBlocklistDialogResponse( GtkDialog *  d,
 
441
                           int response UNUSED,
 
442
                           gpointer     gdata )
 
443
{
 
444
    struct blocklist_data * data = gdata;
 
445
 
 
446
    g_signal_handler_disconnect( data->core, data->id );
 
447
    gtk_widget_destroy( GTK_WIDGET( d ) );
 
448
}
 
449
 
 
450
static void
 
451
onBlocklistStatus( TrCore * core UNUSED,
 
452
                   gboolean      isDone,
 
453
                   const char *  status,
 
454
                   gpointer      gdata )
 
455
{
 
456
    struct blocklist_data * data = gdata;
 
457
 
 
458
    gdk_threads_enter( );
 
459
    gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( data-> dialog ),
 
460
                                              "%s", status );
 
461
    gtk_dialog_set_response_sensitive( GTK_DIALOG( data->dialog ),
 
462
                                       GTK_RESPONSE_CANCEL, !isDone );
 
463
    gtk_dialog_set_response_sensitive( GTK_DIALOG( data->dialog ),
 
464
                                       GTK_RESPONSE_CLOSE, isDone );
 
465
    if( isDone )
 
466
        updateBlocklistText( data->check, core );
 
467
    gdk_threads_leave( );
 
468
}
 
469
 
 
470
static void
 
471
onUpdateBlocklistCB( GtkButton * w,
 
472
                     gpointer    gdata )
 
473
{
 
474
    GtkWidget *             d;
 
475
    struct blocklist_data * data = gdata;
 
476
 
 
477
    d =
 
478
        gtk_message_dialog_new( GTK_WINDOW( gtk_widget_get_toplevel( 
 
479
                                                                    GTK_WIDGET(
 
480
                                                                        w ) ) ),
 
481
                               GTK_DIALOG_DESTROY_WITH_PARENT,
 
482
                               GTK_MESSAGE_INFO,
 
483
                               GTK_BUTTONS_NONE,
 
484
                               _( "Updating Blocklist" ) );
 
485
 
 
486
    data->dialog = d;
 
487
    data->id =
 
488
        g_signal_connect( data->core, "blocklist-status", G_CALLBACK(
 
489
                              onBlocklistStatus ), data );
 
490
 
488
491
    gtk_dialog_add_buttons( GTK_DIALOG( d ),
489
492
                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
490
493
                            GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
491
494
                            NULL );
492
 
    gtk_dialog_set_response_sensitive( GTK_DIALOG( d ), GTK_RESPONSE_CLOSE, FALSE );
493
 
 
494
 
    data->dialog = d;
495
 
 
496
 
    g_signal_connect( d, "response", G_CALLBACK(onUpdateBlocklistResponseCB), data );
 
495
    gtk_dialog_set_response_sensitive( GTK_DIALOG(
 
496
                                           d ), GTK_RESPONSE_CLOSE, FALSE );
 
497
 
 
498
    g_signal_connect( d, "response", G_CALLBACK(
 
499
                          onBlocklistDialogResponse ), data );
497
500
    gtk_widget_show( d );
498
501
 
499
 
    tr_webRun( handle, url, NULL, got_blocklist, data );
 
502
    gtr_blocklist_update( data->core );
500
503
}
501
504
 
502
505
static void
503
 
onEncryptionToggled( GtkToggleButton * w, gpointer core )
 
506
onEncryptionToggled( GtkToggleButton * w,
 
507
                     gpointer          core )
504
508
{
505
509
    const int val = gtk_toggle_button_get_active( w )
506
 
                  ? TR_ENCRYPTION_REQUIRED
507
 
                  : TR_ENCRYPTION_PREFERRED;
 
510
                    ? TR_ENCRYPTION_REQUIRED
 
511
                    : TR_ENCRYPTION_PREFERRED;
 
512
 
508
513
    tr_core_set_pref_int( TR_CORE( core ), PREF_KEY_ENCRYPTION, val );
509
514
}
510
515
 
511
516
static GtkWidget*
512
 
peerPage( GObject * core, gboolean * alive )
 
517
peerPage( GObject * core )
513
518
{
514
 
    int row = 0;
515
 
    const char * s;
516
 
    GtkWidget * t;
517
 
    GtkWidget * w;
518
 
    GtkWidget * w2;
519
 
    GtkWidget * b;
520
 
    GtkWidget * h;
521
 
    GtkWidget * l;
 
519
    int                     row = 0;
 
520
    const char *            s;
 
521
    GtkWidget *             t;
 
522
    GtkWidget *             w;
 
523
    GtkWidget *             b;
 
524
    GtkWidget *             h;
522
525
    struct blocklist_data * data;
523
526
 
 
527
    data = g_new0( struct blocklist_data, 1 );
 
528
    data->core = TR_CORE( core );
 
529
 
524
530
    t = hig_workarea_create( );
525
 
    hig_workarea_add_section_title (t, &row, _("Options"));
526
 
 
527
 
        w = new_check_button( "", PREF_KEY_BLOCKLIST_ENABLED, core );
528
 
        updateBlocklistText( w, TR_CORE( core ) );
529
 
        h = gtk_hbox_new( FALSE, GUI_PAD_BIG );
530
 
        gtk_box_pack_start_defaults( GTK_BOX(h), w );
531
 
        b = gtk_button_new_with_mnemonic( _( "_Update Blocklist" ) );
532
 
 
533
 
        data = g_new0( struct blocklist_data, 1 );
534
 
        data->core = TR_CORE( core );
535
 
        data->check = w;
536
 
 
537
 
        g_object_set_data( G_OBJECT( b ), "handle", tr_core_handle( TR_CORE( core ) ) );
538
 
        g_signal_connect( b, "clicked", G_CALLBACK(onUpdateBlocklistCB), data );
539
 
        gtk_box_pack_start( GTK_BOX(h), b, FALSE, FALSE, 0 );
540
 
        g_signal_connect( w, "toggled", G_CALLBACK(target_cb), b );
541
 
        target_cb( w, b );
542
 
        hig_workarea_add_wide_control( t, &row, h );
543
 
        
544
 
        s = _("_Ignore unencrypted peers");
545
 
        w = gtk_check_button_new_with_mnemonic( s );
546
 
        gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(w),
547
 
                                      pref_int_get(PREF_KEY_ENCRYPTION)==TR_ENCRYPTION_REQUIRED );
548
 
        g_signal_connect( w, "toggled", G_CALLBACK(onEncryptionToggled), core );
549
 
        hig_workarea_add_wide_control( t, &row, w );
550
 
 
551
 
        s = _("Use peer e_xchange");
552
 
        w = new_check_button( s, PREF_KEY_PEX, core );
553
 
        hig_workarea_add_wide_control( t, &row, w );
554
 
 
555
 
        h = gtk_hbox_new( FALSE, GUI_PAD_BIG );
556
 
        w2 = new_spin_button( PREF_KEY_PORT, core, 1, INT_MAX, 1 );
557
 
        gtk_box_pack_start( GTK_BOX(h), w2, FALSE, FALSE, 0 );
558
 
        l = gtk_label_new( NULL );
559
 
        gtk_misc_set_alignment( GTK_MISC(l), 0.0f, 0.5f );
560
 
        gtk_box_pack_start( GTK_BOX(h), l, FALSE, FALSE, 0 );
561
 
        hig_workarea_add_row( t, &row, _("Listening _port:"), h, w2 );
562
 
 
563
 
        g_object_set_data( G_OBJECT(l), "tr-port-spin", w2 );
564
 
        g_object_set_data( G_OBJECT(l), "alive", alive );
565
 
        g_object_set_data( G_OBJECT(l), "handle", tr_core_handle( TR_CORE( core ) ) );
566
 
        testing_port_cb( NULL, l );
567
 
        g_signal_connect( w2, "value-changed", G_CALLBACK(testing_port_cb), l );
568
 
        
 
531
    hig_workarea_add_section_title( t, &row, _( "Blocklist" ) );
 
532
 
 
533
    w = new_check_button( "", PREF_KEY_BLOCKLIST_ENABLED, core );
 
534
    updateBlocklistText( w, TR_CORE( core ) );
 
535
    h = gtk_hbox_new( FALSE, GUI_PAD_BIG );
 
536
    gtk_box_pack_start( GTK_BOX( h ), w, TRUE, TRUE, 0 );
 
537
    b = gtr_button_new_from_stock( GTK_STOCK_REFRESH, _( "_Update" ) );
 
538
    data->check = w;
 
539
    g_object_set_data( G_OBJECT( b ), "session",
 
540
                      tr_core_session( TR_CORE( core ) ) );
 
541
    g_signal_connect( b, "clicked", G_CALLBACK( onUpdateBlocklistCB ), data );
 
542
    gtk_box_pack_start( GTK_BOX( h ), b, FALSE, FALSE, 0 );
 
543
    g_signal_connect( w, "toggled", G_CALLBACK( target_cb ), b );
 
544
    target_cb( w, b );
 
545
    hig_workarea_add_wide_control( t, &row, h );
 
546
 
 
547
    s = _( "Enable _automatic updates" );
 
548
    w = new_check_button( s, PREF_KEY_BLOCKLIST_UPDATES_ENABLED, core );
 
549
    hig_workarea_add_wide_control( t, &row, w );
 
550
    g_signal_connect( data->check, "toggled", G_CALLBACK( target_cb ), w );
 
551
    target_cb( data->check, w );
 
552
 
569
553
    hig_workarea_add_section_divider( t, &row );
570
554
    hig_workarea_add_section_title( t, &row, _( "Limits" ) );
571
 
  
572
 
        w = new_spin_button( PREF_KEY_MAX_PEERS_GLOBAL, core, 1, 3000, 5 );
573
 
        hig_workarea_add_row( t, &row, _( "Maximum peers _overall:" ), w, NULL );
574
 
        w = new_spin_button( PREF_KEY_MAX_PEERS_PER_TORRENT, core, 1, 300, 5 );
575
 
        hig_workarea_add_row( t, &row, _( "Maximum peers per _torrent:" ), w, NULL );
 
555
 
 
556
    w = new_spin_button( PREF_KEY_MAX_PEERS_GLOBAL, core, 1, 3000, 5 );
 
557
    hig_workarea_add_row( t, &row, _( "Maximum peers _overall:" ), w, NULL );
 
558
    w = new_spin_button( PREF_KEY_MAX_PEERS_PER_TORRENT, core, 1, 300, 5 );
 
559
    hig_workarea_add_row( t, &row, _(
 
560
                              "Maximum peers per _torrent:" ), w, NULL );
 
561
 
 
562
    hig_workarea_add_section_divider( t, &row );
 
563
    hig_workarea_add_section_title ( t, &row, _( "Options" ) );
 
564
 
 
565
    s = _( "_Ignore unencrypted peers" );
 
566
    w = gtk_check_button_new_with_mnemonic( s );
 
567
    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ),
 
568
                                  pref_int_get(
 
569
                                      PREF_KEY_ENCRYPTION ) ==
 
570
                                  TR_ENCRYPTION_REQUIRED );
 
571
    g_signal_connect( w, "toggled", G_CALLBACK( onEncryptionToggled ), core );
 
572
    hig_workarea_add_wide_control( t, &row, w );
 
573
 
 
574
    s = _( "Use peer e_xchange" );
 
575
    w = new_check_button( s, PREF_KEY_PEX, core );
 
576
    hig_workarea_add_wide_control( t, &row, w );
576
577
 
577
578
    hig_workarea_finish( t, &row );
578
579
    return t;
579
580
}
580
581
 
581
 
static GtkTreeModel*
582
 
allow_deny_model_new( void )
583
 
{
584
 
    GtkTreeIter iter;
585
 
    GtkListStore * store = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_CHAR );
586
 
    gtk_list_store_append( store, &iter );
587
 
    gtk_list_store_set( store, &iter, 0, _( "Allow" ), 1, '+', -1 );
588
 
    gtk_list_store_append( store, &iter );
589
 
    gtk_list_store_set( store, &iter, 0, _( "Deny" ), 1, '-', -1 );
590
 
    return GTK_TREE_MODEL( store );
591
 
}
 
582
/****
 
583
*****  Web Tab
 
584
****/
592
585
 
593
586
enum
594
587
{
595
588
    COL_ADDRESS,
596
 
    COL_PERMISSION,
597
589
    N_COLS
598
590
};
599
591
 
600
592
static GtkTreeModel*
601
 
acl_tree_model_new( const char * acl )
 
593
whitelist_tree_model_new( const char * whitelist )
602
594
{
603
 
    int i;
604
 
    char ** rules;
 
595
    int            i;
 
596
    char **        rules;
605
597
    GtkListStore * store = gtk_list_store_new( N_COLS,
606
598
                                               G_TYPE_STRING,
607
599
                                               G_TYPE_STRING );
608
 
    rules = g_strsplit( acl, ",", 0 );
609
 
 
610
 
    for( i=0; rules && rules[i]; ++i )
 
600
 
 
601
    rules = g_strsplit( whitelist, ",", 0 );
 
602
 
 
603
    for( i = 0; rules && rules[i]; ++i )
611
604
    {
 
605
        GtkTreeIter iter;
612
606
        const char * s = rules[i];
613
607
        while( isspace( *s ) ) ++s;
614
 
        if( *s=='+' || *s=='-' )
615
 
        {
616
 
            GtkTreeIter iter;
617
 
            gtk_list_store_append( store, &iter );
618
 
            gtk_list_store_set( store, &iter,
619
 
                COL_PERMISSION, *s=='+' ? _( "Allow" ) : _( "Deny" ) ,
620
 
                COL_ADDRESS, s+1,
621
 
                -1 );
622
 
        }
 
608
        gtk_list_store_append( store, &iter );
 
609
        gtk_list_store_set( store, &iter, COL_ADDRESS, s, -1 );
623
610
    }
624
611
 
625
612
    g_strfreev( rules );
628
615
 
629
616
struct remote_page
630
617
{
631
 
    TrCore * core;
632
 
    GtkTreeView * view;
633
 
    GtkListStore * store;
634
 
    GtkWidget * remove_button;
635
 
    GSList * widgets;
636
 
    GSList * auth_widgets;
637
 
    GtkToggleButton * rpc_tb;
638
 
    GtkToggleButton * auth_tb;
 
618
    TrCore *           core;
 
619
    GtkTreeView *      view;
 
620
    GtkListStore *     store;
 
621
    GtkWidget *        remove_button;
 
622
    GSList *           widgets;
 
623
    GSList *           auth_widgets;
 
624
    GSList *           whitelist_widgets;
 
625
    GtkToggleButton *  rpc_tb;
 
626
    GtkToggleButton *  auth_tb;
 
627
    GtkToggleButton *  whitelist_tb;
639
628
};
640
629
 
641
630
static void
642
 
refreshACL( struct remote_page * page )
 
631
refreshWhitelist( struct remote_page * page )
643
632
{
644
 
    GtkTreeIter iter;
 
633
    GtkTreeIter    iter;
645
634
    GtkTreeModel * model = GTK_TREE_MODEL( page->store );
646
 
    GString * gstr = g_string_new( NULL );
 
635
    GString *      gstr = g_string_new( NULL );
647
636
 
648
637
    if( gtk_tree_model_get_iter_first( model, &iter ) ) do
649
 
    {
650
 
        char * permission;
651
 
        char * address;
652
 
        gtk_tree_model_get( model, &iter, COL_PERMISSION, &permission,
653
 
                                          COL_ADDRESS, &address,
654
 
                                          -1 );
655
 
        g_string_append_c( gstr, strcmp(permission,_("Allow")) ? '-' : '+' );
656
 
        g_string_append( gstr, address );
657
 
        g_string_append( gstr, ", " );
658
 
        g_free( address );
659
 
        g_free( permission );
660
 
    }
661
 
    while( gtk_tree_model_iter_next( model, &iter ) );
662
 
 
663
 
    g_string_truncate( gstr, gstr->len-2 ); /* remove the trailing ", " */
664
 
 
665
 
    tr_core_set_pref( page->core, PREF_KEY_RPC_ACL, gstr->str );
 
638
        {
 
639
            char * address;
 
640
            gtk_tree_model_get( model, &iter,
 
641
                                COL_ADDRESS, &address,
 
642
                                -1 );
 
643
            g_string_append( gstr, address );
 
644
            g_string_append( gstr, "," );
 
645
            g_free( address );
 
646
        }
 
647
        while( gtk_tree_model_iter_next( model, &iter ) );
 
648
 
 
649
    g_string_truncate( gstr, gstr->len - 1 ); /* remove the trailing comma */
 
650
 
 
651
    tr_core_set_pref( page->core, PREF_KEY_RPC_WHITELIST, gstr->str );
666
652
 
667
653
    g_string_free( gstr, TRUE );
668
654
}
669
655
 
670
656
static void
671
 
onPermissionEdited( GtkCellRendererText  * renderer UNUSED,
672
 
                    gchar                * path_string,
673
 
                    gchar                * new_text,
674
 
                    gpointer               gpage )
 
657
onAddressEdited( GtkCellRendererText  * r UNUSED,
 
658
                 gchar *                  path_string,
 
659
                 gchar *                  address,
 
660
                 gpointer                 gpage )
675
661
{
676
 
    GtkTreeIter iter;
677
 
    GtkTreePath * path = gtk_tree_path_new_from_string( path_string );
 
662
    GtkTreeIter          iter;
678
663
    struct remote_page * page = gpage;
679
 
    GtkTreeModel * model = GTK_TREE_MODEL( page->store );
 
664
    GtkTreeModel *       model = GTK_TREE_MODEL( page->store );
 
665
    GtkTreePath *        path = gtk_tree_path_new_from_string( path_string );
 
666
 
680
667
    if( gtk_tree_model_get_iter( model, &iter, path ) )
681
 
        gtk_list_store_set( page->store, &iter, COL_PERMISSION, new_text, -1 );
682
 
    gtk_tree_path_free( path );
683
 
    refreshACL( page );
684
 
}
685
 
 
686
 
static void
687
 
onAddressEdited( GtkCellRendererText  * r UNUSED,
688
 
                 gchar                * path_string,
689
 
                 gchar                * new_text,
690
 
                 gpointer               gpage )
691
 
{
692
 
    char * acl;
693
 
    GtkTreeIter iter;
694
 
    struct remote_page * page = gpage;
695
 
    GtkTreeModel * model = GTK_TREE_MODEL( page->store );
696
 
    tr_handle * session = tr_core_handle( page->core );
697
 
    GtkTreePath * path = gtk_tree_path_new_from_string( path_string );
698
 
 
699
 
    acl = g_strdup_printf( "+%s", new_text );
700
 
    if( !tr_sessionTestRPCACL( session, acl, NULL ) )
701
 
        if( gtk_tree_model_get_iter( model, &iter, path ) )
702
 
            gtk_list_store_set( page->store, &iter, COL_ADDRESS, new_text, -1 );
703
 
 
704
 
    g_free( acl );
705
 
    gtk_tree_path_free( path );
706
 
    refreshACL( page );
707
 
}
708
 
 
709
 
static void
710
 
onAddACLClicked( GtkButton * b UNUSED, gpointer gpage )
711
 
{
712
 
    GtkTreeIter iter;
713
 
    GtkTreePath * path;
714
 
    struct remote_page * page = gpage;
 
668
        gtk_list_store_set( page->store, &iter, COL_ADDRESS, address, -1 );
 
669
 
 
670
    gtk_tree_path_free( path );
 
671
    refreshWhitelist( page );
 
672
}
 
673
 
 
674
static void
 
675
onAddWhitelistClicked( GtkButton * b UNUSED,
 
676
                 gpointer      gpage )
 
677
{
 
678
    GtkTreeIter          iter;
 
679
    GtkTreePath *        path;
 
680
    struct remote_page * page = gpage;
 
681
 
715
682
    gtk_list_store_append( page->store, &iter );
716
683
    gtk_list_store_set( page->store, &iter,
717
 
                        COL_PERMISSION, _( "Allow" ),
718
684
                        COL_ADDRESS,  "0.0.0.0",
719
685
                        -1 );
720
686
 
727
693
}
728
694
 
729
695
static void
730
 
onRemoveACLClicked( GtkButton * b UNUSED, gpointer gpage )
 
696
onRemoveWhitelistClicked( GtkButton * b UNUSED,
 
697
                    gpointer      gpage )
731
698
{
732
699
    struct remote_page * page = gpage;
733
 
    GtkTreeSelection * sel = gtk_tree_view_get_selection( page->view );
734
 
    GtkTreeIter iter;
 
700
    GtkTreeSelection *   sel = gtk_tree_view_get_selection( page->view );
 
701
    GtkTreeIter          iter;
 
702
 
735
703
    if( gtk_tree_selection_get_selected( sel, NULL, &iter ) )
736
704
    {
737
705
        gtk_list_store_remove( page->store, &iter );
738
 
        refreshACL( page );
 
706
        refreshWhitelist( page );
739
707
    }
740
708
}
741
709
 
742
710
static void
743
711
refreshRPCSensitivity( struct remote_page * page )
744
712
{
745
 
    GSList * l;
746
 
    const int rpc_active = gtk_toggle_button_get_active( page->rpc_tb );
747
 
    const int auth_active = gtk_toggle_button_get_active( page->auth_tb );
 
713
    GSList *           l;
 
714
    const int          rpc_active = gtk_toggle_button_get_active(
 
715
        page->rpc_tb );
 
716
    const int          auth_active = gtk_toggle_button_get_active(
 
717
        page->auth_tb );
 
718
    const int          whitelist_active = gtk_toggle_button_get_active(
 
719
        page->whitelist_tb );
748
720
    GtkTreeSelection * sel = gtk_tree_view_get_selection( page->view );
749
 
    const int have_addr = gtk_tree_selection_get_selected( sel, NULL, NULL );
750
 
    const int n_rules = gtk_tree_model_iter_n_children(
751
 
                                       GTK_TREE_MODEL( page->store ), NULL );
 
721
    const int          have_addr =
 
722
        gtk_tree_selection_get_selected( sel, NULL,
 
723
                                         NULL );
 
724
    const int          n_rules = gtk_tree_model_iter_n_children(
 
725
        GTK_TREE_MODEL( page->store ), NULL );
752
726
 
753
 
    for( l=page->widgets; l!=NULL; l=l->next )
 
727
    for( l = page->widgets; l != NULL; l = l->next )
754
728
        gtk_widget_set_sensitive( GTK_WIDGET( l->data ), rpc_active );
755
729
 
756
 
    for( l=page->auth_widgets; l!=NULL; l=l->next )
757
 
        gtk_widget_set_sensitive( GTK_WIDGET( l->data ), rpc_active && auth_active);
 
730
    for( l = page->auth_widgets; l != NULL; l = l->next )
 
731
        gtk_widget_set_sensitive( GTK_WIDGET(
 
732
                                      l->data ), rpc_active && auth_active );
 
733
 
 
734
    for( l = page->whitelist_widgets; l != NULL; l = l->next )
 
735
        gtk_widget_set_sensitive( GTK_WIDGET( l->data ),
 
736
                                  rpc_active && whitelist_active );
758
737
 
759
738
    gtk_widget_set_sensitive( page->remove_button,
760
 
                              rpc_active && have_addr && n_rules>1 );
761
 
}
762
 
 
763
 
static void
764
 
onRPCToggled( GtkToggleButton * tb UNUSED, gpointer page )
765
 
{
766
 
    refreshRPCSensitivity( page );
767
 
}
768
 
static void
769
 
onACLSelectionChanged( GtkTreeSelection * sel UNUSED, gpointer page )
770
 
{
771
 
    refreshRPCSensitivity( page );
772
 
}
773
 
 
774
 
static void
775
 
onLaunchClutchCB( GtkButton * w UNUSED, gpointer data UNUSED )
776
 
{
777
 
    int port = pref_int_get( PREF_KEY_RPC_PORT );
778
 
    char * url = g_strdup_printf( "http://localhost:%d/transmission/web", port );
 
739
                              rpc_active && have_addr && n_rules > 1 );
 
740
}
 
741
 
 
742
static void
 
743
onRPCToggled( GtkToggleButton * tb UNUSED,
 
744
              gpointer             page )
 
745
{
 
746
    refreshRPCSensitivity( page );
 
747
}
 
748
 
 
749
static void
 
750
onWhitelistSelectionChanged( GtkTreeSelection * sel UNUSED,
 
751
                       gpointer               page )
 
752
{
 
753
    refreshRPCSensitivity( page );
 
754
}
 
755
 
 
756
static void
 
757
onLaunchClutchCB( GtkButton * w UNUSED,
 
758
                  gpointer data UNUSED )
 
759
{
 
760
    int    port = pref_int_get( PREF_KEY_RPC_PORT );
 
761
    char * url = g_strdup_printf( "http://localhost:%d/transmission/web",
 
762
                                  port );
 
763
 
779
764
    gtr_open_file( url );
780
765
    g_free( url );
781
766
}
783
768
static GtkWidget*
784
769
webPage( GObject * core )
785
770
{
786
 
    const char  * s;
787
 
    int row = 0;
788
 
    GtkWidget * t;
789
 
    GtkWidget * w;
790
 
    GtkWidget * h;
 
771
    const char *         s;
 
772
    int                  row = 0;
 
773
    GtkWidget *          t;
 
774
    GtkWidget *          w;
 
775
    GtkWidget *          h;
791
776
    struct remote_page * page = g_new0( struct remote_page, 1 );
792
777
 
793
778
    page->core = TR_CORE( core );
797
782
 
798
783
    hig_workarea_add_section_title( t, &row, _( "Web Interface" ) );
799
784
 
800
 
        /* "enabled" checkbutton */
801
 
        s = _( "_Enable web interface" );
802
 
        w = new_check_button( s, PREF_KEY_RPC_ENABLED, core );
803
 
        page->rpc_tb = GTK_TOGGLE_BUTTON( w );
804
 
        g_signal_connect( w, "clicked", G_CALLBACK(onRPCToggled), page );
805
 
        h = gtk_hbox_new( FALSE, GUI_PAD_BIG );
806
 
        gtk_box_pack_start_defaults( GTK_BOX(h), w );
807
 
        w = gtk_button_new_from_stock( GTK_STOCK_OPEN );
808
 
        page->widgets = g_slist_append( page->widgets, w );
809
 
        g_signal_connect( w, "clicked", G_CALLBACK(onLaunchClutchCB), NULL );
810
 
        gtk_box_pack_start( GTK_BOX(h), w, FALSE, FALSE, 0 );
811
 
        hig_workarea_add_wide_control( t, &row, h );
812
 
 
813
 
        /* require authentication */
814
 
        s = _( "_Require username" );
815
 
        w = new_check_button( s, PREF_KEY_RPC_AUTH_ENABLED, core );
816
 
        hig_workarea_add_wide_control( t, &row, w );
817
 
        page->auth_tb = GTK_TOGGLE_BUTTON( w );
818
 
        page->widgets = g_slist_append( page->widgets, w );
819
 
        g_signal_connect( w, "clicked", G_CALLBACK(onRPCToggled), page );
820
 
 
821
 
        /* username */
822
 
        s = _( "_Username:" );
823
 
        w = new_entry( PREF_KEY_RPC_USERNAME, core );
824
 
        page->auth_widgets = g_slist_append( page->auth_widgets, w );
825
 
        w = hig_workarea_add_row( t, &row, s, w, NULL );
826
 
        page->auth_widgets = g_slist_append( page->auth_widgets, w );
827
 
 
828
 
        /* password */
829
 
        s = _( "Pass_word:" );
830
 
        w = new_entry( PREF_KEY_RPC_PASSWORD, core );
831
 
        gtk_entry_set_visibility( GTK_ENTRY( w ), FALSE );
832
 
        page->auth_widgets = g_slist_append( page->auth_widgets, w );
833
 
        w = hig_workarea_add_row( t, &row, s, w, NULL );
834
 
        page->auth_widgets = g_slist_append( page->auth_widgets, w );
835
 
 
836
 
        /* port */
837
 
        w = new_spin_button( PREF_KEY_RPC_PORT, core, 0, 65535, 1 );
838
 
        page->widgets = g_slist_append( page->widgets, w );
839
 
        w = hig_workarea_add_row( t, &row, _( "Listening _port:" ), w, NULL );
840
 
        page->widgets = g_slist_append( page->widgets, w );
841
 
 
842
 
        /* access control list */
843
 
        {
844
 
        const char * val = pref_string_get( PREF_KEY_RPC_ACL );
845
 
        GtkTreeModel * m = acl_tree_model_new( val );
 
785
    /* "enabled" checkbutton */
 
786
    s = _( "_Enable web interface" );
 
787
    w = new_check_button( s, PREF_KEY_RPC_ENABLED, core );
 
788
    page->rpc_tb = GTK_TOGGLE_BUTTON( w );
 
789
    g_signal_connect( w, "clicked", G_CALLBACK( onRPCToggled ), page );
 
790
    h = gtk_hbox_new( FALSE, GUI_PAD_BIG );
 
791
    gtk_box_pack_start( GTK_BOX( h ), w, TRUE, TRUE, 0 );
 
792
    w = gtk_button_new_from_stock( GTK_STOCK_OPEN );
 
793
    page->widgets = g_slist_append( page->widgets, w );
 
794
    g_signal_connect( w, "clicked", G_CALLBACK( onLaunchClutchCB ), NULL );
 
795
    gtk_box_pack_start( GTK_BOX( h ), w, FALSE, FALSE, 0 );
 
796
    hig_workarea_add_wide_control( t, &row, h );
 
797
 
 
798
    /* port */
 
799
    w = new_spin_button( PREF_KEY_RPC_PORT, core, 0, 65535, 1 );
 
800
    page->widgets = g_slist_append( page->widgets, w );
 
801
    w = hig_workarea_add_row( t, &row, _( "Listening _port:" ), w, NULL );
 
802
    page->widgets = g_slist_append( page->widgets, w );
 
803
 
 
804
    /* require authentication */
 
805
    s = _( "_Require username" );
 
806
    w = new_check_button( s, PREF_KEY_RPC_AUTH_ENABLED, core );
 
807
    hig_workarea_add_wide_control( t, &row, w );
 
808
    page->auth_tb = GTK_TOGGLE_BUTTON( w );
 
809
    page->widgets = g_slist_append( page->widgets, w );
 
810
    g_signal_connect( w, "clicked", G_CALLBACK( onRPCToggled ), page );
 
811
 
 
812
    /* username */
 
813
    s = _( "_Username:" );
 
814
    w = new_entry( PREF_KEY_RPC_USERNAME, core );
 
815
    page->auth_widgets = g_slist_append( page->auth_widgets, w );
 
816
    w = hig_workarea_add_row( t, &row, s, w, NULL );
 
817
    page->auth_widgets = g_slist_append( page->auth_widgets, w );
 
818
 
 
819
    /* password */
 
820
    s = _( "Pass_word:" );
 
821
    w = new_entry( PREF_KEY_RPC_PASSWORD, core );
 
822
    gtk_entry_set_visibility( GTK_ENTRY( w ), FALSE );
 
823
    page->auth_widgets = g_slist_append( page->auth_widgets, w );
 
824
    w = hig_workarea_add_row( t, &row, s, w, NULL );
 
825
    page->auth_widgets = g_slist_append( page->auth_widgets, w );
 
826
 
 
827
    /* require authentication */
 
828
    s = _( "Only allow the following IP _addresses to connect:" );
 
829
    w = new_check_button( s, PREF_KEY_RPC_WHITELIST_ENABLED, core );
 
830
    hig_workarea_add_wide_control( t, &row, w );
 
831
    page->whitelist_tb = GTK_TOGGLE_BUTTON( w );
 
832
    page->widgets = g_slist_append( page->widgets, w );
 
833
    g_signal_connect( w, "clicked", G_CALLBACK( onRPCToggled ), page );
 
834
 
 
835
    /* access control list */
 
836
    {
 
837
        const char *        val = pref_string_get( PREF_KEY_RPC_WHITELIST );
 
838
        GtkTreeModel *      m = whitelist_tree_model_new( val );
846
839
        GtkTreeViewColumn * c;
847
 
        GtkCellRenderer * r;
848
 
        GtkTreeSelection * sel;
849
 
        GtkTreeView * v;
850
 
        GtkWidget * w;
851
 
        GtkWidget * h;
852
 
        GtkTooltips * tips = gtk_tooltips_new( );
 
840
        GtkCellRenderer *   r;
 
841
        GtkTreeSelection *  sel;
 
842
        GtkTreeView *       v;
 
843
        GtkWidget *         w;
 
844
        GtkWidget *         h;
853
845
 
854
 
        s = _( "Access control list:" );
855
846
        page->store = GTK_LIST_STORE( m );
856
847
        w = gtk_tree_view_new_with_model( m );
 
848
        g_signal_connect( w, "button-release-event",
 
849
                          G_CALLBACK( on_tree_view_button_released ), NULL );
857
850
 
858
 
        page->widgets = g_slist_append( page->widgets, w );
 
851
        page->whitelist_widgets = g_slist_append( page->whitelist_widgets, w );
859
852
        v = page->view = GTK_TREE_VIEW( w );
860
 
        gtk_tooltips_set_tip( tips, w,
861
 
            _( "IP addresses may use wildcards, such as 192.168.*.*" ),
862
 
            NULL );
 
853
#if GTK_CHECK_VERSION( 2,12,0 )
 
854
        gtk_widget_set_tooltip_text( w,
 
855
                  _( "IP addresses may use wildcards, such as 192.168.*.*" ) );
 
856
#endif
863
857
        sel = gtk_tree_view_get_selection( v );
864
858
        g_signal_connect( sel, "changed",
865
 
                          G_CALLBACK( onACLSelectionChanged ), page );
 
859
                          G_CALLBACK( onWhitelistSelectionChanged ), page );
866
860
        g_object_unref( G_OBJECT( m ) );
867
861
        gtk_tree_view_set_headers_visible( v, TRUE );
868
862
        w = gtk_frame_new( NULL );
874
868
        g_signal_connect( r, "edited",
875
869
                          G_CALLBACK( onAddressEdited ), page );
876
870
        g_object_set( G_OBJECT( r ), "editable", TRUE, NULL );
877
 
        c = gtk_tree_view_column_new_with_attributes( _( "IP Address" ), r,
878
 
                "text", COL_ADDRESS,
879
 
                NULL );
 
871
        c = gtk_tree_view_column_new_with_attributes( NULL, r,
 
872
                                                      "text", COL_ADDRESS,
 
873
                                                      NULL );
880
874
        gtk_tree_view_column_set_expand( c, TRUE );
881
875
        gtk_tree_view_append_column( v, c );
 
876
        gtk_tree_view_set_headers_visible( v, FALSE );
882
877
 
 
878
        s = _( "Addresses:" );
883
879
        w = hig_workarea_add_row( t, &row, s, w, NULL );
884
 
        gtk_misc_set_alignment( GTK_MISC( w ), 0.0f, 0.1f );
885
 
        page->widgets = g_slist_append( page->widgets, w );
886
 
 
887
 
        /* permission column */
888
 
        m = allow_deny_model_new( );
889
 
        r = gtk_cell_renderer_combo_new( );
890
 
        g_object_set( G_OBJECT( r ), "model", m,
891
 
                                     "editable", TRUE,
892
 
                                     "has-entry", FALSE,
893
 
                                     "text-column", 0,
894
 
                                     NULL );
895
 
        c = gtk_tree_view_column_new_with_attributes( _( "Permission" ), r,
896
 
                "text", COL_PERMISSION,
897
 
                NULL );
898
 
        g_signal_connect( r, "edited",
899
 
                          G_CALLBACK( onPermissionEdited ), page );
900
 
        gtk_tree_view_append_column( v, c );
 
880
        gtk_misc_set_alignment( GTK_MISC( w ), 0.0f, 0.0f );
 
881
        gtk_misc_set_padding( GTK_MISC( w ), 0, GUI_PAD );
 
882
        page->whitelist_widgets = g_slist_append( page->whitelist_widgets, w );
901
883
 
902
884
        h = gtk_hbox_new( TRUE, GUI_PAD );
903
885
        w = gtk_button_new_from_stock( GTK_STOCK_REMOVE );
904
 
        g_signal_connect( w, "clicked", G_CALLBACK(onRemoveACLClicked), page );
 
886
        g_signal_connect( w, "clicked", G_CALLBACK(
 
887
                              onRemoveWhitelistClicked ), page );
905
888
        page->remove_button = w;
906
 
        onACLSelectionChanged( sel, page );
907
 
        gtk_box_pack_start_defaults( GTK_BOX( h ), w );
 
889
        onWhitelistSelectionChanged( sel, page );
 
890
        gtk_box_pack_start( GTK_BOX( h ), w, TRUE, TRUE, 0 );
908
891
        w = gtk_button_new_from_stock( GTK_STOCK_ADD );
909
 
        page->widgets = g_slist_append( page->widgets, w );
910
 
        g_signal_connect( w, "clicked", G_CALLBACK(onAddACLClicked), page );
911
 
        gtk_box_pack_start_defaults( GTK_BOX( h ), w );
 
892
        page->whitelist_widgets = g_slist_append( page->whitelist_widgets, w );
 
893
        g_signal_connect( w, "clicked", G_CALLBACK( onAddWhitelistClicked ), page );
 
894
        gtk_box_pack_start( GTK_BOX( h ), w, TRUE, TRUE, 0 );
912
895
        w = gtk_hbox_new( FALSE, 0 );
913
 
        gtk_box_pack_start_defaults( GTK_BOX( w ), gtk_alignment_new( 0, 0, 0, 0 ) );
 
896
        gtk_box_pack_start( GTK_BOX( w ), gtk_alignment_new( 0, 0, 0, 0 ),
 
897
                            TRUE, TRUE, 0 );
914
898
        gtk_box_pack_start( GTK_BOX( w ), h, FALSE, FALSE, 0 );
915
899
        hig_workarea_add_wide_control( t, &row, w );
916
 
        }
 
900
    }
917
901
 
918
902
    refreshRPCSensitivity( page );
919
903
    hig_workarea_finish( t, &row );
920
904
    return t;
921
905
}
922
906
 
 
907
/****
 
908
*****  Proxy Tab
 
909
****/
 
910
 
923
911
struct ProxyPage
924
912
{
925
 
    TrCore * core;
926
 
    GSList * proxy_widgets;
927
 
    GSList * proxy_auth_widgets;
 
913
    TrCore *  core;
 
914
    GSList *  proxy_widgets;
 
915
    GSList *  proxy_auth_widgets;
928
916
};
929
917
 
930
918
static void
931
919
refreshProxySensitivity( struct ProxyPage * p )
932
920
{
933
 
    GSList * l;
934
 
    const gboolean proxy_enabled = pref_flag_get( PREF_KEY_PROXY_SERVER_ENABLED );
935
 
    const gboolean proxy_auth_enabled = pref_flag_get( PREF_KEY_PROXY_AUTH_ENABLED );
 
921
    GSList *       l;
 
922
    const gboolean proxy_enabled = pref_flag_get(
 
923
        PREF_KEY_PROXY_SERVER_ENABLED );
 
924
    const gboolean proxy_auth_enabled = pref_flag_get(
 
925
        PREF_KEY_PROXY_AUTH_ENABLED );
936
926
 
937
 
    for( l=p->proxy_widgets; l!=NULL; l=l->next )
 
927
    for( l = p->proxy_widgets; l != NULL; l = l->next )
938
928
        gtk_widget_set_sensitive( GTK_WIDGET( l->data ), proxy_enabled );
939
929
 
940
 
    for( l=p->proxy_auth_widgets; l!=NULL; l=l->next )
 
930
    for( l = p->proxy_auth_widgets; l != NULL; l = l->next )
941
931
        gtk_widget_set_sensitive( GTK_WIDGET( l->data ),
942
 
                                  proxy_enabled && proxy_auth_enabled);
 
932
                                  proxy_enabled && proxy_auth_enabled );
943
933
}
944
934
 
945
935
static void
946
 
onProxyToggled( GtkToggleButton * tb UNUSED, gpointer user_data )
 
936
onProxyToggled( GtkToggleButton * tb UNUSED,
 
937
                gpointer             user_data )
947
938
{
948
939
    refreshProxySensitivity( user_data );
949
940
}
952
943
proxyPageFree( gpointer gpage )
953
944
{
954
945
    struct ProxyPage * page = gpage;
 
946
 
955
947
    g_slist_free( page->proxy_widgets );
956
948
    g_slist_free( page->proxy_auth_widgets );
957
949
    g_free( page );
960
952
static GtkTreeModel*
961
953
proxyTypeModelNew( void )
962
954
{
963
 
    GtkTreeIter iter;
 
955
    GtkTreeIter    iter;
964
956
    GtkListStore * store = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
 
957
 
965
958
    gtk_list_store_append( store, &iter );
966
959
    gtk_list_store_set( store, &iter, 0, "HTTP", 1, TR_PROXY_HTTP, -1 );
967
960
    gtk_list_store_append( store, &iter );
972
965
}
973
966
 
974
967
static void
975
 
onProxyTypeChanged( GtkComboBox * w, gpointer gpage )
 
968
onProxyTypeChanged( GtkComboBox * w,
 
969
                    gpointer      gpage )
976
970
{
977
971
    GtkTreeIter iter;
 
972
 
978
973
    if( gtk_combo_box_get_active_iter( w, &iter ) )
979
974
    {
980
975
        struct ProxyPage * page = gpage;
981
 
        int type = TR_PROXY_HTTP;
982
 
        gtk_tree_model_get( gtk_combo_box_get_model( w ), &iter, 1, &type, -1 );
983
 
        tr_core_set_pref_int( TR_CORE( page->core ), PREF_KEY_PROXY_TYPE, type );
 
976
        int                type = TR_PROXY_HTTP;
 
977
        gtk_tree_model_get( gtk_combo_box_get_model(
 
978
                                w ), &iter, 1, &type, -1 );
 
979
        tr_core_set_pref_int( TR_CORE(
 
980
                                  page->core ), PREF_KEY_PROXY_TYPE, type );
984
981
    }
985
982
}
986
983
 
987
984
static GtkWidget*
988
985
trackerPage( GObject * core )
989
986
{
990
 
    int row = 0;
991
 
    const char * s;
992
 
    GtkWidget * t;
993
 
    GtkWidget * w;
994
 
    GtkTreeModel * m;
995
 
    GtkCellRenderer * r;
 
987
    int                row = 0;
 
988
    const char *       s;
 
989
    GtkWidget *        t;
 
990
    GtkWidget *        w;
 
991
    GtkTreeModel *     m;
 
992
    GtkCellRenderer *  r;
996
993
    struct ProxyPage * page = tr_new0( struct ProxyPage, 1 );
997
994
 
998
995
    page->core = TR_CORE( core );
999
996
 
1000
997
    t = hig_workarea_create( );
1001
 
    hig_workarea_add_section_title (t, &row, _( "Tracker Proxy" ) );
1002
 
 
1003
 
        s = _( "Connect to tracker via a pro_xy" );
1004
 
        w = new_check_button( s, PREF_KEY_PROXY_SERVER_ENABLED, core );
1005
 
        g_signal_connect( w, "toggled", G_CALLBACK(onProxyToggled), page );
1006
 
        hig_workarea_add_wide_control( t, &row, w );
1007
 
 
1008
 
        s = _( "Proxy _server:" );
1009
 
        w = new_entry( PREF_KEY_PROXY_SERVER, core );
1010
 
        page->proxy_widgets = g_slist_append( page->proxy_widgets, w );
1011
 
        w = hig_workarea_add_row( t, &row, s, w, NULL );
1012
 
        page->proxy_widgets = g_slist_append( page->proxy_widgets, w );
1013
 
 
1014
 
        w = new_spin_button( PREF_KEY_PROXY_PORT, core, 0, 65536, 1 );
1015
 
        page->proxy_widgets = g_slist_append( page->proxy_widgets, w );
1016
 
        w = hig_workarea_add_row( t, &row, _( "Proxy _port:" ), w, NULL );
1017
 
        page->proxy_widgets = g_slist_append( page->proxy_widgets, w );
1018
 
 
1019
 
        s = _( "Proxy _type:" );
1020
 
        m = proxyTypeModelNew( );
1021
 
        w = gtk_combo_box_new_with_model( m );
1022
 
        r = gtk_cell_renderer_text_new( );
1023
 
        gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( w ), r, TRUE );
1024
 
        gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT( w ), r, "text", 0, NULL );
1025
 
        gtk_combo_box_set_active( GTK_COMBO_BOX( w ), pref_int_get( PREF_KEY_PROXY_TYPE ) );
1026
 
        g_signal_connect( w, "changed", G_CALLBACK(onProxyTypeChanged), page );
1027
 
        g_object_unref( G_OBJECT( m ) );
1028
 
        page->proxy_widgets = g_slist_append( page->proxy_widgets, w );
1029
 
        w = hig_workarea_add_row( t, &row, s, w, NULL );
1030
 
        page->proxy_widgets = g_slist_append( page->proxy_widgets, w );
1031
 
 
1032
 
        s = _( "_Authentication is required" );
1033
 
        w = new_check_button( s, PREF_KEY_PROXY_AUTH_ENABLED, core );
1034
 
        g_signal_connect( w, "toggled", G_CALLBACK(onProxyToggled), page );
1035
 
        hig_workarea_add_wide_control( t, &row, w );
1036
 
        page->proxy_widgets = g_slist_append( page->proxy_widgets, w );
1037
 
 
1038
 
        s = _( "_Username:" );
1039
 
        w = new_entry( PREF_KEY_PROXY_USERNAME, core );
1040
 
        page->proxy_auth_widgets = g_slist_append( page->proxy_auth_widgets, w );
1041
 
        w = hig_workarea_add_row( t, &row, s, w, NULL );
1042
 
        page->proxy_auth_widgets = g_slist_append( page->proxy_auth_widgets, w );
1043
 
 
1044
 
        s = _( "Pass_word:" );
1045
 
        w = new_entry( PREF_KEY_PROXY_PASSWORD, core );
1046
 
        gtk_entry_set_visibility( GTK_ENTRY( w ), FALSE );
1047
 
        page->proxy_auth_widgets = g_slist_append( page->proxy_auth_widgets, w );
1048
 
        w = hig_workarea_add_row( t, &row, s, w, NULL );
1049
 
        page->proxy_auth_widgets = g_slist_append( page->proxy_auth_widgets, w );
 
998
    hig_workarea_add_section_title ( t, &row, _( "Tracker Proxy" ) );
 
999
 
 
1000
    s = _( "Connect to tracker via a pro_xy" );
 
1001
    w = new_check_button( s, PREF_KEY_PROXY_SERVER_ENABLED, core );
 
1002
    g_signal_connect( w, "toggled", G_CALLBACK( onProxyToggled ), page );
 
1003
    hig_workarea_add_wide_control( t, &row, w );
 
1004
 
 
1005
    s = _( "Proxy _server:" );
 
1006
    w = new_entry( PREF_KEY_PROXY_SERVER, core );
 
1007
    page->proxy_widgets = g_slist_append( page->proxy_widgets, w );
 
1008
    w = hig_workarea_add_row( t, &row, s, w, NULL );
 
1009
    page->proxy_widgets = g_slist_append( page->proxy_widgets, w );
 
1010
 
 
1011
    w = new_spin_button( PREF_KEY_PROXY_PORT, core, 0, 65535, 1 );
 
1012
    page->proxy_widgets = g_slist_append( page->proxy_widgets, w );
 
1013
    w = hig_workarea_add_row( t, &row, _( "Proxy _port:" ), w, NULL );
 
1014
    page->proxy_widgets = g_slist_append( page->proxy_widgets, w );
 
1015
 
 
1016
    s = _( "Proxy _type:" );
 
1017
    m = proxyTypeModelNew( );
 
1018
    w = gtk_combo_box_new_with_model( m );
 
1019
    r = gtk_cell_renderer_text_new( );
 
1020
    gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( w ), r, TRUE );
 
1021
    gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT(
 
1022
                                        w ), r, "text", 0, NULL );
 
1023
    gtk_combo_box_set_active( GTK_COMBO_BOX( w ),
 
1024
                             pref_int_get( PREF_KEY_PROXY_TYPE ) );
 
1025
    g_signal_connect( w, "changed", G_CALLBACK( onProxyTypeChanged ), page );
 
1026
    g_object_unref( G_OBJECT( m ) );
 
1027
    page->proxy_widgets = g_slist_append( page->proxy_widgets, w );
 
1028
    w = hig_workarea_add_row( t, &row, s, w, NULL );
 
1029
    page->proxy_widgets = g_slist_append( page->proxy_widgets, w );
 
1030
 
 
1031
    s = _( "_Authentication is required" );
 
1032
    w = new_check_button( s, PREF_KEY_PROXY_AUTH_ENABLED, core );
 
1033
    g_signal_connect( w, "toggled", G_CALLBACK( onProxyToggled ), page );
 
1034
    hig_workarea_add_wide_control( t, &row, w );
 
1035
    page->proxy_widgets = g_slist_append( page->proxy_widgets, w );
 
1036
 
 
1037
    s = _( "_Username:" );
 
1038
    w = new_entry( PREF_KEY_PROXY_USERNAME, core );
 
1039
    page->proxy_auth_widgets = g_slist_append( page->proxy_auth_widgets, w );
 
1040
    w = hig_workarea_add_row( t, &row, s, w, NULL );
 
1041
    page->proxy_auth_widgets = g_slist_append( page->proxy_auth_widgets, w );
 
1042
 
 
1043
    s = _( "Pass_word:" );
 
1044
    w = new_entry( PREF_KEY_PROXY_PASSWORD, core );
 
1045
    gtk_entry_set_visibility( GTK_ENTRY( w ), FALSE );
 
1046
    page->proxy_auth_widgets = g_slist_append( page->proxy_auth_widgets, w );
 
1047
    w = hig_workarea_add_row( t, &row, s, w, NULL );
 
1048
    page->proxy_auth_widgets = g_slist_append( page->proxy_auth_widgets, w );
1050
1049
 
1051
1050
    hig_workarea_finish( t, &row );
1052
1051
    g_object_set_data_full( G_OBJECT( t ), "page", page, proxyPageFree );
1055
1054
    return t;
1056
1055
}
1057
1056
 
 
1057
/****
 
1058
*****  Bandwidth Tab
 
1059
****/
 
1060
 
 
1061
struct BandwidthPage
 
1062
{
 
1063
    TrCore *  core;
 
1064
    GSList *  sched_widgets;
 
1065
};
 
1066
 
 
1067
static void
 
1068
refreshSchedSensitivity( struct BandwidthPage * p )
 
1069
{
 
1070
    GSList *       l;
 
1071
    const gboolean sched_enabled = pref_flag_get(
 
1072
        PREF_KEY_SCHED_LIMIT_ENABLED );
 
1073
 
 
1074
    for( l = p->sched_widgets; l != NULL; l = l->next )
 
1075
        gtk_widget_set_sensitive( GTK_WIDGET( l->data ), sched_enabled );
 
1076
}
 
1077
 
 
1078
static void
 
1079
onSchedToggled( GtkToggleButton * tb UNUSED,
 
1080
                gpointer             user_data )
 
1081
{
 
1082
    refreshSchedSensitivity( user_data );
 
1083
}
 
1084
 
 
1085
static void
 
1086
onTimeComboChanged( GtkComboBox * w,
 
1087
                    gpointer      core )
 
1088
{
 
1089
    GtkTreeIter iter;
 
1090
 
 
1091
    if( gtk_combo_box_get_active_iter( w, &iter ) )
 
1092
    {
 
1093
        const char * key = g_object_get_data( G_OBJECT( w ), PREF_KEY );
 
1094
        int          val = 0;
 
1095
        gtk_tree_model_get( gtk_combo_box_get_model(
 
1096
                                w ), &iter, 0, &val, -1 );
 
1097
        tr_core_set_pref_int( TR_CORE( core ), key, val );
 
1098
    }
 
1099
}
 
1100
 
 
1101
static GtkWidget*
 
1102
new_time_combo( GObject *    core,
 
1103
                const char * key )
 
1104
{
 
1105
    int               val;
 
1106
    int               i;
 
1107
    GtkWidget *       w;
 
1108
    GtkCellRenderer * r;
 
1109
    GtkListStore *    store;
 
1110
 
 
1111
    /* build a store at 15 minute intervals */
 
1112
    store = gtk_list_store_new( 2, G_TYPE_INT, G_TYPE_STRING );
 
1113
    for( i = 0; i < 60 * 24; i += 15 )
 
1114
    {
 
1115
        char        buf[128];
 
1116
        GtkTreeIter iter;
 
1117
        struct tm   tm;
 
1118
        tm.tm_hour = i / 60;
 
1119
        tm.tm_min = i % 60;
 
1120
        strftime( buf, sizeof( buf ), "%I:%M %p", &tm );
 
1121
        gtk_list_store_append( store, &iter );
 
1122
        gtk_list_store_set( store, &iter, 0, i, 1, buf, -1 );
 
1123
    }
 
1124
 
 
1125
    /* build the widget */
 
1126
    w = gtk_combo_box_new_with_model( GTK_TREE_MODEL( store ) );
 
1127
    gtk_combo_box_set_wrap_width( GTK_COMBO_BOX( w ), 4 );
 
1128
    r = gtk_cell_renderer_text_new( );
 
1129
    gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( w ), r, TRUE );
 
1130
    gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT(
 
1131
                                        w ), r, "text", 1, NULL );
 
1132
    g_object_set_data_full( G_OBJECT( w ), PREF_KEY, tr_strdup(
 
1133
                                key ), g_free );
 
1134
    val = pref_int_get( key );
 
1135
    gtk_combo_box_set_active( GTK_COMBO_BOX( w ), val / ( 15 ) );
 
1136
    g_signal_connect( w, "changed", G_CALLBACK( onTimeComboChanged ), core );
 
1137
 
 
1138
    /* cleanup */
 
1139
    g_object_unref( G_OBJECT( store ) );
 
1140
    return w;
 
1141
}
 
1142
 
 
1143
static void
 
1144
bandwidthPageFree( gpointer gpage )
 
1145
{
 
1146
    struct BandwidthPage * page = gpage;
 
1147
 
 
1148
    g_slist_free( page->sched_widgets );
 
1149
    g_free( page );
 
1150
}
 
1151
 
 
1152
static GtkWidget*
 
1153
bandwidthPage( GObject * core )
 
1154
{
 
1155
    int                    row = 0;
 
1156
    const char *           s;
 
1157
    GtkWidget *            t;
 
1158
    GtkWidget *            w, * w2, * h, * l;
 
1159
    struct BandwidthPage * page = tr_new0( struct BandwidthPage, 1 );
 
1160
 
 
1161
    page->core = TR_CORE( core );
 
1162
 
 
1163
    t = hig_workarea_create( );
 
1164
    hig_workarea_add_section_title( t, &row, _( "Limits" ) );
 
1165
 
 
1166
    s = _( "Limit _download speed (KB/s):" );
 
1167
    w = new_check_button( s, PREF_KEY_DL_LIMIT_ENABLED, core );
 
1168
    w2 = new_spin_button( PREF_KEY_DL_LIMIT, core, 0, INT_MAX, 5 );
 
1169
    gtk_widget_set_sensitive( GTK_WIDGET( w2 ),
 
1170
                             pref_flag_get( PREF_KEY_DL_LIMIT_ENABLED ) );
 
1171
    g_signal_connect( w, "toggled", G_CALLBACK( target_cb ), w2 );
 
1172
    hig_workarea_add_row_w( t, &row, w, w2, NULL );
 
1173
 
 
1174
    s = _( "Limit _upload speed (KB/s):" );
 
1175
    w = new_check_button( s, PREF_KEY_UL_LIMIT_ENABLED, core );
 
1176
    w2 = new_spin_button( PREF_KEY_UL_LIMIT, core, 0, INT_MAX, 5 );
 
1177
    gtk_widget_set_sensitive( GTK_WIDGET( w2 ),
 
1178
                             pref_flag_get( PREF_KEY_UL_LIMIT_ENABLED ) );
 
1179
    g_signal_connect( w, "toggled", G_CALLBACK( target_cb ), w2 );
 
1180
    hig_workarea_add_row_w( t, &row, w, w2, NULL );
 
1181
 
 
1182
    hig_workarea_add_section_divider( t, &row );
 
1183
    hig_workarea_add_section_title( t, &row, _( "Scheduled Limits" ) );
 
1184
 
 
1185
    h = gtk_hbox_new( FALSE, 0 );
 
1186
    w2 = new_time_combo( core, PREF_KEY_SCHED_BEGIN );
 
1187
    page->sched_widgets = g_slist_append( page->sched_widgets, w2 );
 
1188
    gtk_box_pack_start( GTK_BOX( h ), w2, FALSE, FALSE, 0 );
 
1189
    w2 = gtk_label_new ( _( " and " ) );
 
1190
    page->sched_widgets = g_slist_append( page->sched_widgets, w2 );
 
1191
    gtk_box_pack_start( GTK_BOX( h ), w2, FALSE, FALSE, 0 );
 
1192
    w2 = new_time_combo( core, PREF_KEY_SCHED_END );
 
1193
    page->sched_widgets = g_slist_append( page->sched_widgets, w2 );
 
1194
    gtk_box_pack_start( GTK_BOX( h ), w2, FALSE, FALSE, 0 );
 
1195
 
 
1196
    s = _( "_Limit bandwidth between" );
 
1197
    w = new_check_button( s, PREF_KEY_SCHED_LIMIT_ENABLED, core );
 
1198
    g_signal_connect( w, "toggled", G_CALLBACK( onSchedToggled ), page );
 
1199
    hig_workarea_add_row_w( t, &row, w, h, NULL );
 
1200
 
 
1201
    w = new_spin_button( PREF_KEY_SCHED_DL_LIMIT, core, 0, INT_MAX, 5 );
 
1202
    page->sched_widgets = g_slist_append( page->sched_widgets, w );
 
1203
    l = hig_workarea_add_row( t, &row, _(
 
1204
                                  "Limit d_ownload speed (KB/s):" ), w,
 
1205
                              NULL );
 
1206
    page->sched_widgets = g_slist_append( page->sched_widgets, l );
 
1207
 
 
1208
    w = new_spin_button( PREF_KEY_SCHED_UL_LIMIT, core, 0, INT_MAX, 5 );
 
1209
    page->sched_widgets = g_slist_append( page->sched_widgets, w );
 
1210
    l = hig_workarea_add_row( t, &row, _(
 
1211
                                  "Limit u_pload speed (KB/s):" ), w, NULL );
 
1212
    page->sched_widgets = g_slist_append( page->sched_widgets, l );
 
1213
 
 
1214
    hig_workarea_finish( t, &row );
 
1215
    g_object_set_data_full( G_OBJECT( t ), "page", page, bandwidthPageFree );
 
1216
 
 
1217
    refreshSchedSensitivity( page );
 
1218
    return t;
 
1219
}
 
1220
 
 
1221
/****
 
1222
*****  Network Tab
 
1223
****/
 
1224
 
 
1225
struct test_port_data
 
1226
{
 
1227
    GtkWidget *  label;
 
1228
    gboolean *   alive;
 
1229
    char text[128];
 
1230
};
 
1231
 
 
1232
/* this is invoked in the gtk main loop's thread */
 
1233
static gboolean
 
1234
testing_port_done_idle( gpointer gdata )
 
1235
{
 
1236
    struct test_port_data * data = gdata;
 
1237
 
 
1238
    if( *data->alive )
 
1239
    {
 
1240
        gdk_threads_enter( );
 
1241
        gtk_label_set_markup( GTK_LABEL( data->label ), data->text );
 
1242
        gdk_threads_leave( );
 
1243
    }
 
1244
 
 
1245
    return FALSE;
 
1246
}
 
1247
 
 
1248
/* this is invoked in the libtransmission thread */
 
1249
static void
 
1250
testing_port_done( tr_session * session        UNUSED,
 
1251
                   long          response_code UNUSED,
 
1252
                   const void *                response,
 
1253
                   size_t                      response_len,
 
1254
                   void *                      gdata )
 
1255
{
 
1256
    struct test_port_data * data = gdata;
 
1257
 
 
1258
    if( *data->alive )
 
1259
    {
 
1260
        const int isOpen = response_len && *(char*)response == '1';
 
1261
        g_snprintf( data->text, sizeof( data->text ), isOpen 
 
1262
                    ? _( "Port is <b>open</b>" )
 
1263
                    : _( "Port is <b>closed</b>" ) );
 
1264
        g_idle_add( testing_port_done_idle, data );
 
1265
    }
 
1266
}
 
1267
 
 
1268
static gboolean
 
1269
testing_port_begin( gpointer gdata )
 
1270
{
 
1271
    struct test_port_data * data = gdata;
 
1272
 
 
1273
    if( *data->alive )
 
1274
    {
 
1275
        char            url[256];
 
1276
        GObject       * o       = G_OBJECT( data->label );
 
1277
        GtkSpinButton * spin    = g_object_get_data( o, "tr-port-spin" );
 
1278
        tr_session    * session = g_object_get_data( o, "session" );
 
1279
        const int       port    = gtk_spin_button_get_value_as_int( spin );
 
1280
        g_snprintf( url, sizeof( url ),
 
1281
                    "http://portcheck.transmissionbt.com/%d",
 
1282
                    port );
 
1283
        tr_webRun( session, url, NULL, testing_port_done, data );
 
1284
    }
 
1285
    return FALSE;
 
1286
}
 
1287
 
 
1288
struct network_page_data
 
1289
{
 
1290
    gboolean *   alive;
 
1291
    GtkWidget *  label;
 
1292
    guint        id;
 
1293
    TrCore *     core;
 
1294
};
 
1295
 
 
1296
static void
 
1297
onCorePrefsChanged( TrCore * core UNUSED,
 
1298
                    const char *  key,
 
1299
                    gpointer      gdata )
 
1300
{
 
1301
    if( !strcmp( key, PREF_KEY_PORT ) )
 
1302
    {
 
1303
        struct network_page_data * ndata = gdata;
 
1304
        struct test_port_data *    data;
 
1305
 
 
1306
        gtk_label_set_markup( GTK_LABEL( ndata->label ),
 
1307
                             _( "<i>Testing port...</i>" ) );
 
1308
 
 
1309
        /* wait three seconds to give the port forwarding time to kick in */
 
1310
        data = g_new0( struct test_port_data, 1 );
 
1311
        data->label = ndata->label;
 
1312
        data->alive = ndata->alive;
 
1313
        g_timeout_add( 3000, testing_port_begin, data );
 
1314
    }
 
1315
}
 
1316
 
 
1317
static void
 
1318
networkPageDestroyed( gpointer       gdata,
 
1319
                      GObject * dead UNUSED )
 
1320
{
 
1321
    struct network_page_data * data = gdata;
 
1322
 
 
1323
    *data->alive = FALSE;
 
1324
    g_signal_handler_disconnect( data->core, data->id );
 
1325
    g_free( data );
 
1326
}
 
1327
 
1058
1328
static GtkWidget*
1059
1329
networkPage( GObject * core )
1060
1330
{
1061
 
    int row = 0;
1062
 
    const char * s;
1063
 
    GtkWidget * t;
1064
 
    GtkWidget * w, * w2;
1065
 
 
 
1331
    int                        row = 0;
 
1332
    const char *               s;
 
1333
    GtkWidget *                t;
 
1334
    GtkWidget *                w;
 
1335
    GtkWidget *                w2;
 
1336
    GtkWidget *                h;
 
1337
    GtkWidget *                l;
 
1338
    struct network_page_data * data;
 
1339
 
 
1340
    /* register to stop listening to core prefs changes when the page is
 
1341
      destroyed */
 
1342
    data = g_new0( struct network_page_data, 1 );
 
1343
    data->core = TR_CORE( core );
 
1344
 
 
1345
    /* we leak this gboolean* s.t. we know it will still be alive when the port
 
1346
       check is done, whether the dialog was destroyed or not.  kind of
 
1347
       clumsy... */
 
1348
    data->alive = g_new( gboolean, 1 );
 
1349
    *data->alive = TRUE;
 
1350
 
 
1351
    /* build the page */
1066
1352
    t = hig_workarea_create( );
1067
 
    hig_workarea_add_section_title (t, &row, _( "Router" ) );
1068
 
 
1069
 
        s = _("Use UPnP or NAT-PMP port _forwarding from my router" );
1070
 
        w = new_check_button( s, PREF_KEY_PORT_FORWARDING, core );
1071
 
        hig_workarea_add_wide_control( t, &row, w );
1072
 
 
1073
 
    hig_workarea_add_section_divider( t, &row );
1074
 
    hig_workarea_add_section_title (t, &row, _("Bandwidth"));
1075
 
 
1076
 
        s = _("Limit _download speed (KB/s):");
1077
 
        w = new_check_button( s, PREF_KEY_DL_LIMIT_ENABLED, core );
1078
 
        w2 = new_spin_button( PREF_KEY_DL_LIMIT, core, 0, INT_MAX, 5 );
1079
 
        gtk_widget_set_sensitive( GTK_WIDGET(w2), pref_flag_get( PREF_KEY_DL_LIMIT_ENABLED ) );
1080
 
        g_signal_connect( w, "toggled", G_CALLBACK(target_cb), w2 );
1081
 
        hig_workarea_add_row_w( t, &row, w, w2, NULL );
1082
 
 
1083
 
        s = _("Limit _upload speed (KB/s):");
1084
 
        w = new_check_button( s, PREF_KEY_UL_LIMIT_ENABLED, core );
1085
 
        w2 = new_spin_button( PREF_KEY_UL_LIMIT, core, 0, INT_MAX, 5 );
1086
 
        gtk_widget_set_sensitive( GTK_WIDGET(w2), pref_flag_get( PREF_KEY_UL_LIMIT_ENABLED ) );
1087
 
        g_signal_connect( w, "toggled", G_CALLBACK(target_cb), w2 );
1088
 
        hig_workarea_add_row_w( t, &row, w, w2, NULL );
 
1353
    hig_workarea_add_section_title( t, &row, _( "Incoming Peers" ) );
 
1354
 
 
1355
    h = gtk_hbox_new( FALSE, GUI_PAD_BIG );
 
1356
    w2 = new_spin_button( PREF_KEY_PORT, core, 1, 65535, 1 );
 
1357
    gtk_box_pack_start( GTK_BOX( h ), w2, FALSE, FALSE, 0 );
 
1358
    data->label = l = gtk_label_new( NULL );
 
1359
    gtk_misc_set_alignment( GTK_MISC( l ), 0.0f, 0.5f );
 
1360
    gtk_box_pack_start( GTK_BOX( h ), l, FALSE, FALSE, 0 );
 
1361
    hig_workarea_add_row( t, &row, _( "Listening _port:" ), h, w2 );
 
1362
 
 
1363
    g_object_set_data( G_OBJECT( l ), "tr-port-spin", w2 );
 
1364
    g_object_set_data( G_OBJECT( l ), "session",
 
1365
                      tr_core_session( TR_CORE( core ) ) );
 
1366
    data->id = g_signal_connect( TR_CORE(
 
1367
                                     core ), "prefs-changed",
 
1368
                                 G_CALLBACK( onCorePrefsChanged ), data );
 
1369
    onCorePrefsChanged( NULL, PREF_KEY_PORT, data );
 
1370
 
 
1371
    s = _( "Use UPnP or NAT-PMP port _forwarding from my router" );
 
1372
    w = new_check_button( s, PREF_KEY_PORT_FORWARDING, core );
 
1373
    hig_workarea_add_wide_control( t, &row, w );
1089
1374
 
1090
1375
    hig_workarea_finish( t, &row );
 
1376
    g_object_weak_ref( G_OBJECT( t ), networkPageDestroyed, data );
 
1377
 
1091
1378
    return t;
1092
1379
}
1093
1380
 
 
1381
/****
 
1382
*****
 
1383
****/
 
1384
 
1094
1385
GtkWidget *
1095
 
tr_prefs_dialog_new( GObject * core, GtkWindow * parent )
 
1386
tr_prefs_dialog_new( GObject *   core,
 
1387
                     GtkWindow * parent )
1096
1388
{
1097
1389
    GtkWidget * d;
1098
1390
    GtkWidget * n;
1099
 
    gboolean * alive;
1100
 
 
1101
 
    alive = g_new( gboolean, 1 );
1102
 
    *alive = TRUE;
1103
 
 
1104
 
    d = gtk_dialog_new_with_buttons( _( "Transmission Preferences" ), parent,
 
1391
 
 
1392
    d = gtk_dialog_new_with_buttons( _(
 
1393
                                         "Transmission Preferences" ),
 
1394
                                     parent,
1105
1395
                                     GTK_DIALOG_DESTROY_WITH_PARENT,
1106
1396
                                     GTK_STOCK_HELP, GTK_RESPONSE_HELP,
1107
1397
                                     GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1108
1398
                                     NULL );
1109
 
    gtk_window_set_role( GTK_WINDOW(d), "transmission-preferences-dialog" );
 
1399
    gtk_window_set_role( GTK_WINDOW( d ), "transmission-preferences-dialog" );
1110
1400
    gtk_dialog_set_has_separator( GTK_DIALOG( d ), FALSE );
1111
1401
    gtk_container_set_border_width( GTK_CONTAINER( d ), GUI_PAD );
1112
 
    g_object_weak_ref( G_OBJECT( d ), dialogDestroyed, alive );
1113
1402
 
1114
1403
    n = gtk_notebook_new( );
1115
1404
    gtk_container_set_border_width ( GTK_CONTAINER ( n ), GUI_PAD );
1116
1405
 
1117
1406
    gtk_notebook_append_page( GTK_NOTEBOOK( n ),
1118
1407
                              torrentPage( core ),
1119
 
                              gtk_label_new (_("Torrents")) );
1120
 
    gtk_notebook_append_page( GTK_NOTEBOOK( n ),
1121
 
                              peerPage( core, alive ),
1122
 
                              gtk_label_new (_("Peers")) );
1123
 
    gtk_notebook_append_page( GTK_NOTEBOOK( n ),
1124
 
                              trackerPage( core ),
1125
 
                              gtk_label_new (_("Trackers")) );
 
1408
                              gtk_label_new ( _( "Torrents" ) ) );
 
1409
    gtk_notebook_append_page( GTK_NOTEBOOK( n ),
 
1410
                              peerPage( core ),
 
1411
                              gtk_label_new ( _( "Peers" ) ) );
1126
1412
    gtk_notebook_append_page( GTK_NOTEBOOK( n ),
1127
1413
                              networkPage( core ),
1128
 
                              gtk_label_new (_("Network")) );
 
1414
                              gtk_label_new ( _( "Network" ) ) );
 
1415
    gtk_notebook_append_page( GTK_NOTEBOOK( n ),
 
1416
                              desktopPage( core ),
 
1417
                              gtk_label_new ( _( "Desktop" ) ) );
 
1418
    gtk_notebook_append_page( GTK_NOTEBOOK( n ),
 
1419
                              bandwidthPage( core ),
 
1420
                              gtk_label_new ( _( "Bandwidth" ) ) );
1129
1421
    gtk_notebook_append_page( GTK_NOTEBOOK( n ),
1130
1422
                              webPage( core ),
1131
 
                              gtk_label_new (_("Web")) );
 
1423
                              gtk_label_new ( _( "Web" ) ) );
 
1424
    gtk_notebook_append_page( GTK_NOTEBOOK( n ),
 
1425
                              trackerPage( core ),
 
1426
                              gtk_label_new ( _( "Trackers" ) ) );
1132
1427
 
1133
 
    g_signal_connect( d, "response", G_CALLBACK(response_cb), core );
1134
 
    gtk_box_pack_start_defaults( GTK_BOX(GTK_DIALOG(d)->vbox), n );
1135
 
    gtk_widget_show_all( GTK_DIALOG(d)->vbox );
 
1428
    g_signal_connect( d, "response", G_CALLBACK( response_cb ), core );
 
1429
    gtk_box_pack_start( GTK_BOX( GTK_DIALOG( d )->vbox ), n, TRUE, TRUE, 0 );
 
1430
    gtk_widget_show_all( GTK_DIALOG( d )->vbox );
1136
1431
    return d;
1137
1432
}
 
1433