1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2007 Imendio AB
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
25 #include <glib/gi18n.h>
28 #include <libempathy/empathy-debug.h>
30 #include "empathy-ui-utils.h"
31 #include "empathy-theme-boxes.h"
33 #define DEBUG_DOMAIN "FancyTheme"
36 #define HEADER_PADDING 2
38 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_THEME_BOXES, EmpathyThemeBoxesPriv))
40 typedef struct _EmpathyThemeBoxesPriv EmpathyThemeBoxesPriv;
42 struct _EmpathyThemeBoxesPriv {
43 gchar *header_foreground;
44 gchar *header_background;
45 gchar *header_line_background;
46 gchar *text_foreground;
47 gchar *text_background;
48 gchar *action_foreground;
49 gchar *highlight_foreground;
50 gchar *time_foreground;
51 gchar *event_foreground;
52 gchar *invite_foreground;
53 gchar *link_foreground;
56 static void theme_boxes_finalize (GObject *object);
57 static void theme_boxes_get_property (GObject *object,
61 static void theme_boxes_set_property (GObject *object,
65 static void theme_boxes_define_theme_tags (EmpathyTheme *theme,
66 EmpathyChatView *view);
67 static void theme_boxes_update_view (EmpathyTheme *theme,
68 EmpathyChatView *view);
69 static void theme_boxes_append_message (EmpathyTheme *theme,
70 EmpathyChatView *view,
71 EmpathyMessage *message);
72 static void theme_boxes_append_event (EmpathyTheme *theme,
73 EmpathyChatView *view,
75 static void theme_boxes_append_timestamp (EmpathyTheme *theme,
76 EmpathyChatView *view,
77 EmpathyMessage *message,
80 static void theme_boxes_append_spacing (EmpathyTheme *theme,
81 EmpathyChatView *view);
85 PROP_HEADER_FOREGROUND,
86 PROP_HEADER_BACKGROUND,
87 PROP_HEADER_LINE_BACKGROUND,
90 PROP_ACTION_FOREGROUND,
91 PROP_HIGHLIGHT_FOREGROUND,
93 PROP_EVENT_FOREGROUND,
94 PROP_INVITE_FOREGROUND,
103 G_DEFINE_TYPE (EmpathyThemeBoxes, empathy_theme_boxes, EMPATHY_TYPE_THEME);
106 empathy_theme_boxes_class_init (EmpathyThemeBoxesClass *class)
108 GObjectClass *object_class;
109 EmpathyThemeClass *theme_class;
111 object_class = G_OBJECT_CLASS (class);
112 theme_class = EMPATHY_THEME_CLASS (class);
114 object_class->finalize = theme_boxes_finalize;
115 object_class->get_property = theme_boxes_get_property;
116 object_class->set_property = theme_boxes_set_property;
118 theme_class->update_view = theme_boxes_update_view;
119 theme_class->append_message = theme_boxes_append_message;
120 theme_class->append_event = theme_boxes_append_event;
121 theme_class->append_timestamp = theme_boxes_append_timestamp;
122 theme_class->append_spacing = theme_boxes_append_spacing;
124 g_object_class_install_property (object_class,
125 PROP_HEADER_FOREGROUND,
126 g_param_spec_string ("header-foreground",
132 g_object_class_install_property (object_class,
133 PROP_HEADER_BACKGROUND,
134 g_param_spec_string ("header-background",
140 g_object_class_install_property (object_class,
141 PROP_HEADER_LINE_BACKGROUND,
142 g_param_spec_string ("header-line-background",
149 g_object_class_install_property (object_class,
150 PROP_TEXT_FOREGROUND,
151 g_param_spec_string ("text-foreground",
157 g_object_class_install_property (object_class,
158 PROP_TEXT_BACKGROUND,
159 g_param_spec_string ("text-background",
165 g_object_class_install_property (object_class,
166 PROP_ACTION_FOREGROUND,
167 g_param_spec_string ("action-foreground",
173 g_object_class_install_property (object_class,
174 PROP_HIGHLIGHT_FOREGROUND,
175 g_param_spec_string ("highlight-foreground",
181 g_object_class_install_property (object_class,
182 PROP_TIME_FOREGROUND,
183 g_param_spec_string ("time-foreground",
189 g_object_class_install_property (object_class,
190 PROP_EVENT_FOREGROUND,
191 g_param_spec_string ("event-foreground",
197 g_object_class_install_property (object_class,
198 PROP_INVITE_FOREGROUND,
199 g_param_spec_string ("invite-foreground",
205 g_object_class_install_property (object_class,
206 PROP_LINK_FOREGROUND,
207 g_param_spec_string ("link-foreground",
213 g_type_class_add_private (object_class, sizeof (EmpathyThemeBoxesPriv));
217 empathy_theme_boxes_init (EmpathyThemeBoxes *theme)
219 EmpathyThemeBoxesPriv *priv;
221 priv = GET_PRIV (theme);
225 theme_boxes_finalize (GObject *object)
227 EmpathyThemeBoxesPriv *priv;
229 priv = GET_PRIV (object);
231 g_free (priv->header_foreground);
232 g_free (priv->header_background);
233 g_free (priv->header_line_background);
234 g_free (priv->text_foreground);
235 g_free (priv->text_background);
236 g_free (priv->action_foreground);
237 g_free (priv->highlight_foreground);
238 g_free (priv->time_foreground);
239 g_free (priv->event_foreground);
240 g_free (priv->invite_foreground);
241 g_free (priv->link_foreground);
243 (G_OBJECT_CLASS (empathy_theme_boxes_parent_class)->finalize) (object);
247 theme_boxes_get_property (GObject *object,
252 EmpathyThemeBoxesPriv *priv;
254 priv = GET_PRIV (object);
257 case PROP_HEADER_FOREGROUND:
258 g_value_set_string (value, priv->header_foreground);
260 case PROP_HEADER_BACKGROUND:
261 g_value_set_string (value, priv->header_background);
263 case PROP_HEADER_LINE_BACKGROUND:
264 g_value_set_string (value, priv->header_line_background);
266 case PROP_TEXT_FOREGROUND:
267 g_value_set_string (value, priv->text_foreground);
269 case PROP_TEXT_BACKGROUND:
270 g_value_set_string (value, priv->text_background);
272 case PROP_ACTION_FOREGROUND:
273 g_value_set_string (value, priv->action_foreground);
275 case PROP_HIGHLIGHT_FOREGROUND:
276 g_value_set_string (value, priv->highlight_foreground);
278 case PROP_TIME_FOREGROUND:
279 g_value_set_string (value, priv->time_foreground);
281 case PROP_EVENT_FOREGROUND:
282 g_value_set_string (value, priv->event_foreground);
284 case PROP_INVITE_FOREGROUND:
285 g_value_set_string (value, priv->invite_foreground);
287 case PROP_LINK_FOREGROUND:
288 g_value_set_string (value, priv->link_foreground);
291 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
296 theme_boxes_set_property (GObject *object,
301 EmpathyThemeBoxesPriv *priv;
303 priv = GET_PRIV (object);
306 case PROP_HEADER_FOREGROUND:
307 g_free (priv->header_foreground);
308 priv->header_foreground = g_value_dup_string (value);
309 g_object_notify (object, "header-foreground");
311 case PROP_HEADER_BACKGROUND:
312 g_free (priv->header_background);
313 priv->header_background = g_value_dup_string (value);
314 g_object_notify (object, "header-background");
316 case PROP_HEADER_LINE_BACKGROUND:
317 g_free (priv->header_line_background);
318 priv->header_line_background = g_value_dup_string (value);
319 g_object_notify (object, "header-line_background");
321 case PROP_TEXT_FOREGROUND:
322 g_free (priv->text_foreground);
323 priv->text_foreground = g_value_dup_string (value);
324 g_object_notify (object, "text-foreground");
326 case PROP_TEXT_BACKGROUND:
327 g_free (priv->text_background);
328 priv->text_background = g_value_dup_string (value);
329 g_object_notify (object, "text-background");
331 case PROP_ACTION_FOREGROUND:
332 g_free (priv->action_foreground);
333 priv->action_foreground = g_value_dup_string (value);
334 g_object_notify (object, "action-foreground");
336 case PROP_HIGHLIGHT_FOREGROUND:
337 g_free (priv->highlight_foreground);
338 priv->highlight_foreground = g_value_dup_string (value);
339 g_object_notify (object, "highlight-foreground");
341 case PROP_TIME_FOREGROUND:
342 g_free (priv->time_foreground);
343 priv->time_foreground = g_value_dup_string (value);
344 g_object_notify (object, "time-foreground");
346 case PROP_EVENT_FOREGROUND:
347 g_free (priv->event_foreground);
348 priv->event_foreground = g_value_dup_string (value);
349 g_object_notify (object, "event-foreground");
351 case PROP_INVITE_FOREGROUND:
352 g_free (priv->invite_foreground);
353 priv->invite_foreground = g_value_dup_string (value);
354 g_object_notify (object, "invite-foreground");
356 case PROP_LINK_FOREGROUND:
357 g_free (priv->link_foreground);
358 priv->link_foreground = g_value_dup_string (value);
359 g_object_notify (object, "link-foreground");
362 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
368 theme_boxes_define_theme_tags (EmpathyTheme *theme, EmpathyChatView *view)
370 EmpathyThemeBoxesPriv *priv;
371 GtkTextBuffer *buffer;
374 priv = GET_PRIV (theme);
376 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
378 empathy_text_buffer_tag_set (buffer, "fancy-spacing",
380 "pixels-above-lines", 8,
383 tag = empathy_text_buffer_tag_set (buffer, "fancy-header",
384 "weight", PANGO_WEIGHT_BOLD,
385 "pixels-above-lines", HEADER_PADDING,
386 "pixels-below-lines", HEADER_PADDING,
388 if (priv->header_foreground) {
390 "foreground", priv->header_foreground,
391 "paragraph-background", priv->header_background,
395 tag = empathy_text_buffer_tag_set (buffer, "fancy-header-line",
398 if (priv->header_line_background) {
400 "paragraph-background", priv->header_line_background,
404 tag = empathy_text_buffer_tag_set (buffer, "fancy-body",
405 "pixels-above-lines", 4,
407 if (priv->text_background) {
409 "paragraph-background", priv->text_background,
413 if (priv->text_foreground) {
415 "foreground", priv->text_foreground,
419 tag = empathy_text_buffer_tag_set (buffer, "fancy-action",
420 "style", PANGO_STYLE_ITALIC,
421 "pixels-above-lines", 4,
424 if (priv->text_background) {
426 "paragraph-background", priv->text_background,
430 if (priv->action_foreground) {
432 "foreground", priv->action_foreground,
436 tag = empathy_text_buffer_tag_set (buffer, "fancy-highlight",
437 "weight", PANGO_WEIGHT_BOLD,
438 "pixels-above-lines", 4,
440 if (priv->text_background) {
442 "paragraph-background", priv->text_background,
447 if (priv->highlight_foreground) {
449 "foreground", priv->highlight_foreground,
453 tag = empathy_text_buffer_tag_set (buffer, "fancy-time",
454 "justification", GTK_JUSTIFY_CENTER,
456 if (priv->time_foreground) {
458 "foreground", priv->time_foreground,
462 tag = empathy_text_buffer_tag_set (buffer, "fancy-event",
463 "justification", GTK_JUSTIFY_LEFT,
465 if (priv->event_foreground) {
467 "foreground", priv->event_foreground,
471 tag = empathy_text_buffer_tag_set (buffer, "invite", NULL);
472 if (priv->invite_foreground) {
474 "foreground", priv->invite_foreground,
478 tag = empathy_text_buffer_tag_set (buffer, "fancy-link",
479 "underline", PANGO_UNDERLINE_SINGLE,
481 if (priv->link_foreground) {
483 "foreground", priv->link_foreground,
489 theme_boxes_update_view (EmpathyTheme *theme, EmpathyChatView *view)
491 EmpathyThemeBoxesPriv *priv;
493 g_return_if_fail (EMPATHY_IS_THEME_BOXES (theme));
494 g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
496 priv = GET_PRIV (theme);
498 theme_boxes_define_theme_tags (theme, view);
500 empathy_chat_view_set_margin (view, MARGIN);
504 table_size_allocate_cb (GtkWidget *view,
505 GtkAllocation *allocation,
510 gtk_widget_get_size_request (box, NULL, &height);
512 width = allocation->width;
515 gtk_text_view_get_right_margin (GTK_TEXT_VIEW (view)) - \
516 gtk_text_view_get_left_margin (GTK_TEXT_VIEW (view));
518 width -= 2 * HEADER_PADDING;
520 gtk_widget_set_size_request (box, width, height);
524 theme_boxes_maybe_append_header (EmpathyTheme *theme,
525 EmpathyChatView *view,
528 EmpathyThemeBoxesPriv *priv;
529 EmpathyContact *contact;
530 GdkPixbuf *avatar = NULL;
531 GtkTextBuffer *buffer;
535 GtkWidget *label1, *label2;
536 GtkTextChildAnchor *anchor;
543 gboolean parse_success;
546 priv = GET_PRIV (theme);
548 contact = empathy_message_get_sender (msg);
549 from_self = empathy_contact_is_user (contact);
550 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
552 empathy_debug (DEBUG_DOMAIN, "Maybe add fancy header");
554 name = empathy_contact_get_name (contact);
558 /* Only insert a header if the previously inserted block is not the same
559 * as this one. This catches all the different cases:
561 if (empathy_chat_view_get_last_block_type (view) != EMPATHY_CHAT_VIEW_BLOCK_SELF &&
562 empathy_chat_view_get_last_block_type (view) != EMPATHY_CHAT_VIEW_BLOCK_OTHER) {
565 else if (from_self &&
566 empathy_chat_view_get_last_block_type (view) == EMPATHY_CHAT_VIEW_BLOCK_OTHER) {
569 else if (!from_self &&
570 empathy_chat_view_get_last_block_type (view) == EMPATHY_CHAT_VIEW_BLOCK_SELF) {
573 else if (!from_self &&
574 (!empathy_chat_view_get_last_contact (view) ||
575 contact != empathy_chat_view_get_last_contact (view))) {
583 empathy_theme_append_spacing (theme, view);
585 gtk_text_buffer_get_end_iter (buffer, &iter);
586 gtk_text_buffer_insert_with_tags_by_name (buffer,
593 gtk_text_buffer_get_end_iter (buffer, &iter);
594 anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
596 box = gtk_hbox_new (FALSE, 0);
599 if (empathy_theme_get_show_avatars (theme)) {
600 avatar = empathy_chat_view_get_avatar_pixbuf_with_cache (contact);
604 image = gtk_image_new_from_pixbuf (avatar);
606 gtk_box_pack_start (GTK_BOX (box), image,
611 g_signal_connect_object (view, "size-allocate",
612 G_CALLBACK (table_size_allocate_cb),
615 str = g_strdup_printf ("<b>%s</b>", name);
617 label1 = g_object_new (GTK_TYPE_LABEL,
623 parse_success = priv->header_foreground &&
624 gdk_color_parse (priv->header_foreground, &color);
627 gtk_widget_modify_fg (label1, GTK_STATE_NORMAL, &color);
632 time = empathy_message_get_timestamp (msg);
634 tmp = empathy_time_to_string_local (time,
635 EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
636 str = g_strdup_printf ("<i>%s</i>", tmp);
639 label2 = g_object_new (GTK_TYPE_LABEL,
646 gtk_widget_modify_fg (label2, GTK_STATE_NORMAL, &color);
651 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.5);
652 gtk_misc_set_alignment (GTK_MISC (label2), 1.0, 0.5);
654 gtk_box_pack_start (GTK_BOX (box), label1, TRUE, TRUE, 0);
655 gtk_box_pack_start (GTK_BOX (box), label2, TRUE, TRUE, 0);
657 gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view),
661 gtk_widget_show_all (box);
663 gtk_text_buffer_get_end_iter (buffer, &iter);
665 gtk_text_iter_backward_char (&start);
666 gtk_text_buffer_apply_tag_by_name (buffer,
670 gtk_text_buffer_insert_with_tags_by_name (buffer,
677 gtk_text_buffer_get_end_iter (buffer, &iter);
678 gtk_text_buffer_insert_with_tags_by_name (buffer,
687 theme_boxes_append_message (EmpathyTheme *theme,
688 EmpathyChatView *view,
689 EmpathyMessage *message)
691 EmpathyContact *sender;
693 empathy_theme_maybe_append_date_and_time (theme, view, message);
694 theme_boxes_maybe_append_header (theme, view, message);
696 sender = empathy_message_get_sender (message);
698 if (empathy_message_get_type (message) == EMPATHY_MESSAGE_TYPE_ACTION) {
701 body = g_strdup_printf (" * %s %s",
702 empathy_contact_get_name (sender),
703 empathy_message_get_body (message));
704 empathy_theme_append_text (theme, view, body,
705 "fancy-action", "fancy-link");
707 empathy_theme_append_text (theme, view,
708 empathy_message_get_body (message),
709 "fancy-body", "fancy-link");
712 if (empathy_contact_is_user (sender)) {
713 empathy_chat_view_set_last_block_type (view, EMPATHY_CHAT_VIEW_BLOCK_SELF);
714 empathy_chat_view_set_last_contact (view, NULL);
716 empathy_chat_view_set_last_block_type (view, EMPATHY_CHAT_VIEW_BLOCK_OTHER);
717 empathy_chat_view_set_last_contact (view, sender);
722 theme_boxes_append_event (EmpathyTheme *theme,
723 EmpathyChatView *view,
726 GtkTextBuffer *buffer;
730 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
732 empathy_theme_maybe_append_date_and_time (theme, view, NULL);
734 gtk_text_buffer_get_end_iter (buffer, &iter);
736 msg = g_strdup_printf (" - %s\n", str);
738 gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
744 empathy_chat_view_set_last_block_type (view, EMPATHY_CHAT_VIEW_BLOCK_EVENT);
748 theme_boxes_append_timestamp (EmpathyTheme *theme,
749 EmpathyChatView *view,
750 EmpathyMessage *message,
754 GtkTextBuffer *buffer;
764 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
766 date = empathy_message_get_date_and_time (message, ×tamp);
768 str = g_string_new (NULL);
770 if (show_time || show_date) {
771 empathy_theme_append_spacing (theme, view);
773 g_string_append (str, "- ");
779 g_date_strftime (buf, 256, _("%A %d %B %Y"), date);
780 g_string_append (str, buf);
783 g_string_append (str, ", ");
792 tmp = empathy_time_to_string_local (timestamp, EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
793 g_string_append (str, tmp);
797 if (show_time || show_date) {
798 g_string_append (str, " -\n");
800 gtk_text_buffer_get_end_iter (buffer, &iter);
801 gtk_text_buffer_insert_with_tags_by_name (buffer,
807 empathy_chat_view_set_last_block_type (view, EMPATHY_CHAT_VIEW_BLOCK_TIME);
808 empathy_chat_view_set_last_timestamp (view, timestamp);
811 g_string_free (str, TRUE);
816 theme_boxes_append_spacing (EmpathyTheme *theme,
817 EmpathyChatView *view)
819 GtkTextBuffer *buffer;
822 g_return_if_fail (EMPATHY_IS_THEME (theme));
823 g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
825 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
827 gtk_text_buffer_get_end_iter (buffer, &iter);
828 gtk_text_buffer_insert_with_tags_by_name (buffer,