Revert "transmission: update from 2.13 to 2.22"
[tomato.git] / release / src / router / transmission / gtk / msgwin.c
blob015b4926973a8be0049932a0d24b34f72200296d
1 /*
2 * This file Copyright (C) 2008-2010 Mnemosyne LLC
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
10 * $Id: msgwin.c 11337 2010-10-18 22:18:15Z charles $
13 #include <errno.h>
14 #include <stdio.h>
15 #include <string.h>
17 #include <glib/gi18n.h>
18 #include <gtk/gtk.h>
20 #include <libtransmission/transmission.h>
22 #include "conf.h"
23 #include "hig.h"
24 #include "tr-core.h"
25 #include "msgwin.h"
26 #include "tr-prefs.h"
27 #include "util.h"
29 enum
31 COL_SEQUENCE,
32 COL_NAME,
33 COL_MESSAGE,
34 COL_TR_MSG,
35 N_COLUMNS
38 struct MsgData
40 TrCore * core;
41 GtkTreeView * view;
42 GtkListStore * store;
43 GtkTreeModel * filter;
44 GtkTreeModel * sort;
45 tr_msg_level maxLevel;
46 gboolean isPaused;
47 guint refresh_tag;
50 static struct tr_msg_list * myTail = NULL;
51 static struct tr_msg_list * myHead = NULL;
53 /***
54 ****
55 ***/
57 static void
58 level_combo_changed_cb( GtkComboBox * combo_box, gpointer gdata )
60 struct MsgData * data = gdata;
61 const int level = gtr_combo_box_get_active_enum( combo_box );
63 tr_setMessageLevel( level );
64 tr_core_set_pref_int( data->core, TR_PREFS_KEY_MSGLEVEL, level );
65 data->maxLevel = level;
66 gtk_tree_model_filter_refilter( GTK_TREE_MODEL_FILTER( data->filter ) );
69 static void
70 doSave( GtkWindow * parent, struct MsgData * data, const char * filename )
72 FILE * fp = fopen( filename, "w+" );
74 if( !fp )
76 GtkWidget * w = gtk_message_dialog_new( parent, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _( "Couldn't save \"%s\"" ), filename );
77 gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG( w ), "%s", g_strerror( errno ) );
78 g_signal_connect_swapped( w, "response", G_CALLBACK( gtk_widget_destroy ), w );
79 gtk_widget_show( w );
81 else
83 GtkTreeIter iter;
84 GtkTreeModel * model = GTK_TREE_MODEL( data->sort );
85 if( gtk_tree_model_iter_children( model, &iter, NULL ) ) do
87 char * date;
88 const char * levelStr;
89 const struct tr_msg_list * node;
91 gtk_tree_model_get( model, &iter, COL_TR_MSG, &node, -1 );
92 date = gtr_localtime( node->when );
93 switch( node->level ) {
94 case TR_MSG_DBG: levelStr = "debug"; break;
95 case TR_MSG_ERR: levelStr = "error"; break;
96 default: levelStr = " "; break;
98 fprintf( fp, "%s\t%s\t%s\t%s\n", date, levelStr,
99 ( node->name ? node->name : "" ),
100 ( node->message ? node->message : "" ) );
101 g_free( date );
103 while( gtk_tree_model_iter_next( model, &iter ) );
105 fclose( fp );
109 static void
110 onSaveDialogResponse( GtkWidget * d, int response, gpointer data )
112 if( response == GTK_RESPONSE_ACCEPT )
114 char * file = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( d ) );
115 doSave( GTK_WINDOW( d ), data, file );
116 g_free( file );
119 gtk_widget_destroy( d );
122 static void
123 onSaveRequest( GtkWidget * w,
124 gpointer data )
126 GtkWindow * window = GTK_WINDOW( gtk_widget_get_toplevel( w ) );
127 GtkWidget * d = gtk_file_chooser_dialog_new( _( "Save Log" ), window,
128 GTK_FILE_CHOOSER_ACTION_SAVE,
129 GTK_STOCK_CANCEL,
130 GTK_RESPONSE_CANCEL,
131 GTK_STOCK_SAVE,
132 GTK_RESPONSE_ACCEPT,
133 NULL );
135 gtk_dialog_set_alternative_button_order( GTK_DIALOG( d ),
136 GTK_RESPONSE_ACCEPT,
137 GTK_RESPONSE_CANCEL,
138 -1 );
139 g_signal_connect( d, "response",
140 G_CALLBACK( onSaveDialogResponse ), data );
141 gtk_widget_show( d );
144 static void
145 onClearRequest( GtkWidget * w UNUSED, gpointer gdata )
147 struct MsgData * data = gdata;
149 gtk_list_store_clear( data->store );
150 tr_freeMessageList( myHead );
151 myHead = myTail = NULL;
154 static void
155 onPauseToggled( GtkToggleToolButton * w, gpointer gdata )
157 struct MsgData * data = gdata;
159 data->isPaused = gtk_toggle_tool_button_get_active( w );
162 static const char*
163 getForegroundColor( int msgLevel )
165 const char * foreground;
167 switch( msgLevel )
169 case TR_MSG_DBG:
170 foreground = "forestgreen"; break;
172 case TR_MSG_INF:
173 foreground = "black"; break;
175 case TR_MSG_ERR:
176 foreground = "red"; break;
178 default:
179 g_assert_not_reached( );
181 return foreground;
184 static void
185 renderText( GtkTreeViewColumn * column UNUSED,
186 GtkCellRenderer * renderer,
187 GtkTreeModel * tree_model,
188 GtkTreeIter * iter,
189 gpointer gcol )
191 const int col = GPOINTER_TO_INT( gcol );
192 char * str = NULL;
193 const struct tr_msg_list * node;
195 gtk_tree_model_get( tree_model, iter, col, &str, COL_TR_MSG, &node, -1 );
196 g_object_set( renderer, "text", str,
197 "foreground", getForegroundColor( node->level ),
198 "ellipsize", PANGO_ELLIPSIZE_END,
199 NULL );
202 static void
203 renderTime( GtkTreeViewColumn * column UNUSED,
204 GtkCellRenderer * renderer,
205 GtkTreeModel * tree_model,
206 GtkTreeIter * iter,
207 gpointer data UNUSED )
209 struct tm tm;
210 char buf[16];
211 const struct tr_msg_list * node;
213 gtk_tree_model_get( tree_model, iter, COL_TR_MSG, &node, -1 );
214 tm = *localtime( &node->when );
215 g_snprintf( buf, sizeof( buf ), "%02d:%02d:%02d", tm.tm_hour, tm.tm_min,
216 tm.tm_sec );
217 g_object_set ( renderer, "text", buf,
218 "foreground", getForegroundColor( node->level ),
219 NULL );
222 static void
223 appendColumn( GtkTreeView * view,
224 int col )
226 GtkCellRenderer * r;
227 GtkTreeViewColumn * c;
228 const char * title = NULL;
230 switch( col )
232 case COL_SEQUENCE:
233 title = _( "Time" ); break;
235 /* noun. column title for a list */
236 case COL_NAME:
237 title = _( "Name" ); break;
239 /* noun. column title for a list */
240 case COL_MESSAGE:
241 title = _( "Message" ); break;
243 default:
244 g_assert_not_reached( );
247 switch( col )
249 case COL_NAME:
250 r = gtk_cell_renderer_text_new( );
251 c = gtk_tree_view_column_new_with_attributes( title, r, NULL );
252 gtk_tree_view_column_set_cell_data_func( c, r, renderText,
253 GINT_TO_POINTER(
254 col ), NULL );
255 gtk_tree_view_column_set_sizing( c, GTK_TREE_VIEW_COLUMN_FIXED );
256 gtk_tree_view_column_set_fixed_width( c, 200 );
257 gtk_tree_view_column_set_resizable( c, TRUE );
258 break;
260 case COL_MESSAGE:
261 r = gtk_cell_renderer_text_new( );
262 c = gtk_tree_view_column_new_with_attributes( title, r, NULL );
263 gtk_tree_view_column_set_cell_data_func( c, r, renderText,
264 GINT_TO_POINTER(
265 col ), NULL );
266 gtk_tree_view_column_set_sizing( c, GTK_TREE_VIEW_COLUMN_FIXED );
267 gtk_tree_view_column_set_fixed_width( c, 500 );
268 gtk_tree_view_column_set_resizable( c, TRUE );
269 break;
271 case COL_SEQUENCE:
272 r = gtk_cell_renderer_text_new( );
273 c = gtk_tree_view_column_new_with_attributes( title, r, NULL );
274 gtk_tree_view_column_set_cell_data_func( c, r, renderTime, NULL,
275 NULL );
276 gtk_tree_view_column_set_resizable( c, TRUE );
277 break;
279 default:
280 g_assert_not_reached( );
281 break;
284 gtk_tree_view_column_set_sort_column_id( c, col );
285 gtk_tree_view_append_column( view, c );
288 static gboolean
289 isRowVisible( GtkTreeModel * model,
290 GtkTreeIter * iter,
291 gpointer gdata )
293 const struct MsgData * data = gdata;
294 const struct tr_msg_list * node;
296 gtk_tree_model_get( model, iter, COL_TR_MSG, &node, -1 );
297 return node->level <= data->maxLevel;
300 static void
301 onWindowDestroyed( gpointer gdata,
302 GObject * deadWindow UNUSED )
304 struct MsgData * data = gdata;
306 g_source_remove( data->refresh_tag );
307 g_free( data );
310 static tr_msg_list *
311 addMessages( GtkListStore * store,
312 struct tr_msg_list * head )
314 const char * default_name = g_get_application_name( );
315 static unsigned int sequence = 1;
316 tr_msg_list * i;
318 for( i = head; i; i = i->next )
320 GtkTreeIter unused;
322 gtk_list_store_insert_with_values( store, &unused, 0,
323 COL_TR_MSG, i,
324 COL_NAME,
325 ( i->name ? i->name :
326 default_name ),
327 COL_MESSAGE, i->message,
328 COL_SEQUENCE, sequence++,
329 -1 );
331 if( !i->next )
332 break;
335 return i; /* tail */
338 static gboolean
339 onRefresh( gpointer gdata )
341 struct MsgData * data = gdata;
343 if( !data->isPaused )
345 tr_msg_list * msgs = tr_getQueuedMessages( );
346 if( msgs )
348 /* add the new messages and append them to the end of
349 * our persistent list */
350 tr_msg_list * tail = addMessages( data->store, msgs );
351 if( myTail )
352 myTail->next = msgs;
353 else
354 myHead = msgs;
355 myTail = tail;
358 return TRUE;
361 static GtkWidget*
362 debug_level_combo_new( void )
364 GtkWidget * w = gtr_combo_box_new_enum( _( "Error" ), TR_MSG_ERR,
365 _( "Information" ), TR_MSG_INF,
366 _( "Debug" ), TR_MSG_DBG,
367 NULL );
368 gtr_combo_box_set_active_enum( GTK_COMBO_BOX( w ), pref_int_get( TR_PREFS_KEY_MSGLEVEL ) );
369 return w;
373 *** Public Functions
376 GtkWidget *
377 msgwin_new( TrCore * core, GtkWindow * parent )
379 GtkWidget * win;
380 GtkWidget * vbox;
381 GtkWidget * toolbar;
382 GtkWidget * w;
383 GtkWidget * view;
384 GtkToolItem * item;
385 struct MsgData * data;
387 data = g_new0( struct MsgData, 1 );
388 data->core = core;
390 win = gtk_window_new( GTK_WINDOW_TOPLEVEL );
391 gtk_window_set_transient_for( GTK_WINDOW( win ), parent );
392 gtk_window_set_title( GTK_WINDOW( win ), _( "Message Log" ) );
393 gtk_window_set_default_size( GTK_WINDOW( win ), 560, 350 );
394 gtk_window_set_role( GTK_WINDOW( win ), "message-log" );
395 vbox = gtk_vbox_new( FALSE, 0 );
398 *** toolbar
401 toolbar = gtk_toolbar_new( );
402 gtr_toolbar_set_orientation( GTK_TOOLBAR( toolbar ), GTK_ORIENTATION_HORIZONTAL );
403 gtk_toolbar_set_style( GTK_TOOLBAR( toolbar ), GTK_TOOLBAR_BOTH_HORIZ );
405 item = gtk_tool_button_new_from_stock( GTK_STOCK_SAVE_AS );
406 g_object_set( G_OBJECT( item ), "is-important", TRUE, NULL );
407 g_signal_connect( item, "clicked", G_CALLBACK( onSaveRequest ), data );
408 gtk_toolbar_insert( GTK_TOOLBAR( toolbar ), item, -1 );
410 item = gtk_tool_button_new_from_stock( GTK_STOCK_CLEAR );
411 g_object_set( G_OBJECT( item ), "is-important", TRUE, NULL );
412 g_signal_connect( item, "clicked", G_CALLBACK( onClearRequest ), data );
413 gtk_toolbar_insert( GTK_TOOLBAR( toolbar ), item, -1 );
415 item = gtk_separator_tool_item_new( );
416 gtk_toolbar_insert( GTK_TOOLBAR( toolbar ), item, -1 );
418 item = gtk_toggle_tool_button_new_from_stock( GTK_STOCK_MEDIA_PAUSE );
419 g_object_set( G_OBJECT( item ), "is-important", TRUE, NULL );
420 g_signal_connect( item, "toggled", G_CALLBACK( onPauseToggled ), data );
421 gtk_toolbar_insert( GTK_TOOLBAR( toolbar ), item, -1 );
423 item = gtk_separator_tool_item_new( );
424 gtk_toolbar_insert( GTK_TOOLBAR( toolbar ), item, -1 );
426 w = gtk_label_new( _( "Level" ) );
427 gtk_misc_set_padding( GTK_MISC( w ), GUI_PAD, 0 );
428 item = gtk_tool_item_new( );
429 gtk_container_add( GTK_CONTAINER( item ), w );
430 gtk_toolbar_insert( GTK_TOOLBAR( toolbar ), item, -1 );
432 w = debug_level_combo_new( );
433 g_signal_connect( w, "changed", G_CALLBACK( level_combo_changed_cb ), data );
434 item = gtk_tool_item_new( );
435 gtk_container_add( GTK_CONTAINER( item ), w );
436 gtk_toolbar_insert( GTK_TOOLBAR( toolbar ), item, -1 );
438 gtk_box_pack_start( GTK_BOX( vbox ), toolbar, FALSE, FALSE, 0 );
441 *** messages
444 data->store = gtk_list_store_new( N_COLUMNS,
445 G_TYPE_UINT, /* sequence */
446 G_TYPE_POINTER, /* category */
447 G_TYPE_POINTER, /* message */
448 G_TYPE_POINTER ); /* struct tr_msg_list
451 addMessages( data->store, myHead );
452 onRefresh( data ); /* much faster to populate *before* it has listeners */
454 data->filter = gtk_tree_model_filter_new( GTK_TREE_MODEL(
455 data->store ), NULL );
456 data->sort = gtk_tree_model_sort_new_with_model( data->filter );
457 gtk_tree_sortable_set_sort_column_id( GTK_TREE_SORTABLE( data->sort ),
458 COL_SEQUENCE,
459 GTK_SORT_ASCENDING );
460 data->maxLevel = pref_int_get( TR_PREFS_KEY_MSGLEVEL );
461 gtk_tree_model_filter_set_visible_func( GTK_TREE_MODEL_FILTER( data->
462 filter ),
463 isRowVisible, data, NULL );
466 view = gtk_tree_view_new_with_model( data->sort );
467 g_signal_connect( view, "button-release-event",
468 G_CALLBACK( on_tree_view_button_released ), NULL );
469 data->view = GTK_TREE_VIEW( view );
470 gtk_tree_view_set_rules_hint( data->view, TRUE );
471 appendColumn( data->view, COL_SEQUENCE );
472 appendColumn( data->view, COL_NAME );
473 appendColumn( data->view, COL_MESSAGE );
474 w = gtk_scrolled_window_new( NULL, NULL );
475 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( w ),
476 GTK_POLICY_AUTOMATIC,
477 GTK_POLICY_AUTOMATIC );
478 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( w ),
479 GTK_SHADOW_IN );
480 gtk_container_add( GTK_CONTAINER( w ), view );
481 gtk_box_pack_start( GTK_BOX( vbox ), w, TRUE, TRUE, 0 );
482 gtk_container_add( GTK_CONTAINER( win ), vbox );
484 data->refresh_tag = gtr_timeout_add_seconds( SECONDARY_WINDOW_REFRESH_INTERVAL_SECONDS, onRefresh, data );
485 g_object_weak_ref( G_OBJECT( win ), onWindowDestroyed, data );
487 gtk_widget_show_all( win );
488 return win;