~ubuntu-branches/ubuntu/natty/gnome-utils/natty

« back to all changes in this revision

Viewing changes to .pc/04_lpi.patch/logview/logview-window.c

  • Committer: Bazaar Package Importer
  • Author(s): Robert Ancell
  • Date: 2010-06-21 09:42:09 UTC
  • mfrom: (3.1.8 sid)
  • Revision ID: james.westby@ubuntu.com-20100621094209-fqflsc2meszec96c
Tags: 2.30.0-2ubuntu1
* Merge with Debian unstable, remaining Ubuntu changes:
* debian/control:
  - Build-depend on gnome-commong for autotools build
  - Build-depend on liblaunchpad-integration-dev
  - Add Vcs-Bzr link
  - gnome-utils-common replaces gnome-dictionary
  - gnome-system-log doesn't depend on gksu
* debian/rules:
  - Run autotools on build
* debian/patches/01_logview_gksu.patch:
  - Dropped, we have the admin group for this
* debian/patches/01_maintainer_mode.patch:
  - Disable maintainer mode
* debian/patches/04_lpi.patch:
  - Launchpad integration
* debian/patches/05_menu_changes.patch:
  - Ubuntu menu simplification
* debian/patches/06_fix_ftbfs.patch:
  - Don't build with -Werror
* debian/watch:
  - Watch for unstable versions

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
 
2
/* logview-window.c - main window of logview
 
3
 *
 
4
 * Copyright (C) 1998  Cesar Miquel  <miquel@df.uba.ar>
 
5
 * Copyright (C) 2008  Cosimo Cecchi <cosimoc@gnome.org>
 
6
 *
 
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.
 
11
 *
 
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.
 
16
 *
 
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.
 
20
 */
 
21
 
 
22
#include <config.h>
 
23
#include <stdlib.h>
 
24
#include <string.h>
 
25
#include <gtk/gtk.h>
 
26
#include <gdk/gdkkeysyms.h>
 
27
#include <glib/gi18n.h>
 
28
 
 
29
#include "logview-window.h"
 
30
 
 
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"
 
37
 
 
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"
 
41
 
 
42
struct _LogviewWindowPrivate {
 
43
  GtkUIManager *ui_manager;
 
44
  GtkActionGroup *action_group;
 
45
  GtkActionGroup *filter_action_group;
 
46
 
 
47
  GtkWidget *find_bar;
 
48
  GtkWidget *loglist;
 
49
  GtkWidget *sidebar; 
 
50
  GtkWidget *version_bar;
 
51
  GtkWidget *version_selector;
 
52
  GtkWidget *hpaned;
 
53
  GtkWidget *text_view;
 
54
  GtkWidget *statusbar;
 
55
 
 
56
  GtkWidget *message_area;
 
57
  GtkWidget *message_primary;
 
58
  GtkWidget *message_secondary;
 
59
 
 
60
  GtkTextTagTable *tag_table;
 
61
 
 
62
  int original_fontsize, fontsize;
 
63
 
 
64
  LogviewPrefs *prefs;
 
65
  LogviewManager *manager;
 
66
 
 
67
  gulong monitor_id;
 
68
  guint search_timeout_id;
 
69
 
 
70
  guint filter_merge_id;
 
71
  GList *active_filters;
 
72
  gboolean matches_only;
 
73
};
 
74
 
 
75
#define GET_PRIVATE(o) \
 
76
  (G_TYPE_INSTANCE_GET_PRIVATE ((o), LOGVIEW_TYPE_WINDOW, LogviewWindowPrivate))
 
77
 
 
78
G_DEFINE_TYPE (LogviewWindow, logview_window, GTK_TYPE_WINDOW);
 
79
 
 
80
static void findbar_close_cb  (LogviewFindbar *findbar,
 
81
                               gpointer user_data);
 
82
static void read_new_lines_cb (LogviewLog *log,
 
83
                               const char **lines,
 
84
                               GSList *new_days,
 
85
                               GError *error,
 
86
                               gpointer user_data);
 
87
 
 
88
/* private functions */
 
89
 
 
90
static void
 
91
logview_version_selector_changed (GtkComboBox *version_selector, gpointer user_data)
 
92
{
 
93
 
 
94
}
 
95
#if 0
 
96
        LogviewWindow *logview = user_data;
 
97
        Log *log = logview->curlog;
 
98
        int selected;
 
99
 
 
100
    g_assert (LOGVIEW_IS_WINDOW (logview));
 
101
 
 
102
        selected = gtk_combo_box_get_active (version_selector);
 
103
 
 
104
        if (selected == log->current_version)
 
105
                return;
 
106
 
 
107
        /* select a new version */
 
108
        if (selected == 0) {
 
109
                logview_select_log (logview, log->parent_log);
 
110
        } else {
 
111
                Log *new;
 
112
                if (log->parent_log) {
 
113
                        new = log->parent_log->older_logs[selected];
 
114
                } else {
 
115
                        new = log->older_logs[selected];
 
116
                }
 
117
 
 
118
                logview_select_log (logview, new);
 
119
        }
 
120
}
 
121
 
 
122
#endif
 
123
 
 
124
/* private helpers */
 
125
 
 
126
static void
 
127
populate_tag_table (GtkTextTagTable *tag_table)
 
128
{
 
129
  GtkTextTag *tag;
 
130
  
 
131
  tag = gtk_text_tag_new ("bold");
 
132
  g_object_set (tag, "weight", PANGO_WEIGHT_BOLD,
 
133
                "weight-set", TRUE, NULL);
 
134
 
 
135
  gtk_text_tag_table_add (tag_table, tag);
 
136
 
 
137
  tag = gtk_text_tag_new ("invisible");
 
138
  g_object_set (tag, "invisible", TRUE, "invisible-set", TRUE, NULL);
 
139
  gtk_text_tag_table_add (tag_table, tag);
 
140
 
 
141
  tag = gtk_text_tag_new ("invisible-filter");
 
142
  g_object_set (tag, "invisible", TRUE, "invisible-set", TRUE, NULL);
 
143
  gtk_text_tag_table_add (tag_table, tag); 
 
144
}
 
145
 
 
146
 
 
147
static void
 
148
populate_style_tag_table (GtkStyle *style,
 
149
                          GtkTextTagTable *tag_table)
 
150
{
 
151
  GtkTextTag *tag;
 
152
  GdkColor color;
 
153
 
 
154
  tag = gtk_text_tag_table_lookup (tag_table, "gray");
 
155
 
 
156
  if (tag) {
 
157
    /* FIXME: do we need a way to update the buffer/view? */
 
158
    gtk_text_tag_table_remove (tag_table, tag);
 
159
  }
 
160
 
 
161
  tag = gtk_text_tag_new ("gray");
 
162
  color = style->text[GTK_STATE_INSENSITIVE];
 
163
  g_object_set (tag, "foreground-gdk", &color, "foreground-set", TRUE, NULL);
 
164
 
 
165
  gtk_text_tag_table_add (tag_table, tag);
 
166
}
 
167
 
 
168
static void
 
169
_gtk_text_buffer_apply_tag_to_rectangle (GtkTextBuffer *buffer, int line_start, int line_end,
 
170
                                        int offset_start, int offset_end, char *tag_name)
 
171
{
 
172
  GtkTextIter start, end;
 
173
  int line_cur;
 
174
 
 
175
  gtk_text_buffer_get_iter_at_line (buffer, &start, line_start);
 
176
  gtk_text_buffer_get_iter_at_line (buffer, &end, line_start);
 
177
 
 
178
  for (line_cur = line_start; line_cur < line_end + 1; line_cur++) {
 
179
 
 
180
    if (offset_start > 0) {
 
181
      gtk_text_iter_forward_chars (&start, offset_start);
 
182
    }
 
183
 
 
184
    gtk_text_iter_forward_chars (&end, offset_end);
 
185
 
 
186
    gtk_text_buffer_apply_tag_by_name (buffer, tag_name, &start, &end);
 
187
 
 
188
    gtk_text_iter_forward_line (&start);
 
189
    gtk_text_iter_forward_line (&end);
 
190
  }
 
191
}
 
192
 
 
193
static void
 
194
logview_update_statusbar (LogviewWindow *logview, LogviewLog *active)
 
