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 <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 #define HAVE_TOOLTIP_API (GTK_MAJOR_VERSION > 2 || (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 12))
29 struct _MessageViewPrivate
36 AnjutaPreferences
* prefs
;
37 GtkWidget
*popup_menu
;
47 GdkRectangle tooltip_rect
;
48 GtkWidget
*tooltip_window
;
49 gulong tooltip_timeout
;
50 PangoLayout
*tooltip_layout
;
53 /* gconf notification ids */
54 GList
*gconf_notify_ids
;
59 IAnjutaMessageViewType type
;
82 static gpointer parent_class
;
84 static void prefs_init (MessageView
*mview
);
85 static void prefs_finalize (MessageView
*mview
);
87 /* Ask the user for an uri name */
89 ask_user_for_save_uri (GtkWindow
* parent
)
94 dialog
= gtk_file_chooser_dialog_new (_("Save file as"), parent
,
95 GTK_FILE_CHOOSER_ACTION_SAVE
, GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
96 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
, NULL
);
98 if(gtk_dialog_run(GTK_DIALOG(dialog
)) == GTK_RESPONSE_ACCEPT
)
100 uri
= gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog
));
107 gtk_widget_destroy(dialog
);
112 /* Message object creation, copy and freeing */
114 message_new (IAnjutaMessageViewType type
, const gchar
*summary
,
115 const gchar
*details
)
117 /* DEBUG_PRINT ("Creating message"); */
118 Message
*message
= g_new0 (Message
, 1);
119 message
->type
= type
;
121 message
->summary
= g_strdup (summary
);
123 message
->details
= g_strdup (details
);
128 message_copy (const Message
*src
)
130 return message_new (src
->type
, src
->summary
, src
->details
);
134 message_free (Message
*message
)
136 /* DEBUG_PRINT ("Freeing message"); */
137 g_free (message
->summary
);
138 g_free (message
->details
);
143 message_serialize (Message
*message
, AnjutaSerializer
*serializer
)
145 if (!anjuta_serializer_write_int (serializer
, "type",
148 if (!anjuta_serializer_write_string (serializer
, "summary",
151 if (!anjuta_serializer_write_string (serializer
, "details",
158 message_deserialize (Message
*message
, AnjutaSerializer
*serializer
)
161 if (!anjuta_serializer_read_int (serializer
, "type",
164 message
->type
= type
;
165 if (!anjuta_serializer_read_string (serializer
, "summary",
166 &message
->summary
, TRUE
))
168 if (!anjuta_serializer_read_string (serializer
, "details",
169 &message
->details
, TRUE
))
177 static GType type
= 0;
180 type
= g_boxed_type_register_static ("MessageViewMessage",
181 (GBoxedCopyFunc
) message_copy
,
182 (GBoxedFreeFunc
) message_free
);
187 /* Utility functions */
188 /* Adds the char c to the string str */
190 add_char(gchar
** str
, gchar c
)
194 g_return_if_fail(str
!= NULL
);
196 buffer
= g_strdup_printf("%s%c", *str
, c
);
202 escape_string (const gchar
*str
)
207 gstr
= g_string_new ("");
209 while (*iter
!= '\0')
212 gstr
= g_string_append (gstr
, ">");
213 else if (*iter
== '<')
214 gstr
= g_string_append (gstr
, "<");
215 else if (*iter
== '&')
216 gstr
= g_string_append (gstr
, "&");
218 gstr
= g_string_append_c (gstr
, *iter
);
221 return g_string_free (gstr
, FALSE
);
226 message_view_query_tooltip (GtkWidget
* widget
, gint x
, gint y
, gboolean keyboard
,
232 MessageView
* view
= MESSAGE_VIEW(widget
);
234 model
= gtk_tree_view_get_model (GTK_TREE_VIEW (view
->privat
->tree_view
));
236 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW(view
->privat
->tree_view
),
237 x
, y
, &path
, NULL
, NULL
, NULL
))
240 gchar
*text
, *title
, *desc
;
242 gtk_tree_model_get_iter (model
, &iter
, path
);
243 gtk_tree_model_get (model
, &iter
, COLUMN_MESSAGE
, &message
, -1);
244 gtk_tree_path_free(path
);
246 if (!message
->details
|| !message
->summary
||
247 strlen (message
->details
) <= 0 ||
248 strlen (message
->summary
) <= 0)
251 title
= escape_string (message
->summary
);
252 desc
= escape_string (message
->details
);
253 text
= g_strdup_printf ("<b>%s</b>\n%s", title
, desc
);
258 gtk_tooltip_set_markup (tooltip
, text
);
266 #if !HAVE_TOOLTIP_API
267 /* Tooltip operations -- taken from gtodo */
270 tooltip_get_display_text (MessageView
*view
)
276 model
= gtk_tree_view_get_model (GTK_TREE_VIEW (view
->privat
->tree_view
));
278 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW(view
->privat
->tree_view
),
279 view
->privat
->tooltip_rect
.x
, view
->privat
->tooltip_rect
.y
,
280 &path
, NULL
, NULL
, NULL
))
283 gchar
*text
, *title
, *desc
;
285 gtk_tree_model_get_iter (model
, &iter
, path
);
286 gtk_tree_model_get (model
, &iter
, COLUMN_MESSAGE
, &message
, -1);
287 gtk_tree_path_free(path
);
289 if (!message
->details
|| !message
->summary
||
290 strlen (message
->details
) <= 0 ||
291 strlen (message
->summary
) <= 0)
294 title
= escape_string (message
->summary
);
295 desc
= escape_string (message
->details
);
296 text
= g_strdup_printf ("<b>%s</b>\n%s", title
, desc
);
307 tooltip_paint (GtkWidget
*widget
, GdkEventExpose
*event
, MessageView
*view
)
312 tooltiptext
= tooltip_get_display_text (view
);
315 tooltiptext
= g_strdup (_("No message details"));
317 pango_layout_set_markup (view
->privat
->tooltip_layout
,
319 strlen (tooltiptext
));
320 pango_layout_set_wrap(view
->privat
->tooltip_layout
, PANGO_WRAP_CHAR
);
321 pango_layout_set_width(view
->privat
->tooltip_layout
, 600000);
322 style
= view
->privat
->tooltip_window
->style
;
324 gtk_paint_flat_box (style
, view
->privat
->tooltip_window
->window
,
325 GTK_STATE_NORMAL
, GTK_SHADOW_OUT
,
326 NULL
, view
->privat
->tooltip_window
,
327 "tooltip", 0, 0, -1, -1);
329 gtk_paint_layout (style
, view
->privat
->tooltip_window
->window
,
330 GTK_STATE_NORMAL
, TRUE
,
331 NULL
, view
->privat
->tooltip_window
,
332 "tooltip", 4, 4, view
->privat
->tooltip_layout
);
334 g_object_unref(layout);
341 tooltip_timeout (MessageView
*view
)
343 gint scr_w
,scr_h
, w
, h
, x
, y
;
346 tooltiptext
= tooltip_get_display_text (view
);
349 tooltiptext
= g_strdup (_("No message details"));
351 view
->privat
->tooltip_window
= gtk_window_new (GTK_WINDOW_POPUP
);
352 view
->privat
->tooltip_window
->parent
= view
->privat
->tree_view
;
353 gtk_widget_set_app_paintable (view
->privat
->tooltip_window
, TRUE
);
354 gtk_window_set_resizable (GTK_WINDOW(view
->privat
->tooltip_window
), FALSE
);
355 gtk_widget_set_name (view
->privat
->tooltip_window
, "gtk-tooltips");
356 g_signal_connect (G_OBJECT(view
->privat
->tooltip_window
), "expose_event",
357 G_CALLBACK(tooltip_paint
), view
);
358 gtk_widget_ensure_style (view
->privat
->tooltip_window
);
360 view
->privat
->tooltip_layout
=
361 gtk_widget_create_pango_layout (view
->privat
->tooltip_window
, NULL
);
362 pango_layout_set_wrap (view
->privat
->tooltip_layout
, PANGO_WRAP_CHAR
);
363 pango_layout_set_width (view
->privat
->tooltip_layout
, 600000);
364 pango_layout_set_markup (view
->privat
->tooltip_layout
, tooltiptext
,
365 strlen (tooltiptext
));
366 scr_w
= gdk_screen_width();
367 scr_h
= gdk_screen_height();
368 pango_layout_get_size (view
->privat
->tooltip_layout
, &w
, &h
);
369 w
= PANGO_PIXELS(w
) + 8;
370 h
= PANGO_PIXELS(h
) + 8;
372 gdk_window_get_pointer (NULL
, &x
, &y
, NULL
);
373 if (GTK_WIDGET_NO_WINDOW (view
->privat
->tree_view
))
374 y
+= view
->privat
->tree_view
->allocation
.y
;
379 x
-= (x
+ w
) - scr_w
;
383 if ((y
+ h
+ 4) > scr_h
)
388 g_object_unref(layout);
390 gtk_widget_set_size_request (view
->privat
->tooltip_window
, w
, h
);
391 gtk_window_move (GTK_WINDOW (view
->privat
->tooltip_window
), x
, y
);
392 gtk_widget_show (view
->privat
->tooltip_window
);
393 g_free (tooltiptext
);
399 tooltip_motion_cb (GtkWidget
*tv
, GdkEventMotion
*event
, MessageView
*view
)
403 if (view
->privat
->tooltip_rect
.y
== 0 &&
404 view
->privat
->tooltip_rect
.height
== 0 &&
405 view
->privat
->tooltip_timeout
)
407 g_source_remove (view
->privat
->tooltip_timeout
);
408 view
->privat
->tooltip_timeout
= 0;
409 if (view
->privat
->tooltip_window
) {
410 gtk_widget_destroy (view
->privat
->tooltip_window
);
411 view
->privat
->tooltip_window
= NULL
;
415 if (view
->privat
->tooltip_timeout
) {
416 if (((int)event
->y
> view
->privat
->tooltip_rect
.y
) &&
417 (((int)event
->y
- view
->privat
->tooltip_rect
.height
)
418 < view
->privat
->tooltip_rect
.y
))
423 g_source_remove (view
->privat
->tooltip_timeout
);
424 view
->privat
->tooltip_timeout
= 0;
427 /* We've left the cell. Remove the timeout and create a new one below */
428 if (view
->privat
->tooltip_window
) {
429 gtk_widget_destroy (view
->privat
->tooltip_window
);
430 view
->privat
->tooltip_window
= NULL
;
432 g_source_remove (view
->privat
->tooltip_timeout
);
433 view
->privat
->tooltip_timeout
= 0;
436 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW(view
->privat
->tree_view
),
437 event
->x
, event
->y
, &path
,
440 GtkTreeSelection
*selection
;
442 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW(view
->privat
->tree_view
));
443 if (gtk_tree_selection_path_is_selected (selection
, path
))
445 gtk_tree_view_get_cell_area (GTK_TREE_VIEW (view
->privat
->tree_view
),
446 path
, NULL
, &view
->privat
->tooltip_rect
);
448 if (view
->privat
->tooltip_rect
.y
!= 0 &&
449 view
->privat
->tooltip_rect
.height
!= 0)
453 tooltiptext
= tooltip_get_display_text (view
);
454 if (tooltiptext
== NULL
)
456 g_free (tooltiptext
);
458 view
->privat
->tooltip_timeout
=
459 g_timeout_add (500, (GSourceFunc
) tooltip_timeout
, view
);
462 gtk_tree_path_free (path
);
468 tooltip_leave_cb (GtkWidget
*w
, GdkEventCrossing
*e
, MessageView
*view
)
470 if (view
->privat
->tooltip_timeout
) {
471 g_source_remove (view
->privat
->tooltip_timeout
);
472 view
->privat
->tooltip_timeout
= 0;
474 if (view
->privat
->tooltip_window
) {
475 gtk_widget_destroy (view
->privat
->tooltip_window
);
476 g_object_unref (view
->privat
->tooltip_layout
);
477 view
->privat
->tooltip_window
= NULL
;
483 /* MessageView signal callbacks */
484 /* Send a signal if a message was double-clicked or ENTER or SPACE was pressed */
486 on_message_event (GObject
* object
, GdkEvent
* event
, gpointer data
)
488 g_return_val_if_fail(object
!= NULL
, FALSE
);
489 g_return_val_if_fail(event
!= NULL
, FALSE
);
490 g_return_val_if_fail(data
!= NULL
, FALSE
);
492 MessageView
* view
= MESSAGE_VIEW(data
);
497 if (event
->type
== GDK_KEY_PRESS
)
499 switch(((GdkEventKey
*)event
)->keyval
)
504 const gchar
* message
=
505 ianjuta_message_view_get_current_message(IANJUTA_MESSAGE_VIEW (view
), NULL
);
508 g_signal_emit_by_name (G_OBJECT (view
), "message_clicked",
518 else if (event
->type
== GDK_2BUTTON_PRESS
)
520 if (((GdkEventButton
*) event
)->button
== 1)
522 const gchar
* message
=
523 ianjuta_message_view_get_current_message(IANJUTA_MESSAGE_VIEW (view
), NULL
);
526 g_signal_emit_by_name (G_OBJECT (view
), "message_clicked",
533 else if (event
->type
== GDK_BUTTON_PRESS
)
535 if (((GdkEventButton
*) event
)->button
== 3)
537 gtk_menu_popup (GTK_MENU (view
->privat
->popup_menu
), NULL
, NULL
, NULL
, NULL
,
538 ((GdkEventButton
*) event
)->button
,
539 ((GdkEventButton
*) event
)->time
);
547 on_adjustment_changed (GtkAdjustment
* adj
, gpointer data
)
549 gtk_adjustment_set_value (adj
, adj
->upper
- adj
->page_size
);
553 on_adjustment_value_changed (GtkAdjustment
* adj
, gpointer data
)
555 MessageView
*self
= MESSAGE_VIEW (data
);
556 if (adj
->value
> (adj
->upper
- adj
->page_size
) - 0.1)
558 if (!self
->privat
->adj_chgd_hdlr
)
560 self
->privat
->adj_chgd_hdlr
=
561 g_signal_connect (G_OBJECT (adj
), "changed",
562 G_CALLBACK (on_adjustment_changed
), NULL
);
567 if (self
->privat
->adj_chgd_hdlr
)
569 g_signal_handler_disconnect (G_OBJECT (adj
), self
->privat
->adj_chgd_hdlr
);
570 self
->privat
->adj_chgd_hdlr
= 0;
576 message_view_set_property (GObject
* object
,
578 const GValue
* value
, GParamSpec
* pspec
)
580 MessageView
*self
= MESSAGE_VIEW (object
);
581 g_return_if_fail(value
!= NULL
);
582 g_return_if_fail(pspec
!= NULL
);
588 g_free (self
->privat
->label
);
589 self
->privat
->label
= g_value_dup_string (value
);
594 g_free (self
->privat
->pixmap
);
595 self
->privat
->pixmap
= g_value_dup_string (value
);
598 case MV_PROP_HIGHLITE
:
600 self
->privat
->highlite
= g_value_get_boolean (value
);
605 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
612 message_view_get_property (GObject
* object
,
614 GValue
* value
, GParamSpec
* pspec
)
616 MessageView
*self
= MESSAGE_VIEW (object
);
618 g_return_if_fail(value
!= NULL
);
619 g_return_if_fail(pspec
!= NULL
);
625 g_value_set_string (value
, self
->privat
->label
);
630 g_value_set_string (value
, self
->privat
->pixmap
);
633 case MV_PROP_HIGHLITE
:
635 g_value_set_boolean (value
, self
->privat
->highlite
);
640 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
647 message_view_dispose (GObject
*obj
)
649 MessageView
*mview
= MESSAGE_VIEW (obj
);
650 if (mview
->privat
->gconf_notify_ids
)
652 prefs_finalize (mview
);
653 mview
->privat
->gconf_notify_ids
= NULL
;
655 #if !HAVE_TOOLTIP_API
656 if (mview
->privat
->tooltip_timeout
) {
657 g_source_remove (mview
->privat
->tooltip_timeout
);
658 mview
->privat
->tooltip_timeout
= 0;
661 if (mview
->privat
->tooltip_window
) {
662 gtk_widget_destroy (mview
->privat
->tooltip_window
);
663 g_object_unref (mview
->privat
->tooltip_layout
);
664 mview
->privat
->tooltip_window
= NULL
;
667 if (mview
->privat
->tree_view
)
669 mview
->privat
->tree_view
= NULL
;
671 GNOME_CALL_PARENT (G_OBJECT_CLASS
, dispose
, (G_OBJECT(obj
)));
675 message_view_finalize (GObject
*obj
)
677 MessageView
*mview
= MESSAGE_VIEW (obj
);
678 g_free (mview
->privat
->line_buffer
);
679 g_free (mview
->privat
->label
);
680 g_free (mview
->privat
->pixmap
);
681 g_free (mview
->privat
);
682 GNOME_CALL_PARENT (G_OBJECT_CLASS
, finalize
, (G_OBJECT(obj
)));
686 message_view_instance_init (MessageView
* self
)
688 GtkWidget
*scrolled_win
;
689 GtkCellRenderer
*renderer
;
690 GtkCellRenderer
*renderer_pixbuf
;
691 GtkTreeViewColumn
*column
;
692 GtkTreeViewColumn
*column_pixbuf
;
693 GtkTreeSelection
*select
;
697 g_return_if_fail(self
!= NULL
);
698 self
->privat
= g_new0 (MessageViewPrivate
, 1);
700 /* Init private data */
701 self
->privat
->line_buffer
= g_strdup("");
703 /* Create the tree widget */
704 model
= gtk_list_store_new (N_COLUMNS
, G_TYPE_STRING
,
705 G_TYPE_STRING
, MESSAGE_TYPE
, G_TYPE_STRING
);
706 self
->privat
->tree_view
=
707 gtk_tree_view_new_with_model (GTK_TREE_MODEL (model
));
708 gtk_widget_show (self
->privat
->tree_view
);
709 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW
710 (self
->privat
->tree_view
), FALSE
);
712 /* Create pixbuf column */
713 renderer_pixbuf
= gtk_cell_renderer_pixbuf_new ();
714 column_pixbuf
= gtk_tree_view_column_new ();
715 gtk_tree_view_column_set_title (column_pixbuf
, _("Icon"));
716 gtk_tree_view_column_pack_start (column_pixbuf
, renderer_pixbuf
, TRUE
);
717 gtk_tree_view_column_add_attribute
718 (column_pixbuf
, renderer_pixbuf
, "stock-id", COLUMN_PIXBUF
);
719 gtk_tree_view_append_column (GTK_TREE_VIEW (self
->privat
->tree_view
),
721 /* Create columns to hold text and color of a line, this
722 * columns are invisible of course. */
723 renderer
= gtk_cell_renderer_text_new ();
724 g_object_set (renderer
, "yalign", 0.0, "wrap-mode", PANGO_WRAP_WORD
,
725 "wrap-width", 1000, NULL
);
726 column
= gtk_tree_view_column_new ();
727 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
728 gtk_tree_view_column_set_title (column
, _("Messages"));
729 gtk_tree_view_column_add_attribute
730 (column
, renderer
, "foreground", COLUMN_COLOR
);
731 gtk_tree_view_column_add_attribute
732 (column
, renderer
, "markup", COLUMN_SUMMARY
);
733 gtk_tree_view_append_column (GTK_TREE_VIEW (self
->privat
->tree_view
),
736 /* Adjust selection */
737 select
= gtk_tree_view_get_selection
738 (GTK_TREE_VIEW (self
->privat
->tree_view
));
739 gtk_tree_selection_set_mode (select
, GTK_SELECTION_BROWSE
);
741 /* Add tree view to a scrolled window */
742 scrolled_win
= gtk_scrolled_window_new (NULL
, NULL
);
743 gtk_container_add (GTK_CONTAINER (scrolled_win
),
744 self
->privat
->tree_view
);
745 gtk_widget_show (scrolled_win
);
746 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win
),
747 GTK_POLICY_AUTOMATIC
,
748 GTK_POLICY_AUTOMATIC
);
749 adj
= gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW
751 self
->privat
->adj_chgd_hdlr
= g_signal_connect(G_OBJECT(adj
), "changed",
752 G_CALLBACK (on_adjustment_changed
), self
);
753 g_signal_connect(G_OBJECT(adj
), "value_changed",
754 G_CALLBACK(on_adjustment_value_changed
), self
);
756 /* Add it to the dockitem */
757 gtk_container_add (GTK_CONTAINER (self
), scrolled_win
);
759 /* Connect signals */
760 g_signal_connect (G_OBJECT(self
->privat
->tree_view
), "event",
761 G_CALLBACK (on_message_event
), self
);
762 #if !HAVE_TOOLTIP_API
763 g_signal_connect (G_OBJECT (self
->privat
->tree_view
), "motion-notify-event",
764 G_CALLBACK (tooltip_motion_cb
), self
);
765 g_signal_connect (G_OBJECT (self
->privat
->tree_view
), "leave-notify-event",
766 G_CALLBACK (tooltip_leave_cb
), self
);
768 g_object_set (G_OBJECT(self
), "has-tooltip", TRUE
, NULL
);
770 g_object_unref (model
);
774 message_view_class_init (MessageViewClass
* klass
)
776 GParamSpec
*message_view_spec_label
;
777 GParamSpec
*message_view_spec_pixmap
;
778 GParamSpec
*message_view_spec_highlite
;
779 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
780 GtkWidgetClass
* widget_class
= GTK_WIDGET_CLASS (klass
);
782 parent_class
= g_type_class_peek_parent (klass
);
783 gobject_class
->set_property
= message_view_set_property
;
784 gobject_class
->get_property
= message_view_get_property
;
785 gobject_class
->finalize
= message_view_finalize
;
786 gobject_class
->dispose
= message_view_dispose
;
789 widget_class
->query_tooltip
= message_view_query_tooltip
;
792 message_view_spec_label
= g_param_spec_string ("label",
794 "Used to decorate the view,"
798 g_object_class_install_property (gobject_class
,
800 message_view_spec_label
);
802 message_view_spec_pixmap
= g_param_spec_string ("pixmap",
803 "Pixmap of the view",
804 "Used to decorate the view tab,"
808 g_object_class_install_property (gobject_class
,
810 message_view_spec_pixmap
);
812 message_view_spec_highlite
= g_param_spec_boolean ("highlite",
813 "Highlite build messages",
814 "If TRUE, specify colors",
817 g_object_class_install_property (gobject_class
,
819 message_view_spec_highlite
);
822 /* Returns a new message-view instance */
824 message_view_new (AnjutaPreferences
* prefs
, GtkWidget
* popup_menu
)
826 MessageView
* mv
= MESSAGE_VIEW (g_object_new (message_view_get_type (), NULL
));
827 mv
->privat
->prefs
= prefs
;
828 mv
->privat
->popup_menu
= popup_menu
;
830 return GTK_WIDGET(mv
);
834 message_view_serialize (MessageView
*view
, AnjutaSerializer
*serializer
)
839 if (!anjuta_serializer_write_string (serializer
, "label",
840 view
->privat
->label
))
842 if (!anjuta_serializer_write_string (serializer
, "pixmap",
843 view
->privat
->pixmap
))
845 if (!anjuta_serializer_write_int (serializer
, "highlite",
846 view
->privat
->highlite
))
849 /* Serialize individual messages */
850 model
= gtk_tree_view_get_model (GTK_TREE_VIEW (view
->privat
->tree_view
));
852 if (!anjuta_serializer_write_int (serializer
, "messages",
853 gtk_tree_model_iter_n_children (model
, NULL
)))
856 if (gtk_tree_model_get_iter_first (model
, &iter
))
861 gtk_tree_model_get (model
, &iter
, COLUMN_MESSAGE
, &message
, -1);
864 if (!message_serialize (message
, serializer
))
868 while (gtk_tree_model_iter_next (model
, &iter
));
874 message_view_deserialize (MessageView
*view
, AnjutaSerializer
*serializer
)
879 if (!anjuta_serializer_read_string (serializer
, "label",
880 &view
->privat
->label
, TRUE
))
882 if (!anjuta_serializer_read_string (serializer
, "pixmap",
883 &view
->privat
->pixmap
, TRUE
))
885 if (!anjuta_serializer_read_int (serializer
, "highlite",
886 &view
->privat
->highlite
))
889 /* Create individual messages */
890 model
= gtk_tree_view_get_model (GTK_TREE_VIEW (view
->privat
->tree_view
));
891 gtk_list_store_clear (GTK_LIST_STORE (model
));
893 if (!anjuta_serializer_read_int (serializer
, "messages", &messages
))
896 for (i
= 0; i
< messages
; i
++)
900 message
= message_new (0, NULL
, NULL
);
901 if (!message_deserialize (message
, serializer
))
903 message_free (message
);
906 ianjuta_message_view_append (IANJUTA_MESSAGE_VIEW (view
), message
->type
,
907 message
->summary
, message
->details
, NULL
);
908 message_free (message
);
913 void message_view_next(MessageView
* view
)
917 GtkTreeSelection
*select
;
919 model
= gtk_tree_view_get_model (GTK_TREE_VIEW
920 (view
->privat
->tree_view
));
921 select
= gtk_tree_view_get_selection (GTK_TREE_VIEW
922 (view
->privat
->tree_view
));
924 if (!gtk_tree_selection_get_selected (select
, &model
, &iter
))
926 if (gtk_tree_model_get_iter_first (model
, &iter
))
927 gtk_tree_selection_select_iter (select
, &iter
);
929 while (gtk_tree_model_iter_next (model
, &iter
))
932 gtk_tree_model_get (model
, &iter
, COLUMN_MESSAGE
,
934 if (message
->type
!= IANJUTA_MESSAGE_VIEW_TYPE_NORMAL
935 && message
->type
!= IANJUTA_MESSAGE_VIEW_TYPE_INFO
)
937 const gchar
* message
;
938 gtk_tree_selection_select_iter (select
, &iter
);
940 ianjuta_message_view_get_current_message(IANJUTA_MESSAGE_VIEW (view
), NULL
);
944 path
= gtk_tree_model_get_path (model
, &iter
);
945 gtk_tree_view_set_cursor (GTK_TREE_VIEW
946 (view
->privat
->tree_view
),
948 gtk_tree_path_free (path
);
949 g_signal_emit_by_name (G_OBJECT (view
), "message_clicked",
957 void message_view_previous(MessageView
* view
)
961 GtkTreeSelection
*select
;
964 model
= gtk_tree_view_get_model (GTK_TREE_VIEW
965 (view
->privat
->tree_view
));
966 select
= gtk_tree_view_get_selection (GTK_TREE_VIEW
967 (view
->privat
->tree_view
));
969 if (!gtk_tree_selection_get_selected (select
, &model
, &iter
))
971 if (gtk_tree_model_get_iter_first (model
, &iter
))
972 gtk_tree_selection_select_iter (select
, &iter
);
975 /* gtk_tree_model_iter_previous does not exist, use path */
976 path
= gtk_tree_model_get_path (model
, &iter
);
978 while (gtk_tree_path_prev(path
))
981 gtk_tree_model_get_iter(model
, &iter
, path
);
982 gtk_tree_model_get (model
, &iter
, COLUMN_MESSAGE
,
984 if (message
->type
!= IANJUTA_MESSAGE_VIEW_TYPE_NORMAL
985 && message
->type
!= IANJUTA_MESSAGE_VIEW_TYPE_INFO
)
987 const gchar
* message
;
989 gtk_tree_selection_select_iter (select
, &iter
);
991 ianjuta_message_view_get_current_message(IANJUTA_MESSAGE_VIEW (view
), NULL
);
995 path
= gtk_tree_model_get_path (model
, &iter
);
996 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW
997 (view
->privat
->tree_view
),
998 path
, NULL
, FALSE
, 0, 0);
999 gtk_tree_path_free (path
);
1000 g_signal_emit_by_name (G_OBJECT (view
), "message_clicked",
1006 gtk_tree_path_free (path
);
1009 static gboolean
message_view_save_as(MessageView
* view
, gchar
* uri
)
1011 GnomeVFSHandle
* handle
;
1013 GtkTreeModel
*model
;
1016 if (uri
== NULL
) return FALSE
;
1019 if (gnome_vfs_create (&handle
, uri
, GNOME_VFS_OPEN_WRITE
, FALSE
, 0664) != GNOME_VFS_OK
)
1024 /* Save all lines of message view */
1025 model
= gtk_tree_view_get_model (GTK_TREE_VIEW (view
->privat
->tree_view
));
1028 gtk_tree_model_get_iter_first (model
, &iter
);
1029 while (gtk_tree_model_iter_next (model
, &iter
))
1032 GnomeVFSFileSize written
;
1034 gtk_tree_model_get (model
, &iter
, COLUMN_MESSAGE
, &message
, -1);
1037 if (message
->details
&& (strlen (message
->details
) > 0))
1039 if (gnome_vfs_write (handle
, message
->details
, strlen (message
->details
), &written
) != GNOME_VFS_OK
)
1046 if (gnome_vfs_write (handle
, message
->summary
, strlen (message
->summary
), &written
) != GNOME_VFS_OK
)
1051 if (gnome_vfs_write (handle
, "\n", 1, &written
) != GNOME_VFS_OK
)
1057 gnome_vfs_close (handle
);
1062 void message_view_save(MessageView
* view
)
1067 parent
= GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view
)));
1069 uri
= ask_user_for_save_uri (parent
);
1072 if (message_view_save_as (view
, uri
) == FALSE
)
1074 anjuta_util_dialog_error(parent
, _("Error writing %s"), uri
);
1080 /* Preferences notifications */
1082 pref_change_color (MessageView
*mview
, IAnjutaMessageViewType type
,
1083 const gchar
*color_pref_key
)
1086 GtkListStore
*store
;
1090 color
= anjuta_preferences_get (mview
->privat
->prefs
, color_pref_key
);
1091 store
= GTK_LIST_STORE (gtk_tree_view_get_model
1092 (GTK_TREE_VIEW (mview
->privat
->tree_view
)));
1093 success
= gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store
), &iter
);
1097 gtk_tree_model_get (GTK_TREE_MODEL (store
), &iter
, COLUMN_MESSAGE
,
1099 if (message
&& message
->type
== type
)
1101 gtk_list_store_set (store
, &iter
, COLUMN_COLOR
, color
, -1);
1103 success
= gtk_tree_model_iter_next (GTK_TREE_MODEL (store
), &iter
);
1110 on_gconf_notify_color_warning (GConfClient
*gclient
, guint cnxn_id
,
1111 GConfEntry
*entry
, gpointer user_data
)
1113 pref_change_color (MESSAGE_VIEW (user_data
),
1114 IANJUTA_MESSAGE_VIEW_TYPE_WARNING
,
1115 "messages.color.warning");
1119 on_gconf_notify_color_error (GConfClient
*gclient
, guint cnxn_id
,
1120 GConfEntry
*entry
, gpointer user_data
)
1122 pref_change_color (MESSAGE_VIEW (user_data
),
1123 IANJUTA_MESSAGE_VIEW_TYPE_ERROR
,
1124 "messages.color.error");
1127 #define REGISTER_NOTIFY(key, func) \
1128 notify_id = anjuta_preferences_notify_add (mview->privat->prefs, \
1129 key, func, mview, NULL); \
1130 mview->privat->gconf_notify_ids = g_list_prepend (mview->privat->gconf_notify_ids, \
1131 GINT_TO_POINTER(notify_id));
1133 prefs_init (MessageView
*mview
)
1136 REGISTER_NOTIFY ("messages.color.warning", on_gconf_notify_color_warning
);
1137 REGISTER_NOTIFY ("messages.color.error", on_gconf_notify_color_error
);
1141 prefs_finalize (MessageView
*mview
)
1144 node
= mview
->privat
->gconf_notify_ids
;
1147 anjuta_preferences_notify_remove (mview
->privat
->prefs
,
1148 GPOINTER_TO_INT (node
->data
));
1149 node
= g_list_next (node
);
1151 g_list_free (mview
->privat
->gconf_notify_ids
);
1152 mview
->privat
->gconf_notify_ids
= NULL
;
1155 /* IAnjutaMessageView interface implementation */
1157 /* Appends the text in buffer. Flushes the buffer where a newline is found.
1158 * by emiiting buffer_flushed signal. The string is expected to be utf8.
1161 imessage_view_buffer_append (IAnjutaMessageView
* message_view
,
1162 const gchar
* message
, GError
** e
)
1166 int len
= strlen(message
);
1168 g_return_if_fail (MESSAGE_IS_VIEW (message_view
));
1169 g_return_if_fail (message
!= NULL
);
1171 view
= MESSAGE_VIEW (message_view
);
1173 /* Check if message contains newlines */
1174 for (cur_char
= 0; cur_char
< len
; cur_char
++)
1176 /* Replace "\\\n" with " " */
1177 if (message
[cur_char
] == '\\' && cur_char
< len
- 1 &&
1178 message
[cur_char
+1] == '\n')
1180 add_char(&view
->privat
->line_buffer
, ' ');
1185 /* Is newline => print line */
1186 if (message
[cur_char
] != '\n')
1188 add_char(&view
->privat
->line_buffer
, message
[cur_char
]);
1192 g_signal_emit_by_name (G_OBJECT (view
), "buffer_flushed",
1193 view
->privat
->line_buffer
);
1194 g_free(view
->privat
->line_buffer
);
1195 view
->privat
->line_buffer
= g_strdup("");
1201 imessage_view_append (IAnjutaMessageView
*message_view
,
1202 IAnjutaMessageViewType type
,
1203 const gchar
*summary
,
1204 const gchar
*details
,
1208 GtkListStore
*store
;
1213 gchar
* stock_id
= NULL
;
1218 g_return_if_fail (MESSAGE_IS_VIEW (message_view
));
1220 view
= MESSAGE_VIEW (message_view
);
1222 message
= message_new (type
, summary
, details
);
1224 g_object_get (G_OBJECT (view
), "highlite", &highlite
, NULL
);
1228 switch (message
->type
)
1230 case IANJUTA_MESSAGE_VIEW_TYPE_INFO
:
1231 stock_id
= GTK_STOCK_INFO
;
1233 case IANJUTA_MESSAGE_VIEW_TYPE_WARNING
:
1234 color
= anjuta_preferences_get (view
->privat
->prefs
,
1235 "messages.color.warning");
1236 /* FIXME: There is no GTK_STOCK_WARNING which would fit better here */
1237 stock_id
= GTK_STOCK_DIALOG_WARNING
;
1239 case IANJUTA_MESSAGE_VIEW_TYPE_ERROR
:
1240 color
= anjuta_preferences_get (view
->privat
->prefs
,
1241 "messages.color.error");
1242 stock_id
= GTK_STOCK_STOP
;
1249 /* Add the message to the tree */
1250 store
= GTK_LIST_STORE (gtk_tree_view_get_model
1251 (GTK_TREE_VIEW (view
->privat
->tree_view
)));
1252 gtk_list_store_append (store
, &iter
);
1255 * Must be normalized to compose representation to be
1256 * displayed correctly (Bug in gtk_list???)
1258 utf8_msg
= g_utf8_normalize (message
->summary
, -1,
1259 G_NORMALIZE_DEFAULT_COMPOSE
);
1260 if (message
->details
&& strlen (message
->details
) > 0)
1263 summary
= escape_string (message
->summary
);
1264 escaped_str
= g_strdup_printf ("<b>%s</b>", summary
);
1267 escaped_str
= escape_string (message
->summary
);
1269 gtk_list_store_set (store
, &iter
,
1270 COLUMN_SUMMARY
, escaped_str
,
1271 COLUMN_MESSAGE
, message
,
1275 gtk_list_store_set (store
, &iter
,
1276 COLUMN_COLOR
, color
, -1);
1280 gtk_list_store_set (store
, &iter
,
1281 COLUMN_PIXBUF
, stock_id
, -1);
1284 message_free (message
);
1286 g_free (escaped_str
);
1289 /* Clear all messages from the message view */
1291 imessage_view_clear (IAnjutaMessageView
*message_view
, GError
**e
)
1293 GtkListStore
*store
;
1296 g_return_if_fail (MESSAGE_IS_VIEW (message_view
));
1297 view
= MESSAGE_VIEW (message_view
);
1299 store
= GTK_LIST_STORE (gtk_tree_view_get_model
1300 (GTK_TREE_VIEW (view
->privat
->tree_view
)));
1301 gtk_list_store_clear (store
);
1304 /* Move the selection to the next line. */
1306 imessage_view_select_next (IAnjutaMessageView
* message_view
,
1309 MessageView
* view
= MESSAGE_VIEW(message_view
);
1310 message_view_next(view
);
1313 /* Move the selection to the previous line. */
1315 imessage_view_select_previous (IAnjutaMessageView
* message_view
,
1318 MessageView
*view
= MESSAGE_VIEW(message_view
);
1319 message_view_previous(view
);
1322 /* Return the currently selected messages or the first message if no
1323 * message is selected or NULL if no messages are availible. The
1324 * returned message must not be freed.
1326 static const gchar
*
1327 imessage_view_get_current_message (IAnjutaMessageView
* message_view
,
1332 GtkTreeSelection
*select
;
1333 GtkTreeModel
*model
;
1334 const Message
*message
;
1336 g_return_val_if_fail (MESSAGE_IS_VIEW (message_view
), NULL
);
1338 view
= MESSAGE_VIEW (message_view
);
1339 select
= gtk_tree_view_get_selection (GTK_TREE_VIEW
1340 (view
->privat
->tree_view
));
1342 if (!gtk_tree_selection_get_selected (select
, &model
, &iter
))
1344 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model
), &iter
))
1346 gtk_tree_model_get (GTK_TREE_MODEL (model
),
1347 &iter
, COLUMN_MESSAGE
, &message
, -1);
1350 if (message
->details
&& strlen (message
->details
) > 0)
1351 return message
->details
;
1353 return message
->summary
;
1359 gtk_tree_model_get (GTK_TREE_MODEL (model
),
1360 &iter
, COLUMN_MESSAGE
, &message
, -1);
1363 if (message
->details
&& strlen (message
->details
) > 0)
1364 return message
->details
;
1366 return message
->summary
;
1372 /* Returns a GList which contains all messages, the GList itself
1373 * must be freed, the messages are managed by the message view and
1374 * must not be freed. NULL is return if no messages are availible.
1377 imessage_view_get_all_messages (IAnjutaMessageView
* message_view
,
1381 GtkListStore
*store
;
1384 GList
*messages
= NULL
;
1386 g_return_val_if_fail (MESSAGE_IS_VIEW (message_view
), NULL
);
1388 view
= MESSAGE_VIEW (message_view
);
1389 store
= GTK_LIST_STORE (gtk_tree_view_get_model
1390 (GTK_TREE_VIEW (view
->privat
->tree_view
)));
1392 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store
), &iter
))
1396 gtk_tree_model_get (GTK_TREE_MODEL (store
), &iter
,
1397 COLUMN_MESSAGE
, &message
);
1398 messages
= g_list_prepend (messages
, message
->details
);
1400 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store
), &iter
));
1406 imessage_view_iface_init (IAnjutaMessageViewIface
*iface
)
1408 iface
->buffer_append
= imessage_view_buffer_append
;
1409 iface
->append
= imessage_view_append
;
1410 iface
->clear
= imessage_view_clear
;
1411 iface
->select_next
= imessage_view_select_next
;
1412 iface
->select_previous
= imessage_view_select_previous
;
1413 iface
->get_current_message
= imessage_view_get_current_message
;
1414 iface
->get_all_messages
= imessage_view_get_all_messages
;
1417 ANJUTA_TYPE_BEGIN(MessageView
, message_view
, GTK_TYPE_HBOX
);
1418 ANJUTA_TYPE_ADD_INTERFACE(imessage_view
, IANJUTA_TYPE_MESSAGE_VIEW
);