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

Last change on this file since 12856 was 12856, checked in by jordan, 10 years ago

(trunk gtk) #4470 "Search in torrent list is broken" -- fixed.

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