195
{
 
196
  char *statusbar_text;
 
197
  char *size, *modified, *timestring_utf8;
 
198
  time_t timestamp;
 
199
  char timestring[255];
 
200
 
 
201
  if (active == NULL) {
 
202
    gtk_statusbar_pop (GTK_STATUSBAR (logview->priv->statusbar), 0);
 
203
    return;
 
204
  }
 
205
 
 
206
  timestamp = logview_log_get_timestamp (active);
 
207
  strftime (timestring, sizeof (timestring), "%a %b %e %T %Y", localtime (&timestamp));
 
208
  timestring_utf8 = g_locale_to_utf8 (timestring, -1, NULL, NULL, NULL);
 
209
 
 
210
  modified = g_strdup_printf (_("last update: %s"), timestring_utf8);
 
211
 
 
212
  size = g_format_size_for_display (logview_log_get_file_size (active));
 
213
  statusbar_text = g_strdup_printf (_("%d lines (%s) - %s"), 
 
214
                                    logview_log_get_cached_lines_number (active),
 
215
                                    size, modified);
 
216
 
 
217
  gtk_statusbar_pop (GTK_STATUSBAR (logview->priv->statusbar), 0);
 
218
  gtk_statusbar_push (GTK_STATUSBAR (logview->priv->statusbar), 0, statusbar_text);
 
219
  
 
220
  g_free (size);
 
221
  g_free (timestring_utf8);
 
222
  g_free (modified);
 
223
  g_free (statusbar_text);
 
224
}
 
225
 
 
226
#define DEFAULT_LOGVIEW_FONT "Monospace 10"
 
227
 
 
228
static void
 
229
logview_set_font (LogviewWindow *logview,
 
230
                  const char    *fontname)
 
231
{
 
232
  PangoFontDescription *font_desc;
 
233
 
 
234
  if (fontname == NULL)
 
235
    fontname = DEFAULT_LOGVIEW_FONT;
 
236
 
 
237
  font_desc = pango_font_description_from_string (fontname);
 
238
  if (font_desc) {
 
239
    gtk_widget_modify_font (logview->priv->text_view, font_desc);
 
240
    pango_font_description_free (font_desc);
 
241
  }
 
242
}
 
243
 
 
244
static void
 
245
logview_set_fontsize (LogviewWindow *logview, gboolean store)
 
246
{
 
247
  PangoFontDescription *fontdesc;
 
248
  PangoContext *context;
 
249
  LogviewWindowPrivate *priv = logview->priv;
 
250
 
 
251
  context = gtk_widget_get_pango_context (priv->text_view);
 
252
  fontdesc = pango_context_get_font_description (context);
 
253
  pango_font_description_set_size (fontdesc, (priv->fontsize) * PANGO_SCALE);
 
254
  gtk_widget_modify_font (priv->text_view, fontdesc);
 
255
 
 
256
  if (store) {
 
257
    logview_prefs_store_fontsize (logview->priv->prefs, priv->fontsize);
 
258
  }
 
259
}
 
260
 
 
261
static void
 
262
logview_set_window_title (LogviewWindow *logview, const char * log_name)
 
263
{
 
264
  char *window_title;
 
265
 
 
266
  if (log_name) {
 
267
    window_title = g_strdup_printf ("%s - %s", log_name, APP_NAME);
 
268
  } else {
 
269
    window_title = g_strdup_printf (APP_NAME);
 
270
  }
 
271
 
 
272
  gtk_window_set_title (GTK_WINDOW (logview), window_title);
 
273
 
 
274
  g_free (window_title);
 
275
}
 
276
 
 
277
static void
 
278
logview_menu_item_set_state (LogviewWindow *logview, char *path, gboolean state)
 
279
{
 
280
  GtkAction *action;
 
281
 
 
282
  g_assert (path);
 
283
 
 
284
  action = gtk_ui_manager_get_action (logview->priv->ui_manager, path);
 
285
  gtk_action_set_sensitive (action, state);
 
286
}
 
287
 
 
288
static void
 
289
logview_window_menus_set_state (LogviewWindow *logview)
 
290
{
 
291
  LogviewLog *log;
 
292
 
 
293
  log = logview_manager_get_active_log (logview->priv->manager);
 
294
 
 
295
  logview_menu_item_set_state (logview, "/LogviewMenu/FileMenu/CloseLog", (log != NULL));
 
296
  logview_menu_item_set_state (logview, "/LogviewMenu/ViewMenu/Search", (log != NULL));
 
297
  logview_menu_item_set_state (logview, "/LogviewMenu/EditMenu/Copy", (log != NULL));
 
298
  logview_menu_item_set_state (logview, "/LogviewMenu/EditMenu/SelectAll", (log != NULL));
 
299
 
 
300
  g_object_unref (log);
 
301
}
 
302
 
 
303
/* actions callbacks */
 
304
 
 
305
static void
 
306
open_file_selected_cb (GtkWidget *chooser, gint response, LogviewWindow *logview)
 
307
{
 
308
  GFile *f;
 
309
  char *file_uri;
 
310
  LogviewLog *log;
 
311
 
 
312
  gtk_widget_hide (GTK_WIDGET (chooser));
 
313
  if (response != GTK_RESPONSE_OK) {
 
314
          return;
 
315
  }
 
316
 
 
317
  f = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (chooser));
 
318
  file_uri = g_file_get_uri (f);
 
319
 
 
320
  log = logview_manager_get_if_loaded (logview->priv->manager, file_uri);
 
321
 
 
322
  g_free (file_uri);
 
323
 
 
324
  if (log) {
 
325
    logview_manager_set_active_log (logview->priv->manager, log);
 
326
    g_object_unref (log);
 
327
    goto out;
 
328
  }
 
329
 
 
330
  logview_manager_add_log_from_gfile (logview->priv->manager, f, TRUE);
 
331
 
 
332
out:
 
333
  g_object_unref (f);
 
334
}
 
335
 
 
336
static void
 
337
logview_open_log (GtkAction *action, LogviewWindow *logview)
 
338
{
 
339
  static GtkWidget *chooser = NULL;
 
340
  char *active;
 
341
 
 
342
  if (chooser == NULL) {
 
343
    chooser = gtk_file_chooser_dialog_new (_("Open Log"),
 
344
                                           GTK_WINDOW (logview),
 
345
                                           GTK_FILE_CHOOSER_ACTION_OPEN,
 
346
                                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 
347
                                           GTK_STOCK_OPEN, GTK_RESPONSE_OK,
 
348
                                           NULL);
 
349
    gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_OK);
 
350
    gtk_window_set_modal (GTK_WINDOW (chooser), TRUE);
 
351
    g_signal_connect (chooser, "response",
 
352
                      G_CALLBACK (open_file_selected_cb), logview);
 
353
    g_signal_connect (chooser, "destroy",
 
354
                      G_CALLBACK (gtk_widget_destroyed), &chooser);
 
355
    active = logview_prefs_get_active_logfile (logview->priv->prefs);
 
356
    if (active != NULL) {
 
357
      gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (chooser), active);
 
358
      g_free (active);
 
359
    }
 
360
  }
 
361
 
 
362
  gtk_window_present (GTK_WINDOW (chooser));
 
363
}
 
364
 
 
365
static void
 
366
logview_close_log (GtkAction *action, LogviewWindow *logview)
 
367
{
 
368
  findbar_close_cb (LOGVIEW_FINDBAR (logview->priv->find_bar), logview);
 
369
  logview_manager_close_active_log (logview->priv->manager);
 
370
}
 
371
 
 
372
static void
 
373
logview_help (GtkAction *action, GtkWidget *parent_window)
 
374
{
 
375
  GError *error = NULL;
 
376
 
 
377
  gtk_show_uri (gtk_widget_get_screen (parent_window),
 
378
                "ghelp:gnome-system-log", gtk_get_current_event_time (),
 
379
                &error);
 
380
 
 
381
  if (error) {
 
382
    g_warning (_("There was an error displaying help: %s"), error->message);
 
383
    g_error_free (error);
 
384
  }
 
385
}
 
386
 
 
387
static void
 
388
logview_bigger_text (GtkAction *action, LogviewWindow *logview)
 
389
{
 
390
  logview->priv->fontsize = MIN (logview->priv->fontsize + 1, 24);
 
391
  logview_set_fontsize (logview, TRUE);
 
392
}       
 
393
 
 
394
static void
 
395
logview_smaller_text (GtkAction *action, LogviewWindow *logview)
 
396
{
 
397
  logview->priv->fontsize = MAX (logview->priv->fontsize-1, 6);
 
398
  logview_set_fontsize (logview, TRUE);
 
399
}       
 
400
 
 
401
static void
 
402
logview_normal_text (GtkAction *action, LogviewWindow *logview)
 
403
{
 
404
  logview->priv->fontsize = logview->priv->original_fontsize;
 
405
  logview_set_fontsize (logview, TRUE);
 
406
}
 
407
 
 
408
static void
 
409
logview_select_all (GtkAction *action, LogviewWindow *logview)
 
410
{
 
411
  GtkTextIter start, end;
 
412
  GtkTextBuffer *buffer;
 
413
 
 
414
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (logview->priv->text_view));
 
415
 
 
416
  gtk_text_buffer_get_bounds (buffer, &start, &end);
 
