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-main-window.h"
32 #include "empathy-theme-boxes.h"
34 #define DEBUG_DOMAIN "FancyTheme"
37 #define HEADER_PADDING 2
39 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_THEME_BOXES, EmpathyThemeBoxesPriv))
41 typedef struct _EmpathyThemeBoxesPriv EmpathyThemeBoxesPriv;
43 struct _EmpathyThemeBoxesPriv {
44 gchar *header_foreground;
45 gchar *header_background;
46 gchar *header_line_background;
47 gchar *text_foreground;
48 gchar *text_background;
49 gchar *action_foreground;
50 gchar *highlight_foreground;
51 gchar *time_foreground;
52 gchar *event_foreground;
53 gchar *invite_foreground;
54 gchar *link_foreground;
57 static void theme_boxes_finalize (GObject *object);
58 static void theme_boxes_get_property (GObject *object,
62 static void theme_boxes_set_property (GObject *object,
66 static void theme_boxes_define_theme_tags (EmpathyTheme *theme,
67 EmpathyChatView *view);
68 static void theme_boxes_update_view (EmpathyTheme *theme,
69 EmpathyChatView *view);
70 static void theme_boxes_append_message (EmpathyTheme *theme,
71 EmpathyChatView *view,
72 EmpathyMessage *message);
73 static void theme_boxes_append_event (EmpathyTheme *theme,
74 EmpathyChatView *view,
76 static void theme_boxes_append_timestamp (EmpathyTheme *theme,
77 EmpathyChatView *view,
78 EmpathyMessage *message,
81 static void theme_boxes_append_spacing (EmpathyTheme *theme,
82 EmpathyChatView *view);
86 PROP_HEADER_FOREGROUND,
87 PROP_HEADER_BACKGROUND,
88 PROP_HEADER_LINE_BACKGROUND,
91 PROP_ACTION_FOREGROUND,
92 PROP_HIGHLIGHT_FOREGROUND,
94 PROP_EVENT_FOREGROUND,
95 PROP_INVITE_FOREGROUND,
104 G_DEFINE_TYPE (EmpathyThemeBoxes, empathy_theme_boxes, EMPATHY_TYPE_THEME);
107 empathy_theme_boxes_class_init (EmpathyThemeBoxesClass *class)
109 GObjectClass *object_class;
110 EmpathyThemeClass *theme_class;
112 object_class = G_OBJECT_CLASS (class);
113 theme_class = EMPATHY_THEME_CLASS (class);
115 object_class->finalize = theme_boxes_finalize;
116 object_class->get_property = theme_boxes_get_property;
117 object_class->set_property = theme_boxes_set_property;
119 theme_class->update_view = theme_boxes_update_view;
120 theme_class->append_message = theme_boxes_append_message;
121 theme_class->append_event = theme_boxes_append_event;
122 theme_class->append_timestamp = theme_boxes_append_timestamp;
123 theme_class->append_spacing = theme_boxes_append_spacing;
125 g_object_class_install_property (object_class,
126 PROP_HEADER_FOREGROUND,
127 g_param_spec_string ("header-foreground",
133 g_object_class_install_property (object_class,
134 PROP_HEADER_BACKGROUND,
135 g_param_spec_string ("header-background",
141 g_object_class_install_property (object_class,
142 PROP_HEADER_LINE_BACKGROUND,
143 g_param_spec_string ("header-line-background",
150 g_object_class_install_property (object_class,
151 PROP_TEXT_FOREGROUND,
152 g_param_spec_string ("text-foreground",
158 g_object_class_install_property (object_class,
159 PROP_TEXT_BACKGROUND,
160 g_param_spec_string ("text-background",
166 g_object_class_install_property (object_class,
167 PROP_ACTION_FOREGROUND,
168 g_param_spec_string ("action-foreground",
174 g_object_class_install_property (object_class,
175 PROP_HIGHLIGHT_FOREGROUND,
176 g_param_spec_string ("highlight-foreground",
182 g_object_class_install_property (object_class,
183 PROP_TIME_FOREGROUND,
184 g_param_spec_string ("time-foreground",
190 g_object_class_install_property (object_class,
191 PROP_EVENT_FOREGROUND,
192 g_param_spec_string ("event-foreground",
198 g_object_class_install_property (object_class,
199 PROP_INVITE_FOREGROUND,
200 g_param_spec_string ("invite-foreground",
206 g_object_class_install_property (object_class,
207 PROP_LINK_FOREGROUND,
208 g_param_spec_string ("link-foreground",
214 g_type_class_add_private (object_class, sizeof (EmpathyThemeBoxesPriv));
218 empathy_theme_boxes_init (EmpathyThemeBoxes *theme)
220 EmpathyThemeBoxesPriv *priv;
222 priv = GET_PRIV (theme);
226 theme_boxes_finalize (GObject *object)
228 EmpathyThemeBoxesPriv *priv;
230 priv = GET_PRIV (object);
232 g_free (priv->header_foreground);
233 g_free (priv->header_background);
234 g_free (priv->header_line_background);
235 g_free (priv->text_foreground);
236 g_free (priv->text_background);
237 g_free (priv->action_foreground);
238 g_free (priv->highlight_foreground);
239 g_free (priv->time_foreground);
240 g_free (priv->event_foreground);
241 g_free (priv->invite_foreground);
242 g_free (priv->link_foreground);
244 (G_OBJECT_CLASS (empathy_theme_boxes_parent_class)->finalize) (object);
248 theme_boxes_get_property (GObject *object,
253 EmpathyThemeBoxesPriv *priv;
255 priv = GET_PRIV (object);
258 case PROP_HEADER_FOREGROUND:
259 g_value_set_string (value, priv->header_foreground);
261 case PROP_HEADER_BACKGROUND:
262 g_value_set_string (value, priv->header_background);
264 case PROP_HEADER_LINE_BACKGROUND:
265 g_value_set_string (value, priv->header_line_background);
267 case PROP_TEXT_FOREGROUND:
268 g_value_set_string (value, priv->text_foreground);
270 case PROP_TEXT_BACKGROUND:
271 g_value_set_string (value, priv->text_background);
273 case PROP_ACTION_FOREGROUND:
274 g_value_set_string (value, priv->action_foreground);
276 case PROP_HIGHLIGHT_FOREGROUND:
277 g_value_set_string (value, priv->highlight_foreground);
279 case PROP_TIME_FOREGROUND:
280 g_value_set_string (value, priv->time_foreground);
282 case PROP_EVENT_FOREGROUND:
283 g_value_set_string (value, priv->event_foreground);
285 case PROP_INVITE_FOREGROUND:
286 g_value_set_string (value, priv->invite_foreground);
288 case PROP_LINK_FOREGROUND:
289 g_value_set_string (value, priv->link_foreground);
292 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
297 theme_boxes_set_property (GObject *object,
302 EmpathyThemeBoxesPriv *priv;
304 priv = GET_PRIV (object);
307 case PROP_HEADER_FOREGROUND:
308 g_free (priv->header_foreground);
309 priv->header_foreground = g_value_dup_string (value);
310 g_object_notify (object, "header-foreground");
312 case PROP_HEADER_BACKGROUND:
313 g_free (priv->header_background);
314 priv->header_background = g_value_dup_string (value);
315 g_object_notify (object, "header-background");
317 case PROP_HEADER_LINE_BACKGROUND:
318 g_free (priv->header_line_background);
319 priv->header_line_background = g_value_dup_string (value);
320 g_object_notify (object, "header-line_background");
322 case PROP_TEXT_FOREGROUND:
323 g_free (priv->text_foreground);
324 priv->text_foreground = g_value_dup_string (value);
325 g_object_notify (object, "text-foreground");
327 case PROP_TEXT_BACKGROUND:
328 g_free (priv->text_background);
329 priv->text_background = g_value_dup_string (value);
330 g_object_notify (object, "text-background");
332 case PROP_ACTION_FOREGROUND:
333 g_free (priv->action_foreground);
334 priv->action_foreground = g_value_dup_string (value);
335 g_object_notify (object, "action-foreground");
337 case PROP_HIGHLIGHT_FOREGROUND:
338 g_free (priv->highlight_foreground);
339 priv->highlight_foreground = g_value_dup_string (value);
340 g_object_notify (object, "highlight-foreground");
342 case PROP_TIME_FOREGROUND:
343 g_free (priv->time_foreground);
344 priv->time_foreground = g_value_dup_string (value);
345 g_object_notify (object, "time-foreground");
347 case PROP_EVENT_FOREGROUND:
348 g_free (priv->event_foreground);
349 priv->event_foreground = g_value_dup_string (value);
350 g_object_notify (object, "event-foreground");
352 case PROP_INVITE_FOREGROUND:
353 g_free (priv->invite_foreground);
354 priv->invite_foreground = g_value_dup_string (value);
355 g_object_notify (object, "invite-foreground");
357 case PROP_LINK_FOREGROUND:
358 g_free (priv->link_foreground);
359 priv->link_foreground = g_value_dup_string (value);
360 g_object_notify (object, "link-foreground");
363 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
369 theme_boxes_define_theme_tags (EmpathyTheme *theme, EmpathyChatView *view)
371 EmpathyThemeBoxesPriv *priv;
372 GtkTextBuffer *buffer;
375 priv = GET_PRIV (theme);
377 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
379 empathy_text_buffer_tag_set (buffer, "fancy-spacing",
381 "pixels-above-lines", 8,
384 tag = empathy_text_buffer_tag_set (buffer, "fancy-header",
385 "weight", PANGO_WEIGHT_BOLD,
386 "pixels-above-lines", HEADER_PADDING,
387 "pixels-below-lines", HEADER_PADDING,
389 if (priv->header_foreground) {
391 "foreground", priv->header_foreground,
392 "paragraph-background", priv->header_background,
396 tag = empathy_text_buffer_tag_set (buffer, "fancy-header-line",
399 if (priv->header_line_background) {
401 "paragraph-background", priv->header_line_background,
405 tag = empathy_text_buffer_tag_set (buffer, "fancy-body",
406 "pixels-above-lines", 4,
408 if (priv->text_background) {
410 "paragraph-background", priv->text_background,
414 if (priv->text_foreground) {
416 "foreground", priv->text_foreground,
420 tag = empathy_text_buffer_tag_set (buffer, "fancy-action",
421 "style", PANGO_STYLE_ITALIC,
422 "pixels-above-lines", 4,
425 if (priv->text_background) {
427 "paragraph-background", priv->text_background,
431 if (priv->action_foreground) {
433 "foreground", priv->action_foreground,
437 tag = empathy_text_buffer_tag_set (buffer, "fancy-highlight",
438 "weight", PANGO_WEIGHT_BOLD,
439 "pixels-above-lines", 4,
441 if (priv->text_background) {
443 "paragraph-background", priv->text_background,
448 if (priv->highlight_foreground) {
450 "foreground", priv->highlight_foreground,
454 tag = empathy_text_buffer_tag_set (buffer, "fancy-time",
455 "justification", GTK_JUSTIFY_CENTER,
457 if (priv->time_foreground) {
459 "foreground", priv->time_foreground,
463 tag = empathy_text_buffer_tag_set (buffer, "fancy-event",
464 "justification", GTK_JUSTIFY_LEFT,
466 if (priv->event_foreground) {
468 "foreground", priv->event_foreground,
472 tag = empathy_text_buffer_tag_set (buffer, "invite", NULL);
473 if (priv->invite_foreground) {
475 "foreground", priv->invite_foreground,
479 tag = empathy_text_buffer_tag_set (buffer, "fancy-link",
480 "underline", PANGO_UNDERLINE_SINGLE,
482 if (priv->link_foreground) {
484 "foreground", priv->link_foreground,
490 theme_boxes_update_view (EmpathyTheme *theme, EmpathyChatView *view)
492 EmpathyThemeBoxesPriv *priv;
494 g_return_if_fail (EMPATHY_IS_THEME_BOXES (theme));
495 g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
497 priv = GET_PRIV (theme);
499 theme_boxes_define_theme_tags (theme, view);
501 empathy_chat_view_set_margin (view, MARGIN);
505 table_size_allocate_cb (GtkWidget *view,
506 GtkAllocation *allocation,
511 gtk_widget_get_size_request (box, NULL, &height);
513 width = allocation->width;
516 gtk_text_view_get_right_margin (GTK_TEXT_VIEW (view)) - \
517 gtk_text_view_get_left_margin (GTK_TEXT_VIEW (view));
519 width -= 2 * HEADER_PADDING;
521 gtk_widget_set_size_request (box, width, height);
525 theme_boxes_maybe_append_header (EmpathyTheme *theme,
526 EmpathyChatView *view,
529 EmpathyThemeBoxesPriv *priv;
530 EmpathyContact *contact;
531 GdkPixbuf *avatar = NULL;
532 GtkTextBuffer *buffer;
536 GtkWidget *label1, *label2;
537 GtkTextChildAnchor *anchor;
544 gboolean parse_success;
547 priv = GET_PRIV (theme);
549 contact = empathy_message_get_sender (msg);
550 from_self = empathy_contact_is_user (contact);
551 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
553 empathy_debug (DEBUG_DOMAIN, "Maybe add fancy header");
555 name = empathy_contact_get_name (contact);
559 /* Only insert a header if the previously inserted block is not the same
560 * as this one. This catches all the different cases:
562 if (empathy_chat_view_get_last_block_type (view) != BLOCK_TYPE_SELF &&
563 empathy_chat_view_get_last_block_type (view) != BLOCK_TYPE_OTHER) {
566 else if (from_self &&
567 empathy_chat_view_get_last_block_type (view) == BLOCK_TYPE_OTHER) {
570 else if (!from_self &&
571 empathy_chat_view_get_last_block_type (view) == BLOCK_TYPE_SELF) {
574 else if (!from_self &&
575 (!empathy_chat_view_get_last_contact (view) ||
576 !empathy_contact_equal (contact, empathy_chat_view_get_last_contact (view)))) {
584 empathy_theme_append_spacing (theme, view);
586 gtk_text_buffer_get_end_iter (buffer, &iter);
587 gtk_text_buffer_insert_with_tags_by_name (buffer,
594 gtk_text_buffer_get_end_iter (buffer, &iter);
595 anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
597 box = gtk_hbox_new (FALSE, 0);
600 avatar = empathy_chat_view_get_avatar_pixbuf_with_cache (contact);
601 if (avatar && empathy_theme_get_show_avatars (theme)) {
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, BLOCK_TYPE_SELF);
714 empathy_chat_view_set_last_contact (view, NULL);
716 empathy_chat_view_set_last_block_type (view, BLOCK_TYPE_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, BLOCK_TYPE_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, BLOCK_TYPE_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,