source: trunk/gtk/tr_window.c @ 4284

Last change on this file since 4284 was 4284, checked in by charles, 15 years ago

(gtk) fix crash reported by hussam

  • Property svn:keywords set to Date Rev Author Id
File size: 12.5 KB
Line 
1/******************************************************************************
2 * $Id: tr_window.c 4284 2007-12-22 16:34:47Z charles $
3 *
4 * Copyright (c) 2005-2007 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 "torrent-cell-renderer.h"
36#include "tr_prefs.h"
37#include "tr_torrent.h"
38#include "tr_window.h"
39#include "util.h"
40
41#if !GTK_CHECK_VERSION(2,8,0)
42static void
43gtk_tree_view_column_queue_resize( GtkTreeViewColumn * column ) /* yuck */
44{
45   const int spacing = gtk_tree_view_column_get_spacing( column );
46   gtk_tree_view_column_set_spacing( column, spacing+1 );
47   gtk_tree_view_column_set_spacing( column, spacing );
48}
49#endif
50
51typedef struct
52{
53    GtkWidget * scroll;
54    GtkWidget * view;
55    GtkWidget * toolbar;
56    GtkWidget * status;
57    GtkWidget * status_menu;
58    GtkWidget * ul_lb;
59    GtkWidget * dl_lb;
60    GtkWidget * stats_lb;
61    GtkTreeSelection  * selection;
62    GtkCellRenderer   * renderer;
63    GtkTreeViewColumn * column;
64    TrCore * core;
65    gulong pref_handler_id;
66}
67PrivateData;
68
69#define PRIVATE_DATA_KEY "private-data"
70
71PrivateData*
72get_private_data( TrWindow * w )
73{
74    return (PrivateData*) g_object_get_data (G_OBJECT(w), PRIVATE_DATA_KEY);
75}
76
77/***
78****
79***/
80
81static void
82on_popup_menu ( GtkWidget * self UNUSED, GdkEventButton * event )
83{
84    GtkWidget * menu = action_get_widget ( "/main-window-popup" );
85    gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL,
86                    (event ? event->button : 0),
87                    (event ? event->time : 0));
88}
89
90static void
91view_row_activated ( GtkTreeView       * tree_view  UNUSED,
92                     GtkTreePath       * path       UNUSED,
93                     GtkTreeViewColumn * column     UNUSED,
94                     gpointer            user_data  UNUSED )
95{
96    action_activate( "show-torrent-details" );
97}
98
99static GtkWidget*
100makeview( PrivateData * p )
101{
102    GtkWidget         * view;
103    GtkTreeViewColumn * col;
104    GtkTreeSelection  * sel;
105    GtkCellRenderer   * r;
106
107    view = gtk_tree_view_new();
108    gtk_tree_view_set_headers_visible( GTK_TREE_VIEW(view), FALSE );
109
110    p->selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(view) );
111
112    p->renderer = r = torrent_cell_renderer_new( );
113    p->column = col = gtk_tree_view_column_new_with_attributes(
114        _("Torrent"), r, "torrent", MC_TORRENT_RAW, NULL );
115    g_object_set( G_OBJECT(col), "resizable", TRUE,
116                                 "sizing", GTK_TREE_VIEW_COLUMN_FIXED,
117                                 NULL );
118    gtk_tree_view_append_column( GTK_TREE_VIEW( view ), col );
119    g_object_set( r, "xpad", GUI_PAD_SMALL, "ypad", GUI_PAD_SMALL, NULL );
120
121    gtk_tree_view_set_rules_hint( GTK_TREE_VIEW( view ), TRUE );
122    sel = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
123    gtk_tree_selection_set_mode( GTK_TREE_SELECTION( sel ),
124                                 GTK_SELECTION_MULTIPLE );
125
126    g_signal_connect( view, "popup-menu",
127                      G_CALLBACK(on_popup_menu), NULL );
128    g_signal_connect( view, "button-press-event",
129                      G_CALLBACK(on_tree_view_button_pressed),
130                      (void *) on_popup_menu);
131    g_signal_connect( view, "row-activated",
132                      G_CALLBACK(view_row_activated), NULL);
133
134    return view;
135}
136
137static void
138realized_cb ( GtkWidget * wind, gpointer unused UNUSED )
139{
140    PrivateData * p = get_private_data( GTK_WINDOW( wind ) );
141    sizingmagic( GTK_WINDOW(wind),
142                 GTK_SCROLLED_WINDOW( p->scroll ),
143                 GTK_POLICY_NEVER,
144                 GTK_POLICY_ALWAYS );
145}
146
147static void
148prefsChanged( TrCore * core UNUSED, const char * key, gpointer wind )
149{
150    PrivateData * p = get_private_data( GTK_WINDOW( wind ) );
151
152    if( !strcmp( key, PREF_KEY_MINIMAL_VIEW ) )
153    {
154       g_object_set( p->renderer, "minimal", pref_flag_get( key ), NULL );
155       gtk_tree_view_column_queue_resize( p->column );
156    }
157    else if( !strcmp( key, PREF_KEY_STATUS_BAR ) )
158    {
159        const gboolean isEnabled = pref_flag_get( key );
160        g_object_set( p->status, "visible", isEnabled, NULL );
161    }
162    else if( !strcmp( key, PREF_KEY_TOOLBAR ) )
163    {
164        const gboolean isEnabled = pref_flag_get( key );
165        g_object_set( p->toolbar, "visible", isEnabled, NULL );
166    }
167    else if( !strcmp( key, PREF_KEY_STATUS_BAR_STATS ) )
168    {
169        tr_window_update( (TrWindow*)wind );
170    }
171}
172
173static void
174privateFree( gpointer vprivate )
175{
176    PrivateData * p = ( PrivateData * ) vprivate;
177    g_signal_handler_disconnect( p->core, p->pref_handler_id );
178    g_free( p );
179}
180
181static void
182onYinYangReleased( GtkWidget * w UNUSED, GdkEventButton * button UNUSED, gpointer vprivate )
183{
184    PrivateData * p = ( PrivateData * ) vprivate;
185    gtk_menu_popup( GTK_MENU( p->status_menu ), 0, 0, 0, 0, 0, gtk_get_current_event_time( ) );
186}
187
188#define STATS_MODE "stats-mode"
189
190static struct {
191    const char *val, *i18n;
192} stats_modes[] = {
193    { "total-ratio",      N_("Total Ratio") },
194    { "session-ratio",    N_("Session Ratio") },
195    { "total-transfer",   N_("Total Transfer") },
196    { "session-transfer", N_("Session Transfer") }
197};
198
199static void
200status_menu_toggled_cb( GtkCheckMenuItem  * menu_item,
201                        gpointer            vprivate )
202{
203    if( gtk_check_menu_item_get_active( menu_item ) )
204    {
205        PrivateData * p = (PrivateData*) vprivate;
206        const char * val = g_object_get_data( G_OBJECT( menu_item ), STATS_MODE );
207        tr_core_set_pref( p->core, PREF_KEY_STATUS_BAR_STATS, val );
208    }
209}
210
211/***
212****  PUBLIC
213***/
214
215GtkWidget *
216tr_window_new( GtkUIManager * ui_manager, TrCore * core )
217{
218    int i, n;
219    int status_stats_mode;
220    char * pch;
221    PrivateData * p = g_new( PrivateData, 1 );
222    GtkWidget *vbox, *w, *self, *h, *c;
223
224    /* make the window */
225    self = gtk_window_new (GTK_WINDOW_TOPLEVEL);
226    g_object_set_data_full(G_OBJECT(self), PRIVATE_DATA_KEY, p, privateFree );
227    gtk_window_set_title( GTK_WINDOW( self ), g_get_application_name());
228    gtk_window_set_role( GTK_WINDOW( self ), "tr-main" );
229    gtk_window_add_accel_group (GTK_WINDOW(self),
230                                gtk_ui_manager_get_accel_group (ui_manager));
231    g_signal_connect( self, "realize", G_CALLBACK(realized_cb), NULL);
232
233    /* window's main container */
234    vbox = gtk_vbox_new (FALSE, 0);
235    gtk_container_add (GTK_CONTAINER(self), vbox);
236
237    /* main menu */
238    w = action_get_widget( "/main-window-menu" );
239    gtk_box_pack_start( GTK_BOX(vbox), w, FALSE, FALSE, 0 ); 
240
241    /* toolbar */
242    w = p->toolbar = action_get_widget( "/main-window-toolbar" );
243    gtk_box_pack_start( GTK_BOX(vbox), w, FALSE, FALSE, 0 ); 
244
245    /* status menu */
246    GtkWidget * menu = p->status_menu = gtk_menu_new( );
247    status_stats_mode = 0;
248    GSList * l = NULL;
249    pch = pref_string_get( PREF_KEY_STATUS_BAR_STATS );
250    for( i=0, n=G_N_ELEMENTS(stats_modes); i<n; ++i )
251    {
252        const char * val = stats_modes[i].val;
253        w = gtk_radio_menu_item_new_with_label( l, _( stats_modes[i].i18n ) );
254        l = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(w) );
255        gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(w), !strcmp( val, pch ) );
256        g_object_set_data( G_OBJECT(w), STATS_MODE, (gpointer)stats_modes[i].val );
257        g_signal_connect( w, "toggled", G_CALLBACK(status_menu_toggled_cb), p );
258        gtk_menu_shell_append( GTK_MENU_SHELL(menu), w );
259        gtk_widget_show( w );
260    }
261    g_free( pch );
262
263    /* statusbar */
264    h = p->status = gtk_hbox_new( FALSE, GUI_PAD );
265    gtk_container_set_border_width( GTK_CONTAINER(h), GUI_PAD );
266     
267    w = p->ul_lb = gtk_label_new( NULL );
268    gtk_box_pack_end( GTK_BOX(h), w, FALSE, FALSE, 0 );
269    w = gtk_image_new_from_stock( "tr-arrow-up", (GtkIconSize)-1 );
270    gtk_box_pack_end( GTK_BOX(h), w, FALSE, FALSE, 0 );
271    w = gtk_alignment_new( 0.0f, 0.0f, 0.0f, 0.0f );
272    gtk_widget_set_usize( w, GUI_PAD, 0u );
273    gtk_box_pack_end( GTK_BOX(h), w, FALSE, FALSE, 0 );
274    w = p->dl_lb = gtk_label_new( NULL );
275    gtk_box_pack_end( GTK_BOX(h), w, FALSE, FALSE, 0 );
276    w = gtk_image_new_from_stock( "tr-arrow-down", (GtkIconSize)-1 );
277    gtk_box_pack_end( GTK_BOX(h), w, FALSE, FALSE, 0 );
278
279    w = gtk_image_new_from_stock( "tr-yin-yang", (GtkIconSize)-1 );
280    c = gtk_event_box_new( );
281    gtk_container_add( GTK_CONTAINER(c), w );
282    w = c;
283    g_signal_connect( w, "button-release-event", G_CALLBACK(onYinYangReleased), p );
284    gtk_box_pack_start( GTK_BOX(h), w, FALSE, FALSE, 0 );
285    w = p->stats_lb = gtk_label_new( NULL );
286    gtk_box_pack_start( GTK_BOX(h), w, FALSE, FALSE, 0 );
287
288    gtk_box_pack_start( GTK_BOX(vbox), h, FALSE, FALSE, 0 );
289
290    /* workarea */
291    p->view = makeview( p );
292    w = p->scroll = gtk_scrolled_window_new( NULL, NULL );
293    gtk_container_add( GTK_CONTAINER(w), p->view );
294    gtk_box_pack_start_defaults( GTK_BOX(vbox), w );
295    gtk_container_set_focus_child( GTK_CONTAINER( vbox ), w );
296
297    /* spacer */
298    w = gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f);
299    gtk_widget_set_usize (w, 0u, 6u);
300    gtk_box_pack_start( GTK_BOX(vbox), w, FALSE, FALSE, 0 ); 
301
302    /* show all but the window */
303    gtk_widget_show_all( vbox );
304
305    /* listen for prefs changes that affect the window */
306    p->core = core;
307    prefsChanged( core, PREF_KEY_MINIMAL_VIEW, self );
308    prefsChanged( core, PREF_KEY_STATUS_BAR, self );
309    prefsChanged( core, PREF_KEY_STATUS_BAR_STATS, self );
310    prefsChanged( core, PREF_KEY_TOOLBAR, self );
311    p->pref_handler_id = g_signal_connect( core, "prefs-changed",
312                                           G_CALLBACK(prefsChanged), self );
313
314    return self;
315}
316
317void
318tr_window_update( TrWindow * self )
319{
320    PrivateData * p = get_private_data( self );
321    tr_handle * handle = NULL;
322
323    if( p && p->core )
324        handle = tr_core_handle( p->core );
325
326    if( handle )
327    {
328        char * pch;
329        float u, d;
330        char up[32], down[32], buf[64];
331        struct tr_session_stats stats;
332
333        tr_torrentRates( handle, &d, &u );
334        tr_strlspeed( buf, d, sizeof( buf ) );
335        gtk_label_set_text( GTK_LABEL( p->dl_lb ), buf );
336        tr_strlspeed( buf, u, sizeof( buf ) );
337        gtk_label_set_text( GTK_LABEL( p->ul_lb ), buf );
338
339        pch = pref_string_get( PREF_KEY_STATUS_BAR_STATS );
340        if( !strcmp( pch, "session-ratio" ) ) {
341            tr_getSessionStats( handle, &stats );
342            g_snprintf( buf, sizeof(buf), _("Ratio: %.1f"), stats.ratio );
343        } else if( !strcmp( pch, "session-transfer" ) ) {
344            tr_getSessionStats( handle, &stats );
345            tr_strlsize( up, stats.uploadedBytes, sizeof( up ) );
346            tr_strlsize( down, stats.downloadedBytes, sizeof( down ) );
347            g_snprintf( buf, sizeof( buf ), _( "Down: %s  Up: %s" ), down, up );
348        } else if( !strcmp( pch, "total-transfer" ) ) { 
349            tr_getCumulativeSessionStats( handle, &stats );
350            tr_strlsize( up, stats.uploadedBytes, sizeof( up ) );
351            tr_strlsize( down, stats.downloadedBytes, sizeof( down ) );
352            g_snprintf( buf, sizeof( buf ), _( "Down: %s  Up: %s" ), down, up );
353        } else { /* default is total-ratio */
354            tr_getCumulativeSessionStats( handle, &stats );
355            g_snprintf( buf, sizeof(buf), _("Ratio: %.1f"), stats.ratio );
356        }
357        g_free( pch );
358        gtk_label_set_text( GTK_LABEL( p->stats_lb ), buf );
359    }
360}
361
362GtkTreeSelection*
363tr_window_get_selection ( TrWindow * w )
364{
365    return get_private_data(w)->selection;
366}
Note: See TracBrowser for help on using the repository browser.