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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 #include <libgnomevfs/gnome-vfs.h>
20 #include <libanjuta/anjuta-utils.h>
21 #include <libanjuta/anjuta-debug.h>
22 #include <libanjuta/interfaces/ianjuta-message-view.h>
24 #include "message-view.h"
25 #define MESSAGE_TYPE message_get_type()
27 struct _MessageViewPrivate
34 AnjutaPreferences
* prefs
;
35 GtkWidget
*popup_menu
;
44 GdkRectangle tooltip_rect
;
45 GtkWidget
*tooltip_window
;
46 gulong tooltip_timeout
;
47 PangoLayout
*tooltip_layout
;
49 /* gconf notification ids */
50 GList
*gconf_notify_ids
;
55 IAnjutaMessageViewType type
;
78 static gpointer parent_class
;
80 static void prefs_init (MessageView
*mview
);
81 static void prefs_finalize (MessageView
*mview
);
83 /* Ask the user for an uri name */
85 ask_user_for_save_uri (GtkWindow
* parent
)
90 dialog
= gtk_file_chooser_dialog_new (_("Save file as"), parent
,
91 GTK_FILE_CHOOSER_ACTION_SAVE
, GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
92 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
, NULL
);
94 if(gtk_dialog_run(GTK_DIALOG(dialog
)) == GTK_RESPONSE_ACCEPT
)
96 uri
= gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog
));
103 gtk_widget_destroy(dialog
);
108 /* Message object creation, copy and freeing */
110 message_new (IAnjutaMessageViewType type
, const gchar
*summary
,
111 const gchar
*details
)
113 /* DEBUG_PRINT ("Creating message"); */
114 Message
*message
= g_new0 (Message
, 1);
115 message
->type
= type
;
117 message
->summary
= g_strdup (summary
);
119 message
->details
= g_strdup (details
);
124 message_copy (const Message
*src
)
126 return message_new (src
->type
, src
->summary
, src
->details
);
130 message_free (Message
*message
)
132 /* DEBUG_PRINT ("Freeing message"); */
133 g_free (message
->summary
);
134 g_free (message
->details
);
139 message_serialize (Message
*message
, AnjutaSerializer
*serializer
)
141 if (!anjuta_serializer_write_int (serializer
, "type",
144 if (!anjuta_serializer_write_string (serializer
, "summary",
147 if (!anjuta_serializer_write_string (serializer
, "details",
154 message_deserialize (Message
*message
, AnjutaSerializer
*serializer
)
157 if (!anjuta_serializer_read_int (serializer
, "type",
160 message
->type
= type
;
161 if (!anjuta_serializer_read_string (serializer
, "summary",
162 &message
->summary
, TRUE
))
164 if (!anjuta_serializer_read_string (serializer
, "details",
165 &message
->details
, TRUE
))
173 static GType type
= 0;
176 type
= g_boxed_type_register_static ("MessageViewMessage",
177 (GBoxedCopyFunc
) message_copy
,
178 (GBoxedFreeFunc
) message_free
);
183 /* Utility functions */
184 /* Adds the char c to the string str */
186 add_char(gchar
** str
, gchar c
)
190 g_return_if_fail(str
!= NULL
);
192 buffer
= g_strdup_printf("%s%c", *str
, c
);
198 escape_string (const gchar
*str
)
203 gstr
= g_string_new ("");
205 while (*iter
!= '\0')
208 gstr
= g_string_append (gstr
, ">");
209 else if (*iter
== '<')
210 gstr
= g_string_append (gstr
, "<");
211 else if (*iter
== '&')
212 gstr
= g_string_append (gstr
, "&");
214 gstr
= g_string_append_c (gstr
, *iter
);
217 return g_string_free (gstr
, FALSE
);
220 /* Tooltip operations -- taken from gtodo */
223 tooltip_get_display_text (MessageView
*view
)
229 model
= gtk_tree_view_get_model (GTK_TREE_VIEW (view
->privat
->tree_view
));
231 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW(view
->privat
->tree_view
),
232 view
->privat
->tooltip_rect
.x
, view
->privat
->tooltip_rect
.y
,
233 &path
, NULL
, NULL
, NULL
))
236 gchar
*text
, *title
, *desc
;
238 gtk_tree_model_get_iter (model
, &iter
, path
);
239 gtk_tree_model_get (model
, &iter
, COLUMN_MESSAGE
, &message
, -1);
240 gtk_tree_path_free(path
);
242 if (!message
->details
|| !message
->summary
||
243 strlen (message
->details
) <= 0 ||
244 strlen (message
->summary
) <= 0)
247 title
= escape_string (message
->summary
);
248 desc
= escape_string (message
->details
);
249 text
= g_strdup_printf ("<b>%s</b>\n%s", title
, desc
);
260 tooltip_paint (GtkWidget
*widget
, GdkEventExpose
*event
, MessageView
*view
)
265 tooltiptext
= tooltip_get_display_text (view
);
268 tooltiptext
= g_strdup (_("No message details"));
270 pango_layout_set_markup (view
->privat
->tooltip_layout
,
272 strlen (tooltiptext
));
273 pango_layout_set_wrap(view
->privat
->tooltip_layout
, PANGO_WRAP_CHAR
);
274 pango_layout_set_width(view
->privat
->tooltip_layout
, 600000);
275 style
= view
->privat
->tooltip_window
->style
;
277 gtk_paint_flat_box (style
, view
->privat
->tooltip_window
->window
,
278 GTK_STATE_NORMAL
, GTK_SHADOW_OUT
,
279 NULL
, view
->privat
->tooltip_window
,
280 "tooltip", 0, 0, -1, -1);
282 gtk_paint_layout (style
, view
->privat
->tooltip_window
->window
,
283 GTK_STATE_NORMAL
, TRUE
,
284 NULL
, view
->privat
->tooltip_window
,
285 "tooltip", 4, 4, view
->privat
->tooltip_layout
);
287 g_object_unref(layout);
294 tooltip_timeout (MessageView
*view
)
296 gint scr_w
,scr_h
, w
, h
, x
, y
;
299 tooltiptext
= tooltip_get_display_text (view
);
302 tooltiptext
= g_strdup (_("No message details"));
304 view
->privat
->tooltip_window
= gtk_window_new (GTK_WINDOW_POPUP
);
305 view
->privat
->tooltip_window
->parent
= view
->privat
->tree_view
;
306 gtk_widget_set_app_paintable (view
->privat
->tooltip_window
, TRUE
);
307 gtk_window_set_resizable (GTK_WINDOW(view
->privat
->tooltip_window
), FALSE
);
308 gtk_widget_set_name (view
->privat
->tooltip_window
, "gtk-tooltips");
309 g_signal_connect (G_OBJECT(view
->privat
->tooltip_window
), "expose_event",
310 G_CALLBACK(tooltip_paint
), view
);
311 gtk_widget_ensure_style (view
->privat
->tooltip_window
);
313 view
->privat
->tooltip_layout
=
314 gtk_widget_create_pango_layout (view
->privat
->tooltip_window
, NULL
);
315 pango_layout_set_wrap (view
->privat
->tooltip_layout
, PANGO_WRAP_CHAR
);
316 pango_layout_set_width (view
->privat
->tooltip_layout
, 600000);
317 pango_layout_set_markup (view
->privat
->tooltip_layout
, tooltiptext
,
318 strlen (tooltiptext
));
319 scr_w
= gdk_screen_width();
320 scr_h
= gdk_screen_height();
321 pango_layout_get_size (view
->privat
->tooltip_layout
, &w
, &h
);
322 w
= PANGO_PIXELS(w
) + 8;
323 h
= PANGO_PIXELS(h
) + 8;
325 gdk_window_get_pointer (NULL
, &x
, &y
, NULL
);
326 if (GTK_WIDGET_NO_WINDOW (view
->privat
->tree_view
))
327 y
+= view
->privat
->tree_view
->allocation
.y
;
332 x
-= (x
+ w
) - scr_w
;
336 if ((y
+ h
+ 4) > scr_h
)
341 g_object_unref(layout);
343 gtk_widget_set_size_request (view
->privat
->tooltip_window
, w
, h
);
344 gtk_window_move (GTK_WINDOW (view
->privat
->tooltip_window
), x
, y
);
345 gtk_widget_show (view
->privat
->tooltip_window
);
346 g_free (tooltiptext
);
352 tooltip_motion_cb (GtkWidget
*tv
, GdkEventMotion
*event
, MessageView
*view
)
356 if (view
->privat
->tooltip_rect
.y
== 0 &&
357 view
->privat
->tooltip_rect
.height
== 0 &&
358 view
->privat
->tooltip_timeout
)
360 g_source_remove (view
->privat
->tooltip_timeout
);
361 view
->privat
->tooltip_timeout
= 0;
362 if (view
->privat
->tooltip_window
) {
363 gtk_widget_destroy (view
->privat
->tooltip_window
);
364 view
->privat
->tooltip_window
= NULL
;
368 if (view
->privat
->tooltip_timeout
) {
369 if (((int)event
->y
> view
->privat
->tooltip_rect
.y
) &&
370 (((int)event
->y
- view
->privat
->tooltip_rect
.height
)
371 < view
->privat
->tooltip_rect
.y
))
376 g_source_remove (view
->privat
->tooltip_timeout
);
377 view
->privat
->tooltip_timeout
= 0;
380 /* We've left the cell. Remove the timeout and create a new one below */
381 if (view
->privat
->tooltip_window
) {
382 gtk_widget_destroy (view
->privat
->tooltip_window
);
383 view
->privat
->tooltip_window
= NULL
;
385 g_source_remove (view
->privat
->tooltip_timeout
);
386 view
->privat
->tooltip_timeout
= 0;
389 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW(view
->privat
->tree_view
),
390 event
->x
, event
->y
, &path
,
393 GtkTreeSelection
*selection
;
395 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW(view
->privat
->tree_view
));
396 if (gtk_tree_selection_path_is_selected (selection
, path
))
398 gtk_tree_view_get_cell_area (GTK_TREE_VIEW (view
->privat
->tree_view
),
399 path
, NULL
, &view
->privat
->tooltip_rect
);
401 if (view
->privat
->tooltip_rect
.y
!= 0 &&
402 view
->privat
->tooltip_rect
.height
!= 0)
406 tooltiptext
= tooltip_get_display_text (view
);
407 if (tooltiptext
== NULL
)
409 g_free (tooltiptext
);
411 view
->privat
->tooltip_timeout
=
412 g_timeout_add (500, (GSourceFunc
) tooltip_timeout
, view
);
415 gtk_tree_path_free (path
);
421 tooltip_leave_cb (GtkWidget
*w
, GdkEventCrossing
*e
, MessageView
*view
)
423 if (view
->privat
->tooltip_timeout
) {
424 g_source_remove (view
->privat
->tooltip_timeout
);
425 view
->privat
->tooltip_timeout
= 0;
427 if (view
->privat
->tooltip_window
) {
428 gtk_widget_destroy (view
->privat
->tooltip_window
);
429 g_object_unref (view
->privat
->tooltip_layout
);
430 view
->privat
->tooltip_window
= NULL
;
434 /* MessageView signal callbacks */
435 /* Send a signal if a message was double-clicked or ENTER or SPACE was pressed */
437 on_message_event (GObject
* object
, GdkEvent
* event
, gpointer data
)
439 g_return_val_if_fail(object
!= NULL
, FALSE
);
440 g_return_val_if_fail(event
!= NULL
, FALSE
);
441 g_return_val_if_fail(data
!= NULL
, FALSE
);
443 MessageView
* view
= MESSAGE_VIEW(data
);
448 if (event
->type
== GDK_KEY_PRESS
)
450 switch(((GdkEventKey
*)event
)->keyval
)
455 const gchar
* message
=
456 ianjuta_message_view_get_current_message(IANJUTA_MESSAGE_VIEW (view
), NULL
);
459 g_signal_emit_by_name (G_OBJECT (view
), "message_clicked",
469 else if (event
->type
== GDK_2BUTTON_PRESS
)
471 if (((GdkEventButton
*) event
)->button
== 1)
473 const gchar
* message
=
474 ianjuta_message_view_get_current_message(IANJUTA_MESSAGE_VIEW (view
), NULL
);
477 g_signal_emit_by_name (G_OBJECT (view
), "message_clicked",
484 else if (event
->type
== GDK_BUTTON_PRESS
)
486 if (((GdkEventButton
*) event
)->button
== 3)
488 gtk_menu_popup (GTK_MENU (view
->privat
->popup_menu
), NULL
, NULL
, NULL
, NULL
,
489 ((GdkEventButton
*) event
)->button
,
490 ((GdkEventButton
*) event
)->time
);
498 on_adjustment_changed (GtkAdjustment
* adj
, gpointer data
)
500 gtk_adjustment_set_value (adj
, adj
->upper
- adj
->page_size
);
504 on_adjustment_value_changed (GtkAdjustment
* adj
, gpointer data
)
506 MessageView
*self
= MESSAGE_VIEW (data
);
507 if (adj
->value
> (adj
->upper
- adj
->page_size
) - 0.1)
509 if (!self
->privat
->adj_chgd_hdlr
)
511 self
->privat
->adj_chgd_hdlr
=
512 g_signal_connect (G_OBJECT (adj
), "changed",
513 G_CALLBACK (on_adjustment_changed
), NULL
);
518 if (self
->privat
->adj_chgd_hdlr
)
520 g_signal_handler_disconnect (G_OBJECT (adj
), self
->privat
->adj_chgd_hdlr
);
521 self
->privat
->adj_chgd_hdlr
= 0;
527 message_view_set_property (GObject
* object
,
529 const GValue
* value
, GParamSpec
* pspec
)
531 MessageView
*self
= MESSAGE_VIEW (object
);
532 g_return_if_fail(value
!= NULL
);
533 g_return_if_fail(pspec
!= NULL
);
539 g_free (self
->privat
->label
);
540 self
->privat
->label
= g_value_dup_string (value
);
545 g_free (self
->privat
->pixmap
);
546 self
->privat
->pixmap
= g_value_dup_string (value
);
549 case MV_PROP_HIGHLITE
:
551 self
->privat
->highlite
= g_value_get_boolean (value
);
556 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
563 message_view_get_property (GObject
* object
,
565 GValue
* value
, GParamSpec
* pspec
)
567 MessageView
*self
= MESSAGE_VIEW (object
);
569 g_return_if_fail(value
!= NULL
);
570 g_return_if_fail(pspec
!= NULL
);
576 g_value_set_string (value
, self
->privat
->label
);
581 g_value_set_string (value
, self
->privat
->pixmap
);
584 case MV_PROP_HIGHLITE
:
586 g_value_set_boolean (value
, self
->privat
->highlite
);
591 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
598 message_view_dispose (GObject
*obj
)
600 MessageView
*mview
= MESSAGE_VIEW (obj
);
601 if (mview
->privat
->gconf_notify_ids
)
603 prefs_finalize (mview
);
604 mview
->privat
->gconf_notify_ids
= NULL
;
606 if (mview
->privat
->tooltip_timeout
) {
607 g_source_remove (mview
->privat
->tooltip_timeout
);
608 mview
->privat
->tooltip_timeout
= 0;
610 if (mview
->privat
->tooltip_window
) {
611 gtk_widget_destroy (mview
->privat
->tooltip_window
);
612 g_object_unref (mview
->privat
->tooltip_layout
);
613 mview
->privat
->tooltip_window
= NULL
;
615 if (mview
->privat
->tree_view
)
617 mview
->privat
->tree_view
= NULL
;
619 GNOME_CALL_PARENT (G_OBJECT_CLASS
, dispose
, (G_OBJECT(obj
)));
623 message_view_finalize (GObject
*obj
)
625 MessageView
*mview
= MESSAGE_VIEW (obj
);
626 g_free (mview
->privat
->line_buffer
);
627 g_free (mview
->privat
->label
);
628 g_free (mview
->privat
->pixmap
);
629 g_free (mview
->privat
);
630 GNOME_CALL_PARENT (G_OBJECT_CLASS
, finalize
, (G_OBJECT(obj
)));
634 message_view_instance_init (MessageView
* self
)
636 GtkWidget
*scrolled_win
;
637 GtkCellRenderer
*renderer
;
638 GtkCellRenderer
*renderer_pixbuf
;
639 GtkTreeViewColumn
*column
;
640 GtkTreeViewColumn
*column_pixbuf
;
641 GtkTreeSelection
*select
;
645 g_return_if_fail(self
!= NULL
);
646 self
->privat
= g_new0 (MessageViewPrivate
, 1);
648 /* Init private data */
649 self
->privat
->line_buffer
= g_strdup("");
651 /* Create the tree widget */
652 model
= gtk_list_store_new (N_COLUMNS
, GDK_TYPE_COLOR
,
653 G_TYPE_STRING
, MESSAGE_TYPE
, G_TYPE_STRING
);
654 self
->privat
->tree_view
=
655 gtk_tree_view_new_with_model (GTK_TREE_MODEL (model
));
656 gtk_widget_show (self
->privat
->tree_view
);
657 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW
658 (self
->privat
->tree_view
), FALSE
);
660 /* Create pixbuf column */
661 renderer_pixbuf
= gtk_cell_renderer_pixbuf_new ();
662 column_pixbuf
= gtk_tree_view_column_new ();
663 gtk_tree_view_column_set_title (column_pixbuf
, _("Icon"));
664 gtk_tree_view_column_pack_start (column_pixbuf
, renderer_pixbuf
, TRUE
);
665 gtk_tree_view_column_add_attribute
666 (column_pixbuf
, renderer_pixbuf
, "stock-id", COLUMN_PIXBUF
);
667 gtk_tree_view_append_column (GTK_TREE_VIEW (self
->privat
->tree_view
),
669 /* Create columns to hold text and color of a line, this
670 * columns are invisible of course. */
671 renderer
= gtk_cell_renderer_text_new ();
672 g_object_set (renderer
, "yalign", 0.0, "wrap-mode", PANGO_WRAP_WORD
,
673 "wrap-width", 1000, NULL
);
674 column
= gtk_tree_view_column_new ();
675 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
676 gtk_tree_view_column_set_title (column
, _("Messages"));
677 gtk_tree_view_column_add_attribute
678 (column
, renderer
, "foreground-gdk", COLUMN_COLOR
);
679 gtk_tree_view_column_add_attribute
680 (column
, renderer
, "markup", COLUMN_SUMMARY
);
681 gtk_tree_view_append_column (GTK_TREE_VIEW (self
->privat
->tree_view
),
684 /* Adjust selection */
685 select
= gtk_tree_view_get_selection
686 (GTK_TREE_VIEW (self
->privat
->tree_view
));
687 gtk_tree_selection_set_mode (select
, GTK_SELECTION_BROWSE
);
689 /* Add tree view to a scrolled window */
690 scrolled_win
= gtk_scrolled_window_new (NULL
, NULL
);
691 gtk_container_add (GTK_CONTAINER (scrolled_win
),
692 self
->privat
->tree_view
);
693 gtk_widget_show (scrolled_win
);
694 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win
),
695 GTK_POLICY_AUTOMATIC
,
696 GTK_POLICY_AUTOMATIC
);
697 adj
= gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW
699 self
->privat
->adj_chgd_hdlr
= g_signal_connect(G_OBJECT(adj
), "changed",
700 G_CALLBACK (on_adjustment_changed
), self
);
701 g_signal_connect(G_OBJECT(adj
), "value_changed",
702 G_CALLBACK(on_adjustment_value_changed
), self
);
704 /* Add it to the dockitem */
705 gtk_container_add (GTK_CONTAINER (self
), scrolled_win
);
707 /* Connect signals */
708 g_signal_connect (G_OBJECT(self
->privat
->tree_view
), "event",
709 G_CALLBACK (on_message_event
), self
);
710 g_signal_connect (G_OBJECT (self
->privat
->tree_view
), "motion-notify-event",
711 G_CALLBACK (tooltip_motion_cb
), self
);
712 g_signal_connect (G_OBJECT (self
->privat
->tree_view
), "leave-notify-event",
713 G_CALLBACK (tooltip_leave_cb
), self
);
714 g_object_unref (model
);
718 message_view_class_init (MessageViewClass
* klass
)
720 GParamSpec
*message_view_spec_label
;
721 GParamSpec
*message_view_spec_pixmap
;
722 GParamSpec
*message_view_spec_highlite
;
723 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
725 parent_class
= g_type_class_peek_parent (klass
);
726 gobject_class
->set_property
= message_view_set_property
;
727 gobject_class
->get_property
= message_view_get_property
;
728 gobject_class
->finalize
= message_view_finalize
;
729 gobject_class
->dispose
= message_view_dispose
;
731 message_view_spec_label
= g_param_spec_string ("label",
733 "Used to decorate the view,"
737 g_object_class_install_property (gobject_class
,
739 message_view_spec_label
);
741 message_view_spec_pixmap
= g_param_spec_string ("pixmap",
742 "Pixmap of the view",
743 "Used to decorate the view tab,"
747 g_object_class_install_property (gobject_class
,
749 message_view_spec_pixmap
);
751 message_view_spec_highlite
= g_param_spec_boolean ("highlite",
752 "Highlite build messages",
753 "If TRUE, specify colors",
756 g_object_class_install_property (gobject_class
,
758 message_view_spec_highlite
);
761 /* Returns a new message-view instance */
763 message_view_new (AnjutaPreferences
* prefs
, GtkWidget
* popup_menu
)
765 MessageView
* mv
= MESSAGE_VIEW (g_object_new (message_view_get_type (), NULL
));
766 mv
->privat
->prefs
= prefs
;
767 mv
->privat
->popup_menu
= popup_menu
;
769 return GTK_WIDGET(mv
);
773 message_view_serialize (MessageView
*view
, AnjutaSerializer
*serializer
)
778 if (!anjuta_serializer_write_string (serializer
, "label",
779 view
->privat
->label
))
781 if (!anjuta_serializer_write_string (serializer
, "pixmap",
782 view
->privat
->pixmap
))
784 if (!anjuta_serializer_write_int (serializer
, "highlite",
785 view
->privat
->highlite
))
788 /* Serialize individual messages */
789 model
= gtk_tree_view_get_model (GTK_TREE_VIEW (view
->privat
->tree_view
));
791 if (!anjuta_serializer_write_int (serializer
, "messages",
792 gtk_tree_model_iter_n_children (model
, NULL
)))
795 if (gtk_tree_model_get_iter_first (model
, &iter
))
800 gtk_tree_model_get (model
, &iter
, COLUMN_MESSAGE
, &message
, -1);
803 if (!message_serialize (message
, serializer
))
807 while (gtk_tree_model_iter_next (model
, &iter
));
813 message_view_deserialize (MessageView
*view
, AnjutaSerializer
*serializer
)
818 if (!anjuta_serializer_read_string (serializer
, "label",
819 &view
->privat
->label
, TRUE
))
821 if (!anjuta_serializer_read_string (serializer
, "pixmap",
822 &view
->privat
->pixmap
, TRUE
))
824 if (!anjuta_serializer_read_int (serializer
, "highlite",
825 &view
->privat
->highlite
))
828 /* Create individual messages */
829 model
= gtk_tree_view_get_model (GTK_TREE_VIEW (view
->privat
->tree_view
));
830 gtk_list_store_clear (GTK_LIST_STORE (model
));
832 if (!anjuta_serializer_read_int (serializer
, "messages", &messages
))
835 for (i
= 0; i
< messages
; i
++)
839 message
= message_new (0, NULL
, NULL
);
840 if (!message_deserialize (message
, serializer
))
842 message_free (message
);
845 ianjuta_message_view_append (IANJUTA_MESSAGE_VIEW (view
), message
->type
,
846 message
->summary
, message
->details
, NULL
);
847 message_free (message
);
852 void message_view_next(MessageView
* view
)
856 GtkTreeSelection
*select
;
858 model
= gtk_tree_view_get_model (GTK_TREE_VIEW
859 (view
->privat
->tree_view
));
860 select
= gtk_tree_view_get_selection (GTK_TREE_VIEW
861 (view
->privat
->tree_view
));
863 if (!gtk_tree_selection_get_selected (select
, &model
, &iter
))
865 if (gtk_tree_model_get_iter_first (model
, &iter
))
866 gtk_tree_selection_select_iter (select
, &iter
);
868 while (gtk_tree_model_iter_next (model
, &iter
))
871 gtk_tree_model_get (model
, &iter
, COLUMN_MESSAGE
,
873 if (message
->type
!= IANJUTA_MESSAGE_VIEW_TYPE_NORMAL
874 && message
->type
!= IANJUTA_MESSAGE_VIEW_TYPE_INFO
)
876 const gchar
* message
;
877 gtk_tree_selection_select_iter (select
, &iter
);
879 ianjuta_message_view_get_current_message(IANJUTA_MESSAGE_VIEW (view
), NULL
);
883 path
= gtk_tree_model_get_path (model
, &iter
);
884 gtk_tree_view_set_cursor (GTK_TREE_VIEW
885 (view
->privat
->tree_view
),
887 gtk_tree_path_free (path
);
888 g_signal_emit_by_name (G_OBJECT (view
), "message_clicked",
896 void message_view_previous(MessageView
* view
)
900 GtkTreeSelection
*select
;
903 model
= gtk_tree_view_get_model (GTK_TREE_VIEW
904 (view
->privat
->tree_view
));
905 select
= gtk_tree_view_get_selection (GTK_TREE_VIEW
906 (view
->privat
->tree_view
));
908 if (!gtk_tree_selection_get_selected (select
, &model
, &iter
))
910 if (gtk_tree_model_get_iter_first (model
, &iter
))
911 gtk_tree_selection_select_iter (select
, &iter
);
914 /* gtk_tree_model_iter_previous does not exist, use path */
915 path
= gtk_tree_model_get_path (model
, &iter
);
917 while (gtk_tree_path_prev(path
))
920 gtk_tree_model_get_iter(model
, &iter
, path
);
921 gtk_tree_model_get (model
, &iter
, COLUMN_MESSAGE
,
923 if (message
->type
!= IANJUTA_MESSAGE_VIEW_TYPE_NORMAL
924 && message
->type
!= IANJUTA_MESSAGE_VIEW_TYPE_INFO
)
926 const gchar
* message
;
928 gtk_tree_selection_select_iter (select
, &iter
);
930 ianjuta_message_view_get_current_message(IANJUTA_MESSAGE_VIEW (view
), NULL
);
934 path
= gtk_tree_model_get_path (model
, &iter
);
935 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW
936 (view
->privat
->tree_view
),
937 path
, NULL
, FALSE
, 0, 0);
938 gtk_tree_path_free (path
);
939 g_signal_emit_by_name (G_OBJECT (view
), "message_clicked",
945 gtk_tree_path_free (path
);
948 static gboolean
message_view_save_as(MessageView
* view
, gchar
* uri
)
950 GnomeVFSHandle
* handle
;
955 if (uri
== NULL
) return FALSE
;
958 if (gnome_vfs_create (&handle
, uri
, GNOME_VFS_OPEN_WRITE
, FALSE
, 0664) != GNOME_VFS_OK
)
963 /* Save all lines of message view */
964 model
= gtk_tree_view_get_model (GTK_TREE_VIEW (view
->privat
->tree_view
));
967 gtk_tree_model_get_iter_first (model
, &iter
);
968 while (gtk_tree_model_iter_next (model
, &iter
))
971 GnomeVFSFileSize written
;
973 gtk_tree_model_get (model
, &iter
, COLUMN_MESSAGE
, &message
, -1);
976 if (message
->details
&& (strlen (message
->details
) > 0))
978 if (gnome_vfs_write (handle
, message
->details
, strlen (message
->details
), &written
) != GNOME_VFS_OK
)
985 if (gnome_vfs_write (handle
, message
->summary
, strlen (message
->summary
), &written
) != GNOME_VFS_OK
)
990 if (gnome_vfs_write (handle
, "\n", 1, &written
) != GNOME_VFS_OK
)
996 gnome_vfs_close (handle
);
1001 void message_view_save(MessageView
* view
)
1006 parent
= GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view
)));
1008 uri
= ask_user_for_save_uri (parent
);
1011 if (message_view_save_as (view
, uri
) == FALSE
)
1013 anjuta_util_dialog_error(parent
, _("Error writing %s"), uri
);
1019 /* Preferences notifications */
1021 pref_change_color (MessageView
*mview
, IAnjutaMessageViewType type
,
1022 const gchar
*color_pref_key
)
1025 GtkListStore
*store
;
1029 color
= anjuta_util_convert_color (mview
->privat
->prefs
, color_pref_key
);
1030 store
= GTK_LIST_STORE (gtk_tree_view_get_model
1031 (GTK_TREE_VIEW (mview
->privat
->tree_view
)));
1032 success
= gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store
), &iter
);
1036 gtk_tree_model_get (GTK_TREE_MODEL (store
), &iter
, COLUMN_MESSAGE
,
1038 if (message
&& message
->type
== type
)
1040 gtk_list_store_set (store
, &iter
, COLUMN_COLOR
, color
, -1);
1042 success
= gtk_tree_model_iter_next (GTK_TREE_MODEL (store
), &iter
);
1048 on_gconf_notify_color_info (GConfClient
*gclient
, guint cnxn_id
,
1049 GConfEntry
*entry
, gpointer user_data
)
1051 pref_change_color (MESSAGE_VIEW (user_data
),
1052 IANJUTA_MESSAGE_VIEW_TYPE_INFO
,
1053 "messages.color.info");
1057 on_gconf_notify_color_warning (GConfClient
*gclient
, guint cnxn_id
,
1058 GConfEntry
*entry
, gpointer user_data
)
1060 pref_change_color (MESSAGE_VIEW (user_data
),
1061 IANJUTA_MESSAGE_VIEW_TYPE_WARNING
,
1062 "messages.color.warning");
1066 on_gconf_notify_color_error (GConfClient
*gclient
, guint cnxn_id
,
1067 GConfEntry
*entry
, gpointer user_data
)
1069 pref_change_color (MESSAGE_VIEW (user_data
),
1070 IANJUTA_MESSAGE_VIEW_TYPE_ERROR
,
1071 "messages.color.error");
1074 #define REGISTER_NOTIFY(key, func) \
1075 notify_id = anjuta_preferences_notify_add (mview->privat->prefs, \
1076 key, func, mview, NULL); \
1077 mview->privat->gconf_notify_ids = g_list_prepend (mview->privat->gconf_notify_ids, \
1078 GINT_TO_POINTER(notify_id));
1080 prefs_init (MessageView
*mview
)
1083 REGISTER_NOTIFY ("messages.color.info", on_gconf_notify_color_info
);
1084 REGISTER_NOTIFY ("messages.color.warning", on_gconf_notify_color_warning
);
1085 REGISTER_NOTIFY ("messages.color.error", on_gconf_notify_color_error
);
1089 prefs_finalize (MessageView
*mview
)
1092 node
= mview
->privat
->gconf_notify_ids
;
1095 anjuta_preferences_notify_remove (mview
->privat
->prefs
,
1096 GPOINTER_TO_INT (node
->data
));
1097 node
= g_list_next (node
);
1099 g_list_free (mview
->privat
->gconf_notify_ids
);
1100 mview
->privat
->gconf_notify_ids
= NULL
;
1103 /* IAnjutaMessageView interface implementation */
1105 /* Appends the text in buffer. Flushes the buffer where a newline is found.
1106 * by emiiting buffer_flushed signal. The string is expected to be utf8.
1109 imessage_view_buffer_append (IAnjutaMessageView
* message_view
,
1110 const gchar
* message
, GError
** e
)
1114 int len
= strlen(message
);
1116 g_return_if_fail (MESSAGE_IS_VIEW (message_view
));
1117 g_return_if_fail (message
!= NULL
);
1119 view
= MESSAGE_VIEW (message_view
);
1121 /* Check if message contains newlines */
1122 for (cur_char
= 0; cur_char
< len
; cur_char
++)
1124 /* Replace "\\\n" with " " */
1125 if (message
[cur_char
] == '\\' && cur_char
< len
- 1 &&
1126 message
[cur_char
+1] == '\n')
1128 add_char(&view
->privat
->line_buffer
, ' ');
1133 /* Is newline => print line */
1134 if (message
[cur_char
] != '\n')
1136 add_char(&view
->privat
->line_buffer
, message
[cur_char
]);
1140 g_signal_emit_by_name (G_OBJECT (view
), "buffer_flushed",
1141 view
->privat
->line_buffer
);
1142 g_free(view
->privat
->line_buffer
);
1143 view
->privat
->line_buffer
= g_strdup("");
1149 imessage_view_append (IAnjutaMessageView
*message_view
,
1150 IAnjutaMessageViewType type
,
1151 const gchar
*summary
,
1152 const gchar
*details
,
1156 GtkListStore
*store
;
1166 g_return_if_fail (MESSAGE_IS_VIEW (message_view
));
1168 view
= MESSAGE_VIEW (message_view
);
1170 message
= message_new (type
, summary
, details
);
1172 g_object_get (G_OBJECT (view
), "highlite", &highlite
, NULL
);
1176 switch (message
->type
)
1178 case IANJUTA_MESSAGE_VIEW_TYPE_INFO
:
1179 color
= anjuta_util_convert_color(view
->privat
->prefs
,
1180 "messages.color.info");
1181 stock_id
= GTK_STOCK_INFO
;
1183 case IANJUTA_MESSAGE_VIEW_TYPE_WARNING
:
1184 color
= anjuta_util_convert_color(view
->privat
->prefs
,
1185 "messages.color.warning");
1186 /* FIXME: There is no GTK_STOCK_WARNING which would fit better here */
1187 stock_id
= GTK_STOCK_DIALOG_WARNING
;
1189 case IANJUTA_MESSAGE_VIEW_TYPE_ERROR
:
1190 color
= anjuta_util_convert_color(view
->privat
->prefs
,
1191 "messages.color.error");
1192 stock_id
= GTK_STOCK_STOP
;
1199 /* Add the message to the tree */
1200 store
= GTK_LIST_STORE (gtk_tree_view_get_model
1201 (GTK_TREE_VIEW (view
->privat
->tree_view
)));
1202 gtk_list_store_append (store
, &iter
);
1205 * Must be normalized to compose representation to be
1206 * displayed correctly (Bug in gtk_list???)
1208 utf8_msg
= g_utf8_normalize (message
->summary
, -1,
1209 G_NORMALIZE_DEFAULT_COMPOSE
);
1210 if (message
->details
&& strlen (message
->details
) > 0)
1213 summary
= escape_string (message
->summary
);
1214 escaped_str
= g_strdup_printf ("<b>%s</b>", summary
);
1217 escaped_str
= escape_string (message
->summary
);
1221 gtk_list_store_set (store
, &iter
,
1222 COLUMN_COLOR
, color
,
1223 COLUMN_SUMMARY
, escaped_str
,
1224 COLUMN_MESSAGE
, message
,
1225 COLUMN_PIXBUF
, stock_id
,
1228 /* Can we free the color when it's in the tree_view? */
1229 // gdk_color_free (color);
1231 gtk_list_store_set (store
, &iter
,
1232 COLUMN_SUMMARY
, escaped_str
,
1233 COLUMN_MESSAGE
, message
,
1236 message_free (message
);
1238 g_free (escaped_str
);
1241 /* Clear all messages from the message view */
1243 imessage_view_clear (IAnjutaMessageView
*message_view
, GError
**e
)
1245 GtkListStore
*store
;
1248 g_return_if_fail (MESSAGE_IS_VIEW (message_view
));
1249 view
= MESSAGE_VIEW (message_view
);
1251 store
= GTK_LIST_STORE (gtk_tree_view_get_model
1252 (GTK_TREE_VIEW (view
->privat
->tree_view
)));
1253 gtk_list_store_clear (store
);
1256 /* Move the selection to the next line. */
1258 imessage_view_select_next (IAnjutaMessageView
* message_view
,
1261 MessageView
* view
= MESSAGE_VIEW(message_view
);
1262 message_view_next(view
);
1265 /* Move the selection to the previous line. */
1267 imessage_view_select_previous (IAnjutaMessageView
* message_view
,
1270 MessageView
*view
= MESSAGE_VIEW(message_view
);
1271 message_view_previous(view
);
1274 /* Return the currently selected messages or the first message if no
1275 * message is selected or NULL if no messages are availible. The
1276 * returned message must not be freed.
1278 static const gchar
*
1279 imessage_view_get_current_message (IAnjutaMessageView
* message_view
,
1284 GtkTreeSelection
*select
;
1285 GtkTreeModel
*model
;
1286 const Message
*message
;
1288 g_return_val_if_fail (MESSAGE_IS_VIEW (message_view
), NULL
);
1290 view
= MESSAGE_VIEW (message_view
);
1291 select
= gtk_tree_view_get_selection (GTK_TREE_VIEW
1292 (view
->privat
->tree_view
));
1294 if (!gtk_tree_selection_get_selected (select
, &model
, &iter
))
1296 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model
), &iter
))
1298 gtk_tree_model_get (GTK_TREE_MODEL (model
),
1299 &iter
, COLUMN_MESSAGE
, &message
, -1);
1302 if (message
->details
&& strlen (message
->details
) > 0)
1303 return message
->details
;
1305 return message
->summary
;
1311 gtk_tree_model_get (GTK_TREE_MODEL (model
),
1312 &iter
, COLUMN_MESSAGE
, &message
, -1);
1315 if (message
->details
&& strlen (message
->details
) > 0)
1316 return message
->details
;
1318 return message
->summary
;
1324 /* Returns a GList which contains all messages, the GList itself
1325 * must be freed, the messages are managed by the message view and
1326 * must not be freed. NULL is return if no messages are availible.
1329 imessage_view_get_all_messages (IAnjutaMessageView
* message_view
,
1333 GtkListStore
*store
;
1336 GList
*messages
= NULL
;
1338 g_return_val_if_fail (MESSAGE_IS_VIEW (message_view
), NULL
);
1340 view
= MESSAGE_VIEW (message_view
);
1341 store
= GTK_LIST_STORE (gtk_tree_view_get_model
1342 (GTK_TREE_VIEW (view
->privat
->tree_view
)));
1344 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store
), &iter
))
1348 gtk_tree_model_get (GTK_TREE_MODEL (store
), &iter
,
1349 COLUMN_MESSAGE
, &message
);
1350 messages
= g_list_prepend (messages
, message
->details
);
1352 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store
), &iter
));
1358 imessage_view_iface_init (IAnjutaMessageViewIface
*iface
)
1360 iface
->buffer_append
= imessage_view_buffer_append
;
1361 iface
->append
= imessage_view_append
;
1362 iface
->clear
= imessage_view_clear
;
1363 iface
->select_next
= imessage_view_select_next
;
1364 iface
->select_previous
= imessage_view_select_previous
;
1365 iface
->get_current_message
= imessage_view_get_current_message
;
1366 iface
->get_all_messages
= imessage_view_get_all_messages
;
1369 ANJUTA_TYPE_BEGIN(MessageView
, message_view
, GTK_TYPE_HBOX
);
1370 ANJUTA_TYPE_ADD_INTERFACE(imessage_view
, IANJUTA_TYPE_MESSAGE_VIEW
);