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

Last change on this file since 8173 was 8173, checked in by charles, 14 years ago

(gtk) don't stretch the turtle icon in the prefs dialog.

  • Property svn:keywords set to Date Rev Author Id
File size: 29.7 KB
Line 
1/******************************************************************************
2 * $Id: tr-window.c 8173 2009-04-07 04:42:50Z 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#include <libtransmission/transmission.h>
31
32#include "actions.h"
33#include "conf.h"
34#include "hig.h"
35#include "sexy-icon-entry.h"
36#include "torrent-cell-renderer.h"
37#include "tr-prefs.h"
38#include "tr-torrent.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 enum
55{
56    FILTER_TEXT_MODE_NAME,
57    FILTER_TEXT_MODE_FILES,
58    FILTER_TEXT_MODE_TRACKER,
59    FILTER_TEXT_MODE_QTY
60}
61filter_text_mode_t;
62
63typedef enum
64{
65    FILTER_MODE_ALL,
66    FILTER_MODE_ACTIVE,
67    FILTER_MODE_DOWNLOADING,
68    FILTER_MODE_SEEDING,
69    FILTER_MODE_PAUSED,
70    FILTER_MODE_QTY
71}
72filter_mode_t;
73
74typedef struct
75{
76    GtkWidget *           scroll;
77    GtkWidget *           view;
78    GtkWidget *           toolbar;
79    GtkWidget *           filter;
80    GtkWidget *           status;
81    GtkWidget *           status_menu;
82    GtkWidget *           ul_lb;
83    GtkWidget *           dl_lb;
84    GtkWidget *           stats_lb;
85    GtkWidget *           gutter_lb;
86    GtkWidget *           alt_speed_image[2]; /* 0==off, 1==on */
87    GtkWidget *           alt_speed_button;
88    GtkTreeSelection *    selection;
89    GtkCellRenderer *     renderer;
90    GtkTreeViewColumn *   column;
91    GtkTreeModel *        filter_model;
92    TrCore *              core;
93    gulong                pref_handler_id;
94    filter_mode_t         filter_mode;
95    filter_text_mode_t    filter_text_mode;
96    char *                filter_text;
97}
98PrivateData;
99
100#define PRIVATE_DATA_KEY "private-data"
101
102#define FILTER_MODE_KEY "tr-filter-mode"
103#define FILTER_TEXT_MODE_KEY "tr-filter-text-mode"
104#define FILTER_TOGGLES_KEY "tr-filter-toggles"
105
106static PrivateData*
107get_private_data( TrWindow * w )
108{
109    return g_object_get_data ( G_OBJECT( w ), PRIVATE_DATA_KEY );
110}
111
112/***
113****
114***/
115
116static void
117on_popup_menu( GtkWidget * self UNUSED,
118               GdkEventButton * event )
119{
120    GtkWidget * menu = action_get_widget ( "/main-window-popup" );
121
122    gtk_menu_popup ( GTK_MENU( menu ), NULL, NULL, NULL, NULL,
123                    ( event ? event->button : 0 ),
124                    ( event ? event->time : 0 ) );
125}
126
127static void
128view_row_activated( GtkTreeView       * tree_view UNUSED,
129                    GtkTreePath       * path      UNUSED,
130                    GtkTreeViewColumn * column    UNUSED,
131                    gpointer            user_data UNUSED )
132{
133    action_activate( "show-torrent-properties" );
134}
135
136static gboolean is_row_visible( GtkTreeModel *,
137                                GtkTreeIter  *,
138                                gpointer );
139
140static GtkWidget*
141makeview( PrivateData * p,
142          TrCore *      core )
143{
144    GtkWidget *         view;
145    GtkTreeViewColumn * col;
146    GtkTreeSelection *  sel;
147    GtkCellRenderer *   r;
148    GtkTreeModel *      filter_model;
149
150    view = gtk_tree_view_new( );
151    gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
152    gtk_tree_view_set_fixed_height_mode( GTK_TREE_VIEW( view ), TRUE );
153
154    p->selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
155
156    p->column = col = GTK_TREE_VIEW_COLUMN (g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,                 
157        "title", _("Torrent"),
158        "resizable", TRUE,
159        "sizing", GTK_TREE_VIEW_COLUMN_FIXED,
160        NULL));
161
162    p->renderer = r = torrent_cell_renderer_new( );
163    gtk_tree_view_column_pack_start( col, r, FALSE );
164    gtk_tree_view_column_add_attribute( col, r, "torrent", MC_TORRENT_RAW );
165   
166    gtk_tree_view_append_column( GTK_TREE_VIEW( view ), col );
167    g_object_set( r, "xpad", GUI_PAD_SMALL, "ypad", GUI_PAD_SMALL, NULL );
168
169    gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( view ), TRUE );
170    sel = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
171    gtk_tree_selection_set_mode( GTK_TREE_SELECTION( sel ),
172                                 GTK_SELECTION_MULTIPLE );
173
174    g_signal_connect( view, "popup-menu",
175                      G_CALLBACK( on_popup_menu ), NULL );
176    g_signal_connect( view, "button-press-event",
177                      G_CALLBACK( on_tree_view_button_pressed ),
178                      (void *) on_popup_menu );
179    g_signal_connect( view, "button-release-event",
180                      G_CALLBACK( on_tree_view_button_released ), NULL );
181    g_signal_connect( view, "row-activated",
182                      G_CALLBACK( view_row_activated ), NULL );
183
184
185    filter_model = p->filter_model = gtk_tree_model_filter_new(
186                       tr_core_model( core ), NULL );
187
188    gtk_tree_model_filter_set_visible_func( GTK_TREE_MODEL_FILTER(
189                                                filter_model ),
190                                            is_row_visible,
191                                            p, NULL );
192
193    gtk_tree_view_set_model( GTK_TREE_VIEW( view ), filter_model );
194
195    return view;
196}
197
198static void syncAltSpeedButton( PrivateData * p );
199
200static void
201prefsChanged( TrCore * core UNUSED,
202              const char *  key,
203              gpointer      wind )
204{
205    PrivateData * p = get_private_data( GTK_WINDOW( wind ) );
206
207    if( !strcmp( key, PREF_KEY_MINIMAL_VIEW ) )
208    {
209        g_object_set( p->renderer, "minimal", pref_flag_get( key ), NULL );
210        /* since the cell size has changed, we need gtktreeview to revalidate
211         * its fixed-height mode values.  Unfortunately there's not an API call
212         * for that, but it *does* revalidate when it thinks the style's been tweaked */
213        g_signal_emit_by_name( p->view, "style-set", NULL, NULL );
214    }
215    else if( !strcmp( key, PREF_KEY_STATUSBAR ) )
216    {
217        const gboolean isEnabled = pref_flag_get( key );
218        g_object_set( p->status, "visible", isEnabled, NULL );
219    }
220    else if( !strcmp( key, PREF_KEY_FILTERBAR ) )
221    {
222        const gboolean isEnabled = pref_flag_get( key );
223        g_object_set( p->filter, "visible", isEnabled, NULL );
224    }
225    else if( !strcmp( key, PREF_KEY_TOOLBAR ) )
226    {
227        const gboolean isEnabled = pref_flag_get( key );
228        g_object_set( p->toolbar, "visible", isEnabled, NULL );
229    }
230    else if( !strcmp( key, PREF_KEY_STATUSBAR_STATS ) )
231    {
232        tr_window_update( (TrWindow*)wind );
233    }
234    else if( !strcmp( key, TR_PREFS_KEY_ALT_SPEED_ENABLED ) )
235    {
236        syncAltSpeedButton( p );
237    }
238}
239
240static void
241privateFree( gpointer vprivate )
242{
243    PrivateData * p = vprivate;
244
245    g_signal_handler_disconnect( p->core, p->pref_handler_id );
246    g_free( p->filter_text );
247    g_free( p );
248}
249
250static void
251onYinYangReleased( GtkWidget * w           UNUSED,
252                   GdkEventButton * button UNUSED,
253                   gpointer                vprivate )
254{
255    PrivateData * p = vprivate;
256
257    gtk_menu_popup( GTK_MENU(
258                       p->status_menu ), NULL, NULL, NULL, NULL, 0,
259                   gtk_get_current_event_time( ) );
260}
261
262#define STATS_MODE "stats-mode"
263
264static struct
265{
266    const char *  val, *i18n;
267} stats_modes[] = {
268    { "total-ratio",      N_( "Total Ratio" )                },
269    { "session-ratio",    N_( "Session Ratio" )              },
270    { "total-transfer",   N_( "Total Transfer" )             },
271    { "session-transfer", N_( "Session Transfer" )           }
272};
273
274static void
275status_menu_toggled_cb( GtkCheckMenuItem * menu_item,
276                        gpointer           vprivate )
277{
278    if( gtk_check_menu_item_get_active( menu_item ) )
279    {
280        PrivateData * p = vprivate;
281        const char *  val = g_object_get_data( G_OBJECT(
282                                                   menu_item ), STATS_MODE );
283        tr_core_set_pref( p->core, PREF_KEY_STATUSBAR_STATS, val );
284    }
285}
286
287static void
288syncAltSpeedButton( PrivateData * p )
289{
290    const char * tip;
291    const gboolean b = pref_flag_get( TR_PREFS_KEY_ALT_SPEED_ENABLED );
292    GtkWidget * w = p->alt_speed_button;
293
294    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), b );
295
296    gtk_button_set_image( GTK_BUTTON( w ), p->alt_speed_image[b?1:0] );
297    gtk_button_set_alignment( GTK_BUTTON( w ), 0.5, 0.5 );
298
299    tip = b ? _( "Click to disable Speed Limit Mode" )
300            : _( "Click to enable Speed Limit Mode" );
301    gtr_widget_set_tooltip_text( w, tip );
302}
303
304static void
305alt_speed_toggled_cb( GtkToggleButton * button, gpointer vprivate )
306{
307    PrivateData * p = vprivate;
308    const gboolean b = gtk_toggle_button_get_active( button );
309    tr_core_set_pref_bool( p->core, TR_PREFS_KEY_ALT_SPEED_ENABLED,  b );
310}
311   
312/***
313****  FILTER
314***/
315
316static int
317checkFilterText( filter_text_mode_t    filter_text_mode,
318                 const tr_info       * torInfo,
319                 const char          * text )
320{
321    tr_file_index_t i;
322    int             ret = 0;
323    char *          pch;
324
325    switch( filter_text_mode )
326    {
327        case FILTER_TEXT_MODE_FILES:
328            for( i = 0; i < torInfo->fileCount && !ret; ++i )
329            {
330                pch = g_utf8_casefold( torInfo->files[i].name, -1 );
331                ret = !text || strstr( pch, text ) != NULL;
332                g_free( pch );
333            }
334            break;
335
336        case FILTER_TEXT_MODE_TRACKER:
337            pch = g_utf8_casefold( torInfo->trackers[0].announce, -1 );
338            ret = !text || ( strstr( pch, text ) != NULL );
339            g_free( pch );
340            break;
341
342        default: /* NAME */
343            pch = g_utf8_casefold( torInfo->name, -1 );
344            ret = !text || ( strstr( pch, text ) != NULL );
345            g_free( pch );
346            break;
347    }
348
349    return ret;
350}
351
352static int
353checkFilterMode( filter_mode_t filter_mode,
354                 tr_torrent *  tor )
355{
356    int ret = 0;
357
358    switch( filter_mode )
359    {
360        case FILTER_MODE_DOWNLOADING:
361            ret = tr_torrentGetActivity( tor ) == TR_STATUS_DOWNLOAD;
362            break;
363
364        case FILTER_MODE_SEEDING:
365            ret = tr_torrentGetActivity( tor ) == TR_STATUS_SEED;
366            break;
367
368        case FILTER_MODE_PAUSED:
369            ret = tr_torrentGetActivity( tor ) == TR_STATUS_STOPPED;
370            break;
371
372        case FILTER_MODE_ACTIVE:
373        {
374            const tr_stat * s = tr_torrentStatCached( tor );
375            ret = s->peersSendingToUs > 0 || s->peersGettingFromUs > 0;
376            break;
377        }
378
379        default: /* all */
380            ret = 1;
381    }
382
383    return ret;
384}
385
386static gboolean
387is_row_visible( GtkTreeModel * model,
388                GtkTreeIter *  iter,
389                gpointer       vprivate )
390{
391    PrivateData * p = vprivate;
392    tr_torrent *  tor;
393
394    gtk_tree_model_get( model, iter, MC_TORRENT_RAW, &tor, -1 );
395
396    return checkFilterMode( p->filter_mode, tor )
397           && checkFilterText( p->filter_text_mode, tr_torrentInfo( tor ), p->filter_text );
398}
399
400static void updateTorrentCount( PrivateData * p );
401
402static void
403refilter( PrivateData * p )
404{
405    gtk_tree_model_filter_refilter( GTK_TREE_MODEL_FILTER( p->filter_model ) );
406
407    updateTorrentCount( p );
408}
409
410static void
411filter_text_toggled_cb( GtkCheckMenuItem * menu_item,
412                        gpointer           vprivate )
413{
414    if( gtk_check_menu_item_get_active( menu_item ) )
415    {
416        PrivateData * p = vprivate;
417        p->filter_text_mode =
418            GPOINTER_TO_UINT( g_object_get_data( G_OBJECT( menu_item ),
419                                                 FILTER_TEXT_MODE_KEY ) );
420        refilter( p );
421    }
422}
423
424static void
425filter_toggled_cb( GtkToggleButton * toggle,
426                   gpointer          vprivate )
427{
428    PrivateData *       p = vprivate;
429    GSList *            l;
430    GSList *            toggles = g_object_get_data( G_OBJECT(
431                                                         toggle ),
432                                                     FILTER_TOGGLES_KEY );
433    const gboolean      isActive = gtk_toggle_button_get_active( toggle );
434    const filter_mode_t mode =
435        GPOINTER_TO_UINT( g_object_get_data( G_OBJECT( toggle ),
436                                             FILTER_MODE_KEY ) );
437
438    /* update the filter */
439    if( isActive )
440    {
441        p->filter_mode = mode;
442        refilter( p );
443    }
444
445    /* deactivate the other toggles */
446    for( l = toggles; l != NULL; l = l->next )
447    {
448        GtkToggleButton * walk = GTK_TOGGLE_BUTTON( l->data );
449        if( isActive && ( toggle != walk ) )
450            gtk_toggle_button_set_active( walk, FALSE );
451    }
452
453    /* at least one button must always be set */
454    if( !isActive && ( p->filter_mode == mode ) )
455        gtk_toggle_button_set_active( toggle, TRUE );
456}
457
458static void
459filter_entry_changed( GtkEditable * e,
460                      gpointer      vprivate )
461{
462    char *        pch;
463    PrivateData * p = vprivate;
464
465    pch = gtk_editable_get_chars( e, 0, -1 );
466    g_free( p->filter_text );
467    p->filter_text = g_utf8_casefold( pch, -1 );
468    refilter( p );
469    g_free( pch );
470}
471
472static void
473entry_icon_released( SexyIconEntry         * entry  UNUSED,
474                     SexyIconEntryPosition          icon_pos,
475                     int                     button UNUSED,
476                     gpointer                       menu )
477{
478    if( icon_pos == SEXY_ICON_ENTRY_PRIMARY )
479        gtk_menu_popup ( GTK_MENU(
480                            menu ), NULL, NULL, NULL, NULL, 0,
481                        gtk_get_current_event_time( ) );
482}
483
484#if GTK_CHECK_VERSION( 2, 12, 0 )
485
486static void
487findMaxAnnounceTime( GtkTreeModel *      model,
488                     GtkTreePath  * path UNUSED,
489                     GtkTreeIter *       iter,
490                     gpointer            gmaxTime )
491{
492    tr_torrent *    tor;
493    const tr_stat * torStat;
494    time_t *        maxTime = gmaxTime;
495
496    gtk_tree_model_get( model, iter, MC_TORRENT_RAW, &tor, -1 );
497    torStat = tr_torrentStatCached( tor );
498    *maxTime = MAX( *maxTime, torStat->manualAnnounceTime );
499}
500
501static gboolean
502onAskTrackerQueryTooltip( GtkWidget *            widget UNUSED,
503                          gint                   x UNUSED,
504                          gint                   y UNUSED,
505                          gboolean               keyboard_tip UNUSED,
506                          GtkTooltip *           tooltip,
507                          gpointer               gdata )
508{
509    const time_t now = time( NULL );
510    time_t       maxTime = 0;
511    PrivateData * p = gdata;
512
513    gtk_tree_selection_selected_foreach( p->selection,
514                                         findMaxAnnounceTime,
515                                         &maxTime );
516    if( maxTime <= now )
517    {
518        return FALSE;
519    }
520    else
521    {
522        char      buf[128];
523        char      timebuf[64];
524        const int seconds = maxTime - now;
525
526        tr_strltime( timebuf, seconds, sizeof( timebuf ) );
527        g_snprintf( buf, sizeof( buf ),
528                    _( "Tracker will allow requests in %s" ), timebuf );
529        gtk_tooltip_set_text( tooltip, buf );
530        return TRUE;
531    }
532}
533
534#endif
535
536static gboolean
537onAltSpeedToggledIdle( gpointer vp )
538{
539    PrivateData * p = vp;
540    gboolean b = tr_sessionUsesAltSpeed( tr_core_session( p->core ) );
541    tr_core_set_pref_bool( p->core, TR_PREFS_KEY_ALT_SPEED_ENABLED, b );
542
543    return FALSE;
544}
545
546static void
547onAltSpeedToggled( tr_session * s UNUSED, tr_bool isEnabled UNUSED, tr_bool byUser UNUSED, void * p )
548{
549    g_idle_add( onAltSpeedToggledIdle, p );
550}
551
552/***
553****  PUBLIC
554***/
555
556GtkWidget *
557tr_window_new( GtkUIManager * ui_mgr, TrCore * core )
558{
559    int           i, n;
560    const char *  pch;
561    PrivateData * p;
562    GtkWidget   *mainmenu, *toolbar, *filter, *list, *status;
563    GtkWidget *   vbox, *w, *self, *h, *c, *s, *image, *menu;
564    GtkWindow *   win;
565    GSList *      l;
566    GSList *      toggles;
567
568    const char *  filter_names[FILTER_MODE_QTY] = {
569        /* show all torrents */
570        N_( "A_ll" ),
571        /* show only torrents that have connected peers */
572        N_( "_Active" ),
573        /* show only torrents that are trying to download */
574        N_( "_Downloading" ),
575        /* show only torrents that are trying to upload */
576        N_( "_Seeding" ),
577        /* show only torrents that are paused */
578        N_( "_Paused" )
579    };
580    const char *  filter_text_names[FILTER_TEXT_MODE_QTY] = {
581        N_( "Name" ), N_( "Files" ), N_( "Tracker" )
582    };
583
584    p = g_new0( PrivateData, 1 );
585    p->filter_mode = FILTER_MODE_ALL;
586    p->filter_text_mode = FILTER_TEXT_MODE_NAME;
587    p->filter_text = NULL;
588
589    /* make the window */
590    self = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
591    g_object_set_data_full( G_OBJECT(
592                                self ), PRIVATE_DATA_KEY, p, privateFree );
593    win = GTK_WINDOW( self );
594    gtk_window_set_title( win, g_get_application_name( ) );
595    gtk_window_set_role( win, "tr-main" );
596    gtk_window_set_default_size( win,
597                                 pref_int_get( PREF_KEY_MAIN_WINDOW_WIDTH ),
598                                 pref_int_get( PREF_KEY_MAIN_WINDOW_HEIGHT ) );
599    gtk_window_move( win, pref_int_get( PREF_KEY_MAIN_WINDOW_X ),
600                     pref_int_get( PREF_KEY_MAIN_WINDOW_Y ) );
601    gtk_window_add_accel_group( win, gtk_ui_manager_get_accel_group( ui_mgr ) );
602
603    /* window's main container */
604    vbox = gtk_vbox_new ( FALSE, 0 );
605    gtk_container_add ( GTK_CONTAINER( self ), vbox );
606
607    /* main menu */
608    w = mainmenu = action_get_widget( "/main-window-menu" );
609    w = action_get_widget( "/main-window-menu/torrent-menu/update-tracker" );
610#if GTK_CHECK_VERSION( 2, 12, 0 )
611    g_signal_connect( w, "query-tooltip",
612                      G_CALLBACK( onAskTrackerQueryTooltip ), p );
613#endif
614
615    /* toolbar */
616    w = toolbar = p->toolbar = action_get_widget( "/main-window-toolbar" );
617
618    /* filter */
619    toggles = NULL;
620    h = filter = p->filter = gtk_hbox_new( FALSE, 0 );
621    gtk_container_set_border_width( GTK_CONTAINER( h ), GUI_PAD_SMALL );
622    for( i = 0; i < FILTER_MODE_QTY; ++i )
623    {
624        const char * mnemonic = _( filter_names[i] );
625        w = gtk_toggle_button_new_with_mnemonic( mnemonic );
626        g_object_set_data( G_OBJECT( w ), FILTER_MODE_KEY,
627                          GINT_TO_POINTER( i ) );
628        gtk_button_set_relief( GTK_BUTTON( w ), GTK_RELIEF_NONE );
629        gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(
630                                          w ), i == FILTER_MODE_ALL );
631        toggles = g_slist_prepend( toggles, w );
632        g_signal_connect( w, "toggled", G_CALLBACK( filter_toggled_cb ), p );
633        gtk_box_pack_start( GTK_BOX( h ), w, FALSE, FALSE, 0 );
634    }
635    for( l = toggles; l != NULL; l = l->next )
636        g_object_set_data( G_OBJECT( l->data ), FILTER_TOGGLES_KEY, toggles );
637    s = sexy_icon_entry_new( );
638    sexy_icon_entry_add_clear_button( SEXY_ICON_ENTRY( s ) );
639    image = gtk_image_new_from_stock( GTK_STOCK_FIND, GTK_ICON_SIZE_MENU );
640    sexy_icon_entry_set_icon( SEXY_ICON_ENTRY(
641                                 s ), SEXY_ICON_ENTRY_PRIMARY,
642                             GTK_IMAGE( image ) );
643    sexy_icon_entry_set_icon_highlight( SEXY_ICON_ENTRY(
644                                            s ), SEXY_ICON_ENTRY_PRIMARY,
645                                        TRUE );
646    gtk_box_pack_end( GTK_BOX( h ), s, FALSE, FALSE, 0 );
647    g_signal_connect( s, "changed", G_CALLBACK( filter_entry_changed ), p );
648
649    /* status menu */
650    menu = p->status_menu = gtk_menu_new( );
651    l = NULL;
652    pch = pref_string_get( PREF_KEY_STATUSBAR_STATS );
653    for( i = 0, n = G_N_ELEMENTS( stats_modes ); i < n; ++i )
654    {
655        const char * val = stats_modes[i].val;
656        w = gtk_radio_menu_item_new_with_label( l, _( stats_modes[i].i18n ) );
657        l = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM( w ) );
658        gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( w ),
659                                       !strcmp( val, pch ) );
660        g_object_set_data( G_OBJECT(
661                               w ), STATS_MODE,
662                           (gpointer)stats_modes[i].val );
663        g_signal_connect( w, "toggled", G_CALLBACK(
664                              status_menu_toggled_cb ), p );
665        gtk_menu_shell_append( GTK_MENU_SHELL( menu ), w );
666        gtk_widget_show( w );
667    }
668
669    /* status */
670    h = status = p->status = gtk_hbox_new( FALSE, GUI_PAD );
671    gtk_container_set_border_width( GTK_CONTAINER( h ), GUI_PAD_SMALL );
672    p->alt_speed_image[0] = gtk_image_new_from_stock( "alt-speed-off", -1 );
673    p->alt_speed_image[1]  = gtk_image_new_from_stock( "alt-speed-on", -1 );
674    w = p->alt_speed_button = gtk_toggle_button_new( );
675    /*gtk_button_set_relief( GTK_BUTTON( w ), GTK_RELIEF_NONE );*/
676    g_object_ref( G_OBJECT( p->alt_speed_image[0] ) );
677    g_object_ref( G_OBJECT( p->alt_speed_image[1] ) );
678    g_signal_connect( w, "toggled", G_CALLBACK(alt_speed_toggled_cb ), p );
679    gtk_box_pack_start( GTK_BOX( h ), w, 0, 0, 0 );
680    w = p->gutter_lb = gtk_label_new( "N Torrents" );
681    gtk_box_pack_start( GTK_BOX( h ), w, 1, 1, GUI_PAD_BIG );
682    w = p->ul_lb = gtk_label_new( NULL );
683    gtk_box_pack_end( GTK_BOX( h ), w, FALSE, FALSE, 0 );
684    w = gtk_image_new_from_stock( GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU );
685    gtk_box_pack_end( GTK_BOX( h ), w, FALSE, FALSE, 0 );
686    w = gtk_alignment_new( 0.0f, 0.0f, 0.0f, 0.0f );
687    gtk_widget_set_size_request( w, GUI_PAD, 0u );
688    gtk_box_pack_end( GTK_BOX( h ), w, FALSE, FALSE, 0 );
689    w = p->dl_lb = gtk_label_new( NULL );
690    gtk_box_pack_end( GTK_BOX( h ), w, FALSE, FALSE, 0 );
691    w = gtk_image_new_from_stock( GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU );
692    gtk_box_pack_end( GTK_BOX( h ), w, FALSE, FALSE, 0 );
693    w = gtk_alignment_new( 0.0f, 0.0f, 0.0f, 0.0f );
694    gtk_widget_set_size_request( w, GUI_PAD, 0u );
695    gtk_box_pack_end( GTK_BOX( h ), w, FALSE, FALSE, 0 );
696    w = p->stats_lb = gtk_label_new( NULL );
697    gtk_box_pack_end( GTK_BOX( h ), w, FALSE, FALSE, 0 );
698    w = gtk_image_new_from_stock( GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU );
699    c = gtk_event_box_new( );
700    gtk_container_add( GTK_CONTAINER( c ), w );
701    w = c;
702    gtk_box_pack_end( GTK_BOX( h ), w, FALSE, FALSE, 0 );
703    g_signal_connect( w, "button-release-event",
704                      G_CALLBACK( onYinYangReleased ), p );
705
706    menu = gtk_menu_new( );
707    l = NULL;
708    for( i = 0; i < FILTER_TEXT_MODE_QTY; ++i )
709    {
710        const char * name = _( filter_text_names[i] );
711        GtkWidget *  w = gtk_radio_menu_item_new_with_label ( l, name );
712        l = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM( w ) );
713        g_object_set_data( G_OBJECT( w ), FILTER_TEXT_MODE_KEY,
714                           GINT_TO_POINTER( i ) );
715        g_signal_connect( w, "toggled",
716                          G_CALLBACK( filter_text_toggled_cb ), p );
717        gtk_menu_shell_append( GTK_MENU_SHELL( menu ), w );
718        gtk_widget_show( w );
719    }
720    g_signal_connect( s, "icon-released",
721                      G_CALLBACK( entry_icon_released ), menu );
722
723    /* workarea */
724    p->view = makeview( p, core );
725    w = list = p->scroll = gtk_scrolled_window_new( NULL, NULL );
726    gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( w ),
727                                    GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
728    gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( w ),
729                                         GTK_SHADOW_IN );
730    gtk_container_add( GTK_CONTAINER( w ), p->view );
731
732    /* layout the widgets */
733    {
734        const char * str = pref_string_get( PREF_KEY_MAIN_WINDOW_LAYOUT_ORDER );
735        char ** tokens = g_strsplit( str, ",", -1 );
736        for( i=0; tokens && tokens[i]; ++i )
737        {
738            const char * key = tokens[i];
739
740            if( !strcmp( key, "menu" ) )
741                gtk_box_pack_start( GTK_BOX( vbox ), mainmenu, FALSE, FALSE, 0 );
742            else if( !strcmp( key, "toolbar" ) )
743                gtk_box_pack_start( GTK_BOX( vbox ), toolbar, FALSE, FALSE, 0 );
744            else if( !strcmp( key, "filter" ) )
745                gtk_box_pack_start( GTK_BOX( vbox ), filter, FALSE, FALSE, 0 );
746            else if( !strcmp( key, "list" ) )
747                gtk_box_pack_start( GTK_BOX( vbox ), list, TRUE, TRUE, 0 );
748            else if( !strcmp( key, "statusbar" ) )
749                gtk_box_pack_start( GTK_BOX( vbox ), status, FALSE, FALSE, 0 );
750        }
751        g_strfreev( tokens );
752    }
753
754    /* show all but the window */
755    gtk_widget_show_all( vbox );
756
757    /* listen for prefs changes that affect the window */
758    p->core = core;
759    prefsChanged( core, PREF_KEY_MINIMAL_VIEW, self );
760    prefsChanged( core, PREF_KEY_FILTERBAR, self );
761    prefsChanged( core, PREF_KEY_STATUSBAR, self );
762    prefsChanged( core, PREF_KEY_STATUSBAR_STATS, self );
763    prefsChanged( core, PREF_KEY_TOOLBAR, self );
764    prefsChanged( core, TR_PREFS_KEY_ALT_SPEED_ENABLED, self );
765    p->pref_handler_id = g_signal_connect( core, "prefs-changed",
766                                           G_CALLBACK( prefsChanged ), self );
767
768    tr_sessionSetAltSpeedFunc( tr_core_session( core ), onAltSpeedToggled, p );
769
770    filter_entry_changed( GTK_EDITABLE( s ), p );
771    return self;
772}
773
774static void
775updateTorrentCount( PrivateData * p )
776{
777    if( p && p->core )
778    {
779        char      buf[128];
780        const int torrentCount = gtk_tree_model_iter_n_children(
781            tr_core_model( p->core ), NULL );
782        const int visibleCount = gtk_tree_model_iter_n_children(
783            p->filter_model, NULL );
784
785        if( torrentCount != visibleCount )
786            g_snprintf( buf, sizeof( buf ),
787                        ngettext( "%1$'d of %2$'d Torrent",
788                                  "%1$'d of %2$'d Torrents",
789                                  torrentCount ),
790                        visibleCount, torrentCount );
791        else
792            g_snprintf( buf, sizeof( buf ), ngettext( "%'d Torrent",
793                                                      "%'d Torrents",
794                                                      torrentCount ),
795                        torrentCount );
796        gtk_label_set_text( GTK_LABEL( p->gutter_lb ), buf );
797    }
798}
799
800static void
801updateStats( PrivateData * p )
802{
803    const char *            pch;
804    char                    up[32], down[32], ratio[32], buf[128];
805    struct tr_session_stats stats;
806    tr_session *            session = tr_core_session( p->core );
807
808    /* update the stats */
809    pch = pref_string_get( PREF_KEY_STATUSBAR_STATS );
810    if( !strcmp( pch, "session-ratio" ) )
811    {
812        tr_sessionGetStats( session, &stats );
813        tr_strlratio( ratio, stats.ratio, sizeof( ratio ) );
814        g_snprintf( buf, sizeof( buf ), _( "Ratio: %s" ), ratio );
815    }
816    else if( !strcmp( pch, "session-transfer" ) )
817    {
818        tr_sessionGetStats( session, &stats );
819        tr_strlsize( up, stats.uploadedBytes, sizeof( up ) );
820        tr_strlsize( down, stats.downloadedBytes, sizeof( down ) );
821        /* Translators: "size|" is here for disambiguation.  Please remove it from your translation.
822           %1$s is the size of the data we've downloaded
823           %2$s is the size of the data we've uploaded */
824        g_snprintf( buf, sizeof( buf ), Q_(
825                        "size|Down: %1$s, Up: %2$s" ), down, up );
826    }
827    else if( !strcmp( pch, "total-transfer" ) )
828    {
829        tr_sessionGetCumulativeStats( session, &stats );
830        tr_strlsize( up, stats.uploadedBytes, sizeof( up ) );
831        tr_strlsize( down, stats.downloadedBytes, sizeof( down ) );
832        /* Translators: "size|" is here for disambiguation.  Please remove it from your translation.
833           %1$s is the size of the data we've downloaded
834           %2$s is the size of the data we've uploaded */
835        g_snprintf( buf, sizeof( buf ), Q_(
836                        "size|Down: %1$s, Up: %2$s" ), down, up );
837    }
838    else     /* default is total-ratio */
839    {
840        tr_sessionGetCumulativeStats( session, &stats );
841        tr_strlratio( ratio, stats.ratio, sizeof( ratio ) );
842        g_snprintf( buf, sizeof( buf ), _( "Ratio: %s" ), ratio );
843    }
844    gtk_label_set_text( GTK_LABEL( p->stats_lb ), buf );
845}
846
847static void
848updateSpeeds( PrivateData * p )
849{
850    tr_session * session = tr_core_session( p->core );
851
852    if( session != NULL )
853    {
854        char buf[128];
855        double d;
856
857        d = tr_sessionGetPieceSpeed( session, TR_DOWN );
858        tr_strlspeed( buf, d, sizeof( buf ) );
859        gtk_label_set_text( GTK_LABEL( p->dl_lb ), buf );
860
861        d = tr_sessionGetPieceSpeed( session, TR_UP );
862        tr_strlspeed( buf, d, sizeof( buf ) );
863        gtk_label_set_text( GTK_LABEL( p->ul_lb ), buf );
864    }
865}
866
867void
868tr_window_update( TrWindow * self )
869{
870    PrivateData * p = get_private_data( self );
871
872    if( p && p->core && tr_core_session( p->core ) )
873    {
874        updateSpeeds( p );
875        updateTorrentCount( p );
876        updateStats( p );
877        refilter( p );
878    }
879}
880
881GtkTreeSelection*
882tr_window_get_selection( TrWindow * w )
883{
884    return get_private_data( w )->selection;
885}
886
Note: See TracBrowser for help on using the repository browser.