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

Last change on this file since 7465 was 7465, checked in by charles, 12 years ago

(trunk gtk) #1447: Launchpad translations need comment for strings with non-standard context

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