417
  gtk_text_buffer_select_range (buffer, &start, &end);
 
418
 
 
419
  gtk_widget_grab_focus (GTK_WIDGET (logview->priv->text_view));
 
420
}
 
421
 
 
422
static void
 
423
logview_copy (GtkAction *action, LogviewWindow *logview)
 
424
{
 
425
  GtkTextBuffer *buffer;
 
426
  GtkClipboard *clipboard;
 
427
 
 
428
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (logview->priv->text_view));
 
429
  clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
 
430
 
 
431
  gtk_text_buffer_copy_clipboard (buffer, clipboard);
 
432
 
 
433
  gtk_widget_grab_focus (GTK_WIDGET (logview->priv->text_view));
 
434
}
 
435
 
 
436
static void
 
437
findbar_close_cb (LogviewFindbar *findbar,
 
438
                  gpointer user_data)
 
439
{
 
440
  LogviewWindow *logview = user_data;
 
441
 
 
442
  gtk_widget_hide (GTK_WIDGET (findbar));
 
443
  logview_findbar_set_message (findbar, NULL);
 
444
}
 
445
 
 
446
static void
 
447
logview_search_text (LogviewWindow *logview, gboolean forward)
 
448
{
 
449
  GtkTextBuffer *buffer;
 
450
  GtkTextMark *search_start, *search_end;
 
451
  GtkTextIter search, start_m, end_m;
 
452
  const char *text;
 
453
  gboolean res, wrapped;
 
454
 
 
455
  wrapped = FALSE;
 
456
 
 
457
  text = logview_findbar_get_text (LOGVIEW_FINDBAR (logview->priv->find_bar));
 
458
 
 
459
  if (!text || g_strcmp0 (text, "") == 0) {
 
460
    return;
 
461
  }
 
462
 
 
463
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (logview->priv->text_view));
 
464
  search_start = gtk_text_buffer_get_mark (buffer, SEARCH_START_MARK);
 
465
  search_end = gtk_text_buffer_get_mark (buffer, SEARCH_END_MARK);
 
466
 
 
467
  if (!search_start) {
 
468
    /* this is our first search on the buffer, create a new search mark */
 
469
    gtk_text_buffer_get_start_iter (buffer, &search);
 
470
    search_start = gtk_text_buffer_create_mark (buffer, SEARCH_START_MARK,
 
471
                                                &search, TRUE);
 
472
    search_end = gtk_text_buffer_create_mark (buffer, SEARCH_END_MARK,
 
473
                                              &search, TRUE);
 
474
  } else {
 
475
    if (forward) {
 
476
      gtk_text_buffer_get_iter_at_mark (buffer, &search, search_end);
 
477
    } else {
 
478
      gtk_text_buffer_get_iter_at_mark (buffer, &search, search_start);
 
479
    }
 
480
  }
 
481
 
 
482
wrap:
 
483
 
 
484
  if (forward) {
 
485
    res = gtk_text_iter_forward_search (&search, text, GTK_TEXT_SEARCH_VISIBLE_ONLY, &start_m, &end_m, NULL);
 
486
  } else {
 
487
    res = gtk_text_iter_backward_search (&search, text, GTK_TEXT_SEARCH_VISIBLE_ONLY, &start_m, &end_m, NULL);
 
488
  }
 
489
 
 
490
  if (res) {
 
491
    gtk_text_buffer_select_range (buffer, &start_m, &end_m);
 
492
    gtk_text_buffer_move_mark (buffer, search_start, &start_m);
 
493
    gtk_text_buffer_move_mark (buffer, search_end, &end_m);
 
494
 
 
495
    gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (logview->priv->text_view), search_end);
 
496
 
 
497
    if (wrapped) {
 
498
      logview_findbar_set_message (LOGVIEW_FINDBAR (logview->priv->find_bar), _("Wrapped"));
 
499
    }
 
500
  } else {
 
501
    if (wrapped) {
 
502
      
 
503
      GtkTextMark *mark;
 
504
      GtkTextIter iter;
 
505
 
 
506
      if (gtk_text_buffer_get_has_selection (buffer)) {
 
507
        /* unselect */
 
508
        mark = gtk_text_buffer_get_mark (buffer, "insert");
 
509
        gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
 
510
        gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &iter);
 
511
      }
 
512
 
 
513
      logview_findbar_set_message (LOGVIEW_FINDBAR (logview->priv->find_bar), _("Not found"));
 
514
    } else {
 
515
      if (forward) {
 
516
        gtk_text_buffer_get_start_iter (buffer, &search);
 
517
      } else {
 
518
        gtk_text_buffer_get_end_iter (buffer, &search);
 
519
      }
 
520
 
 
521
      wrapped = TRUE;
 
522
      goto wrap;
 
523
    }
 
524
  }
 
525
}
 
526
 
 
527
static void
 
528
findbar_previous_cb (LogviewFindbar *findbar,
 
529
                     gpointer user_data)
 
530
{
 
531
  LogviewWindow *logview = user_data;
 
532
 
 
533
  logview_search_text (logview, FALSE);
 
534
}
 
535
 
 
536
static void
 
537
findbar_next_cb (LogviewFindbar *findbar,
 
538
                 gpointer user_data)
 
539
{
 
540
  LogviewWindow *logview = user_data;
 
541
 
 
542
  logview_search_text (logview, TRUE);
 
543
}
 
544
 
 
545
static gboolean
 
546
text_changed_timeout_cb (gpointer user_data)
 
547
{
 
548
  LogviewWindow *logview = user_data;
 
549
  GtkTextMark *search_start, *search_end;
 
550
  GtkTextIter start;
 
551
  GtkTextBuffer *buffer;
 
552
 
 
553
  logview->priv->search_timeout_id = 0;
 
554
 
 
555
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (logview->priv->text_view));
 
556
  search_start = gtk_text_buffer_get_mark (buffer, SEARCH_START_MARK);
 
557
  search_end = gtk_text_buffer_get_mark (buffer, SEARCH_END_MARK);
 
558
  
 
559
  if (search_start) {
 
560
    /* reset the search mark to the start */
 
561
    gtk_text_buffer_get_start_iter (buffer, &start);
 
562
    gtk_text_buffer_move_mark (buffer, search_start, &start);
 
563
    gtk_text_buffer_move_mark (buffer, search_end, &start);
 
564
  }
 
565
 
 
566
  logview_findbar_set_message (LOGVIEW_FINDBAR (logview->priv->find_bar), NULL);
 
567
 
 
568
  logview_search_text (logview, TRUE);
 
569
 
 
570
  return FALSE;
 
571
}
 
572
 
 
573
static void
 
574
findbar_text_changed_cb (LogviewFindbar *findbar,
 
575
                         gpointer user_data)
 
576
{
 
577
  LogviewWindow *logview = user_data;
 
578
 
 
579
  if (logview->priv->search_timeout_id != 0) {
 
580
    g_source_remove (logview->priv->search_timeout_id);
 
581
  }
 
582
 
 
583
  logview->priv->search_timeout_id = g_timeout_add (300, text_changed_timeout_cb, logview);
 
584
}
 
585
 
 
586
static void
 
587
logview_search (GtkAction *action, LogviewWindow *logview)
 
588
{
 
589
  logview_findbar_open (LOGVIEW_FINDBAR (logview->priv->find_bar));
 
590
}
 
591
 
 
592
static void
 
593
filter_buffer (LogviewWindow *logview, gint start_line)
 
594
{
 
595
  GtkTextBuffer *buffer;
 
596
  GtkTextIter start, *end;
 
597
  gchar* text;
 
598
  GList* cur_filter;
 
599
  gboolean matched, invisible_set;
 
600
  int lines, i;
 
601
 
 
602
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (logview->priv->text_view));
 
603
  lines = gtk_text_buffer_get_line_count (buffer);
 
604
 
 
605
  for (i = start_line; i < lines; i++) {
 
606
    matched = FALSE;
 
607
 
 
608
    gtk_text_buffer_get_iter_at_line (buffer, &start, i);
 
609
    end = gtk_text_iter_copy (&start);
 
610
    gtk_text_iter_forward_line (end);
 
611
 
 
612
    text = gtk_text_buffer_get_text (buffer, &start, end, TRUE);
 
613
 
 
614
    for (cur_filter = logview->priv->active_filters; cur_filter != NULL;
 
615
         cur_filter = g_list_next (cur_filter))
 
616
    {
 
617
      if (logview_filter_filter (LOGVIEW_FILTER (cur_filter->data), text)) {
 
618
        gtk_text_buffer_apply_tag (buffer, 
 
619
                                   logview_filter_get_tag (LOGVIEW_FILTER (cur_filter->data)),
 
620
                                   &start, end);
 
621
        matched = TRUE;
 
622
      }
 
623
    }
 
624
 
 
625
    g_free (text);
 
626
 
 
627
    if (!matched && logview->priv->matches_only) {
 
628
      gtk_text_buffer_apply_tag_by_name (buffer, 
 
629
                                         "invisible-filter",
 
630
                                         &start, end);
 
631
    } else {
 
632
      gtk_text_buffer_remove_tag_by_name (buffer,
 
633
                                          "invisible-filter",
 
634
                                          &start, end);
 
635
    }
 
636
 
 
637
    gtk_text_iter_free (end);
 
638
  }
 
