message-view: bgo #727634 - Cannot copy build output
[anjuta.git] / plugins / message-view / message-view.c
bloba9846322b16863caf341baa6d1136ae91c7617a8
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /* (c) Johannes Schmid 2003
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Library General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 #include <libanjuta/anjuta-utils.h>
19 #include <libanjuta/anjuta-debug.h>
20 #include <libanjuta/anjuta-preferences.h>
21 #include <libanjuta/interfaces/ianjuta-message-view.h>
23 #include "message-view.h"
24 #define MESSAGE_TYPE message_get_type()
26 #define PREFERENCES_SCHEMA "org.gnome.anjuta.plugins.message-manager"
27 #define COLOR_ERROR "color-error"
28 #define COLOR_WARNING "color-warning"
30 struct _MessageViewPrivate
32 //guint num_messages;
33 gchar *line_buffer;
35 GtkWidget *tree_view;
36 GtkTreeModel *model;
37 GtkTreeModel *filter;
39 GtkWidget *popup_menu;
41 gint adj_chgd_hdlr;
43 /* Messages filter */
44 MessageViewFlags flags;
45 gint normal_count;
46 gint warn_count;
47 gint error_count;
48 gint info_count;
50 /* Properties */
51 gchar *label;
52 gchar *pixmap;
53 gboolean highlite;
55 GSettings* settings;
58 typedef struct
60 IAnjutaMessageViewType type;
61 gchar *summary;
62 gchar *details;
64 } Message;
66 enum
68 COLUMN_COLOR = 0,
69 COLUMN_SUMMARY,
70 COLUMN_MESSAGE,
71 COLUMN_PIXBUF,
72 N_COLUMNS
75 enum
77 MV_PROP_ID = 0,
78 MV_PROP_LABEL,
79 MV_PROP_PIXMAP,
80 MV_PROP_HIGHLITE
83 static gpointer parent_class;
85 static void prefs_init (MessageView *mview);
86 static void prefs_finalize (MessageView *mview);
88 static gboolean
89 message_view_tree_view_filter (GtkTreeModel *model,
90 GtkTreeIter *iter,
91 gpointer data);
93 /* Message object creation, copy and freeing */
94 static Message*
95 message_new (IAnjutaMessageViewType type, const gchar *summary,
96 const gchar *details)
98 /* DEBUG_PRINT ("%s", "Creating message"); */
99 Message *message = g_new0 (Message, 1);
100 message->type = type;
101 if (summary)
102 message->summary = g_strdup (summary);
103 if (details)
104 message->details = g_strdup (details);
105 return message;
108 static Message*
109 message_copy (const Message *src)
111 return message_new (src->type, src->summary, src->details);
114 static void
115 message_free (Message *message)
117 /* DEBUG_PRINT ("%s", "Freeing message"); */
118 g_free (message->summary);
119 g_free (message->details);
120 g_free (message);
123 static gboolean
124 message_serialize (Message *message, AnjutaSerializer *serializer)
126 if (!anjuta_serializer_write_int (serializer, "type",
127 message->type))
128 return FALSE;
129 if (!anjuta_serializer_write_string (serializer, "summary",
130 message->summary))
131 return FALSE;
132 if (!anjuta_serializer_write_string (serializer, "details",
133 message->details))
134 return FALSE;
135 return TRUE;
138 static gboolean
139 message_deserialize (Message *message, AnjutaSerializer *serializer)
141 gint type;
142 if (!anjuta_serializer_read_int (serializer, "type",
143 &type))
144 return FALSE;
145 message->type = type;
146 if (!anjuta_serializer_read_string (serializer, "summary",
147 &message->summary, TRUE))
148 return FALSE;
149 if (!anjuta_serializer_read_string (serializer, "details",
150 &message->details, TRUE))
151 return FALSE;
152 return TRUE;
155 static GType
156 message_get_type (void)
158 static GType type = 0;
159 if (!type)
161 type = g_boxed_type_register_static ("MessageViewMessage",
162 (GBoxedCopyFunc) message_copy,
163 (GBoxedFreeFunc) message_free);
165 return type;
168 /* Utility functions */
169 /* Adds the char c to the string str */
170 static void
171 add_char(gchar** str, gchar c)
173 gchar* buffer;
175 g_return_if_fail(str != NULL);
177 buffer = g_strdup_printf("%s%c", *str, c);
178 g_free(*str);
179 *str = buffer;
182 static gchar*
183 escape_string (const gchar *str)
185 GString *gstr;
186 const gchar *iter;
188 gstr = g_string_new ("");
189 iter = str;
190 while (*iter != '\0')
192 if (*iter == '>')
193 gstr = g_string_append (gstr, "&gt;");
194 else if (*iter == '<')
195 gstr = g_string_append (gstr, "&lt;");
196 else if (*iter == '&')
197 gstr = g_string_append (gstr, "&amp;");
198 else
199 gstr = g_string_append_c (gstr, *iter);
200 iter++;
202 return g_string_free (gstr, FALSE);
205 static gboolean
206 message_view_query_tooltip (GtkWidget* widget, gint x, gint y, gboolean keyboard,
207 GtkTooltip* tooltip)
209 GtkTreePath *path;
210 GtkTreeIter iter;
211 GtkTreeModel *model;
212 MessageView* view = MESSAGE_VIEW(widget);
214 model = view->privat->model;
216 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW(view->privat->tree_view),
217 x, y, &path, NULL, NULL, NULL))
219 Message *message;
220 gchar *text, *title, *desc;
222 gtk_tree_model_get_iter (model, &iter, path);
223 gtk_tree_model_get (model, &iter, COLUMN_MESSAGE, &message, -1);
224 gtk_tree_path_free(path);
226 if (!message->details || !message->summary ||
227 strlen (message->details) <= 0 ||
228 strlen (message->summary) <= 0)
229 return FALSE;
231 title = escape_string (message->summary);
232 desc = escape_string (message->details);
233 text = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
235 g_free (title);
236 g_free (desc);
238 gtk_tooltip_set_markup (tooltip, text);
239 g_free (text);
240 return TRUE;
242 return FALSE;
245 /* MessageView signal callbacks */
246 /* Send a signal if a message was double-clicked or ENTER or SPACE was pressed */
247 static gboolean
248 on_message_event (GObject* object, GdkEvent* event, gpointer data)
250 g_return_val_if_fail(object != NULL, FALSE);
251 g_return_val_if_fail(event != NULL, FALSE);
252 g_return_val_if_fail(data != NULL, FALSE);
254 MessageView* view = MESSAGE_VIEW(data);
256 if (event == NULL)
257 return FALSE;
259 if (event->type == GDK_KEY_PRESS)
261 switch(((GdkEventKey *)event)->keyval)
263 case GDK_KEY_space:
264 case GDK_KEY_Return:
266 const gchar* message =
267 ianjuta_message_view_get_current_message(IANJUTA_MESSAGE_VIEW (view), NULL);
268 if (message)
270 g_signal_emit_by_name (G_OBJECT (view), "message_clicked",
271 message);
272 return TRUE;
274 break;
276 default:
277 return FALSE;
280 else if (event->type == GDK_2BUTTON_PRESS)
282 if (((GdkEventButton *) event)->button == 1)
284 const gchar* message =
285 ianjuta_message_view_get_current_message(IANJUTA_MESSAGE_VIEW (view), NULL);
286 if (message)
288 g_signal_emit_by_name (G_OBJECT (view), "message_clicked",
289 message);
290 return TRUE;
293 return FALSE;
295 else if (event->type == GDK_BUTTON_PRESS)
297 if (((GdkEventButton *) event)->button == 3)
299 gtk_menu_popup (GTK_MENU (view->privat->popup_menu), NULL, NULL, NULL, NULL,
300 ((GdkEventButton *) event)->button,
301 ((GdkEventButton *) event)->time);
302 return TRUE;
305 return FALSE;
308 static void
309 on_adjustment_changed (GtkAdjustment* adj, gpointer data)
311 gtk_adjustment_set_value (adj,
312 gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj));
315 static void
316 on_adjustment_value_changed (GtkAdjustment* adj, gpointer data)
318 MessageView *self = MESSAGE_VIEW (data);
319 if (gtk_adjustment_get_value (adj) > (gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj)) - 0.1)
321 if (!self->privat->adj_chgd_hdlr)
323 self->privat->adj_chgd_hdlr =
324 g_signal_connect (G_OBJECT (adj), "changed",
325 G_CALLBACK (on_adjustment_changed), NULL);
328 else
330 if (self->privat->adj_chgd_hdlr)
332 g_signal_handler_disconnect (G_OBJECT (adj), self->privat->adj_chgd_hdlr);
333 self->privat->adj_chgd_hdlr = 0;
338 static void
339 message_view_set_property (GObject * object,
340 guint property_id,
341 const GValue * value, GParamSpec * pspec)
343 MessageView *self = MESSAGE_VIEW (object);
344 g_return_if_fail(value != NULL);
345 g_return_if_fail(pspec != NULL);
347 switch (property_id)
349 case MV_PROP_LABEL:
351 g_free (self->privat->label);
352 self->privat->label = g_value_dup_string (value);
353 break;
355 case MV_PROP_PIXMAP:
357 g_free (self->privat->pixmap);
358 self->privat->pixmap = g_value_dup_string (value);
359 break;
361 case MV_PROP_HIGHLITE:
363 self->privat->highlite = g_value_get_boolean (value);
364 break;
366 default:
368 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
369 break;
374 static void
375 message_view_get_property (GObject * object,
376 guint property_id,
377 GValue * value, GParamSpec * pspec)
379 MessageView *self = MESSAGE_VIEW (object);
381 g_return_if_fail(value != NULL);
382 g_return_if_fail(pspec != NULL);
384 switch (property_id)
386 case MV_PROP_LABEL:
388 g_value_set_string (value, self->privat->label);
389 break;
391 case MV_PROP_PIXMAP:
393 g_value_set_string (value, self->privat->pixmap);
394 break;
396 case MV_PROP_HIGHLITE:
398 g_value_set_boolean (value, self->privat->highlite);
399 break;
401 default:
403 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
404 break;
409 static void
410 message_view_dispose (GObject *obj)
412 MessageView *mview = MESSAGE_VIEW (obj);
413 prefs_finalize (mview);
414 if (mview->privat->tree_view)
416 mview->privat->tree_view = NULL;
418 G_OBJECT_CLASS (parent_class)->dispose (obj);
421 static void
422 message_view_finalize (GObject *obj)
424 MessageView *mview = MESSAGE_VIEW (obj);
425 g_free (mview->privat->line_buffer);
426 g_free (mview->privat->label);
427 g_free (mview->privat->pixmap);
428 g_free (mview->privat);
429 G_OBJECT_CLASS (parent_class)->finalize (obj);
432 static void
433 message_view_instance_init (MessageView * self)
435 GtkWidget *scrolled_win;
436 GtkCellRenderer *renderer;
437 GtkCellRenderer *renderer_pixbuf;
438 GtkTreeViewColumn *column;
439 GtkTreeViewColumn *column_pixbuf;
440 GtkTreeSelection *select;
441 GtkListStore *model;
442 GtkAdjustment* adj;
444 g_return_if_fail(self != NULL);
445 self->privat = g_new0 (MessageViewPrivate, 1);
447 /* Init private data */
448 self->privat->line_buffer = g_strdup("");
449 self->privat->flags = 0xF;
451 /* Create the tree widget */
452 model = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING,
453 G_TYPE_STRING, MESSAGE_TYPE, G_TYPE_STRING);
454 self->privat->model = GTK_TREE_MODEL (model);
456 /* message filter */
457 self->privat->filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (model), NULL);
458 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (self->privat->filter),
459 message_view_tree_view_filter,
460 self, NULL);
462 self->privat->tree_view =
463 gtk_tree_view_new_with_model (GTK_TREE_MODEL (self->privat->filter));
464 gtk_widget_show (self->privat->tree_view);
465 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW
466 (self->privat->tree_view), FALSE);
468 /* Create pixbuf column */
469 renderer_pixbuf = gtk_cell_renderer_pixbuf_new ();
470 g_object_set (G_OBJECT(renderer_pixbuf), "stock-size", GTK_ICON_SIZE_MENU, NULL);
471 column_pixbuf = gtk_tree_view_column_new ();
472 gtk_tree_view_column_set_title (column_pixbuf, _("Icon"));
473 gtk_tree_view_column_pack_start (column_pixbuf, renderer_pixbuf, TRUE);
474 gtk_tree_view_column_add_attribute
475 (column_pixbuf, renderer_pixbuf, "stock-id", COLUMN_PIXBUF);
476 gtk_tree_view_append_column (GTK_TREE_VIEW (self->privat->tree_view),
477 column_pixbuf);
478 /* Create columns to hold text and color of a line, this
479 * columns are invisible of course. */
480 renderer = gtk_cell_renderer_text_new ();
481 g_object_set (renderer, "yalign", 0.0, "wrap-mode", PANGO_WRAP_WORD,
482 "wrap-width", 1000, NULL);
483 column = gtk_tree_view_column_new ();
485 gtk_tree_view_column_pack_start (column, renderer, TRUE);
486 gtk_tree_view_column_set_title (column, _("Messages"));
487 gtk_tree_view_column_add_attribute
488 (column, renderer, "foreground", COLUMN_COLOR);
489 gtk_tree_view_column_add_attribute
490 (column, renderer, "markup", COLUMN_SUMMARY);
491 gtk_tree_view_append_column (GTK_TREE_VIEW (self->privat->tree_view),
492 column);
494 /* Adjust selection */
495 select = gtk_tree_view_get_selection
496 (GTK_TREE_VIEW (self->privat->tree_view));
497 gtk_tree_selection_set_mode (select, GTK_SELECTION_BROWSE);
499 /* Add tree view to a scrolled window */
500 scrolled_win = gtk_scrolled_window_new (NULL, NULL);
501 gtk_container_add (GTK_CONTAINER (scrolled_win),
502 self->privat->tree_view);
503 gtk_widget_show (scrolled_win);
504 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
505 GTK_POLICY_AUTOMATIC,
506 GTK_POLICY_AUTOMATIC);
507 adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW
508 (scrolled_win));
509 self->privat->adj_chgd_hdlr = g_signal_connect(G_OBJECT(adj), "changed",
510 G_CALLBACK (on_adjustment_changed), self);
511 g_signal_connect(G_OBJECT(adj), "value_changed",
512 G_CALLBACK(on_adjustment_value_changed), self);
514 gtk_container_add (GTK_CONTAINER (self), scrolled_win);
516 /* Connect signals */
517 g_signal_connect (G_OBJECT(self->privat->tree_view), "event",
518 G_CALLBACK (on_message_event), self);
520 g_object_set (G_OBJECT(self), "has-tooltip", TRUE, NULL);
524 static void
525 message_view_class_init (MessageViewClass * klass)
527 GParamSpec *message_view_spec_label;
528 GParamSpec *message_view_spec_pixmap;
529 GParamSpec *message_view_spec_highlite;
530 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
531 GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass);
533 parent_class = g_type_class_peek_parent (klass);
534 gobject_class->set_property = message_view_set_property;
535 gobject_class->get_property = message_view_get_property;
536 gobject_class->finalize = message_view_finalize;
537 gobject_class->dispose = message_view_dispose;
539 widget_class->query_tooltip = message_view_query_tooltip;
542 message_view_spec_label = g_param_spec_string ("label",
543 "Label of the view",
544 "Used to decorate the view,"
545 "translateable",
546 "no label",
547 G_PARAM_READWRITE);
548 g_object_class_install_property (gobject_class,
549 MV_PROP_LABEL,
550 message_view_spec_label);
552 message_view_spec_pixmap = g_param_spec_string ("pixmap",
553 "Pixmap of the view",
554 "Used to decorate the view tab,"
555 "translateable",
556 "no label",
557 G_PARAM_READWRITE);
558 g_object_class_install_property (gobject_class,
559 MV_PROP_PIXMAP,
560 message_view_spec_pixmap);
562 message_view_spec_highlite = g_param_spec_boolean ("highlite",
563 "Highlite build messages",
564 "If TRUE, specify colors",
565 FALSE,
566 G_PARAM_READWRITE);
567 g_object_class_install_property (gobject_class,
568 MV_PROP_HIGHLITE,
569 message_view_spec_highlite);
572 /* Returns a new message-view instance */
573 GtkWidget *
574 message_view_new (GtkWidget* popup_menu)
576 MessageView * mv = MESSAGE_VIEW (g_object_new (message_view_get_type (), NULL));
577 mv->privat->popup_menu = popup_menu;
578 prefs_init (mv);
579 return GTK_WIDGET(mv);
582 gboolean
583 message_view_serialize (MessageView *view, AnjutaSerializer *serializer)
585 GtkTreeModel *model;
586 GtkTreeIter iter;
588 g_return_val_if_fail (view != NULL && MESSAGE_IS_VIEW (view), FALSE);
590 if (!anjuta_serializer_write_string (serializer, "label",
591 view->privat->label))
592 return FALSE;
593 if (!anjuta_serializer_write_string (serializer, "pixmap",
594 view->privat->pixmap))
595 return FALSE;
596 if (!anjuta_serializer_write_int (serializer, "highlite",
597 view->privat->highlite))
598 return FALSE;
600 /* Serialize individual messages */
601 model = view->privat->model;
603 if (!anjuta_serializer_write_int (serializer, "messages",
604 gtk_tree_model_iter_n_children (model, NULL)))
605 return FALSE;
607 if (gtk_tree_model_get_iter_first (model, &iter))
611 Message *message;
612 gtk_tree_model_get (model, &iter, COLUMN_MESSAGE, &message, -1);
613 if (message)
615 if (!message_serialize (message, serializer))
616 return FALSE;
619 while (gtk_tree_model_iter_next (model, &iter));
621 return TRUE;
624 gboolean
625 message_view_deserialize (MessageView *view, AnjutaSerializer *serializer)
627 GtkTreeModel *model;
628 gint messages, i;
630 g_return_val_if_fail (view != NULL && MESSAGE_IS_VIEW (view), FALSE);
632 if (!anjuta_serializer_read_string (serializer, "label",
633 &view->privat->label, TRUE))
634 return FALSE;
635 if (!anjuta_serializer_read_string (serializer, "pixmap",
636 &view->privat->pixmap, TRUE))
637 return FALSE;
638 if (!anjuta_serializer_read_int (serializer, "highlite",
639 &view->privat->highlite))
640 return FALSE;
642 /* Create individual messages */
643 model = view->privat->model;
644 gtk_list_store_clear (GTK_LIST_STORE (model));
646 if (!anjuta_serializer_read_int (serializer, "messages", &messages))
647 return FALSE;
649 for (i = 0; i < messages; i++)
651 Message *message;
653 message = message_new (0, NULL, NULL);
654 if (!message_deserialize (message, serializer))
656 message_free (message);
657 return FALSE;
659 ianjuta_message_view_append (IANJUTA_MESSAGE_VIEW (view), message->type,
660 message->summary, message->details, NULL);
661 message_free (message);
663 return TRUE;
666 void message_view_next(MessageView* view)
668 GtkTreeIter iter;
669 GtkTreeModel *model;
670 GtkTreeSelection *select;
672 g_return_if_fail (view != NULL && MESSAGE_IS_VIEW (view));
674 model = view->privat->model;
675 select = gtk_tree_view_get_selection (GTK_TREE_VIEW
676 (view->privat->tree_view));
678 if (!gtk_tree_selection_get_selected (select, &model, &iter))
680 if (gtk_tree_model_get_iter_first (model, &iter))
681 gtk_tree_selection_select_iter (select, &iter);
683 while (gtk_tree_model_iter_next (model, &iter))
685 Message *message;
686 gtk_tree_model_get (model, &iter, COLUMN_MESSAGE,
687 &message, -1);
688 if (message->type == IANJUTA_MESSAGE_VIEW_TYPE_WARNING
689 || message->type == IANJUTA_MESSAGE_VIEW_TYPE_ERROR)
691 const gchar* message;
692 gtk_tree_selection_select_iter (select, &iter);
693 message =
694 ianjuta_message_view_get_current_message(IANJUTA_MESSAGE_VIEW (view), NULL);
695 if (message)
697 GtkTreePath *path;
698 path = gtk_tree_model_get_path (model, &iter);
699 gtk_tree_view_set_cursor (GTK_TREE_VIEW
700 (view->privat->tree_view),
701 path, NULL, FALSE);
702 gtk_tree_path_free (path);
703 g_signal_emit_by_name (G_OBJECT (view), "message_clicked",
704 message);
705 break;
711 void message_view_previous(MessageView* view)
713 GtkTreeIter iter;
714 GtkTreeModel *model;
715 GtkTreeSelection *select;
716 GtkTreePath *path;
718 g_return_if_fail (view != NULL && MESSAGE_IS_VIEW (view));
720 model = view->privat->model;
721 select = gtk_tree_view_get_selection (GTK_TREE_VIEW
722 (view->privat->tree_view));
724 if (!gtk_tree_selection_get_selected (select, &model, &iter))
726 if (gtk_tree_model_get_iter_first (model, &iter))
727 gtk_tree_selection_select_iter (select, &iter);
730 /* gtk_tree_model_iter_previous does not exist, use path */
731 path = gtk_tree_model_get_path (model, &iter);
733 while (gtk_tree_path_prev(path))
735 Message *message;
736 gtk_tree_model_get_iter(model, &iter, path);
737 gtk_tree_model_get (model, &iter, COLUMN_MESSAGE,
738 &message, -1);
739 if (message->type == IANJUTA_MESSAGE_VIEW_TYPE_WARNING
740 || message->type == IANJUTA_MESSAGE_VIEW_TYPE_ERROR)
742 const gchar* message;
744 gtk_tree_selection_select_iter (select, &iter);
745 message =
746 ianjuta_message_view_get_current_message(IANJUTA_MESSAGE_VIEW (view), NULL);
747 if (message)
749 GtkTreePath *path;
750 path = gtk_tree_model_get_path (model, &iter);
751 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW
752 (view->privat->tree_view),
753 path, NULL, FALSE, 0, 0);
754 gtk_tree_path_free (path);
755 g_signal_emit_by_name (G_OBJECT (view), "message_clicked",
756 message);
757 break;
761 gtk_tree_path_free (path);
764 void message_view_copy_all(MessageView* view)
766 GtkTreeIter iter;
767 GtkTreeModel *model;
768 GString *messages;
770 g_return_if_fail (view != NULL && MESSAGE_IS_VIEW (view));
772 /* Copy all lines of message view */
773 model = view->privat->model;
775 messages = g_string_new (NULL);
776 gtk_tree_model_get_iter_first (model, &iter);
779 Message *message;
781 gtk_tree_model_get (model, &iter, COLUMN_MESSAGE, &message, -1);
782 if (message)
784 if (message->details && (strlen (message->details) > 0))
786 g_string_append (messages, message->details);
787 g_string_append_c (messages, '\n');
789 else
791 g_string_append (messages, message->summary);
792 g_string_append_c (messages, '\n');
795 } while (gtk_tree_model_iter_next (model, &iter));
797 if (messages->len != 0)
799 GtkClipboard *clipboard;
801 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), GDK_SELECTION_CLIPBOARD);
803 gtk_clipboard_set_text (clipboard, messages->str, messages->len);
806 g_string_free (messages, TRUE);
809 void message_view_copy(MessageView* view)
811 GtkTreeIter iter;
812 GtkTreeModel *model;
813 GtkTreeSelection *select;
815 g_return_if_fail (view != NULL && MESSAGE_IS_VIEW (view));
817 model = view->privat->model;
818 select = gtk_tree_view_get_selection (GTK_TREE_VIEW
819 (view->privat->tree_view));
821 if (gtk_tree_selection_get_selected (select, &model, &iter))
823 Message *message;
824 const gchar *text;
825 GtkClipboard *clipboard;
827 gtk_tree_model_get (model, &iter, COLUMN_MESSAGE, &message, -1);
829 if (message->details && (*message->details != '\0'))
831 text = message->details;
833 else if (message->summary && (*message->summary != '\0'))
835 text = message->summary;
837 else
839 /* No message */
840 return;
843 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), GDK_SELECTION_CLIPBOARD);
845 gtk_clipboard_set_text (clipboard, text, -1);
849 /* Preferences notifications */
850 static void
851 pref_change_color (MessageView *mview, IAnjutaMessageViewType type,
852 const gchar *color_pref_key)
854 gchar* color;
855 GtkListStore *store;
856 GtkTreeIter iter;
857 gboolean success;
859 color = g_settings_get_string (mview->privat->settings, color_pref_key);
860 store = GTK_LIST_STORE (mview->privat->model);
861 success = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
862 while (success)
864 Message *message;
865 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, COLUMN_MESSAGE,
866 &message, -1);
867 if (message && message->type == type)
869 gtk_list_store_set (store, &iter, COLUMN_COLOR, color, -1);
871 success = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
873 g_free(color);
877 static void
878 on_notify_color (GSettings* settings, const gchar* key,
879 gpointer user_data)
881 if (g_str_equal (key, COLOR_ERROR))
882 pref_change_color (MESSAGE_VIEW (user_data),
883 IANJUTA_MESSAGE_VIEW_TYPE_ERROR,
884 key);
885 else
886 pref_change_color (MESSAGE_VIEW (user_data),
887 IANJUTA_MESSAGE_VIEW_TYPE_WARNING,
888 key);
891 static void
892 prefs_init (MessageView *mview)
894 mview->privat->settings = g_settings_new (PREFERENCES_SCHEMA);
895 g_signal_connect (mview->privat->settings, "changed::" COLOR_ERROR,
896 G_CALLBACK (on_notify_color), mview);
897 g_signal_connect (mview->privat->settings, "changed::" COLOR_WARNING,
898 G_CALLBACK (on_notify_color), mview);
901 static void
902 prefs_finalize (MessageView *mview)
904 if (mview->privat->settings)
905 g_object_unref (mview->privat->settings);
906 mview->privat->settings = NULL;
909 /* IAnjutaMessageView interface implementation */
911 /* Appends the text in buffer. Flushes the buffer where a newline is found.
912 * by emiiting buffer_flushed signal. The string is expected to be utf8.
914 static void
915 imessage_view_buffer_append (IAnjutaMessageView * message_view,
916 const gchar * message, GError ** e)
918 MessageView *view;
919 gint cur_char;
920 int len;
922 g_return_if_fail (MESSAGE_IS_VIEW (message_view));
924 if (!message)
925 return;
927 len = strlen(message);
929 view = MESSAGE_VIEW (message_view);
931 /* Check if message contains newlines */
932 for (cur_char = 0; cur_char < len; cur_char++)
934 /* Is newline => print line */
935 if (message[cur_char] != '\n')
937 add_char(&view->privat->line_buffer, message[cur_char]);
939 else
941 g_signal_emit_by_name (G_OBJECT (view), "buffer_flushed",
942 view->privat->line_buffer);
943 g_free(view->privat->line_buffer);
944 view->privat->line_buffer = g_strdup("");
949 static void
950 imessage_view_append (IAnjutaMessageView *message_view,
951 IAnjutaMessageViewType type,
952 const gchar *summary,
953 const gchar *details,
954 GError ** e)
956 gchar* color;
957 GtkListStore *store;
958 GtkTreeIter iter;
959 gboolean highlite;
960 gchar *utf8_msg;
961 gchar *escaped_str;
962 gchar* stock_id = NULL;
964 MessageView *view;
965 Message *message;
967 g_return_if_fail (MESSAGE_IS_VIEW (message_view));
969 view = MESSAGE_VIEW (message_view);
971 message = message_new (type, summary, details);
973 g_object_get (G_OBJECT (view), "highlite", &highlite, NULL);
974 color = NULL;
975 if (highlite)
977 switch (message->type)
979 case IANJUTA_MESSAGE_VIEW_TYPE_INFO:
980 view->privat->info_count++;
981 stock_id = GTK_STOCK_INFO;
982 break;
983 case IANJUTA_MESSAGE_VIEW_TYPE_WARNING:
984 color = g_settings_get_string (view->privat->settings, COLOR_WARNING);
985 /* FIXME: There is no GTK_STOCK_WARNING which would fit better here */
986 view->privat->warn_count++;
987 stock_id = GTK_STOCK_DIALOG_WARNING;
988 break;
989 case IANJUTA_MESSAGE_VIEW_TYPE_ERROR:
990 color = g_settings_get_string (view->privat->settings, COLOR_ERROR);
991 view->privat->error_count++;
992 stock_id = GTK_STOCK_STOP;
993 break;
994 default:
995 color = NULL;
996 view->privat->normal_count++;
1000 /* Add the message to the tree */
1001 store = GTK_LIST_STORE (view->privat->model);
1002 gtk_list_store_append (store, &iter);
1005 * Must be normalized to compose representation to be
1006 * displayed correctly (Bug in gtk_list???)
1008 utf8_msg = g_utf8_normalize (message->summary, -1,
1009 G_NORMALIZE_DEFAULT_COMPOSE);
1010 if (message->details && strlen (message->details) > 0)
1012 gchar *summary;
1013 summary = escape_string (message->summary);
1014 escaped_str = g_strdup_printf ("<b>%s</b>", summary);
1015 g_free (summary);
1016 } else {
1017 escaped_str = escape_string (message->summary);
1019 gtk_list_store_set (store, &iter,
1020 COLUMN_SUMMARY, escaped_str,
1021 COLUMN_MESSAGE, message,
1022 -1);
1023 if (color)
1025 gtk_list_store_set (store, &iter,
1026 COLUMN_COLOR, color, -1);
1028 if (stock_id)
1030 gtk_list_store_set (store, &iter,
1031 COLUMN_PIXBUF, stock_id, -1);
1033 g_free(color);
1034 message_free (message);
1035 g_free (utf8_msg);
1036 g_free (escaped_str);
1039 /* Clear all messages from the message view */
1040 static void
1041 imessage_view_clear (IAnjutaMessageView *message_view, GError **e)
1043 GtkListStore *store;
1044 MessageView *view;
1046 g_return_if_fail (MESSAGE_IS_VIEW (message_view));
1047 view = MESSAGE_VIEW (message_view);
1049 /* filter settings restart */
1050 view->privat->normal_count = 0;
1051 view->privat->info_count = 0;
1052 view->privat->warn_count = 0;
1053 view->privat->error_count = 0;
1055 store = GTK_LIST_STORE (view->privat->model);
1056 gtk_list_store_clear (store);
1059 /* Move the selection to the next line. */
1060 static void
1061 imessage_view_select_next (IAnjutaMessageView * message_view,
1062 GError ** e)
1064 MessageView* view = MESSAGE_VIEW(message_view);
1065 message_view_next(view);
1068 /* Move the selection to the previous line. */
1069 static void
1070 imessage_view_select_previous (IAnjutaMessageView * message_view,
1071 GError ** e)
1073 MessageView *view = MESSAGE_VIEW(message_view);
1074 message_view_previous(view);
1077 /* Return the currently selected messages or the first message if no
1078 * message is selected or NULL if no messages are availible. The
1079 * returned message must not be freed.
1081 static const gchar *
1082 imessage_view_get_current_message (IAnjutaMessageView * message_view,
1083 GError ** e)
1085 MessageView *view;
1086 GtkTreeIter iter;
1087 GtkTreeSelection *select;
1088 GtkTreeModel *model;
1089 const Message *message;
1091 g_return_val_if_fail (MESSAGE_IS_VIEW (message_view), NULL);
1093 view = MESSAGE_VIEW (message_view);
1094 select = gtk_tree_view_get_selection (GTK_TREE_VIEW
1095 (view->privat->tree_view));
1097 if (!gtk_tree_selection_get_selected (select, &model, &iter))
1099 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter))
1101 gtk_tree_model_get (GTK_TREE_MODEL (model),
1102 &iter, COLUMN_MESSAGE, &message, -1);
1103 if (message)
1105 if (message->details && strlen (message->details) > 0)
1106 return message->details;
1107 else
1108 return message->summary;
1112 else
1114 gtk_tree_model_get (GTK_TREE_MODEL (model),
1115 &iter, COLUMN_MESSAGE, &message, -1);
1116 if (message)
1118 if (message->details && strlen (message->details) > 0)
1119 return message->details;
1120 else
1121 return message->summary;
1124 return NULL;
1127 /* Returns a GList which contains all messages, the GList itself
1128 * must be freed, the messages are managed by the message view and
1129 * must not be freed. NULL is return if no messages are availible.
1131 static GList *
1132 imessage_view_get_all_messages (IAnjutaMessageView * message_view,
1133 GError ** e)
1135 MessageView *view;
1136 GtkListStore *store;
1137 GtkTreeIter iter;
1138 Message *message;
1139 GList *messages = NULL;
1141 g_return_val_if_fail (MESSAGE_IS_VIEW (message_view), NULL);
1143 view = MESSAGE_VIEW (message_view);
1144 store = GTK_LIST_STORE (view->privat->model);
1146 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
1150 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
1151 COLUMN_MESSAGE, &message);
1152 messages = g_list_prepend (messages, message->details);
1154 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
1156 return messages;
1159 static void
1160 imessage_view_iface_init (IAnjutaMessageViewIface *iface)
1162 iface->buffer_append = imessage_view_buffer_append;
1163 iface->append = imessage_view_append;
1164 iface->clear = imessage_view_clear;
1165 iface->select_next = imessage_view_select_next;
1166 iface->select_previous = imessage_view_select_previous;
1167 iface->get_current_message = imessage_view_get_current_message;
1168 iface->get_all_messages = imessage_view_get_all_messages;
1171 static gboolean
1172 message_view_tree_view_filter (GtkTreeModel *model, GtkTreeIter *iter,
1173 gpointer data)
1175 Message *msg;
1176 MessageView *msgview;
1178 msgview = MESSAGE_VIEW (data);
1179 gtk_tree_model_get (msgview->privat->model, iter, COLUMN_MESSAGE, &msg, -1);
1181 if (msg != NULL) {
1182 if (msg->type == IANJUTA_MESSAGE_VIEW_TYPE_NORMAL) {
1183 return msgview->privat->flags & MESSAGE_VIEW_SHOW_NORMAL;
1184 } else if (msg->type == IANJUTA_MESSAGE_VIEW_TYPE_INFO) {
1185 return msgview->privat->flags & MESSAGE_VIEW_SHOW_INFO;
1186 } else if (msg->type == IANJUTA_MESSAGE_VIEW_TYPE_WARNING) {
1187 return msgview->privat->flags & MESSAGE_VIEW_SHOW_WARNING;
1188 } else if (msg->type == IANJUTA_MESSAGE_VIEW_TYPE_ERROR) {
1189 return msgview->privat->flags & MESSAGE_VIEW_SHOW_ERROR;
1190 } else return TRUE;
1191 } else return FALSE;
1194 MessageViewFlags
1195 message_view_get_flags (MessageView* view)
1197 g_return_val_if_fail (view != NULL && MESSAGE_IS_VIEW (view), MESSAGE_VIEW_SHOW_NORMAL);
1199 return view->privat->flags;
1202 void message_view_set_flags (MessageView* view, MessageViewFlags flags)
1204 g_return_if_fail (view != NULL && MESSAGE_IS_VIEW (view));
1206 view->privat->flags = flags;
1207 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER(view->privat->filter));
1210 gint message_view_get_count (MessageView* view, MessageViewFlags flags)
1212 g_return_val_if_fail (view != NULL && MESSAGE_IS_VIEW (view), 0);
1214 switch (flags)
1216 case MESSAGE_VIEW_SHOW_NORMAL:
1217 return view->privat->normal_count;
1218 case MESSAGE_VIEW_SHOW_INFO:
1219 return view->privat->info_count;
1220 case MESSAGE_VIEW_SHOW_WARNING:
1221 return view->privat->warn_count;
1222 case MESSAGE_VIEW_SHOW_ERROR:
1223 return view->privat->error_count;
1224 default:
1225 g_assert_not_reached ();
1229 ANJUTA_TYPE_BEGIN(MessageView, message_view, GTK_TYPE_HBOX);
1230 ANJUTA_TYPE_ADD_INTERFACE(imessage_view, IANJUTA_TYPE_MESSAGE_VIEW);
1231 ANJUTA_TYPE_END;