1
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2
/* logview-window.c - main window of logview
4
* Copyright (C) 1998 Cesar Miquel <miquel@df.uba.ar>
5
* Copyright (C) 2008 Cosimo Cecchi <cosimoc@gnome.org>
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26
#include <gdk/gdkkeysyms.h>
27
#include <glib/gi18n.h>
29
#include "logview-window.h"
31
#include "logview-loglist.h"
32
#include "logview-findbar.h"
33
#include "logview-about.h"
34
#include "logview-prefs.h"
35
#include "logview-manager.h"
36
#include "logview-filter-manager.h"
38
#define APP_NAME _("System Log Viewer")
39
#define SEARCH_START_MARK "lw-search-start-mark"
40
#define SEARCH_END_MARK "lw-search-end-mark"
42
struct _LogviewWindowPrivate {
43
GtkUIManager *ui_manager;
44
GtkActionGroup *action_group;
45
GtkActionGroup *filter_action_group;
50
GtkWidget *version_bar;
51
GtkWidget *version_selector;
56
GtkWidget *message_area;
57
GtkWidget *message_primary;
58
GtkWidget *message_secondary;
60
GtkTextTagTable *tag_table;
62
int original_fontsize, fontsize;
65
LogviewManager *manager;
68
guint search_timeout_id;
70
guint filter_merge_id;
71
GList *active_filters;
72
gboolean matches_only;
76
#define GET_PRIVATE(o) \
77
(G_TYPE_INSTANCE_GET_PRIVATE ((o), LOGVIEW_TYPE_WINDOW, LogviewWindowPrivate))
79
G_DEFINE_TYPE (LogviewWindow, logview_window, GTK_TYPE_WINDOW);
81
static void findbar_close_cb (LogviewFindbar *findbar,
83
static void read_new_lines_cb (LogviewLog *log,
89
/* private functions */
92
logview_version_selector_changed (GtkComboBox *version_selector, gpointer user_data)
97
LogviewWindow *logview = user_data;
98
Log *log = logview->curlog;
101
g_assert (LOGVIEW_IS_WINDOW (logview));
103
selected = gtk_combo_box_get_active (version_selector);
105
if (selected == log->current_version)
108
/* select a new version */
110
logview_select_log (logview, log->parent_log);
113
if (log->parent_log) {
114
new = log->parent_log->older_logs[selected];
116
new = log->older_logs[selected];
119
logview_select_log (logview, new);
125
/* private helpers */
128
populate_tag_table (GtkTextTagTable *tag_table)
132
tag = gtk_text_tag_new ("bold");
133
g_object_set (tag, "weight", PANGO_WEIGHT_BOLD,
134
"weight-set", TRUE, NULL);
136
gtk_text_tag_table_add (tag_table, tag);
138
tag = gtk_text_tag_new ("invisible");
139
g_object_set (tag, "invisible", TRUE, "invisible-set", TRUE, NULL);
140
gtk_text_tag_table_add (tag_table, tag);
142
tag = gtk_text_tag_new ("invisible-filter");
143
g_object_set (tag, "invisible", TRUE, "invisible-set", TRUE, NULL);
144
gtk_text_tag_table_add (tag_table, tag);
149
populate_style_tag_table (GtkStyle *style,
150
GtkTextTagTable *tag_table)
155
tag = gtk_text_tag_table_lookup (tag_table, "gray");
158
/* FIXME: do we need a way to update the buffer/view? */
159
gtk_text_tag_table_remove (tag_table, tag);
162
tag = gtk_text_tag_new ("gray");
163
color = style->text[GTK_STATE_INSENSITIVE];
164
g_object_set (tag, "foreground-gdk", &color, "foreground-set", TRUE, NULL);
166
gtk_text_tag_table_add (tag_table, tag);
170
_gtk_text_buffer_apply_tag_to_rectangle (GtkTextBuffer *buffer, int line_start, int line_end,
171
int offset_start, int offset_end, char *tag_name)
173
GtkTextIter start, end;
176
gtk_text_buffer_get_iter_at_line (buffer, &start, line_start);
177
gtk_text_buffer_get_iter_at_line (buffer, &end, line_start);
179
for (line_cur = line_start; line_cur < line_end + 1; line_cur++) {
181
if (offset_start > 0) {
182
gtk_text_iter_forward_chars (&start, offset_start);
185
gtk_text_iter_forward_chars (&end, offset_end);
187
gtk_text_buffer_apply_tag_by_name (buffer, tag_name, &start, &end);
189
gtk_text_iter_forward_line (&start);
190
gtk_text_iter_forward_line (&end);
195
logview_update_statusbar (LogviewWindow *logview, LogviewLog *active)
197
char *statusbar_text;
198
char *size, *modified, *timestring_utf8;
200
char timestring[255];
202
if (active == NULL) {
203
gtk_statusbar_pop (GTK_STATUSBAR (logview->priv->statusbar), 0);
207
timestamp = logview_log_get_timestamp (active);
208
strftime (timestring, sizeof (timestring), "%a %b %e %T %Y", localtime (×tamp));
209
timestring_utf8 = g_locale_to_utf8 (timestring, -1, NULL, NULL, NULL);
211
modified = g_strdup_printf (_("last update: %s"), timestring_utf8);
213
size = g_format_size_for_display (logview_log_get_file_size (active));
214
statusbar_text = g_strdup_printf (_("%d lines (%s) - %s"),
215
logview_log_get_cached_lines_number (active),
218
gtk_statusbar_pop (GTK_STATUSBAR (logview->priv->statusbar), 0);
219
gtk_statusbar_push (GTK_STATUSBAR (logview->priv->statusbar), 0, statusbar_text);
222
g_free (timestring_utf8);
224
g_free (statusbar_text);
227
#define DEFAULT_LOGVIEW_FONT "Monospace 10"
230
logview_set_font (LogviewWindow *logview,
231
const char *fontname)
233
PangoFontDescription *font_desc;
235
if (fontname == NULL)
236
fontname = DEFAULT_LOGVIEW_FONT;
238
font_desc = pango_font_description_from_string (fontname);
240
gtk_widget_modify_font (logview->priv->text_view, font_desc);
241
pango_font_description_free (font_desc);
246
logview_set_fontsize (LogviewWindow *logview, gboolean store)
248
PangoFontDescription *fontdesc;
249
PangoContext *context;
250
LogviewWindowPrivate *priv = logview->priv;
252
context = gtk_widget_get_pango_context (priv->text_view);
253
fontdesc = pango_context_get_font_description (context);
254
pango_font_description_set_size (fontdesc, (priv->fontsize) * PANGO_SCALE);
255
gtk_widget_modify_font (priv->text_view, fontdesc);
258
logview_prefs_store_fontsize (logview->priv->prefs, priv->fontsize);
263
logview_set_window_title (LogviewWindow *logview, const char * log_name)
268
window_title = g_strdup_printf ("%s - %s", log_name, APP_NAME);
270
window_title = g_strdup_printf (APP_NAME);
273
gtk_window_set_title (GTK_WINDOW (logview), window_title);
275
g_free (window_title);
278
/* actions callbacks */
281
open_file_selected_cb (GtkWidget *chooser, gint response, LogviewWindow *logview)
287
gtk_widget_hide (GTK_WIDGET (chooser));
288
if (response != GTK_RESPONSE_OK) {
292
f = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (chooser));
293
file_uri = g_file_get_uri (f);
295
log = logview_manager_get_if_loaded (logview->priv->manager, file_uri);
300
logview_manager_set_active_log (logview->priv->manager, log);
301
g_object_unref (log);
305
logview_manager_add_log_from_gfile (logview->priv->manager, f, TRUE);
312
logview_open_log (GtkAction *action, LogviewWindow *logview)
314
static GtkWidget *chooser = NULL;
317
if (chooser == NULL) {
318
chooser = gtk_file_chooser_dialog_new (_("Open Log"),
319
GTK_WINDOW (logview),
320
GTK_FILE_CHOOSER_ACTION_OPEN,
321
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
322
GTK_STOCK_OPEN, GTK_RESPONSE_OK,
324
gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_OK);
325
gtk_window_set_modal (GTK_WINDOW (chooser), TRUE);
326
g_signal_connect (chooser, "response",
327
G_CALLBACK (open_file_selected_cb), logview);
328
g_signal_connect (chooser, "destroy",
329
G_CALLBACK (gtk_widget_destroyed), &chooser);
330
active = logview_prefs_get_active_logfile (logview->priv->prefs);
331
if (active != NULL) {
332
gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (chooser), active);
337
gtk_window_present (GTK_WINDOW (chooser));
341
logview_close_log (GtkAction *action, LogviewWindow *logview)
343
findbar_close_cb (LOGVIEW_FINDBAR (logview->priv->find_bar), logview);
344
logview_manager_close_active_log (logview->priv->manager);
348
logview_help (GtkAction *action, GtkWidget *parent_window)
350
GError *error = NULL;
352
gtk_show_uri (gtk_widget_get_screen (parent_window),
353
"ghelp:gnome-system-log", gtk_get_current_event_time (),
357
g_warning (_("There was an error displaying help: %s"), error->message);
358
g_error_free (error);
363
logview_bigger_text (GtkAction *action, LogviewWindow *logview)
365
logview->priv->fontsize = MIN (logview->priv->fontsize + 1, 24);
366
logview_set_fontsize (logview, TRUE);
370
logview_smaller_text (GtkAction *action, LogviewWindow *logview)
372
logview->priv->fontsize = MAX (logview->priv->fontsize-1, 6);
373
logview_set_fontsize (logview, TRUE);
377
logview_normal_text (GtkAction *action, LogviewWindow *logview)
379
logview->priv->fontsize = logview->priv->original_fontsize;
380
logview_set_fontsize (logview, TRUE);
384
logview_select_all (GtkAction *action, LogviewWindow *logview)
386
GtkTextIter start, end;
387
GtkTextBuffer *buffer;
389
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (logview->priv->text_view));
391
gtk_text_buffer_get_bounds (buffer, &start, &end);
392
gtk_text_buffer_select_range (buffer, &start, &end);
394
gtk_widget_grab_focus (GTK_WIDGET (logview->priv->text_view));
398
logview_copy (GtkAction *action, LogviewWindow *logview)
400
GtkTextBuffer *buffer;
401
GtkClipboard *clipboard;
403
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (logview->priv->text_view));
404
clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
406
gtk_text_buffer_copy_clipboard (buffer, clipboard);
408
gtk_widget_grab_focus (GTK_WIDGET (logview->priv->text_view));
412
findbar_close_cb (LogviewFindbar *findbar,
415
gtk_widget_hide (GTK_WIDGET (findbar));
416
logview_findbar_set_message (findbar, NULL);
420
logview_search_text (LogviewWindow *logview, gboolean forward)
422
GtkTextBuffer *buffer;
423
GtkTextMark *search_start, *search_end;
424
GtkTextIter search, start_m, end_m;
426
gboolean res, wrapped;
430
text = logview_findbar_get_text (LOGVIEW_FINDBAR (logview->priv->find_bar));
432
if (!text || g_strcmp0 (text, "") == 0) {
436
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (logview->priv->text_view));
437
search_start = gtk_text_buffer_get_mark (buffer, SEARCH_START_MARK);
438
search_end = gtk_text_buffer_get_mark (buffer, SEARCH_END_MARK);
441
/* this is our first search on the buffer, create a new search mark */
442
gtk_text_buffer_get_start_iter (buffer, &search);
443
search_start = gtk_text_buffer_create_mark (buffer, SEARCH_START_MARK,
445
search_end = gtk_text_buffer_create_mark (buffer, SEARCH_END_MARK,
449
gtk_text_buffer_get_iter_at_mark (buffer, &search, search_end);
451
gtk_text_buffer_get_iter_at_mark (buffer, &search, search_start);
458
res = gtk_text_iter_forward_search (&search, text, GTK_TEXT_SEARCH_VISIBLE_ONLY, &start_m, &end_m, NULL);
460
res = gtk_text_iter_backward_search (&search, text, GTK_TEXT_SEARCH_VISIBLE_ONLY, &start_m, &end_m, NULL);
464
gtk_text_buffer_select_range (buffer, &start_m, &end_m);
465
gtk_text_buffer_move_mark (buffer, search_start, &start_m);
466
gtk_text_buffer_move_mark (buffer, search_end, &end_m);
468
gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (logview->priv->text_view), search_end);
471
logview_findbar_set_message (LOGVIEW_FINDBAR (logview->priv->find_bar), _("Wrapped"));
479
if (gtk_text_buffer_get_has_selection (buffer)) {
481
mark = gtk_text_buffer_get_mark (buffer, "insert");
482
gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
483
gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &iter);
486
logview_findbar_set_message (LOGVIEW_FINDBAR (logview->priv->find_bar), _("Not found"));
489
gtk_text_buffer_get_start_iter (buffer, &search);
491
gtk_text_buffer_get_end_iter (buffer, &search);
501
findbar_previous_cb (LogviewFindbar *findbar,
504
LogviewWindow *logview = user_data;
506
logview_search_text (logview, FALSE);
510
findbar_next_cb (LogviewFindbar *findbar,
513
LogviewWindow *logview = user_data;
515
logview_search_text (logview, TRUE);
519
text_changed_timeout_cb (gpointer user_data)
521
LogviewWindow *logview = user_data;
522
GtkTextMark *search_start, *search_end;
524
GtkTextBuffer *buffer;
526
logview->priv->search_timeout_id = 0;
528
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (logview->priv->text_view));
529
search_start = gtk_text_buffer_get_mark (buffer, SEARCH_START_MARK);
530
search_end = gtk_text_buffer_get_mark (buffer, SEARCH_END_MARK);
533
/* reset the search mark to the start */
534
gtk_text_buffer_get_start_iter (buffer, &start);
535
gtk_text_buffer_move_mark (buffer, search_start, &start);
536
gtk_text_buffer_move_mark (buffer, search_end, &start);
539
logview_findbar_set_message (LOGVIEW_FINDBAR (logview->priv->find_bar), NULL);
541
logview_search_text (logview, TRUE);
547
findbar_text_changed_cb (LogviewFindbar *findbar,
550
LogviewWindow *logview = user_data;
552
if (logview->priv->search_timeout_id != 0) {
553
g_source_remove (logview->priv->search_timeout_id);
556
logview->priv->search_timeout_id = g_timeout_add (300, text_changed_timeout_cb, logview);
560
logview_search (GtkAction *action, LogviewWindow *logview)
562
logview_findbar_open (LOGVIEW_FINDBAR (logview->priv->find_bar));
566
filter_buffer (LogviewWindow *logview, gint start_line)
568
GtkTextBuffer *buffer;
569
GtkTextIter start, *end;
575
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (logview->priv->text_view));
576
lines = gtk_text_buffer_get_line_count (buffer);
578
for (i = start_line; i < lines; i++) {
581
gtk_text_buffer_get_iter_at_line (buffer, &start, i);
582
end = gtk_text_iter_copy (&start);
583
gtk_text_iter_forward_line (end);
585
text = gtk_text_buffer_get_text (buffer, &start, end, TRUE);
587
for (cur_filter = logview->priv->active_filters; cur_filter != NULL;
588
cur_filter = g_list_next (cur_filter))
590
if (logview_filter_filter (LOGVIEW_FILTER (cur_filter->data), text)) {
591
gtk_text_buffer_apply_tag (buffer,
592
logview_filter_get_tag (LOGVIEW_FILTER (cur_filter->data)),
600
if (!matched && logview->priv->matches_only) {
601
gtk_text_buffer_apply_tag_by_name (buffer,
605
gtk_text_buffer_remove_tag_by_name (buffer,
610
gtk_text_iter_free (end);
615
filter_remove (LogviewWindow *logview, LogviewFilter *filter)
617
GtkTextIter start, end;
618
GtkTextBuffer *buffer;
620
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (logview->priv->text_view));
621
gtk_text_buffer_get_bounds (buffer, &start, &end);
623
gtk_text_buffer_remove_tag (buffer, logview_filter_get_tag (filter),
628
on_filter_toggled (GtkToggleAction *action, LogviewWindow *logview)
630
LogviewWindowPrivate *priv = GET_PRIVATE (logview);
632
LogviewFilter *filter;
634
name = gtk_action_get_name (GTK_ACTION (action));
636
if (gtk_toggle_action_get_active (action)) {
637
priv->active_filters = g_list_append (priv->active_filters,
638
logview_prefs_get_filter (priv->prefs,
640
filter_buffer(logview, 0);
642
filter = logview_prefs_get_filter (priv->prefs, name);
643
priv->active_filters = g_list_remove (priv->active_filters,
646
filter_remove (logview, filter);
650
#define FILTER_PLACEHOLDER "/LogviewMenu/FilterMenu/PlaceholderFilters"
652
update_filter_menu (LogviewWindow *window)
654
LogviewWindowPrivate *priv;
659
GtkTextBuffer *buffer;
660
GtkTextTagTable *table;
662
GtkToggleAction *action;
665
priv = GET_PRIVATE (window);
666
ui = priv->ui_manager;
668
g_return_if_fail (priv->filter_action_group != NULL);
670
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view));
671
table = priv->tag_table;
673
if (priv->filter_merge_id != 0) {
674
gtk_ui_manager_remove_ui (ui,
675
priv->filter_merge_id);
678
actions = gtk_action_group_list_actions (priv->filter_action_group);
680
for (l = actions; l != NULL; l = g_list_next (l)) {
681
tag = gtk_text_tag_table_lookup (table, gtk_action_get_name (GTK_ACTION (l->data)));
682
gtk_text_tag_table_remove (table, tag);
684
g_signal_handlers_disconnect_by_func (GTK_ACTION (l->data),
685
G_CALLBACK (on_filter_toggled),
687
gtk_action_group_remove_action (priv->filter_action_group,
688
GTK_ACTION (l->data));
691
g_list_free (actions);
693
filters = logview_prefs_get_filters (logview_prefs_get ());
695
id = (g_list_length (filters) > 0) ? gtk_ui_manager_new_merge_id (ui) : 0;
697
for (l = filters; l != NULL; l = g_list_next (l)) {
698
g_object_get (l->data, "name", &name, NULL);
700
action = gtk_toggle_action_new (name, name, NULL, NULL);
701
gtk_action_group_add_action (priv->filter_action_group,
702
GTK_ACTION (action));
704
g_signal_connect (action,
706
G_CALLBACK (on_filter_toggled),
709
gtk_ui_manager_add_ui (ui, id, FILTER_PLACEHOLDER,
710
name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
711
gtk_text_tag_table_add (table,
712
logview_filter_get_tag (LOGVIEW_FILTER (l->data)));
714
g_object_unref (action);
718
g_list_free (filters);
720
priv->filter_merge_id = id;
724
on_logview_filter_manager_response (GtkDialog *dialog,
726
LogviewWindow *logview)
728
update_filter_menu (logview);
730
g_list_free (logview->priv->active_filters);
731
logview->priv->active_filters = NULL;
735
logview_manage_filters (GtkAction *action, LogviewWindow *logview)
739
manager = logview_filter_manager_new ();
741
g_signal_connect (manager, "response",
742
G_CALLBACK (on_logview_filter_manager_response),
745
gtk_window_set_transient_for (GTK_WINDOW (manager),
746
GTK_WINDOW (logview));
747
gtk_widget_show (GTK_WIDGET (manager));
751
logview_about (GtkWidget *widget, GtkWidget *window)
753
g_return_if_fail (GTK_IS_WINDOW (window));
755
char *license_trans = g_strjoin ("\n\n", _(logview_about_license[0]),
756
_(logview_about_license[1]),
757
_(logview_about_license[2]), NULL);
759
gtk_show_about_dialog (GTK_WINDOW (window),
760
"name", _("System Log Viewer"),
762
"copyright", "Copyright \xc2\xa9 1998-2008 Free Software Foundation, Inc.",
763
"license", license_trans,
764
"wrap-license", TRUE,
765
"comments", _("A system log viewer for GNOME."),
766
"authors", logview_about_authors,
767
"documenters", logview_about_documenters,
768
"translator_credits", strcmp (logview_about_translator_credits,
769
"translator-credits") != 0 ?
770
logview_about_translator_credits : NULL,
771
"logo_icon_name", "logview",
773
g_free (license_trans);
779
logview_toggle_statusbar (GtkAction *action, LogviewWindow *logview)
781
if (gtk_widget_get_visible (logview->priv->statusbar))
782
gtk_widget_hide (logview->priv->statusbar);
784
gtk_widget_show (logview->priv->statusbar);
788
logview_toggle_sidebar (GtkAction *action, LogviewWindow *logview)
790
if (gtk_widget_get_visible (logview->priv->sidebar))
791
gtk_widget_hide (logview->priv->sidebar);
793
gtk_widget_show (logview->priv->sidebar);
797
logview_toggle_match_filters (GtkToggleAction *action, LogviewWindow *logview)
799
logview->priv->matches_only = gtk_toggle_action_get_active (action);
800
filter_buffer (logview, 0);
804
logview_toggle_autoscroll (GtkToggleAction *action, LogviewWindow *logview)
806
logview->priv->auto_scroll = gtk_toggle_action_get_active (action);
809
/* GObject functions */
813
static GtkActionEntry entries[] = {
814
{ "FileMenu", NULL, N_("_File"), NULL, NULL, NULL },
815
{ "EditMenu", NULL, N_("_Edit"), NULL, NULL, NULL },
816
{ "ViewMenu", NULL, N_("_View"), NULL, NULL, NULL },
817
{ "FilterMenu", NULL, N_("_Filters"), NULL, NULL, NULL },
818
{ "HelpMenu", NULL, N_("_Help"), NULL, NULL, NULL },
820
{ "OpenLog", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a log from file"),
821
G_CALLBACK (logview_open_log) },
822
{ "CloseLog", GTK_STOCK_CLOSE, N_("_Close"), "<control>W", N_("Close this log"),
823
G_CALLBACK (logview_close_log) },
824
{ "Quit", GTK_STOCK_QUIT, N_("_Quit"), "<control>Q", N_("Quit the log viewer"),
825
G_CALLBACK (gtk_main_quit) },
827
{ "Copy", GTK_STOCK_COPY, N_("_Copy"), "<control>C", N_("Copy the selection"),
828
G_CALLBACK (logview_copy) },
829
{ "SelectAll", NULL, N_("Select _All"), "<Control>A", N_("Select the entire log"),
830
G_CALLBACK (logview_select_all) },
831
{ "Search", GTK_STOCK_FIND, N_("_Find..."), "<control>F", N_("Find a word or phrase in the log"),
832
G_CALLBACK (logview_search) },
834
{ "ViewZoomIn", GTK_STOCK_ZOOM_IN, NULL, "<control>plus", N_("Bigger text size"),
835
G_CALLBACK (logview_bigger_text)},
836
{ "ViewZoomOut", GTK_STOCK_ZOOM_OUT, NULL, "<control>minus", N_("Smaller text size"),
837
G_CALLBACK (logview_smaller_text)},
838
{ "ViewZoom100", GTK_STOCK_ZOOM_100, NULL, "<control>0", N_("Normal text size"),
839
G_CALLBACK (logview_normal_text)},
841
{ "FilterManage", NULL, N_("Manage Filters"), NULL, N_("Manage filters"),
842
G_CALLBACK (logview_manage_filters)},
844
{ "HelpContents", GTK_STOCK_HELP, N_("_Contents"), "F1", N_("Open the help contents for the log viewer"),
845
G_CALLBACK (logview_help) },
846
{ "AboutAction", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("Show the about dialog for the log viewer"),
847
G_CALLBACK (logview_about) },
850
static GtkToggleActionEntry toggle_entries[] = {
851
{ "ShowStatusBar", NULL, N_("_Statusbar"), NULL, N_("Show Status Bar"),
852
G_CALLBACK (logview_toggle_statusbar), TRUE },
853
{ "ShowSidebar", NULL, N_("Side _Pane"), "F9", N_("Show Side Pane"),
854
G_CALLBACK (logview_toggle_sidebar), TRUE },
855
{ "FilterMatchOnly", NULL, N_("Show matches only"), NULL, N_("Only show lines that match one of the given filters"),
856
G_CALLBACK (logview_toggle_match_filters), FALSE },
857
{ "AutoScroll", NULL, N_("_Auto Scroll"), "F8", N_("Automatically scroll down when new lines appear"),
858
G_CALLBACK (logview_toggle_autoscroll), TRUE }
862
window_size_changed_cb (GtkWidget *widget, GdkEventConfigure *event,
865
LogviewWindow *window = data;
867
logview_prefs_store_window_size (window->priv->prefs,
868
event->width, event->height);
874
real_select_day (LogviewWindow *logview,
875
GDate *date, int first_line, int last_line)
877
GtkTextBuffer *buffer;
878
GtkTextIter start_iter, end_iter, start_vis, end_vis;
879
GdkRectangle visible_rect;
881
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (logview->priv->text_view));
883
gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
884
gtk_text_buffer_get_iter_at_line (buffer, &start_vis, first_line);
885
gtk_text_buffer_get_iter_at_line (buffer, &end_vis, last_line + 1);
887
/* clear all previous invisible tags */
888
gtk_text_buffer_remove_tag_by_name (buffer, "invisible",
889
&start_iter, &end_iter);
891
gtk_text_buffer_apply_tag_by_name (buffer, "invisible",
892
&start_iter, &start_vis);
893
gtk_text_buffer_apply_tag_by_name (buffer, "invisible",
894
&end_vis, &end_iter);
896
/* FIXME: why is this needed to update the view when selecting a day back? */
897
gtk_text_view_get_visible_rect (GTK_TEXT_VIEW (logview->priv->text_view),
899
gdk_window_invalidate_rect (gtk_widget_get_window (logview->priv->text_view),
900
&visible_rect, TRUE);
904
loglist_day_selected_cb (LogviewLoglist *loglist,
908
LogviewWindow *logview = user_data;
910
real_select_day (logview, day->date, day->first_line, day->last_line);
914
loglist_day_cleared_cb (LogviewLoglist *loglist,
917
LogviewWindow *logview = user_data;
918
GtkTextBuffer *buffer;
919
GtkTextIter start, end;
921
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (logview->priv->text_view));
922
gtk_text_buffer_get_bounds (buffer, &start, &end);
924
/* clear all previous invisible tags */
925
gtk_text_buffer_remove_tag_by_name (buffer, "invisible",
930
log_monitor_changed_cb (LogviewLog *log,
933
/* reschedule a read */
934
logview_log_read_new_lines (log, (LogviewNewLinesCallback) read_new_lines_cb,
939
paint_timestamps (GtkTextBuffer *buffer, int old_line_count,
944
for (l = days; l; l = l->next) {
947
_gtk_text_buffer_apply_tag_to_rectangle (buffer,
948
old_line_count + day->first_line - 1,
949
old_line_count + day->last_line,
950
0, day->timestamp_len, "gray");
955
read_new_lines_cb (LogviewLog *log,
961
LogviewWindow *window = user_data;
962
GtkTextBuffer *buffer;
963
gboolean boldify = FALSE;
964
int i, old_line_count, filter_start_line;
965
GtkTextIter iter, start;
967
char *converted, *primary;
971
primary = g_strdup_printf (_("Can't read from \"%s\""),
972
logview_log_get_display_name (log));
973
logview_window_add_error (window, primary, error->message);
980
/* there's no error, but no lines have been read */
984
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->priv->text_view));
985
old_line_count = gtk_text_buffer_get_line_count (buffer);
986
filter_start_line = old_line_count > 0 ? (old_line_count - 1) : 0;
988
if (gtk_text_buffer_get_char_count (buffer) != 0) {
992
gtk_text_buffer_get_end_iter (buffer, &iter);
995
mark = gtk_text_buffer_create_mark (buffer, NULL, &iter, TRUE);
998
for (i = 0; lines[i]; i++) {
999
len = strlen (lines[i]);
1001
if (!g_utf8_validate (lines[i], len, NULL)) {
1002
converted = g_locale_to_utf8 (lines[i], (gssize) len, NULL, &len, NULL);
1003
gtk_text_buffer_insert (buffer, &iter, lines[i], len);
1005
gtk_text_buffer_insert (buffer, &iter, lines[i], strlen (lines[i]));
1008
gtk_text_iter_forward_to_end (&iter);
1009
gtk_text_buffer_insert (buffer, &iter, "\n", 1);
1010
gtk_text_iter_forward_char (&iter);
1014
gtk_text_buffer_get_iter_at_mark (buffer, &start, mark);
1015
gtk_text_buffer_apply_tag_by_name (buffer, "bold", &start, &iter);
1016
gtk_text_buffer_delete_mark (buffer, mark);
1018
filter_buffer (window, filter_start_line);
1020
if (window->priv->auto_scroll) {
1021
gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (window->priv->text_view),
1022
&iter, 0.0, FALSE, 0.0, 0.0);
1025
paint_timestamps (buffer, old_line_count, new_days);
1027
if (window->priv->monitor_id == 0) {
1028
window->priv->monitor_id = g_signal_connect (log, "log-changed",
1029
G_CALLBACK (log_monitor_changed_cb), window);
1032
logview_update_statusbar (window, log);
1033
logview_loglist_update_lines (LOGVIEW_LOGLIST (window->priv->loglist), log);
1037
active_log_changed_cb (LogviewManager *manager,
1039
LogviewLog *old_log,
1042
LogviewWindow *window = data;
1044
GtkTextBuffer *buffer;
1046
findbar_close_cb (LOGVIEW_FINDBAR (window->priv->find_bar),
1049
logview_set_window_title (window, logview_log_get_display_name (log));
1051
if (window->priv->monitor_id) {
1052
g_signal_handler_disconnect (old_log, window->priv->monitor_id);
1053
window->priv->monitor_id = 0;
1056
lines = logview_log_get_cached_lines (log);
1057
buffer = gtk_text_buffer_new (window->priv->tag_table);
1059
if (lines != NULL) {
1063
/* update the text view to show the current lines */
1064
gtk_text_buffer_get_end_iter (buffer, &iter);
1066
for (i = 0; lines[i]; i++) {
1067
gtk_text_buffer_insert (buffer, &iter, lines[i], strlen (lines[i]));
1068
gtk_text_iter_forward_to_end (&iter);
1069
gtk_text_buffer_insert (buffer, &iter, "\n", 1);
1070
gtk_text_iter_forward_char (&iter);
1073
paint_timestamps (buffer, 1, logview_log_get_days_for_cached_lines (log));
1076
if (lines == NULL || logview_log_has_new_lines (log)) {
1077
/* read the new lines */
1078
logview_log_read_new_lines (log, (LogviewNewLinesCallback) read_new_lines_cb, window);
1080
/* start now monitoring the log for changes */
1081
window->priv->monitor_id = g_signal_connect (log, "log-changed",
1082
G_CALLBACK (log_monitor_changed_cb), window);
1085
/* we set the buffer to the view anyway;
1086
* if there are no lines it will be empty for the duration of the thread
1087
* and will help us to distinguish the two cases of the following if
1088
* cause in the callback.
1090
gtk_text_view_set_buffer (GTK_TEXT_VIEW (window->priv->text_view), buffer);
1091
g_object_unref (buffer);
1095
font_changed_cb (LogviewPrefs *prefs,
1096
const char *font_name,
1099
LogviewWindow *window = user_data;
1101
logview_set_font (window, font_name);
1105
tearoff_changed_cb (LogviewPrefs *prefs,
1106
gboolean have_tearoffs,
1109
LogviewWindow *window = user_data;
1111
gtk_ui_manager_set_add_tearoffs (window->priv->ui_manager, have_tearoffs);
1115
style_set_cb (GtkWidget *widget,
1119
LogviewWindow *logview = user_data;
1120
GtkStyle *style = gtk_widget_get_style (widget);
1122
populate_style_tag_table (style, logview->priv->tag_table);
1125
static const struct {
1127
GdkModifierType modifier;
1128
const gchar *action;
1129
} extra_keybindings [] = {
1130
{ GDK_KEY_KP_Add, GDK_CONTROL_MASK, "ViewZoomIn" },
1131
{ GDK_KEY_KP_Subtract, GDK_CONTROL_MASK, "ViewZoomOut" },
1132
{ GDK_KEY_KP_0, GDK_CONTROL_MASK, "ViewZoom100" }
1136
key_press_event_cb (GtkWidget *widget,
1140
LogviewWindow *window = user_data;
1141
guint modifier = event->state & gtk_accelerator_get_default_mod_mask ();
1145
/* handle accelerators that we want bound, but aren't associated with
1147
for (i = 0; i < G_N_ELEMENTS (extra_keybindings); i++) {
1148
if (event->keyval == extra_keybindings[i].keyval &&
1149
modifier == extra_keybindings[i].modifier) {
1151
action = gtk_action_group_get_action (window->priv->action_group,
1152
extra_keybindings[i].action);
1153
gtk_action_activate (action);
1161
/* adapted from GEdit */
1164
message_area_create_error_box (LogviewWindow *window,
1165
GtkWidget *message_area)
1167
GtkWidget *hbox_content;
1170
GtkWidget *primary_label;
1171
GtkWidget *secondary_label;
1173
hbox_content = gtk_hbox_new (FALSE, 8);
1174
gtk_widget_show (hbox_content);
1176
image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_ERROR,
1177
GTK_ICON_SIZE_DIALOG);
1178
gtk_widget_show (image);
1179
gtk_box_pack_start (GTK_BOX (hbox_content), image, FALSE, FALSE, 0);
1180
gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0);
1182
vbox = gtk_vbox_new (FALSE, 6);
1183
gtk_widget_show (vbox);
1184
gtk_box_pack_start (GTK_BOX (hbox_content), vbox, TRUE, TRUE, 0);
1186
primary_label = gtk_label_new (NULL);
1187
gtk_widget_show (primary_label);
1188
gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0);
1189
gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE);
1190
gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE);
1191
gtk_misc_set_alignment (GTK_MISC (primary_label), 0, 0.5);
1192
gtk_widget_set_can_focus (primary_label, TRUE);
1193
gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE);
1195
window->priv->message_primary = primary_label;
1197
secondary_label = gtk_label_new (NULL);
1198
gtk_widget_show (secondary_label);
1199
gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0);
1200
gtk_widget_set_can_focus (secondary_label, TRUE);
1201
gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE);
1202
gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE);
1203
gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE);
1204
gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5);
1206
window->priv->message_secondary = secondary_label;
1209
(GTK_CONTAINER (gtk_info_bar_get_content_area
1210
(GTK_INFO_BAR (message_area))),
1215
message_area_set_labels (LogviewWindow *window,
1216
const char *primary,
1217
const char *secondary)
1219
char *primary_markup, *secondary_markup;
1221
primary_markup = g_markup_printf_escaped ("<b>%s</b>", primary);
1222
secondary_markup = g_markup_printf_escaped ("<small>%s</small>",
1225
gtk_label_set_markup (GTK_LABEL (window->priv->message_primary),
1227
gtk_label_set_markup (GTK_LABEL (window->priv->message_secondary),
1230
g_free (primary_markup);
1231
g_free (secondary_markup);
1235
message_area_response_cb (GtkInfoBar *message_area,
1236
int response_id, gpointer user_data)
1238
gtk_widget_hide (GTK_WIDGET (message_area));
1240
g_signal_handlers_disconnect_by_func (message_area,
1241
message_area_response_cb,
1246
logview_window_finalize (GObject *object)
1248
LogviewWindow *logview = LOGVIEW_WINDOW (object);
1250
g_object_unref (logview->priv->ui_manager);
1251
G_OBJECT_CLASS (logview_window_parent_class)->finalize (object);
1255
logview_window_init (LogviewWindow *logview)
1257
GtkActionGroup *action_group;
1258
GtkAccelGroup *accel_group;
1259
GError *error = NULL;
1260
GtkWidget *hpaned, *main_view, *vbox, *w;
1261
PangoContext *context;
1262
PangoFontDescription *fontdesc;
1263
gchar *monospace_font_name;
1264
LogviewWindowPrivate *priv;
1268
priv = logview->priv = GET_PRIVATE (logview);
1269
priv->prefs = logview_prefs_get ();
1270
priv->manager = logview_manager_get ();
1271
priv->monitor_id = 0;
1273
logview_prefs_get_stored_window_size (priv->prefs, &width, &height);
1274
gtk_window_set_default_size (GTK_WINDOW (logview), width, height);
1276
vbox = gtk_vbox_new (FALSE, 0);
1277
gtk_container_add (GTK_CONTAINER (logview), vbox);
1280
action_group = gtk_action_group_new ("LogviewMenuActions");
1281
gtk_action_group_set_translation_domain (action_group, NULL);
1282
gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), logview);
1283
gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), logview);
1284
priv->action_group = action_group;
1285
priv->auto_scroll = TRUE;
1287
priv->ui_manager = gtk_ui_manager_new ();
1289
gtk_ui_manager_insert_action_group (priv->ui_manager, action_group, 0);
1290
accel_group = gtk_ui_manager_get_accel_group (priv->ui_manager);
1291
gtk_window_add_accel_group (GTK_WINDOW (logview), accel_group);
1293
res = gtk_ui_manager_add_ui_from_file (priv->ui_manager,
1294
LOGVIEW_DATADIR "/logview-toolbar.xml",
1298
priv->ui_manager = NULL;
1299
g_critical ("Can't load the UI description: %s", error->message);
1300
g_error_free (error);
1304
gtk_ui_manager_set_add_tearoffs (priv->ui_manager,
1305
logview_prefs_get_have_tearoff (priv->prefs));
1307
w = gtk_ui_manager_get_widget (priv->ui_manager, "/LogviewMenu");
1308
gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0);
1309
gtk_widget_show (w);
1312
hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
1313
gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
1314
priv->hpaned = hpaned;
1315
gtk_widget_show (hpaned);
1317
/* first pane : sidebar (list of logs) */
1318
priv->sidebar = gtk_vbox_new (FALSE, 0);
1319
gtk_widget_show (priv->sidebar);
1321
/* first pane: log list */
1322
w = gtk_scrolled_window_new (NULL, NULL);
1323
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w),
1324
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1325
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w),
1326
GTK_SHADOW_ETCHED_IN);
1328
priv->loglist = logview_loglist_new ();
1329
gtk_container_add (GTK_CONTAINER (w), priv->loglist);
1330
gtk_box_pack_start (GTK_BOX (priv->sidebar), w, TRUE, TRUE, 0);
1331
gtk_paned_pack1 (GTK_PANED (hpaned), priv->sidebar, FALSE, FALSE);
1332
gtk_widget_show (w);
1333
gtk_widget_show (priv->loglist);
1335
g_signal_connect (priv->loglist, "day_selected",
1336
G_CALLBACK (loglist_day_selected_cb), logview);
1337
g_signal_connect (priv->loglist, "day_cleared",
1338
G_CALLBACK (loglist_day_cleared_cb), logview);
1340
/* second pane: log */
1341
main_view = gtk_vbox_new (FALSE, 0);
1342
gtk_paned_pack2 (GTK_PANED (hpaned), main_view, TRUE, TRUE);
1344
/* second pane: error message area */
1345
priv->message_area = gtk_info_bar_new ();
1346
message_area_create_error_box (logview, priv->message_area);
1347
gtk_info_bar_add_button (GTK_INFO_BAR (priv->message_area),
1348
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
1349
gtk_box_pack_start (GTK_BOX (main_view), priv->message_area, FALSE, FALSE, 0);
1351
/* second pane: text view */
1352
w = gtk_scrolled_window_new (NULL, NULL);
1353
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w),
1354
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1355
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w), GTK_SHADOW_IN);
1356
gtk_box_pack_start (GTK_BOX (main_view), w, TRUE, TRUE, 0);
1357
gtk_widget_show (w);
1359
priv->tag_table = gtk_text_tag_table_new ();
1360
populate_tag_table (priv->tag_table);
1361
priv->text_view = gtk_text_view_new ();
1362
g_object_set (priv->text_view, "editable", FALSE, NULL);
1364
gtk_container_add (GTK_CONTAINER (w), priv->text_view);
1365
gtk_widget_show (priv->text_view);
1367
/* use the desktop monospace font */
1368
monospace_font_name = logview_prefs_get_monospace_font_name (priv->prefs);
1369
logview_set_font (logview, monospace_font_name);
1370
g_free (monospace_font_name);
1372
/* remember the original font size */
1373
context = gtk_widget_get_pango_context (priv->text_view);
1374
fontdesc = pango_context_get_font_description (context);
1375
priv->original_fontsize = pango_font_description_get_size (fontdesc) / PANGO_SCALE;
1377
/* restore saved zoom */
1378
priv->fontsize = logview_prefs_get_stored_fontsize (priv->prefs);
1380
if (priv->fontsize <= 0) {
1381
/* restore the default */
1382
logview_normal_text (NULL, logview);
1384
logview_set_fontsize (logview, FALSE);
1387
/* version selector */
1388
priv->version_bar = gtk_hbox_new (FALSE, 0);
1389
gtk_container_set_border_width (GTK_CONTAINER (priv->version_bar), 3);
1390
priv->version_selector = gtk_combo_box_text_new ();
1391
g_signal_connect (priv->version_selector, "changed",
1392
G_CALLBACK (logview_version_selector_changed), logview);
1393
w = gtk_label_new (_("Version: "));
1395
gtk_box_pack_end (GTK_BOX (priv->version_bar), priv->version_selector, FALSE, FALSE, 0);
1396
gtk_box_pack_end (GTK_BOX (priv->version_bar), w, FALSE, FALSE, 0);
1397
gtk_box_pack_end (GTK_BOX (main_view), priv->version_bar, FALSE, FALSE, 0);
1399
priv->find_bar = logview_findbar_new ();
1400
gtk_box_pack_end (GTK_BOX (main_view), priv->find_bar, FALSE, FALSE, 0);
1402
g_signal_connect (priv->find_bar, "previous",
1403
G_CALLBACK (findbar_previous_cb), logview);
1404
g_signal_connect (priv->find_bar, "next",
1405
G_CALLBACK (findbar_next_cb), logview);
1406
g_signal_connect (priv->find_bar, "text_changed",
1407
G_CALLBACK (findbar_text_changed_cb), logview);
1408
g_signal_connect (priv->find_bar, "close",
1409
G_CALLBACK (findbar_close_cb), logview);
1412
* - first is used to remember/restore the window size on quit.
1414
g_signal_connect (logview, "configure_event",
1415
G_CALLBACK (window_size_changed_cb), logview);
1416
g_signal_connect (priv->prefs, "system-font-changed",
1417
G_CALLBACK (font_changed_cb), logview);
1418
g_signal_connect (priv->prefs, "have-tearoff-changed",
1419
G_CALLBACK (tearoff_changed_cb), logview);
1420
g_signal_connect (priv->manager, "active-changed",
1421
G_CALLBACK (active_log_changed_cb), logview);
1422
g_signal_connect (logview, "style-set",
1423
G_CALLBACK (style_set_cb), logview);
1424
g_signal_connect (logview, "key-press-event",
1425
G_CALLBACK (key_press_event_cb), logview);
1427
/* status area at bottom */
1428
priv->statusbar = gtk_statusbar_new ();
1429
gtk_box_pack_start (GTK_BOX (vbox), priv->statusbar, FALSE, FALSE, 0);
1430
gtk_widget_show (priv->statusbar);
1433
priv->filter_action_group = gtk_action_group_new ("ActionGroupFilter");
1434
gtk_ui_manager_insert_action_group (priv->ui_manager, priv->filter_action_group,
1436
priv->active_filters = NULL;
1437
update_filter_menu (logview);
1439
gtk_widget_show (vbox);
1440
gtk_widget_show (main_view);
1444
logview_window_class_init (LogviewWindowClass *klass)
1446
GObjectClass *object_class = (GObjectClass *) klass;
1448
object_class->finalize = logview_window_finalize;
1450
g_type_class_add_private (klass, sizeof (LogviewWindowPrivate));
1453
/* public methods */
1456
logview_window_new ()
1458
LogviewWindow *logview;
1460
logview = g_object_new (LOGVIEW_TYPE_WINDOW, NULL);
1462
if (logview->priv->ui_manager == NULL) {
1466
return GTK_WIDGET (logview);
1470
logview_window_add_error (LogviewWindow *window,
1471
const char *primary,
1472
const char *secondary)
1474
LogviewWindowPrivate *priv;
1476
g_assert (LOGVIEW_IS_WINDOW (window));
1477
priv = window->priv;
1479
message_area_set_labels (window,
1480
primary, secondary);
1482
gtk_widget_show (priv->message_area);
1484
g_signal_connect (priv->message_area, "response",
1485
G_CALLBACK (message_area_response_cb), window);
1489
logview_window_add_errors (LogviewWindow *window,
1492
char *primary, *secondary;
1497
g_assert (LOGVIEW_IS_WINDOW (window));
1498
g_assert (errors->len > 1);
1500
primary = g_strdup (_("Could not open the following files:"));
1501
str = g_string_new (NULL);
1503
for (i = 0; i < errors->len; i++) {
1504
err = (char **) g_ptr_array_index (errors, i);
1505
g_string_append (str, err[0]);
1506
g_string_append (str, ": ");
1507
g_string_append (str, err[1]);
1508
g_string_append (str, "\n");
1511
secondary = g_string_free (str, FALSE);
1513
message_area_set_labels (window, primary, secondary);
1515
gtk_widget_show (window->priv->message_area);
1517
g_signal_connect (window->priv->message_area, "response",
1518
G_CALLBACK (message_area_response_cb), window);