639
}
 
640
 
 
641
static void
 
642
filter_remove (LogviewWindow *logview, LogviewFilter *filter)
 
643
{
 
644
  GtkTextIter start, end;  
 
645
  GtkTextBuffer *buffer;
 
646
 
 
647
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (logview->priv->text_view));
 
648
  gtk_text_buffer_get_bounds (buffer, &start, &end);
 
649
 
 
650
  gtk_text_buffer_remove_tag (buffer, logview_filter_get_tag (filter),
 
651
                              &start, &end);
 
652
}
 
653
 
 
654
static void
 
655
on_filter_toggled (GtkToggleAction *action, LogviewWindow *logview)
 
656
{
 
657
  LogviewWindowPrivate *priv = GET_PRIVATE (logview);
 
658
  const gchar* name;
 
659
  LogviewFilter *filter;
 
660
 
 
661
  name = gtk_action_get_name (GTK_ACTION (action));
 
662
  
 
663
  if (gtk_toggle_action_get_active (action)) {
 
664
    priv->active_filters = g_list_append (priv->active_filters,
 
665
                                          logview_prefs_get_filter (priv->prefs,
 
666
                                                                    name));
 
667
    filter_buffer(logview, 0);
 
668
  } else {
 
669
    filter = logview_prefs_get_filter (priv->prefs, name);
 
670
    priv->active_filters = g_list_remove (priv->active_filters,
 
671
                                          filter);
 
672
 
 
673
    filter_remove (logview, filter);
 
674
  }
 
675
}
 
676
 
 
677
#define FILTER_PLACEHOLDER "/LogviewMenu/FilterMenu/PlaceholderFilters"
 
678
static void
 
679
update_filter_menu (LogviewWindow *window)
 
680
{
 
681
  LogviewWindowPrivate *priv;
 
682
  GtkUIManager* ui;
 
683
  GList *actions, *l;
 
684
  gint n, i;
 
685
  guint id;
 
686
  GList *filters;
 
687
  GtkTextBuffer *buffer;
 
688
  GtkTextTagTable *table;
 
689
  GtkTextTag *tag;
 
690
  GtkToggleAction *action;
 
691
  gchar* name;
 
692
 
 
693
  priv = GET_PRIVATE (window);
 
694
  ui = priv->ui_manager;
 
695
 
 
696
  g_return_if_fail (priv->filter_action_group != NULL);
 
697
 
 
698
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view));
 
699
  table = priv->tag_table;
 
700
 
 
701
  if (priv->filter_merge_id != 0) {
 
702
    gtk_ui_manager_remove_ui (ui,
 
703
                              priv->filter_merge_id);
 
704
  }
 
705
 
 
706
  actions = gtk_action_group_list_actions (priv->filter_action_group);
 
707
 
 
708
  for (l = actions; l != NULL; l = g_list_next (l)) {
 
709
    tag = gtk_text_tag_table_lookup (table, gtk_action_get_name (GTK_ACTION (l->data)));
 
710
    gtk_text_tag_table_remove (table, tag);
 
711
 
 
712
    g_signal_handlers_disconnect_by_func (GTK_ACTION (l->data),
 
713
                                          G_CALLBACK (on_filter_toggled),
 
714
                                          window);
 
715
    gtk_action_group_remove_action (priv->filter_action_group,
 
716
                                    GTK_ACTION (l->data));
 
717
  }
 
718
 
 
719
  g_list_free (actions);
 
720
  
 
721
  filters = logview_prefs_get_filters (logview_prefs_get ());
 
722
 
 
723
  id = (g_list_length (filters) > 0) ? gtk_ui_manager_new_merge_id (ui) : 0;
 
724
 
 
725
  for (l = filters; l != NULL; l = g_list_next (l)) {
 
726
    g_object_get (l->data, "name", &name, NULL);
 
727
 
 
728
    action = gtk_toggle_action_new (name, name, NULL, NULL);
 
729
    gtk_action_group_add_action (priv->filter_action_group,
 
730
                                 GTK_ACTION (action));
 
731
 
 
732
    g_signal_connect (action,
 
733
                      "toggled",
 
734
                      G_CALLBACK (on_filter_toggled),
 
735
                      window);
 
736
 
 
737
    gtk_ui_manager_add_ui (ui, id, FILTER_PLACEHOLDER,
 
738
                           name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
 
739
    gtk_text_tag_table_add (table, 
 
740
                            logview_filter_get_tag (LOGVIEW_FILTER (l->data)));
 
741
 
 
742
    g_object_unref (action);
 
743
    g_free(name);
 
744
  }
 
745
 
 
746
  g_list_free (filters);
 
747
 
 
748
  priv->filter_merge_id = id;
 
749
}
 
750
 
 
751
static void
 
752
on_logview_filter_manager_response (GtkDialog *dialog, 
 
753
                                    gint response,
 
754
                                    LogviewWindow *logview)
 
755
{
 
756
  update_filter_menu (logview);
 
757
 
 
758
  g_list_free (logview->priv->active_filters);
 
759
  logview->priv->active_filters = NULL;
 
760
}
 
761
 
 
762
static void
 
763
logview_manage_filters (GtkAction *action, LogviewWindow *logview)
 
764
{
 
765
  GtkWidget *manager;
 
766
 
 
767
  manager = logview_filter_manager_new ();
 
768
 
 
769
  g_signal_connect (manager, "response", 
 
770
                    G_CALLBACK (on_logview_filter_manager_response),
 
771
                    logview);
 
772
  
 
773
  gtk_window_set_transient_for (GTK_WINDOW (manager),
 
774
                                GTK_WINDOW (logview));
 
775
  gtk_widget_show (GTK_WIDGET (manager));
 
776
}
 
777
 
 
778
static void
 
779
logview_about (GtkWidget *widget, GtkWidget *window)
 
780
{
 
781
  g_return_if_fail (GTK_IS_WINDOW (window));
 
782
 
 
783
  char *license_trans = g_strjoin ("\n\n", _(logview_about_license[0]),
 
784
                                   _(logview_about_license[1]),
 
785
                                   _(logview_about_license[2]), NULL);
 
786
 
 
787
  gtk_show_about_dialog (GTK_WINDOW (window),
 
788
                         "name",  _("System Log Viewer"),
 
789
                         "version", VERSION,
 
790
                         "copyright", "Copyright \xc2\xa9 1998-2008 Free Software Foundation, Inc.",
 
791
                         "license", license_trans,
 
792
                         "wrap-license", TRUE,
 
793
                         "comments", _("A system log viewer for GNOME."),
 
794
                         "authors", logview_about_authors,
 
795
                         "documenters", logview_about_documenters,
 
796
                         "translator_credits", strcmp (logview_about_translator_credits,
 
797
                                                       "translator-credits") != 0 ?
 
798
                                               logview_about_translator_credits : NULL,
 
799
                         "logo_icon_name", "logviewer",
 
800
                         NULL);
 
801
  g_free (license_trans);
 
802
 
 
803
  return;
 
804
}
 
805
 
 
806
static void
 
807
logview_toggle_statusbar (GtkAction *action, LogviewWindow *logview)
 
808
{
 
809
  if (GTK_WIDGET_VISIBLE (logview->priv->statusbar))
 
810
    gtk_widget_hide (logview->priv->statusbar);
 
811
  else
 
812
    gtk_widget_show (logview->priv->statusbar);
 
813
}
 
814
 
 
815
static void
 
816
logview_toggle_sidebar (GtkAction *action, LogviewWindow *logview)
 
817
{
 
818
  if (GTK_WIDGET_VISIBLE (logview->priv->sidebar))
 
819
    gtk_widget_hide (logview->priv->sidebar);
 
820
  else
 
821
    gtk_widget_show (logview->priv->sidebar);
 
822
}
 
823
 
 
824
static void
 
825
logview_toggle_match_filters (GtkToggleAction *action, LogviewWindow *logview)
 
826
{
 
827
  logview->priv->matches_only = gtk_toggle_action_get_active (action);
 
828
  filter_buffer (logview, 0);
 
829
}
 
830
 
 
831
/* GObject functions */
 
832
 
 
833
/* Menus */
 
