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 $
17 #include <glib/gi18n.h>
20 #include <libtransmission/transmission.h>
43 GtkTreeModel
* filter
;
45 tr_msg_level maxLevel
;
50 static struct tr_msg_list
* myTail
= NULL
;
51 static struct tr_msg_list
* myHead
= NULL
;
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
) );
70 doSave( GtkWindow
* parent
, struct MsgData
* data
, const char * filename
)
72 FILE * fp
= fopen( filename
, "w+" );
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
);
84 GtkTreeModel
* model
= GTK_TREE_MODEL( data
->sort
);
85 if( gtk_tree_model_iter_children( model
, &iter
, NULL
) ) do
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
: "" ) );
103 while( gtk_tree_model_iter_next( model
, &iter
) );
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
);
119 gtk_widget_destroy( d
);
123 onSaveRequest( GtkWidget
* w
,
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
,
135 gtk_dialog_set_alternative_button_order( GTK_DIALOG( d
),
139 g_signal_connect( d
, "response",
140 G_CALLBACK( onSaveDialogResponse
), data
);
141 gtk_widget_show( d
);
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
;
155 onPauseToggled( GtkToggleToolButton
* w
, gpointer gdata
)
157 struct MsgData
* data
= gdata
;
159 data
->isPaused
= gtk_toggle_tool_button_get_active( w
);
163 getForegroundColor( int msgLevel
)
165 const char * foreground
;
170 foreground
= "forestgreen"; break;
173 foreground
= "black"; break;
176 foreground
= "red"; break;
179 g_assert_not_reached( );
185 renderText( GtkTreeViewColumn
* column UNUSED
,
186 GtkCellRenderer
* renderer
,
187 GtkTreeModel
* tree_model
,
191 const int col
= GPOINTER_TO_INT( gcol
);
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
,
203 renderTime( GtkTreeViewColumn
* column UNUSED
,
204 GtkCellRenderer
* renderer
,
205 GtkTreeModel
* tree_model
,
207 gpointer data UNUSED
)
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
,
217 g_object_set ( renderer
, "text", buf
,
218 "foreground", getForegroundColor( node
->level
),
223 appendColumn( GtkTreeView
* view
,
227 GtkTreeViewColumn
* c
;
228 const char * title
= NULL
;
233 title
= _( "Time" ); break;
235 /* noun. column title for a list */
237 title
= _( "Name" ); break;
239 /* noun. column title for a list */
241 title
= _( "Message" ); break;
244 g_assert_not_reached( );
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
,
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
);
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
,
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
);
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
,
276 gtk_tree_view_column_set_resizable( c
, TRUE
);
280 g_assert_not_reached( );
284 gtk_tree_view_column_set_sort_column_id( c
, col
);
285 gtk_tree_view_append_column( view
, c
);
289 isRowVisible( GtkTreeModel
* model
,
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
;
301 onWindowDestroyed( gpointer gdata
,
302 GObject
* deadWindow UNUSED
)
304 struct MsgData
* data
= gdata
;
306 g_source_remove( data
->refresh_tag
);
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;
318 for( i
= head
; i
; i
= i
->next
)
322 gtk_list_store_insert_with_values( store
, &unused
, 0,
325 ( i
->name
? i
->name
:
327 COL_MESSAGE
, i
->message
,
328 COL_SEQUENCE
, sequence
++,
339 onRefresh( gpointer gdata
)
341 struct MsgData
* data
= gdata
;
343 if( !data
->isPaused
)
345 tr_msg_list
* msgs
= tr_getQueuedMessages( );
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
);
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
,
368 gtr_combo_box_set_active_enum( GTK_COMBO_BOX( w
), pref_int_get( TR_PREFS_KEY_MSGLEVEL
) );
377 msgwin_new( TrCore
* core
, GtkWindow
* parent
)
385 struct MsgData
* data
;
387 data
= g_new0( struct MsgData
, 1 );
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 );
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 );
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
),
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
->
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
),
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
);