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

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

(trunk) #4138 "use stdbool.h instead of tr_bool" -- done.

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