834
 
 
835
static GtkActionEntry entries[] = {
 
836
    { "FileMenu", NULL, N_("_File"), NULL, NULL, NULL },
 
837
    { "EditMenu", NULL, N_("_Edit"), NULL, NULL, NULL },
 
838
    { "ViewMenu", NULL, N_("_View"), NULL, NULL, NULL },
 
839
    { "FilterMenu", NULL, N_("_Filters"), NULL, NULL, NULL },  
 
840
    { "HelpMenu", NULL, N_("_Help"), NULL, NULL, NULL },
 
841
 
 
842
    { "OpenLog", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a log from file"), 
 
843
      G_CALLBACK (logview_open_log) },
 
844
    { "CloseLog", GTK_STOCK_CLOSE, N_("_Close"), "<control>W", N_("Close this log"), 
 
845
      G_CALLBACK (logview_close_log) },
 
846
    { "Quit", GTK_STOCK_QUIT, N_("_Quit"), "<control>Q", N_("Quit the log viewer"), 
 
847
      G_CALLBACK (gtk_main_quit) },
 
848
 
 
849
    { "Copy", GTK_STOCK_COPY, N_("_Copy"), "<control>C", N_("Copy the selection"),
 
850
      G_CALLBACK (logview_copy) },
 
851
    { "SelectAll", NULL, N_("Select _All"), "<Control>A", N_("Select the entire log"),
 
852
      G_CALLBACK (logview_select_all) },
 
853
    { "Search", GTK_STOCK_FIND, N_("_Find..."), "<control>F", N_("Find a word or phrase in the log"),
 
854
      G_CALLBACK (logview_search) },
 
855
 
 
856
    { "ViewZoomIn", GTK_STOCK_ZOOM_IN, NULL, "<control>plus", N_("Bigger text size"),
 
857
      G_CALLBACK (logview_bigger_text)},
 
858
    { "ViewZoomOut", GTK_STOCK_ZOOM_OUT, NULL, "<control>minus", N_("Smaller text size"),
 
859
      G_CALLBACK (logview_smaller_text)},
 
860
    { "ViewZoom100", GTK_STOCK_ZOOM_100, NULL, "<control>0", N_("Normal text size"),
 
861
      G_CALLBACK (logview_normal_text)},
 
862
 
 
863
    { "FilterManage", NULL, N_("Manage Filters"), NULL, N_("Manage filters"),
 
864
      G_CALLBACK (logview_manage_filters)},
 
865
  
 
866
    { "HelpContents", GTK_STOCK_HELP, N_("_Contents"), "F1", N_("Open the help contents for the log viewer"), 
 
867
      G_CALLBACK (logview_help) },
 
868
    { "AboutAction", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("Show the about dialog for the log viewer"), 
 
869
      G_CALLBACK (logview_about) },
 
870
};
 
871
 
 
872
static GtkToggleActionEntry toggle_entries[] = {
 
873
    { "ShowStatusBar", NULL, N_("_Statusbar"), NULL, N_("Show Status Bar"),
 
874
      G_CALLBACK (logview_toggle_statusbar), TRUE },
 
875
    { "ShowSidebar", NULL, N_("Side _Pane"), "F9", N_("Show Side Pane"), 
 
876
      G_CALLBACK (logview_toggle_sidebar), TRUE }, 
 
877
    { "FilterMatchOnly", NULL, N_("Show matches only"), NULL, N_("Only show lines that match one of the given filters"),
 
878
      G_CALLBACK (logview_toggle_match_filters), FALSE}
 
879
};
 
880
 
 
881
static gboolean 
 
882
window_size_changed_cb (GtkWidget *widget, GdkEventConfigure *event, 
 
883
                        gpointer data)
 
884
{
 
885
  LogviewWindow *window = data;
 
886
 
 
887
  logview_prefs_store_window_size (window->priv->prefs,
 
888
                                   event->width, event->height);
 
889
 
 
890
  return FALSE;
 
891
}
 
892
 
 
893
static void
 
894
real_select_day (LogviewWindow *logview,
 
895
                 GDate *date, int first_line, int last_line)
 
896
{
 
897
  GtkTextBuffer *buffer;
 
898
  GtkTextIter start_iter, end_iter, start_vis, end_vis;
 
899
  GdkRectangle visible_rect;
 
900
 
 
901
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (logview->priv->text_view));
 
902
 
 
903
  gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
 
904
  gtk_text_buffer_get_iter_at_line (buffer, &start_vis, first_line);
 
905
  gtk_text_buffer_get_iter_at_line (buffer, &end_vis, last_line + 1);
 
906
 
 
907
  /* clear all previous invisible tags */
 
908
  gtk_text_buffer_remove_tag_by_name (buffer, "invisible",
 
909
                                      &start_iter, &end_iter);
 
910
 
 
911
  gtk_text_buffer_apply_tag_by_name (buffer, "invisible",
 
912
                                     &start_iter, &start_vis);
 
913
  gtk_text_buffer_apply_tag_by_name (buffer, "invisible",
 
914
                                     &end_vis, &end_iter);
 
915
 
 
916
  /* FIXME: why is this needed to update the view when selecting a day back? */
 
917
  gtk_text_view_get_visible_rect (GTK_TEXT_VIEW (logview->priv->text_view),
 
918
                                  &visible_rect);
 
919
  gdk_window_invalidate_rect (gtk_widget_get_window (logview->priv->text_view),
 
920
                              &visible_rect, TRUE);
 
921
}
 
922
 
 
923
static void
 
924
loglist_day_selected_cb (LogviewLoglist *loglist,
 
925
                         Day *day,
 
926
                         gpointer user_data)
 
927
{
 
928
  LogviewWindow *logview = user_data;
 
929
 
 
930
  real_select_day (logview, day->date, day->first_line, day->last_line);
 
931
}
 
932
 
 
933
static void
 
934
loglist_day_cleared_cb (LogviewLoglist *loglist,
 
935
                        gpointer user_data)
 
936
{
 
937
  LogviewWindow *logview = user_data;
 
938
  GtkTextBuffer *buffer;
 
939
  GtkTextIter start, end;
 
940
 
 
941
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (logview->priv->text_view));
 
942
  gtk_text_buffer_get_bounds (buffer, &start, &end);
 
943
 
 
944
  /* clear all previous invisible tags */
 
945
  gtk_text_buffer_remove_tag_by_name (buffer, "invisible",
 
946
                                      &start, &end);
 
947
}
 
948
 
 
949
static void
 
950
logview_window_select_date (LogviewWindow *logview, GDate *date)
 
951
{
 
952
  LogviewLog *log;
 
953
  GSList *days, *l;
 
954
  Day *day;
 
955
  gboolean found = FALSE;
 
956
 
 
957
  log = logview_manager_get_active_log (logview->priv->manager);
 
958
 
 
959
  for (l = days; l; l = l->next) {
 
960
    day = l->data;
 
961
    if (g_date_compare (date, day->date) == 0) {
 
962
      found = TRUE;
 
963
      break;
 
964
    }
 
965
  }
 
966
 
 
967
  if (found) {
 
968
    real_select_day (logview, day->date, day->first_line, day->last_line);
 
969
  }   
 
970
}
 
971
 
 
972
static void
 
973
log_monitor_changed_cb (LogviewLog *log,
 
974
                        gpointer user_data)
 
975
{
 
976
  /* reschedule a read */
 
977
  logview_log_read_new_lines (log, (LogviewNewLinesCallback) read_new_lines_cb,
 
978
                              user_data);
 
979
}
 
980
 
 
981
static void
 
982
paint_timestamps (GtkTextBuffer *buffer, int old_line_count,
 
983
                  GSList *days)
 
984
{
 
985
  GSList *l;
 
986
 
 
987
  for (l = days; l; l = l->next) {
 
988
    Day *day = l->data;
 
989
 
 
990
    _gtk_text_buffer_apply_tag_to_rectangle (buffer,
 
991
                                             old_line_count + day->first_line - 1,
 
992
                                             old_line_count + day->last_line,
 
993
                                             0, day->timestamp_len, "gray");
 
994
  }
 
995
}
 
996
 
 
997
static void
 
998
read_new_lines_cb (LogviewLog *log,
 
999
                   const char **lines,
 
1000
                   GSList *new_days,
 
1001
                   GError *error,
 
1002
                   gpointer user_data)
 
1003
{
 
1004
  LogviewWindow *window = user_data;
 
1005
  GtkTextBuffer *buffer;
 
1006
  gboolean boldify = FALSE;
 
1007
  int i, old_line_count, filter_start_line;
 
1008
  GtkTextIter iter, start;
 
1009
  GtkTextMark *mark;
 
1010
  char *converted, *primary;
 
1011
  gsize len;
 
1012
 
 
1013
  if (error != NULL) {
 
1014
    primary = g_strdup_printf (_("Can't read from \"%s\""),
 
1015
                               logview_log_get_display_name (log));
 
1016
    logview_window_add_error (window, primary, error->message);
 
1017
    g_free (primary);
 
1018
 
 
1019
    return;
 
1020
  }
 
1021
 
 
1022
  if (lines == NULL) {
 
1023
    /* there's no error, but no lines have been read */
 
1024
    return;
 
1025
  }
 
1026
 
 
1027
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->priv->text_view));
 
