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

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

(trunk) add a did-the-user-do-this flag to the "alt speed toggled" callback

  • Property svn:keywords set to Date Rev Author Id
File size: 29.6 KB
Line 
1/******************************************************************************
2 * $Id: tr-window.c 8118 2009-03-30 17:25:35Z 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
298    tip = b ? _( "Click to disable Speed Limit Mode" )
299            : _( "Click to enable Speed Limit Mode" );
300    gtr_widget_set_tooltip_text( w, tip );
301}
302
303static void
304alt_speed_toggled_cb( GtkToggleButton * button, gpointer vprivate )
305{
306    PrivateData * p = vprivate;
307    const gboolean b = gtk_toggle_button_get_active( button );
308    tr_core_set_pref_bool( p->core, TR_PREFS_KEY_ALT_SPEED_ENABLED,  b );
309}
310   
311/***
312****  FILTER
313***/
314
315static int
316checkFilterText( filter_text_mode_t    filter_text_mode,
317                 const tr_info       * torInfo,
318                 const char          * text )
319{
320    tr_file_index_t i;
321    int             ret = 0;
322    char *          pch;
323
324    switch( filter_text_mode )
325    {
326        case FILTER_TEXT_MODE_FILES:
327            for( i = 0; i < torInfo->fileCount && !ret; ++i )
328            {
329                pch = g_utf8_casefold( torInfo->files[i].name, -1 );
330                ret = !text || strstr( pch, text ) != NULL;
331                g_free( pch );
332            }
333            break;
334
335        case FILTER_TEXT_MODE_TRACKER:
336            pch = g_utf8_casefold( torInfo->trackers[0].announce, -1 );
337            ret = !text || ( strstr( pch, text ) != NULL );
338            g_free( pch );
339            break;
340
341        default: /* NAME */
342            pch = g_utf8_casefold( torInfo->name, -1 );
343            ret = !text || ( strstr( pch, text ) != NULL );
344            g_free( pch );
345            break;
346    }
347
348    return ret;
349}
350
351static int
352checkFilterMode( filter_mode_t filter_mode,
353                 tr_torrent *  tor )
354{
355    int ret = 0;
356
357    switch( filter_mode )
358    {
359        case FILTER_MODE_DOWNLOADING:
360            ret = tr_torrentGetActivity( tor ) == TR_STATUS_DOWNLOAD;
361            break;
362
363        case FILTER_MODE_SEEDING:
364            ret = tr_torrentGetActivity( tor ) == TR_STATUS_SEED;
365            break;
366
367        case FILTER_MODE_PAUSED:
368            ret = tr_torrentGetActivity( tor ) == TR_STATUS_STOPPED;
369            break;
370
371        case FILTER_MODE_ACTIVE:
372        {
373            const tr_stat * s = tr_torrentStatCached( tor );
374            ret = s->peersSendingToUs > 0 || s->peersGettingFromUs > 0;
375            break;
376        }
377
378        default: /* all */
379            ret = 1;
380    }
381
382    return ret;
383}
384
385static gboolean
386is_row_visible( GtkTreeModel * model,
387                GtkTreeIter *  iter,
388                gpointer       vprivate )
389{
390    PrivateData * p = vprivate;
391    tr_torrent *  tor;
392
393    gtk_tree_model_get( model, iter, MC_TORRENT_RAW, &tor, -1 );
394
395    return checkFilterMode( p->filter_mode, tor )
396           && checkFilterText( p->filter_text_mode, tr_torrentInfo( tor ), p->filter_text );
397}
398
399static void updateTorrentCount( PrivateData * p );
400
401static void
402refilter( PrivateData * p )
403{
404    gtk_tree_model_filter_refilter( GTK_TREE_MODEL_FILTER( p->filter_model ) );
405
406    updateTorrentCount( p );
407}
408
409static void
410filter_text_toggled_cb( GtkCheckMenuItem * menu_item,
411                        gpointer           vprivate )
412{
413    if( gtk_check_menu_item_get_active( menu_item ) )
414    {
415        PrivateData * p = vprivate;
416        p->filter_text_mode =
417            GPOINTER_TO_UINT( g_object_get_data( G_OBJECT( menu_item ),
418                                                 FILTER_TEXT_MODE_KEY ) );
419        refilter( p );
420    }
421}
422
423static void
424filter_toggled_cb( GtkToggleButton * toggle,
425                   gpointer          vprivate )
426{
427    PrivateData *       p = vprivate;
428    GSList *            l;
429    GSList *            toggles = g_object_get_data( G_OBJECT(
430                                                         toggle ),
431                                                     FILTER_TOGGLES_KEY );
432    const gboolean      isActive = gtk_toggle_button_get_active( toggle );
433    const filter_mode_t mode =
434        GPOINTER_TO_UINT( g_object_get_data( G_OBJECT( toggle ),
435                                             FILTER_MODE_KEY ) );
436
437    /* update the filter */
438    if( isActive )
439    {
440        p->filter_mode = mode;
441        refilter( p );
442    }
443
444    /* deactivate the other toggles */
445    for( l = toggles; l != NULL; l = l->next )
446    {
447        GtkToggleButton * walk = GTK_TOGGLE_BUTTON( l->data );
448        if( isActive && ( toggle != walk ) )
449            gtk_toggle_button_set_active( walk, FALSE );
450    }
451
452    /* at least one button must always be set */
453    if( !isActive && ( p->filter_mode == mode ) )
454        gtk_toggle_button_set_active( toggle, TRUE );
455}
456
457static void
458filter_entry_changed( GtkEditable * e,
459                      gpointer      vprivate )
460{
461    char *        pch;
462    PrivateData * p = vprivate;
463
464    pch = gtk_editable_get_chars( e, 0, -1 );
465    g_free( p->filter_text );
466    p->filter_text = g_utf8_casefold( pch, -1 );
467    refilter( p );
468    g_free( pch );
469}
470
471static void
472entry_icon_released( SexyIconEntry         * entry  UNUSED,
473                     SexyIconEntryPosition          icon_pos,
474                     int                     button UNUSED,
475                     gpointer                       menu )
476{
477    if( icon_pos == SEXY_ICON_ENTRY_PRIMARY )
478        gtk_menu_popup ( GTK_MENU(
479                            menu ), NULL, NULL, NULL, NULL, 0,
480                        gtk_get_current_event_time( ) );
481}
482
483#if GTK_CHECK_VERSION( 2, 12, 0 )
484
485static void
486findMaxAnnounceTime( GtkTreeModel *      model,
487                     GtkTreePath  * path UNUSED,
488                     GtkTreeIter *       iter,
489                     gpointer            gmaxTime )
490{
491    tr_torrent *    tor;
492    const tr_stat * torStat;
493    time_t *        maxTime = gmaxTime;
494
495    gtk_tree_model_get( model, iter, MC_TORRENT_RAW, &tor, -1 );
496    torStat = tr_torrentStatCached( tor );
497    *maxTime = MAX( *maxTime, torStat->manualAnnounceTime );
498}
499
500static gboolean
501onAskTrackerQueryTooltip( GtkWidget *            widget UNUSED,
502                          gint                   x UNUSED,
503                          gint                   y UNUSED,
504                          gboolean               keyboard_tip UNUSED,
505                          GtkTooltip *           tooltip,
506                          gpointer               gdata )
507{
508    const time_t now = time( NULL );
509    time_t       maxTime = 0;
510    PrivateData * p = gdata;
511
512    gtk_tree_selection_selected_foreach( p->selection,
513                                         findMaxAnnounceTime,
514                                         &maxTime );
515    if( maxTime <= now )
516    {
517        return FALSE;
518    }
519    else
520    {
521        char      buf[128];
522        char      timebuf[64];
523        const int seconds = maxTime - now;
524
525        tr_strltime( timebuf, seconds, sizeof( timebuf ) );
526        g_snprintf( buf, sizeof( buf ),
527                    _( "Tracker will allow requests in %s" ), timebuf );
528        gtk_tooltip_set_text( tooltip, buf );
529        return TRUE;
530    }
531}
532
533#endif
534
535static gboolean
536onAltSpeedToggledIdle( gpointer vp )
537{
538    PrivateData * p = vp;
539    gboolean b = tr_sessionUsesAltSpeed( tr_core_session( p->core ) );
540    tr_core_set_pref_bool( p->core, TR_PREFS_KEY_ALT_SPEED_ENABLED, b );
541
542    return FALSE;
543}
544
545static void
546onAltSpeedToggled( tr_session * s UNUSED, tr_bool isEnabled UNUSED, tr_bool byUser UNUSED, void * p )
547{
548    g_idle_add( onAltSpeedToggledIdle, p );
549}
550
551/***
552****  PUBLIC
553***/
554
555GtkWidget *
556tr_window_new( GtkUIManager * ui_mgr, TrCore * core )
557{
558    int           i, n;
559    const char *  pch;
560    PrivateData * p;
561    GtkWidget   *mainmenu, *toolbar, *filter, *list, *status;
562    GtkWidget *   vbox, *w, *self, *h, *c, *s, *image, *menu;
563    GtkWindow *   win;
564    GSList *      l;
565    GSList *      toggles;
566
567    const char *  filter_names[FILTER_MODE_QTY] = {
568        /* show all torrents */
569        N_( "A_ll" ),
570        /* show only torrents that have connected peers */
571        N_( "_Active" ),
572        /* show only torrents that are trying to download */
573        N_( "_Downloading" ),
574        /* show only torrents that are trying to upload */
575        N_( "_Seeding" ),
576        /* show only torrents that are paused */
577        N_( "_Paused" )
578    };
579    const char *  filter_text_names[FILTER_TEXT_MODE_QTY] = {
580        N_( "Name" ), N_( "Files" ), N_( "Tracker" )
581    };
582
583    p = g_new0( PrivateData, 1 );
584    p->filter_mode = FILTER_MODE_ALL;
585    p->filter_text_mode = FILTER_TEXT_MODE_NAME;
586    p->filter_text = NULL;
587
588    /* make the window */
589    self = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
590    g_object_set_data_full( G_OBJECT(
591                                self ), PRIVATE_DATA_KEY, p, privateFree );
592    win = GTK_WINDOW( self );
593    gtk_window_set_title( win, g_get_application_name( ) );
594    gtk_window_set_role( win, "tr-main" );
595    gtk_window_set_default_size( win,
596                                 pref_int_get( PREF_KEY_MAIN_WINDOW_WIDTH ),
597                                 pref_int_get( PREF_KEY_MAIN_WINDOW_HEIGHT ) );
598    gtk_window_move( win, pref_int_get( PREF_KEY_MAIN_WINDOW_X ),
599                     pref_int_get( PREF_KEY_MAIN_WINDOW_Y ) );
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    w = 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    w = toolbar = p->toolbar = action_get_widget( "/main-window-toolbar" );
616
617    /* filter */
618    toggles = NULL;
619    h = filter = p->filter = gtk_hbox_new( FALSE, 0 );
620    gtk_container_set_border_width( GTK_CONTAINER( h ), GUI_PAD_SMALL );
621    for( i = 0; i < FILTER_MODE_QTY; ++i )
622    {
623        const char * mnemonic = _( filter_names[i] );
624        w = gtk_toggle_button_new_with_mnemonic( mnemonic );
625        g_object_set_data( G_OBJECT( w ), FILTER_MODE_KEY,
626                          GINT_TO_POINTER( i ) );
627        gtk_button_set_relief( GTK_BUTTON( w ), GTK_RELIEF_NONE );
628        gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(
629                                          w ), i == FILTER_MODE_ALL );
630        toggles = g_slist_prepend( toggles, w );
631        g_signal_connect( w, "toggled", G_CALLBACK( filter_toggled_cb ), p );
632        gtk_box_pack_start( GTK_BOX( h ), w, FALSE, FALSE, 0 );
633    }
634    for( l = toggles; l != NULL; l = l->next )
635        g_object_set_data( G_OBJECT( l->data ), FILTER_TOGGLES_KEY, toggles );
636    s = sexy_icon_entry_new( );
637    sexy_icon_entry_add_clear_button( SEXY_ICON_ENTRY( s ) );
638    image = gtk_image_new_from_stock( GTK_STOCK_FIND, GTK_ICON_SIZE_MENU );
639    sexy_icon_entry_set_icon( SEXY_ICON_ENTRY(
640                                 s ), SEXY_ICON_ENTRY_PRIMARY,
641                             GTK_IMAGE( image ) );
642    sexy_icon_entry_set_icon_highlight( SEXY_ICON_ENTRY(
643                                            s ), SEXY_ICON_ENTRY_PRIMARY,
644                                        TRUE );
645    gtk_box_pack_end( GTK_BOX( h ), s, FALSE, FALSE, 0 );
646    g_signal_connect( s, "changed", G_CALLBACK( filter_entry_changed ), p );
647
648    /* status menu */
649    menu = p->status_menu = gtk_menu_new( );
650    l = NULL;
651    pch = pref_string_get( PREF_KEY_STATUSBAR_STATS );
652    for( i = 0, n = G_N_ELEMENTS( stats_modes ); i < n; ++i )
653    {
654        const char * val = stats_modes[i].val;
655        w = gtk_radio_menu_item_new_with_label( l, _( stats_modes[i].i18n ) );
656        l = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM( w ) );
657        gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( w ),
658                                       !strcmp( val, pch ) );
659        g_object_set_data( G_OBJECT(
660                               w ), STATS_MODE,
661                           (gpointer)stats_modes[i].val );
662        g_signal_connect( w, "toggled", G_CALLBACK(
663                              status_menu_toggled_cb ), p );
664        gtk_menu_shell_append( GTK_MENU_SHELL( menu ), w );
665        gtk_widget_show( w );
666    }
667
668    /* status */
669    h = status = p->status = gtk_hbox_new( FALSE, GUI_PAD );
670    gtk_container_set_border_width( GTK_CONTAINER( h ), GUI_PAD_SMALL );
671    p->alt_speed_image[0] = gtk_image_new_from_stock( "alt-speed-off", -1 );
672    p->alt_speed_image[1]  = gtk_image_new_from_stock( "alt-speed-on", -1 );
673    w = p->alt_speed_button = gtk_toggle_button_new( );
674    /*gtk_button_set_relief( GTK_BUTTON( w ), GTK_RELIEF_NONE );*/
675    g_object_ref( G_OBJECT( p->alt_speed_image[0] ) );
676    g_object_ref( G_OBJECT( p->alt_speed_image[1] ) );
677    g_signal_connect( w, "toggled", G_CALLBACK(alt_speed_toggled_cb ), p );
678    gtk_box_pack_start( GTK_BOX( h ), w, 0, 0, 0 );
679    w = p->gutter_lb = gtk_label_new( "N Torrents" );
680    gtk_box_pack_start( GTK_BOX( h ), w, 1, 1, GUI_PAD_BIG );
681    w = p->ul_lb = gtk_label_new( NULL );
682    gtk_box_pack_end( GTK_BOX( h ), w, FALSE, FALSE, 0 );
683    w = gtk_image_new_from_stock( GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU );
684    gtk_box_pack_end( GTK_BOX( h ), w, FALSE, FALSE, 0 );
685    w = gtk_alignment_new( 0.0f, 0.0f, 0.0f, 0.0f );
686    gtk_widget_set_size_request( w, GUI_PAD, 0u );
687    gtk_box_pack_end( GTK_BOX( h ), w, FALSE, FALSE, 0 );
688    w = p->dl_lb = gtk_label_new( NULL );
689    gtk_box_pack_end( GTK_BOX( h ), w, FALSE, FALSE, 0 );
690    w = gtk_image_new_from_stock( GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU );
691    gtk_box_pack_end( GTK_BOX( h ), w, FALSE, FALSE, 0 );
692    w = gtk_alignment_new( 0.0f, 0.0f, 0.0f, 0.0f );
693    gtk_widget_set_size_request( w, GUI_PAD, 0u );
694    gtk_box_pack_end( GTK_BOX( h ), w, FALSE, FALSE, 0 );
695    w = p->stats_lb = gtk_label_new( NULL );
696    gtk_box_pack_end( GTK_BOX( h ), w, FALSE, FALSE, 0 );
697    w = gtk_image_new_from_stock( GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU );
698    c = gtk_event_box_new( );
699    gtk_container_add( GTK_CONTAINER( c ), w );
700    w = c;
701    gtk_box_pack_end( GTK_BOX( h ), w, FALSE, FALSE, 0 );
702    g_signal_connect( w, "button-release-event",
703                      G_CALLBACK( onYinYangReleased ), p );
704
705    menu = gtk_menu_new( );
706    l = NULL;
707    for( i = 0; i < FILTER_TEXT_MODE_QTY; ++i )
708    {
709        const char * name = _( filter_text_names[i] );
710        GtkWidget *  w = gtk_radio_menu_item_new_with_label ( l, name );
711        l = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM( w ) );
712        g_object_set_data( G_OBJECT( w ), FILTER_TEXT_MODE_KEY,
713                           GINT_TO_POINTER( i ) );
714        g_signal_connect( w, "toggled",
715                          G_CALLBACK( filter_text_toggled_cb ), p );
716        gtk_menu_shell_append( GTK_MENU_SHELL( menu ), w );
717        gtk_widget_show( w );
718    }
719    g_signal_connect( s, "icon-released",
720                      G_CALLBACK( entry_icon_released ), menu );
721
722    /* workarea */
723    p->view = makeview( p, core );
724    w = list = p->scroll = gtk_scrolled_window_new( NULL, NULL );
725    gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( w ),
726                                    GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
727    gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( w ),
728                                         GTK_SHADOW_IN );
729    gtk_container_add( GTK_CONTAINER( w ), p->view );
730
731    /* layout the widgets */
732    {
733        const char * str = pref_string_get( PREF_KEY_MAIN_WINDOW_LAYOUT_ORDER );
734        char ** tokens = g_strsplit( str, ",", -1 );
735        for( i=0; tokens && tokens[i]; ++i )
736        {
737            const char * key = tokens[i];
738
739            if( !strcmp( key, "menu" ) )
740                gtk_box_pack_start( GTK_BOX( vbox ), mainmenu, FALSE, FALSE, 0 );
741            else if( !strcmp( key, "toolbar" ) )
742                gtk_box_pack_start( GTK_BOX( vbox ), toolbar, FALSE, FALSE, 0 );
743            else if( !strcmp( key, "filter" ) )
744                gtk_box_pack_start( GTK_BOX( vbox ), filter, FALSE, FALSE, 0 );
745            else if( !strcmp( key, "list" ) )
746                gtk_box_pack_start( GTK_BOX( vbox ), list, TRUE, TRUE, 0 );
747            else if( !strcmp( key, "statusbar" ) )
748                gtk_box_pack_start( GTK_BOX( vbox ), status, FALSE, FALSE, 0 );
749        }
750        g_strfreev( tokens );
751    }
752
753    /* show all but the window */
754    gtk_widget_show_all( vbox );
755
756    /* listen for prefs changes that affect the window */
757    p->core = core;
758    prefsChanged( core, PREF_KEY_MINIMAL_VIEW, self );
759    prefsChanged( core, PREF_KEY_FILTERBAR, self );
760    prefsChanged( core, PREF_KEY_STATUSBAR, self );
761    prefsChanged( core, PREF_KEY_STATUSBAR_STATS, self );
762    prefsChanged( core, PREF_KEY_TOOLBAR, self );
763    prefsChanged( core, TR_PREFS_KEY_ALT_SPEED_ENABLED, self );
764    p->pref_handler_id = g_signal_connect( core, "prefs-changed",
765                                           G_CALLBACK( prefsChanged ), self );
766
767    tr_sessionSetAltSpeedFunc( tr_core_session( core ), onAltSpeedToggled, p );
768
769    filter_entry_changed( GTK_EDITABLE( s ), p );
770    return self;
771}
772
773static void
774updateTorrentCount( PrivateData * p )
775{
776    if( p && p->core )
777    {
778        char      buf[128];
779        const int torrentCount = gtk_tree_model_iter_n_children(
780            tr_core_model( p->core ), NULL );
781        const int visibleCount = gtk_tree_model_iter_n_children(
782            p->filter_model, NULL );
783
784        if( torrentCount != visibleCount )
785            g_snprintf( buf, sizeof( buf ),
786                        ngettext( "%1$'d of %2$'d Torrent",
787                                  "%1$'d of %2$'d Torrents",
788                                  torrentCount ),
789                        visibleCount, torrentCount );
790        else
791            g_snprintf( buf, sizeof( buf ), ngettext( "%'d Torrent",
792                                                      "%'d Torrents",
793                                                      torrentCount ),
794                        torrentCount );
795        gtk_label_set_text( GTK_LABEL( p->gutter_lb ), buf );
796    }
797}
798
799static void
800updateStats( PrivateData * p )
801{
802    const char *            pch;
803    char                    up[32], down[32], ratio[32], buf[128];
804    struct tr_session_stats stats;
805    tr_session *            session = tr_core_session( p->core );
806
807    /* update the stats */
808    pch = pref_string_get( PREF_KEY_STATUSBAR_STATS );
809    if( !strcmp( pch, "session-ratio" ) )
810    {
811        tr_sessionGetStats( session, &stats );
812        tr_strlratio( ratio, stats.ratio, sizeof( ratio ) );
813        g_snprintf( buf, sizeof( buf ), _( "Ratio: %s" ), ratio );
814    }
815    else if( !strcmp( pch, "session-transfer" ) )
816    {
817        tr_sessionGetStats( session, &stats );
818        tr_strlsize( up, stats.uploadedBytes, sizeof( up ) );
819        tr_strlsize( down, stats.downloadedBytes, sizeof( down ) );
820        /* Translators: "size|" is here for disambiguation.  Please remove it from your translation.
821           %1$s is the size of the data we've downloaded
822           %2$s is the size of the data we've uploaded */
823        g_snprintf( buf, sizeof( buf ), Q_(
824                        "size|Down: %1$s, Up: %2$s" ), down, up );
825    }
826    else if( !strcmp( pch, "total-transfer" ) )
827    {
828        tr_sessionGetCumulativeStats( session, &stats );
829        tr_strlsize( up, stats.uploadedBytes, sizeof( up ) );
830        tr_strlsize( down, stats.downloadedBytes, sizeof( down ) );
831        /* Translators: "size|" is here for disambiguation.  Please remove it from your translation.
832           %1$s is the size of the data we've downloaded
833           %2$s is the size of the data we've uploaded */
834        g_snprintf( buf, sizeof( buf ), Q_(
835                        "size|Down: %1$s, Up: %2$s" ), down, up );
836    }
837    else     /* default is total-ratio */
838    {
839        tr_sessionGetCumulativeStats( session, &stats );
840        tr_strlratio( ratio, stats.ratio, sizeof( ratio ) );
841        g_snprintf( buf, sizeof( buf ), _( "Ratio: %s" ), ratio );
842    }
843    gtk_label_set_text( GTK_LABEL( p->stats_lb ), buf );
844}
845
846static void
847updateSpeeds( PrivateData * p )
848{
849    tr_session * session = tr_core_session( p->core );
850
851    if( session != NULL )
852    {
853        char buf[128];
854        double d;
855
856        d = tr_sessionGetPieceSpeed( session, TR_DOWN );
857        tr_strlspeed( buf, d, sizeof( buf ) );
858        gtk_label_set_text( GTK_LABEL( p->dl_lb ), buf );
859
860        d = tr_sessionGetPieceSpeed( session, TR_UP );
861        tr_strlspeed( buf, d, sizeof( buf ) );
862        gtk_label_set_text( GTK_LABEL( p->ul_lb ), buf );
863    }
864}
865
866void
867tr_window_update( TrWindow * self )
868{
869    PrivateData * p = get_private_data( self );
870
871    if( p && p->core && tr_core_session( p->core ) )
872    {
873        updateSpeeds( p );
874        updateTorrentCount( p );
875        updateStats( p );
876        refilter( p );
877    }
878}
879
880GtkTreeSelection*
881tr_window_get_selection( TrWindow * w )
882{
883    return get_private_data( w )->selection;
884}
885
Note: See TracBrowser for help on using the repository browser.