source: trunk/gtk/tr-window.c @ 12318

Last change on this file since 12318 was 12318, checked in by jordan, 11 years ago

(trunk gtk) use GQuarks instead of strings for keys in tr-icon and tr-window

  • Property svn:keywords set to Date Rev Author Id
File size: 31.6 KB
Line 
1/******************************************************************************
2 * $Id: tr-window.c 12318 2011-04-05 16:16:06Z jordan $
3 *
4 * Copyright (c) Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <string.h>
26
27#include <gtk/gtk.h>
28#include <glib/gi18n.h>
29
30#include <libtransmission/transmission.h>
31#include <libtransmission/utils.h> /* tr_formatter_speed_KBps() */
32
33#include "actions.h"
34#include "conf.h"
35#include "filter.h"
36#include "hig.h"
37#include "torrent-cell-renderer.h"
38#include "tr-prefs.h"
39#include "tr-window.h"
40#include "util.h"
41
42#if !GTK_CHECK_VERSION( 2, 8, 0 )
43static void
44gtk_tree_view_column_queue_resize( GtkTreeViewColumn * column ) /* yuck */
45{
46    const int spacing = gtk_tree_view_column_get_spacing( column );
47
48    gtk_tree_view_column_set_spacing( column, spacing + 1 );
49    gtk_tree_view_column_set_spacing( column, spacing );
50}
51
52#endif
53
54typedef struct
55{
56    GtkWidget *           speedlimit_on_item[2];
57    GtkWidget *           speedlimit_off_item[2];
58    GtkWidget *           ratio_on_item;
59    GtkWidget *           ratio_off_item;
60    GtkWidget *           scroll;
61    GtkWidget *           view;
62    GtkWidget *           toolbar;
63    GtkWidget *           filter;
64    GtkWidget *           status;
65    GtkWidget *           status_menu;
66    GtkWidget *           ul_lb;
67    GtkWidget *           dl_lb;
68    GtkWidget *           stats_lb;
69    GtkWidget *           gutter_lb;
70    GtkWidget *           alt_speed_image;
71    GtkWidget *           alt_speed_button;
72    GtkWidget *           options_menu;
73    GtkTreeSelection *    selection;
74    GtkCellRenderer *     renderer;
75    GtkTreeViewColumn *   column;
76    GtkTreeModel *        filter_model;
77    TrCore *              core;
78    gulong                pref_handler_id;
79}
80PrivateData;
81
82static GQuark
83get_private_data_key( void )
84{
85    static GQuark q = 0;
86    if( !q ) q = g_quark_from_static_string( "private-data" );
87    return q;
88}
89
90static PrivateData*
91get_private_data( TrWindow * w )
92{
93    return g_object_get_qdata ( G_OBJECT( w ), get_private_data_key( ) );
94}
95
96/***
97****
98***/
99
100static void
101on_popup_menu( GtkWidget * self UNUSED,
102               GdkEventButton * event )
103{
104    GtkWidget * menu = gtr_action_get_widget ( "/main-window-popup" );
105
106    gtk_menu_popup ( GTK_MENU( menu ), NULL, NULL, NULL, NULL,
107                    ( event ? event->button : 0 ),
108                    ( event ? event->time : 0 ) );
109}
110
111static void
112view_row_activated( GtkTreeView       * tree_view UNUSED,
113                    GtkTreePath       * path      UNUSED,
114                    GtkTreeViewColumn * column    UNUSED,
115                    gpointer            user_data UNUSED )
116{
117    gtr_action_activate( "show-torrent-properties" );
118}
119
120static GtkWidget*
121makeview( PrivateData * p )
122{
123    GtkWidget *         view;
124    GtkTreeViewColumn * col;
125    GtkTreeSelection *  sel;
126    GtkCellRenderer *   r;
127
128    view = gtk_tree_view_new( );
129    gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
130    gtk_tree_view_set_fixed_height_mode( GTK_TREE_VIEW( view ), TRUE );
131
132    p->selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
133
134    p->column = col = GTK_TREE_VIEW_COLUMN (g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
135        "title", _("Torrent"),
136        "resizable", TRUE,
137        "sizing", GTK_TREE_VIEW_COLUMN_FIXED,
138        NULL));
139
140    p->renderer = r = torrent_cell_renderer_new( );
141    gtk_tree_view_column_pack_start( col, r, FALSE );
142    gtk_tree_view_column_add_attribute( col, r, "torrent", MC_TORRENT );
143    gtk_tree_view_column_add_attribute( col, r, "piece-upload-speed", MC_SPEED_UP );
144    gtk_tree_view_column_add_attribute( col, r, "piece-download-speed", MC_SPEED_DOWN );
145
146    gtk_tree_view_append_column( GTK_TREE_VIEW( view ), col );
147    g_object_set( r, "xpad", GUI_PAD_SMALL, "ypad", GUI_PAD_SMALL, NULL );
148
149    gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( view ), TRUE );
150    sel = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
151    gtk_tree_selection_set_mode( GTK_TREE_SELECTION( sel ),
152                                 GTK_SELECTION_MULTIPLE );
153
154    g_signal_connect( view, "popup-menu",
155                      G_CALLBACK( on_popup_menu ), NULL );
156    g_signal_connect( view, "button-press-event",
157                      G_CALLBACK( on_tree_view_button_pressed ),
158                      (void *) on_popup_menu );
159    g_signal_connect( view, "button-release-event",
160                      G_CALLBACK( on_tree_view_button_released ), NULL );
161    g_signal_connect( view, "row-activated",
162                      G_CALLBACK( view_row_activated ), NULL );
163
164
165    gtk_tree_view_set_model( GTK_TREE_VIEW( view ), p->filter_model );
166    g_object_unref( p->filter_model );
167
168    return view;
169}
170
171static void syncAltSpeedButton( PrivateData * p );
172
173static void
174prefsChanged( TrCore * core UNUSED,
175              const char *  key,
176              gpointer      wind )
177{
178    PrivateData * p = get_private_data( GTK_WINDOW( wind ) );
179
180    if( !strcmp( key, PREF_KEY_COMPACT_VIEW ) )
181    {
182        g_object_set( p->renderer, "compact", gtr_pref_flag_get( key ), NULL );
183        /* since the cell size has changed, we need gtktreeview to revalidate
184         * its fixed-height mode values. Unfortunately there's not an API call
185         * for that, but it *does* revalidate when it thinks the style's been tweaked */
186        g_signal_emit_by_name( p->view, "style-set", NULL, NULL );
187    }
188    else if( !strcmp( key, PREF_KEY_STATUSBAR ) )
189    {
190        const gboolean isEnabled = gtr_pref_flag_get( key );
191        g_object_set( p->status, "visible", isEnabled, NULL );
192    }
193    else if( !strcmp( key, PREF_KEY_FILTERBAR ) )
194    {
195        const gboolean isEnabled = gtr_pref_flag_get( key );
196        g_object_set( p->filter, "visible", isEnabled, NULL );
197    }
198    else if( !strcmp( key, PREF_KEY_TOOLBAR ) )
199    {
200        const gboolean isEnabled = gtr_pref_flag_get( key );
201        g_object_set( p->toolbar, "visible", isEnabled, NULL );
202    }
203    else if( !strcmp( key, PREF_KEY_STATUSBAR_STATS ) )
204    {
205        gtr_window_refresh( (TrWindow*)wind );
206    }
207    else if( !strcmp( key, TR_PREFS_KEY_ALT_SPEED_ENABLED ) ||
208             !strcmp( key, TR_PREFS_KEY_ALT_SPEED_UP_KBps ) ||
209             !strcmp( key, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps ) )
210    {
211        syncAltSpeedButton( p );
212    }
213}
214
215static void
216privateFree( gpointer vprivate )
217{
218    PrivateData * p = vprivate;
219    g_signal_handler_disconnect( p->core, p->pref_handler_id );
220    g_free( p );
221}
222
223static void
224onYinYangReleased( GtkWidget * w UNUSED, gpointer vprivate )
225{
226    PrivateData * p = vprivate;
227
228    gtk_menu_popup( GTK_MENU( p->status_menu ),
229                    NULL, NULL, NULL, NULL, 0,
230                    gtk_get_current_event_time( ) );
231}
232
233#define STATS_MODE "stats-mode"
234
235static struct
236{
237    const char *  val, *i18n;
238} stats_modes[] = {
239    { "total-ratio",      N_( "Total Ratio" )                },
240    { "session-ratio",    N_( "Session Ratio" )              },
241    { "total-transfer",   N_( "Total Transfer" )             },
242    { "session-transfer", N_( "Session Transfer" )           }
243};
244
245static void
246status_menu_toggled_cb( GtkCheckMenuItem * menu_item,
247                        gpointer           vprivate )
248{
249    if( gtk_check_menu_item_get_active( menu_item ) )
250    {
251        PrivateData * p = vprivate;
252        const char *  val = g_object_get_data( G_OBJECT(
253                                                   menu_item ), STATS_MODE );
254        gtr_core_set_pref( p->core, PREF_KEY_STATUSBAR_STATS, val );
255    }
256}
257
258static void
259syncAltSpeedButton( PrivateData * p )
260{
261    char u[32];
262    char d[32];
263    char * str;
264    const char * fmt;
265    const gboolean b = gtr_pref_flag_get( TR_PREFS_KEY_ALT_SPEED_ENABLED );
266    const char * stock = b ? "alt-speed-on" : "alt-speed-off";
267    GtkWidget * w = p->alt_speed_button;
268
269    tr_formatter_speed_KBps( u, gtr_pref_int_get( TR_PREFS_KEY_ALT_SPEED_UP_KBps ), sizeof( u ) );
270    tr_formatter_speed_KBps( d, gtr_pref_int_get( TR_PREFS_KEY_ALT_SPEED_DOWN_KBps ), sizeof( d ) );
271    fmt = b ? _( "Click to disable Alternative Speed Limits\n(%1$s down, %2$s up)" )
272            : _( "Click to enable Alternative Speed Limits\n(%1$s down, %2$s up)" );
273    str = g_strdup_printf( fmt, d, u );
274
275    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), b );
276    gtk_image_set_from_stock( GTK_IMAGE( p->alt_speed_image ), stock, -1 );
277    gtk_button_set_alignment( GTK_BUTTON( w ), 0.5, 0.5 );
278    gtr_widget_set_tooltip_text( w, str );
279
280    g_free( str );
281}
282
283static void
284alt_speed_toggled_cb( GtkToggleButton * button, gpointer vprivate )
285{
286    PrivateData * p = vprivate;
287    const gboolean b = gtk_toggle_button_get_active( button );
288    gtr_core_set_pref_bool( p->core, TR_PREFS_KEY_ALT_SPEED_ENABLED,  b );
289}
290
291/***
292****  FILTER
293***/
294
295#if GTK_CHECK_VERSION( 2, 12, 0 )
296
297static void
298findMaxAnnounceTime( GtkTreeModel *      model,
299                     GtkTreePath  * path UNUSED,
300                     GtkTreeIter *       iter,
301                     gpointer            gmaxTime )
302{
303    tr_torrent *    tor;
304    const tr_stat * torStat;
305    time_t *        maxTime = gmaxTime;
306
307    gtk_tree_model_get( model, iter, MC_TORRENT, &tor, -1 );
308    torStat = tr_torrentStatCached( tor );
309    *maxTime = MAX( *maxTime, torStat->manualAnnounceTime );
310}
311
312static gboolean
313onAskTrackerQueryTooltip( GtkWidget *            widget UNUSED,
314                          gint                   x UNUSED,
315                          gint                   y UNUSED,
316                          gboolean               keyboard_tip UNUSED,
317                          GtkTooltip *           tooltip,
318                          gpointer               gdata )
319{
320    const time_t now = time( NULL );
321    time_t       maxTime = 0;
322    PrivateData * p = gdata;
323
324    gtk_tree_selection_selected_foreach( p->selection,
325                                         findMaxAnnounceTime,
326                                         &maxTime );
327    if( maxTime <= now )
328    {
329        return FALSE;
330    }
331    else
332    {
333        char      buf[512];
334        char      timebuf[64];
335        const int seconds = maxTime - now;
336
337        tr_strltime( timebuf, seconds, sizeof( timebuf ) );
338        g_snprintf( buf, sizeof( buf ),
339                    _( "Tracker will allow requests in %s" ), timebuf );
340        gtk_tooltip_set_text( tooltip, buf );
341        return TRUE;
342    }
343}
344
345#endif
346
347static gboolean
348onAltSpeedToggledIdle( gpointer vp )
349{
350    PrivateData * p = vp;
351    gboolean b = tr_sessionUsesAltSpeed( gtr_core_session( p->core ) );
352    gtr_core_set_pref_bool( p->core, TR_PREFS_KEY_ALT_SPEED_ENABLED, b );
353
354    return FALSE;
355}
356
357static void
358onAltSpeedToggled( tr_session * s UNUSED, bool isEnabled UNUSED, bool byUser UNUSED, void * p )
359{
360    gtr_idle_add( onAltSpeedToggledIdle, p );
361}
362
363/***
364****  Speed limit menu
365***/
366
367#define DIRECTION_KEY "direction-key"
368#define ENABLED_KEY "enabled-key"
369#define SPEED_KEY "speed-key"
370
371static void
372onSpeedToggled( GtkCheckMenuItem * check, gpointer vp )
373{
374    PrivateData * p = vp;
375    GObject * o = G_OBJECT( check );
376    gboolean isEnabled = g_object_get_data( o, ENABLED_KEY ) != 0;
377    tr_direction dir = GPOINTER_TO_INT( g_object_get_data( o, DIRECTION_KEY ) );
378    const char * key = dir == TR_UP ? TR_PREFS_KEY_USPEED_ENABLED
379                                    : TR_PREFS_KEY_DSPEED_ENABLED;
380
381    if( gtk_check_menu_item_get_active( check ) )
382        gtr_core_set_pref_bool( p->core, key, isEnabled );
383}
384
385static void
386onSpeedSet( GtkCheckMenuItem * check, gpointer vp )
387{
388    const char * key;
389    PrivateData * p = vp;
390    GObject * o = G_OBJECT( check );
391    const int KBps = GPOINTER_TO_INT( g_object_get_data( o, SPEED_KEY ) );
392    tr_direction dir = GPOINTER_TO_INT( g_object_get_data( o, DIRECTION_KEY ) );
393
394    key = dir==TR_UP ? TR_PREFS_KEY_USPEED_KBps : TR_PREFS_KEY_DSPEED_KBps;
395    gtr_core_set_pref_int( p->core, key, KBps );
396
397    key = dir==TR_UP ? TR_PREFS_KEY_USPEED_ENABLED : TR_PREFS_KEY_DSPEED_ENABLED;
398    gtr_core_set_pref_bool( p->core, key, TRUE );
399}
400
401static GtkWidget*
402createSpeedMenu( PrivateData * p, tr_direction dir )
403{
404    int i, n;
405    GtkWidget *w, *m;
406    const int speeds_KBps[] = { 5, 10, 20, 30, 40, 50, 75, 100, 150, 200, 250, 500, 750 };
407
408    m = gtk_menu_new( );
409
410    w = gtk_radio_menu_item_new_with_label( NULL, _( "Unlimited" ) );
411    p->speedlimit_off_item[dir] = w;
412    g_object_set_data( G_OBJECT( w ), DIRECTION_KEY, GINT_TO_POINTER( dir ) );
413    g_object_set_data( G_OBJECT( w ), ENABLED_KEY, GINT_TO_POINTER( FALSE ) );
414    g_signal_connect( w, "toggled", G_CALLBACK(onSpeedToggled), p );
415    gtk_menu_shell_append( GTK_MENU_SHELL( m ), w );
416
417    w = gtk_radio_menu_item_new_with_label_from_widget( GTK_RADIO_MENU_ITEM( w ), "" );
418    p->speedlimit_on_item[dir] = w;
419    g_object_set_data( G_OBJECT( w ), DIRECTION_KEY, GINT_TO_POINTER( dir ) );
420    g_object_set_data( G_OBJECT( w ), ENABLED_KEY, GINT_TO_POINTER( TRUE ) );
421    g_signal_connect( w, "toggled", G_CALLBACK(onSpeedToggled), p );
422    gtk_menu_shell_append( GTK_MENU_SHELL( m ), w );
423
424    w = gtk_separator_menu_item_new( );
425    gtk_menu_shell_append( GTK_MENU_SHELL( m ), w );
426
427    for( i=0, n=G_N_ELEMENTS(speeds_KBps); i<n; ++i )
428    {
429        char buf[128];
430        tr_formatter_speed_KBps( buf, speeds_KBps[i], sizeof( buf ) );
431        w = gtk_menu_item_new_with_label( buf );
432        g_object_set_data( G_OBJECT( w ), DIRECTION_KEY, GINT_TO_POINTER( dir ) );
433        g_object_set_data( G_OBJECT( w ), SPEED_KEY, GINT_TO_POINTER( speeds_KBps[i] ) );
434        g_signal_connect( w, "activate", G_CALLBACK(onSpeedSet), p );
435        gtk_menu_shell_append( GTK_MENU_SHELL( m ), w );
436    }
437
438    return m;
439}
440
441/***
442****  Speed limit menu
443***/
444
445#define RATIO_KEY "stock-ratio-index"
446
447static const double stockRatios[] = { 0.25, 0.5, 0.75, 1, 1.5, 2, 3 };
448
449static void
450onRatioToggled( GtkCheckMenuItem * check, gpointer vp )
451{
452    PrivateData * p = vp;
453    if( gtk_check_menu_item_get_active( check ) )
454    {
455        gboolean f = g_object_get_data( G_OBJECT( check ), ENABLED_KEY ) != 0;
456        gtr_core_set_pref_bool( p->core, TR_PREFS_KEY_RATIO_ENABLED, f );
457    }
458}
459static void
460onRatioSet( GtkCheckMenuItem * check, gpointer vp )
461{
462    PrivateData * p = vp;
463    int i = GPOINTER_TO_INT( g_object_get_data( G_OBJECT( check ), RATIO_KEY ) );
464    const double ratio = stockRatios[i];
465    gtr_core_set_pref_double( p->core, TR_PREFS_KEY_RATIO, ratio );
466    gtr_core_set_pref_bool  ( p->core, TR_PREFS_KEY_RATIO_ENABLED, TRUE );
467}
468
469static GtkWidget*
470createRatioMenu( PrivateData * p )
471{
472    int i, n;
473    GtkWidget *m, *w;
474
475    m = gtk_menu_new( );
476
477    w = gtk_radio_menu_item_new_with_label( NULL, _( "Seed Forever" ) );
478    p->ratio_off_item = w;
479    g_object_set_data( G_OBJECT( w ), ENABLED_KEY, GINT_TO_POINTER( FALSE ) );
480    g_signal_connect( w, "toggled", G_CALLBACK(onRatioToggled), p );
481    gtk_menu_shell_append( GTK_MENU_SHELL( m ), w );
482
483    w = gtk_radio_menu_item_new_with_label_from_widget( GTK_RADIO_MENU_ITEM( w ), "" );
484    p->ratio_on_item = w;
485    g_object_set_data( G_OBJECT( w ), ENABLED_KEY, GINT_TO_POINTER( TRUE ) );
486    g_signal_connect( w, "toggled", G_CALLBACK(onRatioToggled), p );
487    gtk_menu_shell_append( GTK_MENU_SHELL( m ), w );
488
489    w = gtk_separator_menu_item_new( );
490    gtk_menu_shell_append( GTK_MENU_SHELL( m ), w );
491
492    for( i=0, n=G_N_ELEMENTS(stockRatios); i<n; ++i )
493    {
494        char buf[128];
495        tr_strlratio( buf, stockRatios[i], sizeof( buf ) );
496        w = gtk_menu_item_new_with_label( buf );
497        g_object_set_data( G_OBJECT( w ), RATIO_KEY, GINT_TO_POINTER( i ) );
498        g_signal_connect( w, "activate", G_CALLBACK(onRatioSet), p );
499        gtk_menu_shell_append( GTK_MENU_SHELL( m ), w );
500    }
501
502    return m;
503}
504
505/***
506****  Option menu
507***/
508
509static GtkWidget*
510createOptionsMenu( PrivateData * p )
511{
512    GtkWidget * m;
513    GtkWidget * top = gtk_menu_new( );
514
515    m = gtk_menu_item_new_with_label( _( "Limit Download Speed" ) );
516    gtk_menu_item_set_submenu( GTK_MENU_ITEM( m ), createSpeedMenu( p, TR_DOWN ) );
517    gtk_menu_shell_append( GTK_MENU_SHELL( top ), m );
518
519    m = gtk_menu_item_new_with_label( _( "Limit Upload Speed" ) );
520    gtk_menu_item_set_submenu( GTK_MENU_ITEM( m ), createSpeedMenu( p, TR_UP ) );
521    gtk_menu_shell_append( GTK_MENU_SHELL( top ), m );
522
523    m = gtk_separator_menu_item_new( );
524    gtk_menu_shell_append( GTK_MENU_SHELL( top ), m );
525
526    m = gtk_menu_item_new_with_label( _( "Stop Seeding at Ratio" ) );
527    gtk_menu_item_set_submenu( GTK_MENU_ITEM( m ), createRatioMenu( p ) );
528    gtk_menu_shell_append( GTK_MENU_SHELL( top ), m );
529
530    gtk_widget_show_all( top );
531    return top;
532}
533
534static void
535onOptionsClicked( GtkButton * button UNUSED, gpointer vp )
536{
537    char buf1[512];
538    char buf2[512];
539    gboolean b;
540    GtkWidget * w;
541    PrivateData * p = vp;
542
543    w = p->speedlimit_on_item[TR_DOWN];
544    tr_formatter_speed_KBps( buf1, gtr_pref_int_get( TR_PREFS_KEY_DSPEED_KBps ), sizeof( buf1 ) );
545    gtr_label_set_text( GTK_LABEL( gtk_bin_get_child( GTK_BIN( w ) ) ), buf1 );
546
547    b = gtr_pref_flag_get( TR_PREFS_KEY_DSPEED_ENABLED );
548    w = b ? p->speedlimit_on_item[TR_DOWN] : p->speedlimit_off_item[TR_DOWN];
549    gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( w ), TRUE );
550
551    w = p->speedlimit_on_item[TR_UP];
552    tr_formatter_speed_KBps( buf1, gtr_pref_int_get( TR_PREFS_KEY_USPEED_KBps ), sizeof( buf1 ) );
553    gtr_label_set_text( GTK_LABEL( gtk_bin_get_child( GTK_BIN( w ) ) ), buf1 );
554
555    b = gtr_pref_flag_get( TR_PREFS_KEY_USPEED_ENABLED );
556    w = b ? p->speedlimit_on_item[TR_UP] : p->speedlimit_off_item[TR_UP];
557    gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( w ), TRUE );
558
559    tr_strlratio( buf1, gtr_pref_double_get( TR_PREFS_KEY_RATIO ), sizeof( buf1 ) );
560    g_snprintf( buf2, sizeof( buf2 ), _( "Stop at Ratio (%s)" ), buf1 );
561    gtr_label_set_text( GTK_LABEL( gtk_bin_get_child( GTK_BIN( p->ratio_on_item ) ) ), buf2 );
562
563    b = gtr_pref_flag_get( TR_PREFS_KEY_RATIO_ENABLED );
564    gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( b ? p->ratio_on_item : p->ratio_off_item ), TRUE );
565
566    gtk_menu_popup ( GTK_MENU( p->options_menu ), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time( ) );
567}
568
569/***
570****  PUBLIC
571***/
572
573GtkWidget *
574gtr_window_new( GtkUIManager * ui_mgr, TrCore * core )
575{
576    int           i, n;
577    const char  * pch;
578    PrivateData * p;
579    GtkWidget   * mainmenu, *toolbar, *filter, *list, *status;
580    GtkWidget   * vbox, *w, *self, *h, *hbox, *menu;
581    GtkWindow   * win;
582    GSList      * l;
583
584    p = g_new0( PrivateData, 1 );
585
586    /* make the window */
587    self = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
588    g_object_set_qdata_full( G_OBJECT(self), get_private_data_key( ), p, privateFree );
589    win = GTK_WINDOW( self );
590    gtk_window_set_title( win, g_get_application_name( ) );
591    gtk_window_set_role( win, "tr-main" );
592    gtk_window_set_default_size( win,
593                                 gtr_pref_int_get( PREF_KEY_MAIN_WINDOW_WIDTH ),
594                                 gtr_pref_int_get( PREF_KEY_MAIN_WINDOW_HEIGHT ) );
595    gtk_window_move( win, gtr_pref_int_get( PREF_KEY_MAIN_WINDOW_X ),
596                          gtr_pref_int_get( PREF_KEY_MAIN_WINDOW_Y ) );
597    if( gtr_pref_flag_get( PREF_KEY_MAIN_WINDOW_IS_MAXIMIZED ) )
598        gtk_window_maximize( win );
599    gtk_window_add_accel_group( win, gtk_ui_manager_get_accel_group( ui_mgr ) );
600
601    /* window's main container */
602    vbox = gtk_vbox_new ( FALSE, 0 );
603    gtk_container_add ( GTK_CONTAINER( self ), vbox );
604
605    /* main menu */
606    mainmenu = gtr_action_get_widget( "/main-window-menu" );
607    w = gtr_action_get_widget( "/main-window-menu/torrent-menu/update-tracker" );
608#if GTK_CHECK_VERSION( 2, 12, 0 )
609    g_signal_connect( w, "query-tooltip",
610                      G_CALLBACK( onAskTrackerQueryTooltip ), p );
611#endif
612
613    /* toolbar */
614    toolbar = p->toolbar = gtr_action_get_widget( "/main-window-toolbar" );
615    gtr_action_set_important( "open-torrent-toolbar", TRUE );
616    gtr_action_set_important( "show-torrent-properties", TRUE );
617
618    /* filter */
619    h = filter = p->filter = gtr_filter_bar_new( gtr_core_session( core ),
620                                                 gtr_core_model( core ),
621                                                 &p->filter_model );
622    gtk_container_set_border_width( GTK_CONTAINER( h ), GUI_PAD_SMALL );
623
624    /* status menu */
625    menu = p->status_menu = gtk_menu_new( );
626    l = NULL;
627    pch = gtr_pref_string_get( PREF_KEY_STATUSBAR_STATS );
628    for( i = 0, n = G_N_ELEMENTS( stats_modes ); i < n; ++i )
629    {
630        const char * val = stats_modes[i].val;
631        w = gtk_radio_menu_item_new_with_label( l, _( stats_modes[i].i18n ) );
632        l = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM( w ) );
633        gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( w ),
634                                       !strcmp( val, pch ) );
635        g_object_set_data( G_OBJECT(
636                               w ), STATS_MODE,
637                           (gpointer)stats_modes[i].val );
638        g_signal_connect( w, "toggled", G_CALLBACK(
639                              status_menu_toggled_cb ), p );
640        gtk_menu_shell_append( GTK_MENU_SHELL( menu ), w );
641        gtk_widget_show( w );
642    }
643
644    /* status */
645    h = status = p->status = gtk_hbox_new( FALSE, GUI_PAD_BIG );
646    gtk_container_set_border_width( GTK_CONTAINER( h ), GUI_PAD_SMALL );
647
648        w = gtk_button_new( );
649        gtk_container_add( GTK_CONTAINER( w ), gtk_image_new_from_stock( "utilities", -1 ) );
650        gtr_widget_set_tooltip_text( w, _( "Options" ) );
651        gtk_box_pack_start( GTK_BOX( h ), w, 0, 0, 0 );
652        gtk_button_set_relief( GTK_BUTTON( w ), GTK_RELIEF_NONE );
653        p->options_menu = createOptionsMenu( p );
654        g_signal_connect( w, "clicked", G_CALLBACK(onOptionsClicked), p );
655
656        p->alt_speed_image = gtk_image_new( );
657        w = p->alt_speed_button = gtk_toggle_button_new( );
658        gtk_button_set_image( GTK_BUTTON( w ), p->alt_speed_image );
659        gtk_button_set_relief( GTK_BUTTON( w ), GTK_RELIEF_NONE );
660        g_signal_connect( w, "toggled", G_CALLBACK(alt_speed_toggled_cb ), p );
661        gtk_box_pack_start( GTK_BOX( h ), w, 0, 0, 0 );
662
663        w = p->gutter_lb = gtk_label_new( "N Torrents" );
664        gtk_label_set_single_line_mode( GTK_LABEL( w ), TRUE );
665        gtk_box_pack_start( GTK_BOX( h ), w, 1, 1, GUI_PAD );
666
667        hbox = gtk_hbox_new( FALSE, GUI_PAD );
668            w = gtk_alignment_new( 0.0f, 0.0f, 0.0f, 0.0f );
669            gtk_widget_set_size_request( w, GUI_PAD, 0u );
670            gtk_box_pack_start( GTK_BOX( hbox ), w, FALSE, FALSE, 0 );
671            w = p->ul_lb = gtk_label_new( NULL );
672            gtk_label_set_single_line_mode( GTK_LABEL( w ), TRUE );
673            gtk_box_pack_start( GTK_BOX( hbox ), w, FALSE, FALSE, 0 );
674            w = gtk_image_new_from_stock( GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU );
675            gtk_box_pack_start( GTK_BOX( hbox ), w, FALSE, FALSE, 0 );
676        gtk_box_pack_end( GTK_BOX( h ), hbox, FALSE, FALSE, 0 );
677
678        hbox = gtk_hbox_new( FALSE, GUI_PAD );
679            w = gtk_alignment_new( 0.0f, 0.0f, 0.0f, 0.0f );
680            gtk_widget_set_size_request( w, GUI_PAD, 0u );
681            gtk_box_pack_start( GTK_BOX( hbox ), w, FALSE, FALSE, 0 );
682            w = p->dl_lb = gtk_label_new( NULL );
683            gtk_label_set_single_line_mode( GTK_LABEL( w ), TRUE );
684            gtk_box_pack_start( GTK_BOX( hbox ), w, FALSE, FALSE, 0 );
685            w = gtk_image_new_from_stock( GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU );
686            gtk_box_pack_start( GTK_BOX( hbox ), w, FALSE, FALSE, 0 );
687        gtk_box_pack_end( GTK_BOX( h ), hbox, FALSE, FALSE, 0 );
688
689        hbox = gtk_hbox_new( FALSE, GUI_PAD );
690            w = gtk_button_new( );
691            gtr_widget_set_tooltip_text( w, _( "Statistics" ) );
692            gtk_container_add( GTK_CONTAINER( w ), gtk_image_new_from_stock( "ratio", -1 ) );
693            gtk_button_set_relief( GTK_BUTTON( w ), GTK_RELIEF_NONE );
694            g_signal_connect( w, "clicked", G_CALLBACK( onYinYangReleased ), p );
695            gtk_box_pack_start( GTK_BOX( hbox ), w, FALSE, FALSE, 0 );
696            w = p->stats_lb = gtk_label_new( NULL );
697            gtk_label_set_single_line_mode( GTK_LABEL( w ), TRUE );
698            gtk_box_pack_end( GTK_BOX( hbox ), w, FALSE, FALSE, 0 );
699        gtk_box_pack_end( GTK_BOX( h ), hbox, FALSE, FALSE, 0 );
700
701
702    /* workarea */
703    p->view = makeview( p );
704    w = list = p->scroll = gtk_scrolled_window_new( NULL, NULL );
705    gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( w ),
706                                    GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
707    gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( w ),
708                                         GTK_SHADOW_IN );
709    gtk_container_add( GTK_CONTAINER( w ), p->view );
710
711    /* lay out the widgets */
712    gtk_box_pack_start( GTK_BOX( vbox ), mainmenu, FALSE, FALSE, 0 );
713    gtk_box_pack_start( GTK_BOX( vbox ), toolbar, FALSE, FALSE, 0 );
714    gtk_box_pack_start( GTK_BOX( vbox ), filter, FALSE, FALSE, 0 );
715    gtk_box_pack_start( GTK_BOX( vbox ), list, TRUE, TRUE, 0 );
716    gtk_box_pack_start( GTK_BOX( vbox ), status, FALSE, FALSE, 0 );
717
718    {
719        /* this is to determine the maximum width/height for the label */
720        int w=0, h=0;
721        PangoLayout * pango_layout;
722        pango_layout = gtk_widget_create_pango_layout( p->ul_lb, "999.99 KiB/s" );
723        pango_layout_get_pixel_size( pango_layout, &w, &h );
724        gtk_widget_set_size_request( p->ul_lb, w, h );
725        gtk_widget_set_size_request( p->dl_lb, w, h );
726        gtk_misc_set_alignment( GTK_MISC( p->ul_lb ), 1.0, 0.5 );
727        gtk_misc_set_alignment( GTK_MISC( p->dl_lb ), 1.0, 0.5 );
728        g_object_unref( G_OBJECT( pango_layout ) );
729    }
730
731    /* show all but the window */
732    gtk_widget_show_all( vbox );
733
734    /* listen for prefs changes that affect the window */
735    p->core = core;
736    prefsChanged( core, PREF_KEY_COMPACT_VIEW, self );
737    prefsChanged( core, PREF_KEY_FILTERBAR, self );
738    prefsChanged( core, PREF_KEY_STATUSBAR, self );
739    prefsChanged( core, PREF_KEY_STATUSBAR_STATS, self );
740    prefsChanged( core, PREF_KEY_TOOLBAR, self );
741    prefsChanged( core, TR_PREFS_KEY_ALT_SPEED_ENABLED, self );
742    p->pref_handler_id = g_signal_connect( core, "prefs-changed",
743                                           G_CALLBACK( prefsChanged ), self );
744
745    tr_sessionSetAltSpeedFunc( gtr_core_session( core ), onAltSpeedToggled, p );
746
747    return self;
748}
749
750static void
751updateTorrentCount( PrivateData * p )
752{
753    if( p && p->core )
754    {
755        char      buf[512];
756        const int torrentCount = gtk_tree_model_iter_n_children( gtr_core_model( p->core ), NULL );
757        const int visibleCount = gtk_tree_model_iter_n_children( p->filter_model, NULL );
758
759        if( !torrentCount )
760            *buf = '\0';
761        else if( torrentCount != visibleCount )
762            g_snprintf( buf, sizeof( buf ),
763                        gtr_ngettext( "%1$'d of %2$'d Torrent",
764                                      "%1$'d of %2$'d Torrents",
765                                      torrentCount ),
766                        visibleCount, torrentCount );
767        else
768            g_snprintf( buf, sizeof( buf ),
769                        gtr_ngettext( "%'d Torrent", "%'d Torrents", torrentCount ),
770                        torrentCount );
771        gtr_label_set_text( GTK_LABEL( p->gutter_lb ), buf );
772    }
773}
774
775static void
776updateStats( PrivateData * p )
777{
778    const char *            pch;
779    char                    up[32], down[32], ratio[32], buf[512];
780    struct tr_session_stats stats;
781    tr_session *            session = gtr_core_session( p->core );
782
783    /* update the stats */
784    pch = gtr_pref_string_get( PREF_KEY_STATUSBAR_STATS );
785    if( !strcmp( pch, "session-ratio" ) )
786    {
787        tr_sessionGetStats( session, &stats );
788        tr_strlratio( ratio, stats.ratio, sizeof( ratio ) );
789        g_snprintf( buf, sizeof( buf ), _( "Ratio: %s" ), ratio );
790    }
791    else if( !strcmp( pch, "session-transfer" ) )
792    {
793        tr_sessionGetStats( session, &stats );
794        tr_strlsize( up, stats.uploadedBytes, sizeof( up ) );
795        tr_strlsize( down, stats.downloadedBytes, sizeof( down ) );
796        /* Translators: "size|" is here for disambiguation. Please remove it from your translation.
797           %1$s is the size of the data we've downloaded
798           %2$s is the size of the data we've uploaded */
799        g_snprintf( buf, sizeof( buf ), Q_(
800                        "size|Down: %1$s, Up: %2$s" ), down, up );
801    }
802    else if( !strcmp( pch, "total-transfer" ) )
803    {
804        tr_sessionGetCumulativeStats( session, &stats );
805        tr_strlsize( up, stats.uploadedBytes, sizeof( up ) );
806        tr_strlsize( down, stats.downloadedBytes, sizeof( down ) );
807        /* Translators: "size|" is here for disambiguation. Please remove it from your translation.
808           %1$s is the size of the data we've downloaded
809           %2$s is the size of the data we've uploaded */
810        g_snprintf( buf, sizeof( buf ), Q_(
811                        "size|Down: %1$s, Up: %2$s" ), down, up );
812    }
813    else     /* default is total-ratio */
814    {
815        tr_sessionGetCumulativeStats( session, &stats );
816        tr_strlratio( ratio, stats.ratio, sizeof( ratio ) );
817        g_snprintf( buf, sizeof( buf ), _( "Ratio: %s" ), ratio );
818    }
819    gtr_label_set_text( GTK_LABEL( p->stats_lb ), buf );
820}
821
822static void
823updateSpeeds( PrivateData * p )
824{
825    tr_session * session = gtr_core_session( p->core );
826
827    if( session != NULL )
828    {
829        char buf[128];
830        double up=0, down=0;
831        GtkTreeIter iter;
832        GtkTreeModel * model = gtr_core_model( p->core );
833
834        if( gtk_tree_model_get_iter_first( model, &iter ) ) do
835        {
836            double u, d;
837            gtk_tree_model_get( model, &iter, MC_SPEED_UP, &u,
838                                              MC_SPEED_DOWN, &d,
839                                              -1 );
840            up += u;
841            down += d;
842        }
843        while( gtk_tree_model_iter_next( model, &iter ) );
844
845        tr_formatter_speed_KBps( buf, down, sizeof( buf ) );
846        gtr_label_set_text( GTK_LABEL( p->dl_lb ), buf );
847
848        tr_formatter_speed_KBps( buf, up, sizeof( buf ) );
849        gtr_label_set_text( GTK_LABEL( p->ul_lb ), buf );
850    }
851}
852
853void
854gtr_window_refresh( TrWindow * self )
855{
856    PrivateData * p = get_private_data( self );
857
858    if( p && p->core && gtr_core_session( p->core ) )
859    {
860        updateSpeeds( p );
861        updateTorrentCount( p );
862        updateStats( p );
863    }
864}
865
866GtkTreeSelection*
867gtr_window_get_selection( TrWindow * w )
868{
869    return get_private_data( w )->selection;
870}
871
872void
873gtr_window_set_busy( TrWindow * w, gboolean isBusy )
874{
875    if( w && gtr_widget_get_realized( GTK_WIDGET( w ) ) )
876    {
877        GdkDisplay * display = gtk_widget_get_display( GTK_WIDGET( w ) );
878        GdkCursor * cursor = isBusy ? gdk_cursor_new_for_display( display, GDK_WATCH ) : NULL;
879
880        gdk_window_set_cursor( gtr_widget_get_window( GTK_WIDGET( w ) ), cursor );
881        gdk_display_flush( display );
882
883        if( cursor ) {
884#if GTK_CHECK_VERSION(3,0,0)
885            g_object_unref( cursor );
886#else
887            gdk_cursor_unref( cursor );
888#endif
889        }
890    }
891}
Note: See TracBrowser for help on using the repository browser.