1028
  old_line_count = gtk_text_buffer_get_line_count (buffer);
 
1029
  filter_start_line = old_line_count > 0 ? (old_line_count - 1) : 0;
 
1030
 
 
1031
  if (gtk_text_buffer_get_char_count (buffer) != 0) {
 
1032
    boldify = TRUE;
 
1033
  }
 
1034
 
 
1035
  gtk_text_buffer_get_end_iter (buffer, &iter);
 
1036
 
 
1037
  if (boldify) {
 
1038
    mark = gtk_text_buffer_create_mark (buffer, NULL, &iter, TRUE);
 
1039
  }
 
1040
 
 
1041
  for (i = 0; lines[i]; i++) {
 
1042
    len = strlen (lines[i]);
 
1043
 
 
1044
    if (!g_utf8_validate (lines[i], len, NULL)) {
 
1045
      converted = g_locale_to_utf8 (lines[i], (gssize) len, NULL, &len, NULL);
 
1046
      gtk_text_buffer_insert (buffer, &iter, lines[i], len);
 
1047
    } else {
 
1048
      gtk_text_buffer_insert (buffer, &iter, lines[i], strlen (lines[i]));
 
1049
    }
 
1050
 
 
1051
    gtk_text_iter_forward_to_end (&iter);
 
1052
    gtk_text_buffer_insert (buffer, &iter, "\n", 1);
 
1053
    gtk_text_iter_forward_char (&iter);
 
1054
  }
 
1055
 
 
1056
  if (boldify) {
 
1057
    gtk_text_buffer_get_iter_at_mark (buffer, &start, mark);
 
1058
    gtk_text_buffer_apply_tag_by_name (buffer, "bold", &start, &iter);
 
1059
    gtk_text_buffer_delete_mark (buffer, mark);
 
1060
  }
 
1061
  filter_buffer (window, filter_start_line);
 
1062
 
 
1063
  gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (window->priv->text_view),
 
1064
                                &iter, 0.0, FALSE, 0.0, 0.0);
 
1065
 
 
1066
  paint_timestamps (buffer, old_line_count, new_days);
 
1067
 
 
1068
  if (window->priv->monitor_id == 0) {
 
1069
    window->priv->monitor_id = g_signal_connect (log, "log-changed",
 
1070
                                                 G_CALLBACK (log_monitor_changed_cb), window);
 
1071
  }
 
1072
 
 
1073
  logview_update_statusbar (window, log);
 
1074
  logview_loglist_update_lines (LOGVIEW_LOGLIST (window->priv->loglist), log);
 
1075
}
 
1076
 
 
1077
static void
 
1078
active_log_changed_cb (LogviewManager *manager,
 
1079
                       LogviewLog *log,
 
1080
                       LogviewLog *old_log,
 
1081
                       gpointer data)
 
1082
{
 
1083
  LogviewWindow *window = data;
 
1084
  const char **lines;
 
1085
  GtkTextBuffer *buffer;
 
1086
 
 
1087
  findbar_close_cb (LOGVIEW_FINDBAR (window->priv->find_bar),
 
1088
                    window);
 
1089
 
 
1090
  logview_set_window_title (window, logview_log_get_display_name (log));
 
1091
 
 
1092
  if (window->priv->monitor_id) {
 
1093
    g_signal_handler_disconnect (old_log, window->priv->monitor_id);
 
1094
    window->priv->monitor_id = 0;
 
1095
  }
 
1096
 
 
1097
  lines = logview_log_get_cached_lines (log);
 
1098
  buffer = gtk_text_buffer_new (window->priv->tag_table);
 
1099
 
 
1100
  if (lines != NULL) {
 
1101
    int i;
 
1102
    GtkTextIter iter;
 
1103
 
 
1104
    /* update the text view to show the current lines */
 
1105
    gtk_text_buffer_get_end_iter (buffer, &iter);
 
1106
 
 
1107
    for (i = 0; lines[i]; i++) {
 
1108
      gtk_text_buffer_insert (buffer, &iter, lines[i], strlen (lines[i]));
 
1109
      gtk_text_iter_forward_to_end (&iter);
 
1110
      gtk_text_buffer_insert (buffer, &iter, "\n", 1);
 
1111
      gtk_text_iter_forward_char (&iter);
 
1112
    }
 
1113
 
 
1114
    paint_timestamps (buffer, 1, logview_log_get_days_for_cached_lines (log));
 
1115
  }
 
1116
 
 
1117
  if (lines == NULL || logview_log_has_new_lines (log)) {
 
1118
    /* read the new lines */
 
1119
    logview_log_read_new_lines (log, (LogviewNewLinesCallback) read_new_lines_cb, window);
 
1120
  } else {
 
1121
    /* start now monitoring the log for changes */
 
1122
    window->priv->monitor_id = g_signal_connect (log, "log-changed",
 
1123
                                                 G_CALLBACK (log_monitor_changed_cb), window);
 
1124
  }
 
1125
 
 
1126
  /* we set the buffer to the view anyway;
 
1127
   * if there are no lines it will be empty for the duration of the thread
 
1128
   * and will help us to distinguish the two cases of the following if
 
1129
   * cause in the callback.
 
1130
   */
 
1131
  gtk_text_view_set_buffer (GTK_TEXT_VIEW (window->priv->text_view), buffer);
 
1132
  g_object_unref (buffer);
 
1133
}
 
1134
 
 
1135
static void
 
1136
font_changed_cb (LogviewPrefs *prefs,
 
1137
                 const char *font_name,
 
1138
                 gpointer user_data)
 
1139
{
 
1140
  LogviewWindow *window = user_data;
 
1141
 
 
1142
  logview_set_font (window, font_name);
 
1143
}
 
1144
 
 
1145
static void
 
1146
tearoff_changed_cb (LogviewPrefs *prefs,
 
1147
                    gboolean have_tearoffs,
 
1148
                    gpointer user_data)
 
1149
{
 
1150
  LogviewWindow *window = user_data;
 
1151
 
 
1152
  gtk_ui_manager_set_add_tearoffs (window->priv->ui_manager, have_tearoffs);
 
1153
}
 
1154
 
 
1155
static void
 
1156
style_set_cb (GtkWidget *widget,
 
1157
              GtkStyle *prev,
 
1158
              gpointer user_data)
 
1159
{
 
1160
  LogviewWindow *logview = user_data;
 
1161
  GtkStyle *style = gtk_widget_get_style (widget);
 
1162
 
 
1163
  populate_style_tag_table (style, logview->priv->tag_table);
 
1164
}
 
1165
 
 
1166
static const struct {
 
1167
  guint keyval;
 
1168
  GdkModifierType modifier;
 
1169
  const gchar *action;
 
1170
} extra_keybindings [] = {
 
1171
  { GDK_KP_Add,      GDK_CONTROL_MASK, "ViewZoomIn" },
 
1172
  { GDK_KP_Subtract, GDK_CONTROL_MASK, "ViewZoomOut" },
 
1173
  { GDK_KP_0,        GDK_CONTROL_MASK, "ViewZoom100" }
 
1174
};
 
1175
 
 
1176
static gboolean
 
1177
key_press_event_cb (GtkWidget *widget,
 
1178
                    GdkEventKey *event,
 
1179
                    gpointer user_data)
 
1180
{
 
1181
  LogviewWindow *window = user_data;
 
1182
  guint modifier = event->state & gtk_accelerator_get_default_mod_mask ();
 
1183
  GtkAction *action;
 
1184
  int i;
 
1185
 
 
1186
  /* handle accelerators that we want bound, but aren't associated with
 
1187
   * an action */
 
1188
  for (i = 0; i < G_N_ELEMENTS (extra_keybindings); i++) {
 
1189
    if (event->keyval == extra_keybindings[i].keyval &&
 
1190
        modifier == extra_keybindings[i].modifier) {
 
1191
 
 
1192
      action = gtk_action_group_get_action (window->priv->action_group,
 
1193
                                            extra_keybindings[i].action);
 
1194
      gtk_action_activate (action);
 
1195
      return TRUE;
 
1196
    }
 
1197
  }
 
1198
 
 
1199
  return FALSE;
 
1200
}
 
1201
 
 
1202
/* adapted from GEdit */
 
1203
 
 
1204
static void
 
