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

Last change on this file since 10536 was 10536, checked in by charles, 13 years ago

(trunk) #3174 "Use IEC standard units (KiB, MiB, GiB) instead of (KB, MB, GB)" -- implemented for gtk, qt, daemon, cli, and web client

  • Property svn:keywords set to Date Rev Author Id
File size: 31.6 KB
Line 
1/******************************************************************************
2 * $Id: tr-window.c 10536 2010-04-28 01:26:46Z charles $
3 *
4 * Copyright (c) 2005-2008 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#if !GTK_CHECK_VERSION( 2,16,0 )
31 /* FIXME: when 2.16 has been out long enough, it would be really nice to
32  * get rid of this libsexy usage because of its makefile strangeness */
33 #define USE_SEXY
34 #include "sexy-icon-entry.h"
35#endif
36
37#include <libtransmission/transmission.h>
38
39#include "actions.h"
40#include "conf.h"
41#include "filter.h"
42#include "hig.h"
43#include "torrent-cell-renderer.h"
44#include "tr-prefs.h"
45#include "tr-torrent.h"
46#include "tr-window.h"
47#include "util.h"
48
49#if !GTK_CHECK_VERSION( 2, 8, 0 )
50static void
51gtk_tree_view_column_queue_resize( GtkTreeViewColumn * column ) /* yuck */
52{
53    const int spacing = gtk_tree_view_column_get_spacing( column );
54
55    gtk_tree_view_column_set_spacing( column, spacing + 1 );
56    gtk_tree_view_column_set_spacing( column, spacing );
57}
58
59#endif
60
61typedef struct
62{
63    GtkWidget *           speedlimit_on_item[2];
64    GtkWidget *           speedlimit_off_item[2];
65    GtkWidget *           ratio_on_item;
66    GtkWidget *           ratio_off_item;
67    GtkWidget *           scroll;
68    GtkWidget *           view;
69    GtkWidget *           toolbar;
70    GtkWidget *           filter;
71    GtkWidget *           status;
72    GtkWidget *           status_menu;
73    GtkWidget *           ul_lb;
74    GtkWidget *           dl_lb;
75    GtkWidget *           stats_lb;
76    GtkWidget *           gutter_lb;
77    GtkWidget *           alt_speed_image;
78    GtkWidget *           alt_speed_button;
79    GtkWidget *           options_menu;
80    GtkTreeSelection *    selection;
81    GtkCellRenderer *     renderer;
82    GtkTreeViewColumn *   column;
83    GtkTreeModel *        filter_model;
84    TrCore *              core;
85    gulong                pref_handler_id;
86}
87PrivateData;
88
89#define PRIVATE_DATA_KEY "private-data"
90
91static PrivateData*
92get_private_data( TrWindow * w )
93{
94    return g_object_get_data ( G_OBJECT( w ), PRIVATE_DATA_KEY );
95}
96
97/***
98****
99***/
100
101static void
102on_popup_menu( GtkWidget * self UNUSED,
103               GdkEventButton * event )
104{
105    GtkWidget * menu = action_get_widget ( "/main-window-popup" );
106
107    gtk_menu_popup ( GTK_MENU( menu ), NULL, NULL, NULL, NULL,
108                    ( event ? event->button : 0 ),
109                    ( event ? event->time : 0 ) );
110}
111
112static void
113view_row_activated( GtkTreeView       * tree_view UNUSED,
114                    GtkTreePath       * path      UNUSED,
115                    GtkTreeViewColumn * column    UNUSED,
116                    gpointer            user_data UNUSED )
117{
118    action_activate( "show-torrent-properties" );
119}
120
121static GtkWidget*
122makeview( PrivateData * p )
123{
124    GtkWidget *         view;
125    GtkTreeViewColumn * col;
126    GtkTreeSelection *  sel;
127    GtkCellRenderer *   r;
128
129    view = gtk_tree_view_new( );
130    gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
131    gtk_tree_view_set_fixed_height_mode( GTK_TREE_VIEW( view ), TRUE );
132
133    p->selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
134
135    p->column = col = GTK_TREE_VIEW_COLUMN (g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
136        "title", _("Torrent"),
137        "resizable", TRUE,
138        "sizing", GTK_TREE_VIEW_COLUMN_FIXED,
139        NULL));
140
141    p->renderer = r = torrent_cell_renderer_new( );
142    gtk_tree_view_column_pack_start( col, r, FALSE );
143    gtk_tree_view_column_add_attribute( col, r, "torrent", MC_TORRENT_RAW );
144    gtk_tree_view_column_add_attribute( col, r, "piece-upload-speed", MC_SPEED_UP );
145    gtk_tree_view_column_add_attribute( col, r, "piece-download-speed", MC_SPEED_DOWN );
146
147    gtk_tree_view_append_column( GTK_TREE_VIEW( view ), col );
148    g_object_set( r, "xpad", GUI_PAD_SMALL, "ypad", GUI_PAD_SMALL, NULL );
149
150    gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( view ), TRUE );
151    sel = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
152    gtk_tree_selection_set_mode( GTK_TREE_SELECTION( sel ),
153                                 GTK_SELECTION_MULTIPLE );
154
155    g_signal_connect( view, "popup-menu",
156                      G_CALLBACK( on_popup_menu ), NULL );
157    g_signal_connect( view, "button-press-event",
158                      G_CALLBACK( on_tree_view_button_pressed ),
159                      (void *) on_popup_menu );
160    g_signal_connect( view, "button-release-event",
161                      G_CALLBACK( on_tree_view_button_released ), NULL );
162    g_signal_connect( view, "row-activated",
163                      G_CALLBACK( view_row_activated ), NULL );
164
165
166    gtk_tree_view_set_model( GTK_TREE_VIEW( view ), 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", 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 = 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 = 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 = 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        tr_window_update( (TrWindow*)wind );
206    }
207    else if( !strcmp( key, TR_PREFS_KEY_ALT_SPEED_ENABLED ) ||
208             !strcmp( key, TR_PREFS_KEY_ALT_SPEED_UP ) ||
209             !strcmp( key, TR_PREFS_KEY_ALT_SPEED_DOWN ) )
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        tr_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 = 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_strlspeed( u, pref_int_get( TR_PREFS_KEY_ALT_SPEED_UP ), sizeof( u ) );
270    tr_strlspeed( d, pref_int_get( TR_PREFS_KEY_ALT_SPEED_DOWN ), sizeof( d ) );
271    fmt = b ? _( "Click to disable Temporary Speed Limits\n(%1$s down, %2$s up)" )
272            : _( "Click to enable Temporary 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    tr_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_RAW, &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( tr_core_session( p->core ) );
352    tr_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, tr_bool isEnabled UNUSED, tr_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        tr_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 speed = 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 : TR_PREFS_KEY_DSPEED;
395    tr_core_set_pref_int( p->core, key, speed );
396
397    key = dir==TR_UP ? TR_PREFS_KEY_USPEED_ENABLED : TR_PREFS_KEY_DSPEED_ENABLED;
398    tr_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[] = { 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); i<n; ++i )
428    {
429        char buf[128];
430        tr_strlspeed( buf, speeds[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[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        tr_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    tr_core_set_pref_double( p->core, TR_PREFS_KEY_RATIO, ratio );
466    tr_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_strlspeed( buf1, pref_int_get( TR_PREFS_KEY_DSPEED ), sizeof( buf1 ) );
545    gtk_label_set_text( GTK_LABEL( gtk_bin_get_child( GTK_BIN( w ) ) ), buf1 );
546
547    b = 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_strlspeed( buf1, pref_int_get( TR_PREFS_KEY_USPEED ), sizeof( buf1 ) );
553    gtk_label_set_text( GTK_LABEL( gtk_bin_get_child( GTK_BIN( w ) ) ), buf1 );
554
555    b = 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, pref_double_get( TR_PREFS_KEY_RATIO ), sizeof( buf1 ) );
560    g_snprintf( buf2, sizeof( buf2 ), _( "Stop at Ratio (%s)" ), buf1 );
561    gtk_label_set_text( GTK_LABEL( gtk_bin_get_child( GTK_BIN( p->ratio_on_item ) ) ), buf2 );
562
563    b = 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 *
574tr_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_data_full( G_OBJECT(
589                                self ), PRIVATE_DATA_KEY, p, privateFree );
590    win = GTK_WINDOW( self );
591    gtk_window_set_title( win, g_get_application_name( ) );
592    gtk_window_set_role( win, "tr-main" );
593    gtk_window_set_default_size( win,
594                                 pref_int_get( PREF_KEY_MAIN_WINDOW_WIDTH ),
595                                 pref_int_get( PREF_KEY_MAIN_WINDOW_HEIGHT ) );
596    gtk_window_move( win, pref_int_get( PREF_KEY_MAIN_WINDOW_X ),
597                          pref_int_get( PREF_KEY_MAIN_WINDOW_Y ) );
598    if( pref_flag_get( PREF_KEY_MAIN_WINDOW_IS_MAXIMIZED ) )
599        gtk_window_maximize( win );
600    gtk_window_add_accel_group( win, gtk_ui_manager_get_accel_group( ui_mgr ) );
601
602    /* window's main container */
603    vbox = gtk_vbox_new ( FALSE, 0 );
604    gtk_container_add ( GTK_CONTAINER( self ), vbox );
605
606    /* main menu */
607    mainmenu = action_get_widget( "/main-window-menu" );
608    w = action_get_widget( "/main-window-menu/torrent-menu/update-tracker" );
609#if GTK_CHECK_VERSION( 2, 12, 0 )
610    g_signal_connect( w, "query-tooltip",
611                      G_CALLBACK( onAskTrackerQueryTooltip ), p );
612#endif
613
614    /* toolbar */
615    toolbar = p->toolbar = action_get_widget( "/main-window-toolbar" );
616    action_set_important( "add-torrent-toolbar", TRUE );
617    action_set_important( "show-torrent-properties", TRUE );
618
619    /* filter */
620    h = filter = p->filter = gtr_filter_bar_new( tr_core_session( core ),
621                                                 tr_core_model( core ),
622                                                 &p->filter_model );
623    gtk_container_set_border_width( GTK_CONTAINER( h ), GUI_PAD_SMALL );
624
625    /* status menu */
626    menu = p->status_menu = gtk_menu_new( );
627    l = NULL;
628    pch = pref_string_get( PREF_KEY_STATUSBAR_STATS );
629    for( i = 0, n = G_N_ELEMENTS( stats_modes ); i < n; ++i )
630    {
631        const char * val = stats_modes[i].val;
632        w = gtk_radio_menu_item_new_with_label( l, _( stats_modes[i].i18n ) );
633        l = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM( w ) );
634        gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( w ),
635                                       !strcmp( val, pch ) );
636        g_object_set_data( G_OBJECT(
637                               w ), STATS_MODE,
638                           (gpointer)stats_modes[i].val );
639        g_signal_connect( w, "toggled", G_CALLBACK(
640                              status_menu_toggled_cb ), p );
641        gtk_menu_shell_append( GTK_MENU_SHELL( menu ), w );
642        gtk_widget_show( w );
643    }
644
645    /* status */
646    h = status = p->status = gtk_hbox_new( FALSE, GUI_PAD );
647    gtk_container_set_border_width( GTK_CONTAINER( h ), GUI_PAD_SMALL );
648
649        w = gtk_button_new( );
650        gtk_container_add( GTK_CONTAINER( w ), gtk_image_new_from_stock( "options", GTK_ICON_SIZE_SMALL_TOOLBAR ) );
651        gtr_widget_set_tooltip_text( w, _( "Options" ) );
652        gtk_box_pack_start( GTK_BOX( h ), w, 0, 0, 0 );
653        gtk_button_set_relief( GTK_BUTTON( w ), GTK_RELIEF_NONE );
654        p->options_menu = createOptionsMenu( p );
655        g_signal_connect( w, "clicked", G_CALLBACK(onOptionsClicked), p );
656
657        p->alt_speed_image = gtk_image_new( );
658        w = p->alt_speed_button = gtk_toggle_button_new( );
659        gtk_button_set_image( GTK_BUTTON( w ), p->alt_speed_image );
660        gtk_button_set_relief( GTK_BUTTON( w ), GTK_RELIEF_NONE );
661        g_signal_connect( w, "toggled", G_CALLBACK(alt_speed_toggled_cb ), p );
662        gtk_box_pack_start( GTK_BOX( h ), w, 0, 0, 0 );
663
664        w = p->gutter_lb = gtk_label_new( "N Torrents" );
665        gtk_box_pack_start( GTK_BOX( h ), w, 1, 1, GUI_PAD_BIG );
666
667        hbox = gtk_hbox_new( FALSE, GUI_PAD_SMALL );
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 = gtk_image_new_from_stock( GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU );
672            gtk_box_pack_start( GTK_BOX( hbox ), w, FALSE, FALSE, 0 );
673            w = p->ul_lb = gtk_label_new( NULL );
674            gtk_box_pack_start( GTK_BOX( hbox ), w, FALSE, FALSE, 0 );
675        gtk_box_pack_end( GTK_BOX( h ), hbox, FALSE, FALSE, 0 );
676
677        hbox = gtk_hbox_new( FALSE, GUI_PAD_SMALL );
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 = gtk_image_new_from_stock( GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU );
682            gtk_box_pack_start( GTK_BOX( hbox ), w, FALSE, FALSE, 0 );
683            w = p->dl_lb = gtk_label_new( NULL );
684            gtk_box_pack_start( GTK_BOX( hbox ), w, FALSE, FALSE, 0 );
685        gtk_box_pack_end( GTK_BOX( h ), hbox, FALSE, FALSE, 0 );
686
687        hbox = gtk_hbox_new( FALSE, GUI_PAD_SMALL );
688            w = gtk_alignment_new( 0.0f, 0.0f, 0.0f, 0.0f );
689            gtk_widget_set_size_request( w, GUI_PAD, 0u );
690            gtk_box_pack_start( GTK_BOX( hbox ), w, FALSE, FALSE, 0 );
691            w = gtk_button_new( );
692            gtr_widget_set_tooltip_text( w, _( "Statistics" ) );
693            gtk_container_add( GTK_CONTAINER( w ), gtk_image_new_from_stock( "ratio", GTK_ICON_SIZE_SMALL_TOOLBAR ) );
694            gtk_button_set_relief( GTK_BUTTON( w ), GTK_RELIEF_NONE );
695            g_signal_connect( w, "clicked", G_CALLBACK( onYinYangReleased ), p );
696            gtk_box_pack_start( GTK_BOX( hbox ), w, FALSE, FALSE, 0 );
697            w = p->stats_lb = gtk_label_new( NULL );
698            gtk_box_pack_end( GTK_BOX( h ), 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    /* layout the widgets */
712    {
713        const char * str = pref_string_get( PREF_KEY_MAIN_WINDOW_LAYOUT_ORDER );
714        char ** tokens = g_strsplit( str, ",", -1 );
715        for( i=0; tokens && tokens[i]; ++i )
716        {
717            const char * key = tokens[i];
718
719            if( !strcmp( key, "menu" ) )
720                gtk_box_pack_start( GTK_BOX( vbox ), mainmenu, FALSE, FALSE, 0 );
721            else if( !strcmp( key, "toolbar" ) )
722                gtk_box_pack_start( GTK_BOX( vbox ), toolbar, FALSE, FALSE, 0 );
723            else if( !strcmp( key, "filter" ) )
724                gtk_box_pack_start( GTK_BOX( vbox ), filter, FALSE, FALSE, 0 );
725            else if( !strcmp( key, "list" ) )
726                gtk_box_pack_start( GTK_BOX( vbox ), list, TRUE, TRUE, 0 );
727            else if( !strcmp( key, "statusbar" ) )
728                gtk_box_pack_start( GTK_BOX( vbox ), status, FALSE, FALSE, 0 );
729        }
730        g_strfreev( tokens );
731    }
732
733    {
734        int w=0, h=0;
735        /* this is to determine the maximum width/height for the label */
736        PangoLayout * pango_layout =
737            gtk_widget_create_pango_layout( p->ul_lb, _( "999.9 KiB/s" ) );
738        pango_layout_get_pixel_size( pango_layout, &w, &h );
739        gtk_widget_set_size_request( p->ul_lb, w, h );
740        gtk_widget_set_size_request( p->dl_lb, w, h );
741        gtk_misc_set_alignment( GTK_MISC( p->ul_lb ), 1.0, 0.5 );
742        gtk_misc_set_alignment( GTK_MISC( p->dl_lb ), 1.0, 0.5 );
743        g_object_unref( G_OBJECT( pango_layout ) );
744    }
745
746    /* show all but the window */
747    gtk_widget_show_all( vbox );
748
749    /* listen for prefs changes that affect the window */
750    p->core = core;
751    prefsChanged( core, PREF_KEY_COMPACT_VIEW, self );
752    prefsChanged( core, PREF_KEY_FILTERBAR, self );
753    prefsChanged( core, PREF_KEY_STATUSBAR, self );
754    prefsChanged( core, PREF_KEY_STATUSBAR_STATS, self );
755    prefsChanged( core, PREF_KEY_TOOLBAR, self );
756    prefsChanged( core, PREF_KEY_FILTER_MODE, self );
757    prefsChanged( core, TR_PREFS_KEY_ALT_SPEED_ENABLED, self );
758    p->pref_handler_id = g_signal_connect( core, "prefs-changed",
759                                           G_CALLBACK( prefsChanged ), self );
760
761    tr_sessionSetAltSpeedFunc( tr_core_session( core ), onAltSpeedToggled, p );
762
763    return self;
764}
765
766static void
767updateTorrentCount( PrivateData * p )
768{
769    if( p && p->core )
770    {
771        char      buf[512];
772        const int torrentCount = gtk_tree_model_iter_n_children(
773            tr_core_model( p->core ), NULL );
774        const int visibleCount = gtk_tree_model_iter_n_children(
775            p->filter_model, NULL );
776
777        if( !torrentCount )
778            *buf = '\0';
779        else if( torrentCount != visibleCount )
780            g_snprintf( buf, sizeof( buf ),
781                        ngettext( "%1$'d of %2$'d Torrent",
782                                  "%1$'d of %2$'d Torrents",
783                                  torrentCount ),
784                        visibleCount, torrentCount );
785        else
786            g_snprintf( buf, sizeof( buf ),
787                        ngettext( "%'d Torrent", "%'d Torrents", torrentCount ),
788                        torrentCount );
789        gtk_label_set_text( GTK_LABEL( p->gutter_lb ), buf );
790    }
791}
792
793static void
794updateStats( PrivateData * p )
795{
796    const char *            pch;
797    char                    up[32], down[32], ratio[32], buf[512];
798    struct tr_session_stats stats;
799    tr_session *            session = tr_core_session( p->core );
800
801    /* update the stats */
802    pch = pref_string_get( PREF_KEY_STATUSBAR_STATS );
803    if( !strcmp( pch, "session-ratio" ) )
804    {
805        tr_sessionGetStats( session, &stats );
806        tr_strlratio( ratio, stats.ratio, sizeof( ratio ) );
807        g_snprintf( buf, sizeof( buf ), _( "Ratio: %s" ), ratio );
808    }
809    else if( !strcmp( pch, "session-transfer" ) )
810    {
811        tr_sessionGetStats( session, &stats );
812        tr_strlsize( up, stats.uploadedBytes, sizeof( up ) );
813        tr_strlsize( down, stats.downloadedBytes, sizeof( down ) );
814        /* Translators: "size|" is here for disambiguation.  Please remove it from your translation.
815           %1$s is the size of the data we've downloaded
816           %2$s is the size of the data we've uploaded */
817        g_snprintf( buf, sizeof( buf ), Q_(
818                        "size|Down: %1$s, Up: %2$s" ), down, up );
819    }
820    else if( !strcmp( pch, "total-transfer" ) )
821    {
822        tr_sessionGetCumulativeStats( session, &stats );
823        tr_strlsize( up, stats.uploadedBytes, sizeof( up ) );
824        tr_strlsize( down, stats.downloadedBytes, sizeof( down ) );
825        /* Translators: "size|" is here for disambiguation.  Please remove it from your translation.
826           %1$s is the size of the data we've downloaded
827           %2$s is the size of the data we've uploaded */
828        g_snprintf( buf, sizeof( buf ), Q_(
829                        "size|Down: %1$s, Up: %2$s" ), down, up );
830    }
831    else     /* default is total-ratio */
832    {
833        tr_sessionGetCumulativeStats( session, &stats );
834        tr_strlratio( ratio, stats.ratio, sizeof( ratio ) );
835        g_snprintf( buf, sizeof( buf ), _( "Ratio: %s" ), ratio );
836    }
837    gtk_label_set_text( GTK_LABEL( p->stats_lb ), buf );
838}
839
840static void
841updateSpeeds( PrivateData * p )
842{
843    tr_session * session = tr_core_session( p->core );
844
845    if( session != NULL )
846    {
847        char buf[128];
848        double up=0, down=0;
849        GtkTreeIter iter;
850        GtkTreeModel * model = tr_core_model( p->core );
851
852        if( gtk_tree_model_get_iter_first( model, &iter ) ) do
853        {
854            double u, d;
855            gtk_tree_model_get( model, &iter, MC_SPEED_UP, &u,
856                                              MC_SPEED_DOWN, &d,
857                                              -1 );
858            up += u;
859            down += d;
860        }
861        while( gtk_tree_model_iter_next( model, &iter ) );
862
863        tr_strlspeed( buf, down, sizeof( buf ) );
864        gtk_label_set_text( GTK_LABEL( p->dl_lb ), buf );
865
866        tr_strlspeed( buf, up, sizeof( buf ) );
867        gtk_label_set_text( GTK_LABEL( p->ul_lb ), buf );
868    }
869}
870
871void
872tr_window_update( TrWindow * self )
873{
874    PrivateData * p = get_private_data( self );
875
876    if( p && p->core && tr_core_session( p->core ) )
877    {
878        updateSpeeds( p );
879        updateTorrentCount( p );
880        updateStats( p );
881        gtk_tree_model_filter_refilter( GTK_TREE_MODEL_FILTER( p->filter_model ) );
882    }
883}
884
885GtkTreeSelection*
886tr_window_get_selection( TrWindow * w )
887{
888    return get_private_data( w )->selection;
889}
890
Note: See TracBrowser for help on using the repository browser.