1205
message_area_create_error_box (LogviewWindow *window,
 
1206
                               GtkWidget *message_area)
 
1207
{
 
1208
  GtkWidget *hbox_content;
 
1209
  GtkWidget *image;
 
1210
  GtkWidget *vbox;
 
1211
  GtkWidget *primary_label;
 
1212
  GtkWidget *secondary_label;
 
1213
  
 
1214
  hbox_content = gtk_hbox_new (FALSE, 8);
 
1215
  gtk_widget_show (hbox_content);
 
1216
 
 
1217
  image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_ERROR,
 
1218
                                    GTK_ICON_SIZE_DIALOG);
 
1219
  gtk_widget_show (image);
 
1220
  gtk_box_pack_start (GTK_BOX (hbox_content), image, FALSE, FALSE, 0);
 
1221
  gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0);
 
1222
 
 
1223
  vbox = gtk_vbox_new (FALSE, 6);
 
1224
  gtk_widget_show (vbox);
 
1225
  gtk_box_pack_start (GTK_BOX (hbox_content), vbox, TRUE, TRUE, 0);
 
1226
 
 
1227
  primary_label = gtk_label_new (NULL);
 
1228
  gtk_widget_show (primary_label);
 
1229
  gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0);
 
1230
  gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE);
 
1231
  gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE);
 
1232
  gtk_misc_set_alignment (GTK_MISC (primary_label), 0, 0.5);
 
1233
  GTK_WIDGET_SET_FLAGS (primary_label, GTK_CAN_FOCUS);
 
1234
  gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE);
 
1235
 
 
1236
  window->priv->message_primary = primary_label;
 
1237
 
 
1238
  secondary_label = gtk_label_new (NULL);
 
1239
  gtk_widget_show (secondary_label);
 
1240
  gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0);
 
1241
  GTK_WIDGET_SET_FLAGS (secondary_label, GTK_CAN_FOCUS);
 
1242
  gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE);
 
1243
  gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE);
 
1244
  gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE);
 
1245
  gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5);
 
1246
 
 
1247
  window->priv->message_secondary = secondary_label;
 
1248
 
 
1249
  gtk_container_add
 
1250
      (GTK_CONTAINER (gtk_info_bar_get_content_area
 
1251
                      (GTK_INFO_BAR (message_area))),
 
1252
       hbox_content);
 
1253
}
 
1254
 
 
1255
static void
 
1256
message_area_set_labels (LogviewWindow *window,
 
1257
                         const char *primary,
 
1258
                         const char *secondary)
 
1259
{
 
1260
  char *primary_markup, *secondary_markup;
 
1261
 
 
1262
  primary_markup = g_markup_printf_escaped ("<b>%s</b>", primary);
 
1263
  secondary_markup = g_markup_printf_escaped ("<small>%s</small>",
 
1264
                                              secondary);
 
1265
 
 
1266
  gtk_label_set_markup (GTK_LABEL (window->priv->message_primary),
 
1267
                        primary_markup);
 
1268
  gtk_label_set_markup (GTK_LABEL (window->priv->message_secondary),
 
1269
                        secondary_markup);
 
1270
 
 
1271
  g_free (primary_markup);
 
1272
  g_free (secondary_markup);
 
1273
}
 
1274
 
 
1275
static void
 
1276
message_area_response_cb (GtkInfoBar *message_area,
 
1277
                          int response_id, gpointer user_data)
 
1278
{
 
1279
  LogviewWindow *window = user_data;
 
1280
 
 
1281
  gtk_widget_hide (GTK_WIDGET (message_area));
 
1282
 
 
1283
  g_signal_handlers_disconnect_by_func (message_area,
 
1284
                                        message_area_response_cb,
 
1285
                                        user_data);
 
1286
}
 
1287
 
 
1288
static void
 
1289
logview_window_finalize (GObject *object)
 
1290
{
 
1291
  LogviewWindow *logview = LOGVIEW_WINDOW (object);
 
1292
 
 
1293
  g_object_unref (logview->priv->ui_manager);
 
1294
  G_OBJECT_CLASS (logview_window_parent_class)->finalize (object);
 
1295
}
 
1296
 
 
1297
static void
 
1298
logview_window_init (LogviewWindow *logview)
 
1299
{
 
1300
  gint i;
 
1301
  GtkActionGroup *action_group;
 
1302
  GtkAccelGroup *accel_group;
 
1303
  GError *error = NULL;
 
1304
  GtkWidget *hpaned, *main_view, *scrolled, *vbox, *w;
 
1305
  PangoContext *context;
 
1306
  PangoFontDescription *fontdesc;
 
1307
  gchar *monospace_font_name;
 
1308
  LogviewWindowPrivate *priv;
 
1309
  int width, height;
 
1310
  gboolean res;
 
1311
 
 
1312
  priv = logview->priv = GET_PRIVATE (logview);
 
1313
  priv->prefs = logview_prefs_get ();
 
1314
  priv->manager = logview_manager_get ();
 
1315
  priv->monitor_id = 0;
 
1316
 
 
1317
  logview_prefs_get_stored_window_size (priv->prefs, &width, &height);
 
1318
  gtk_window_set_default_size (GTK_WINDOW (logview), width, height);
 
1319
 
 
1320
  vbox = gtk_vbox_new (FALSE, 0);
 
1321
  gtk_container_add (GTK_CONTAINER (logview), vbox);
 
1322
 
 
1323
  /* create menus */
 
1324
  action_group = gtk_action_group_new ("LogviewMenuActions");
 
1325
  gtk_action_group_set_translation_domain (action_group, NULL);
 
1326
  gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), logview);
 
1327
  gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), logview);
 
1328
  priv->action_group = action_group;
 
1329
 
 
1330
  priv->ui_manager = gtk_ui_manager_new ();
 
1331
 
 
1332
  gtk_ui_manager_insert_action_group (priv->ui_manager, action_group, 0);
 
1333
  accel_group = gtk_ui_manager_get_accel_group (priv->ui_manager);
 
1334
  gtk_window_add_accel_group (GTK_WINDOW (logview), accel_group);
 
1335
 
 
1336
  res = gtk_ui_manager_add_ui_from_file (priv->ui_manager,
 
1337
                                         LOGVIEW_DATADIR "/logview-toolbar.xml",
 
1338
                                         &error);
 
1339
 
 
1340
  if (res == FALSE) {
 
1341
    priv->ui_manager = NULL;
 
1342
    g_critical ("Can't load the UI description: %s", error->message);
 
1343
    g_error_free (error);
 
1344
    return;
 
1345
  }
 
1346
 
 
1347
  gtk_ui_manager_set_add_tearoffs (priv->ui_manager,
 
1348
                                   logview_prefs_get_have_tearoff (priv->prefs));
 
1349
 
 
1350
  w = gtk_ui_manager_get_widget (priv->ui_manager, "/LogviewMenu");
 
1351
  gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0);
 
1352
  gtk_widget_show (w);
 
1353
  
 
1354
  /* panes */
 
1355
  hpaned = gtk_hpaned_new ();
 
1356
  gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
 
1357
  priv->hpaned = hpaned;
 
1358
  gtk_widget_show (hpaned);
 
1359
 
 
1360
  /* first pane : sidebar (list of logs) */
 
1361
  priv->sidebar = gtk_vbox_new (FALSE, 0);
 
1362
  gtk_widget_show (priv->sidebar);
 
1363
 
 
1364
  /* first pane: log list */
 
1365
  w = gtk_scrolled_window_new (NULL, NULL);
 
1366
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w),
 
1367
                                  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
 
1368
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w),
 
1369
                                       GTK_SHADOW_ETCHED_IN);
 
1370
 
 
1371
  priv->loglist = logview_loglist_new ();
 
1372
  gtk_container_add (GTK_CONTAINER (w), priv->loglist);
 
1373
  gtk_box_pack_start (GTK_BOX (priv->sidebar), w, TRUE, TRUE, 0);
 
1374
  gtk_paned_pack1 (GTK_PANED (hpaned), priv->sidebar, FALSE, FALSE);
 
1375
  gtk_widget_show (w);
 
1376
  gtk_widget_show (priv->loglist);
 
1377
 
 
1378
  g_signal_connect (priv->loglist, "day_selected",
 
1379
                    G_CALLBACK (loglist_day_selected_cb), logview);
 
1380
  g_signal_connect (priv->loglist, "day_cleared",
 
1381
                    G_CALLBACK (loglist_day_cleared_cb), logview);
 
1382
 
 
1383
  /* second pane: log */
 
1384
  main_view = gtk_vbox_new (FALSE, 0);
 
1385
  gtk_paned_pack2 (GTK_PANED (hpaned), main_view, TRUE, TRUE);
 
1386
 
 
1387
  /* second pane: error message area */
 
1388
  priv->message_area = gtk_info_bar_new ();
 
1389
  message_area_create_error_box (logview, priv->message_area);
 
1390
  gtk_info_bar_add_button (GTK_INFO_BAR (priv->message_area),
 
1391
                           GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
 
1392
  gtk_box_pack_start (GTK_BOX (main_view), priv->message_area, FALSE, FALSE, 0);
 
1393
 
 
1394
  /* second pane: text view */
 
1395
  w = gtk_scrolled_window_new (NULL, NULL);
 
1396
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w),
 
1397
                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 
1398
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w), GTK_SHADOW_IN);
 
1399
  gtk_box_pack_start (GTK_BOX (main_view), w, TRUE, TRUE, 0);
 
1400
  gtk_widget_show (w);
 
1401
 
 
1402
  priv->tag_table = gtk_text_tag_table_new ();
 
1403
  populate_tag_table (priv->tag_table);
 
1404
  priv->text_view = gtk_text_view_new ();
 
1405
  g_object_set (priv->text_view, "editable", FALSE, NULL);
 
1406
 
 
1407
  gtk_container_add (GTK_CONTAINER (w), priv->text_view);
 
1408
  gtk_widget_show (priv->text_view);
 
1409
 
 
1410
  /* use the desktop monospace font */
 
1411
  monospace_font_name = logview_prefs_get_monospace_font_name (priv->prefs);
 
1412
  logview_set_font (logview, monospace_font_name);
 
1413
  g_free (monospace_font_name);
 
1414
 
 
1415
  /* remember the original font size */
 
1416
  context = gtk_widget_get_pango_context (priv->text_view);
 
1417
  fontdesc = pango_context_get_font_description (context);
 
1418
  priv->original_fontsize = pango_font_description_get_size (fontdesc) / PANGO_SCALE;
 
1419
 
 
1420
  /* restore saved zoom */
 
1421
  priv->fontsize = logview_prefs_get_stored_fontsize (priv->prefs);
 
1422
 
 
1423
  if (priv->fontsize <= 0) {
 
1424
    /* restore the default */
 
1425
    logview_normal_text (NULL, logview);
 
1426
  } else {
 
1427
    logview_set_fontsize (logview, FALSE);
 
1428
  }
 
1429
 
 
1430
  /* version selector */
 
1431
  priv->version_bar = gtk_hbox_new (FALSE, 0);
 
1432
  gtk_container_set_border_width (GTK_CONTAINER (priv->version_bar), 3);
 
1433
  priv->version_selector = gtk_combo_box_new_text ();
 
1434
  g_signal_connect (priv->version_selector, "changed",
 
1435
                    G_CALLBACK (logview_version_selector_changed), logview);
 
1436
  w = gtk_label_new (_("Version: "));
 
1437
 
 
1438
  gtk_box_pack_end (GTK_BOX (priv->version_bar), priv->version_selector, FALSE, FALSE, 0);
 
1439
  gtk_box_pack_end (GTK_BOX (priv->version_bar), w, FALSE, FALSE, 0);
 
1440
  gtk_box_pack_end (GTK_BOX (main_view), priv->version_bar, FALSE, FALSE, 0);
 
1441
 
 
1442
  priv->find_bar = logview_findbar_new ();
 
1443
  gtk_box_pack_end (GTK_BOX (main_view), priv->find_bar, FALSE, FALSE, 0);
 
1444
 
 
1445
  g_signal_connect (priv->find_bar, "previous",
 
1446
                    G_CALLBACK (findbar_previous_cb), logview);
 
1447
  g_signal_connect (priv->find_bar, "next",
 
1448
                    G_CALLBACK (findbar_next_cb), logview);
 
1449
  g_signal_connect (priv->find_bar, "text_changed",
 
1450
                    G_CALLBACK (findbar_text_changed_cb), logview);
 
1451
  g_signal_connect (priv->find_bar, "close",
 
1452
                    G_CALLBACK (findbar_close_cb), logview);
 
1453
 
 
1454
  /* signal handlers
 
1455
   * - first is used to remember/restore the window size on quit.
 
1456
   */
 
1457
  g_signal_connect (logview, "configure_event",
 
1458
                    G_CALLBACK (window_size_changed_cb), logview);
 
1459
  g_signal_connect (priv->prefs, "system-font-changed",
 
1460
                    G_CALLBACK (font_changed_cb), logview);
 
1461
  g_signal_connect (priv->prefs, "have-tearoff-changed",
 
1462
                    G_CALLBACK (tearoff_changed_cb), logview);
 
1463
  g_signal_connect (priv->manager, "active-changed",
 
1464
                    G_CALLBACK (active_log_changed_cb), logview);
 
1465
  g_signal_connect (logview, "style-set",
 
1466
                    G_CALLBACK (style_set_cb), logview);
 
1467
  g_signal_connect (logview, "key-press-event",
 
1468
                    G_CALLBACK (key_press_event_cb), logview);
 
1469
 
 
1470
  /* status area at bottom */
 
1471
  priv->statusbar = gtk_statusbar_new ();
 
1472
  gtk_box_pack_start (GTK_BOX (vbox), priv->statusbar, FALSE, FALSE, 0);
 
1473
  gtk_widget_show (priv->statusbar);
 
1474
 
 
1475
  /* Filter menu */
 
1476
  priv->filter_action_group = gtk_action_group_new ("ActionGroupFilter");
 
1477
  gtk_ui_manager_insert_action_group (priv->ui_manager, priv->filter_action_group,
 
1478
                                      1);
 
1479
  priv->active_filters = NULL;
 
1480
  update_filter_menu (logview);
 
1481
  
 
1482
  gtk_widget_show (vbox);
 
1483
  gtk_widget_show (main_view);
 
1484
}
 
1485
 
 
1486
static void
 
1487
logview_window_class_init (LogviewWindowClass *klass)
 
1488
{
 
1489
  GObjectClass *object_class = (GObjectClass *) klass;
 
1490
 
 
1491
  object_class->finalize = logview_window_finalize;
 
1492
 
 
1493
  g_type_class_add_private (klass, sizeof (LogviewWindowPrivate));
 
1494
}
 
1495
 
 
1496
/* public methods */
 
1497
 
 
1498
GtkWidget *
 
1499
logview_window_new ()
 
1500
{
 
1501
  LogviewWindow *logview;
 
1502
 
 
1503
  logview = g_object_new (LOGVIEW_TYPE_WINDOW, NULL);
 
1504
 
 
1505
  if (logview->priv->ui_manager == NULL) {
 
1506
    return NULL;
 
1507
  }
 
1508
 
 
1509
  return GTK_WIDGET (logview);
 
1510
}
 
1511
 
 
1512
void
 
1513
logview_window_add_error (LogviewWindow *window,
 
1514
                          const char *primary,
 
1515
                          const char *secondary)
 
1516
{
 
1517
  LogviewWindowPrivate *priv;
 
1518
 
 
1519
  g_assert (LOGVIEW_IS_WINDOW (window));
 
1520
  priv = window->priv;
 
1521
 
 
1522
  message_area_set_labels (window,
 
1523
                           primary, secondary); 
 
1524
 
 
1525
  gtk_widget_show (priv->message_area);
 
1526
 
 
1527
  g_signal_connect (priv->message_area, "response",
 
1528
                    G_CALLBACK (message_area_response_cb), window);
 
1529
}
 
1530
 
 
1531
void
 
1532
logview_window_add_errors (LogviewWindow *window,
 
1533
                           GPtrArray *errors)
 
1534
{
 
1535
  char *primary, *secondary;
 
1536
  GString *str;
 
1537
  char **err;
 
1538
  int i;
 
1539
 
 
1540
  g_assert (LOGVIEW_IS_WINDOW (window));
 
1541
  g_assert (errors->len > 1);
 
1542
 
 
1543
  primary = g_strdup (_("Could not open the following files:"));
 
1544
  str = g_string_new (NULL);
 
1545
 
 
1546
  for (i = 0; i < errors->len; i++) {
 
1547
    err = (char **) g_ptr_array_index (errors, i);
 
1548
    g_string_append (str, err[0]);
 
1549
    g_string_append (str, ": ");
 
1550
    g_string_append (str, err[1]);
 
1551
    g_string_append (str, "\n");
 
1552
  }
 
1553
 
 
1554
  secondary = g_string_free (str, FALSE);
 
1555
 
 
1556
  message_area_set_labels (window, primary, secondary);
 
1557
 
 
1558
  gtk_widget_show (window->priv->message_area);
 
1559
 
 
1560
  g_signal_connect (window->priv->message_area, "response",
 
1561
                    G_CALLBACK (message_area_response_cb), window);
 
1562
 
 
1563
  g_free (primary);
 
1564
  g_free (secondary);
 
1565
}
 
1566
 
 
1567