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-utils.h>
29 #include "empathy-ui-utils.h"
30 #include "empathy-theme-boxes.h"
32 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
33 #include <libempathy/empathy-debug.h>
36 #define HEADER_PADDING 2
38 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyThemeBoxes)
40 gchar *header_foreground;
41 gchar *header_background;
42 gchar *header_line_background;
43 gchar *text_foreground;
44 gchar *text_background;
45 gchar *action_foreground;
46 gchar *highlight_foreground;
47 gchar *time_foreground;
48 gchar *event_foreground;
49 gchar *invite_foreground;
50 gchar *link_foreground;
51 } EmpathyThemeBoxesPriv;
53 static void theme_boxes_finalize (GObject *object);
54 static void theme_boxes_get_property (GObject *object,
58 static void theme_boxes_set_property (GObject *object,
62 static void theme_boxes_define_theme_tags (EmpathyTheme *theme,
63 EmpathyChatView *view);
64 static void theme_boxes_update_view (EmpathyTheme *theme,
65 EmpathyChatView *view);
66 static void theme_boxes_append_message (EmpathyTheme *theme,
67 EmpathyChatView *view,
68 EmpathyMessage *message);
69 static void theme_boxes_append_event (EmpathyTheme *theme,
70 EmpathyChatView *view,
72 static void theme_boxes_append_timestamp (EmpathyTheme *theme,
73 EmpathyChatView *view,
74 EmpathyMessage *message,
77 static void theme_boxes_append_spacing (EmpathyTheme *theme,
78 EmpathyChatView *view);
82 PROP_HEADER_FOREGROUND,
83 PROP_HEADER_BACKGROUND,
84 PROP_HEADER_LINE_BACKGROUND,
87 PROP_ACTION_FOREGROUND,
88 PROP_HIGHLIGHT_FOREGROUND,
90 PROP_EVENT_FOREGROUND,
91 PROP_INVITE_FOREGROUND,
100 G_DEFINE_TYPE (EmpathyThemeBoxes, empathy_theme_boxes, EMPATHY_TYPE_THEME);
103 empathy_theme_boxes_class_init (EmpathyThemeBoxesClass *class)
105 GObjectClass *object_class;
106 EmpathyThemeClass *theme_class;
108 object_class = G_OBJECT_CLASS (class);
109 theme_class = EMPATHY_THEME_CLASS (class);
111 object_class->finalize = theme_boxes_finalize;
112 object_class->get_property = theme_boxes_get_property;
113 object_class->set_property = theme_boxes_set_property;
115 theme_class->update_view = theme_boxes_update_view;
116 theme_class->append_message = theme_boxes_append_message;
117 theme_class->append_event = theme_boxes_append_event;
118 theme_class->append_timestamp = theme_boxes_append_timestamp;
119 theme_class->append_spacing = theme_boxes_append_spacing;
121 g_object_class_install_property (object_class,
122 PROP_HEADER_FOREGROUND,
123 g_param_spec_string ("header-foreground",
129 g_object_class_install_property (object_class,
130 PROP_HEADER_BACKGROUND,
131 g_param_spec_string ("header-background",
137 g_object_class_install_property (object_class,
138 PROP_HEADER_LINE_BACKGROUND,
139 g_param_spec_string ("header-line-background",
146 g_object_class_install_property (object_class,
147 PROP_TEXT_FOREGROUND,
148 g_param_spec_string ("text-foreground",
154 g_object_class_install_property (object_class,
155 PROP_TEXT_BACKGROUND,
156 g_param_spec_string ("text-background",
162 g_object_class_install_property (object_class,
163 PROP_ACTION_FOREGROUND,
164 g_param_spec_string ("action-foreground",
170 g_object_class_install_property (object_class,
171 PROP_HIGHLIGHT_FOREGROUND,
172 g_param_spec_string ("highlight-foreground",
178 g_object_class_install_property (object_class,
179 PROP_TIME_FOREGROUND,
180 g_param_spec_string ("time-foreground",
186 g_object_class_install_property (object_class,
187 PROP_EVENT_FOREGROUND,
188 g_param_spec_string ("event-foreground",
194 g_object_class_install_property (object_class,
195 PROP_INVITE_FOREGROUND,
196 g_param_spec_string ("invite-foreground",
202 g_object_class_install_property (object_class,
203 PROP_LINK_FOREGROUND,
204 g_param_spec_string ("link-foreground",
210 g_type_class_add_private (object_class, sizeof (EmpathyThemeBoxesPriv));
214 empathy_theme_boxes_init (EmpathyThemeBoxes *theme)
216 EmpathyThemeBoxesPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (theme,
217 EMPATHY_TYPE_THEME_BOXES, EmpathyThemeBoxesPriv);
223 theme_boxes_finalize (GObject *object)
225 EmpathyThemeBoxesPriv *priv;
227 priv = GET_PRIV (object);
229 g_free (priv->header_foreground);
230 g_free (priv->header_background);
231 g_free (priv->header_line_background);
232 g_free (priv->text_foreground);
233 g_free (priv->text_background);
234 g_free (priv->action_foreground);
235 g_free (priv->highlight_foreground);
236 g_free (priv->time_foreground);
237 g_free (priv->event_foreground);
238 g_free (priv->invite_foreground);
239 g_free (priv->link_foreground);
241 (G_OBJECT_CLASS (empathy_theme_boxes_parent_class)->finalize) (object);
245 theme_boxes_get_property (GObject *object,
250 EmpathyThemeBoxesPriv *priv;
252 priv = GET_PRIV (object);
255 case PROP_HEADER_FOREGROUND:
256 g_value_set_string (value, priv->header_foreground);
258 case PROP_HEADER_BACKGROUND:
259 g_value_set_string (value, priv->header_background);
261 case PROP_HEADER_LINE_BACKGROUND:
262 g_value_set_string (value, priv->header_line_background);
264 case PROP_TEXT_FOREGROUND:
265 g_value_set_string (value, priv->text_foreground);
267 case PROP_TEXT_BACKGROUND:
268 g_value_set_string (value, priv->text_background);
270 case PROP_ACTION_FOREGROUND:
271 g_value_set_string (value, priv->action_foreground);
273 case PROP_HIGHLIGHT_FOREGROUND:
274 g_value_set_string (value, priv->highlight_foreground);
276 case PROP_TIME_FOREGROUND:
277 g_value_set_string (value, priv->time_foreground);
279 case PROP_EVENT_FOREGROUND:
280 g_value_set_string (value, priv->event_foreground);
282 case PROP_INVITE_FOREGROUND:
283 g_value_set_string (value, priv->invite_foreground);
285 case PROP_LINK_FOREGROUND:
286 g_value_set_string (value, priv->link_foreground);
289 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
294 theme_boxes_set_property (GObject *object,
299 EmpathyThemeBoxesPriv *priv;
301 priv = GET_PRIV (object);
304 case PROP_HEADER_FOREGROUND:
305 g_free (priv->header_foreground);
306 priv->header_foreground = g_value_dup_string (value);
307 g_object_notify (object, "header-foreground");
309 case PROP_HEADER_BACKGROUND:
310 g_free (priv->header_background);
311 priv->header_background = g_value_dup_string (value);
312 g_object_notify (object, "header-background");
314 case PROP_HEADER_LINE_BACKGROUND:
315 g_free (priv->header_line_background);
316 priv->header_line_background = g_value_dup_string (value);
317 g_object_notify (object, "header-line_background");
319 case PROP_TEXT_FOREGROUND:
320 g_free (priv->text_foreground);
321 priv->text_foreground = g_value_dup_string (value);
322 g_object_notify (object, "text-foreground");
324 case PROP_TEXT_BACKGROUND:
325 g_free (priv->text_background);
326 priv->text_background = g_value_dup_string (value);
327 g_object_notify (object, "text-background");
329 case PROP_ACTION_FOREGROUND:
330 g_free (priv->action_foreground);
331 priv->action_foreground = g_value_dup_string (value);
332 g_object_notify (object, "action-foreground");
334 case PROP_HIGHLIGHT_FOREGROUND:
335 g_free (priv->highlight_foreground);
336 priv->highlight_foreground = g_value_dup_string (value);
337 g_object_notify (object, "highlight-foreground");
339 case PROP_TIME_FOREGROUND:
340 g_free (priv->time_foreground);
341 priv->time_foreground = g_value_dup_string (value);
342 g_object_notify (object, "time-foreground");
344 case PROP_EVENT_FOREGROUND:
345 g_free (priv->event_foreground);
346 priv->event_foreground = g_value_dup_string (value);
347 g_object_notify (object, "event-foreground");
349 case PROP_INVITE_FOREGROUND:
350 g_free (priv->invite_foreground);
351 priv->invite_foreground = g_value_dup_string (value);
352 g_object_notify (object, "invite-foreground");
354 case PROP_LINK_FOREGROUND:
355 g_free (priv->link_foreground);
356 priv->link_foreground = g_value_dup_string (value);
357 g_object_notify (object, "link-foreground");
360 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
366 theme_boxes_define_theme_tags (EmpathyTheme *theme, EmpathyChatView *view)
368 EmpathyThemeBoxesPriv *priv;
369 GtkTextBuffer *buffer;
372 priv = GET_PRIV (theme);
374 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
376 empathy_text_buffer_tag_set (buffer, "fancy-spacing",
378 "pixels-above-lines", 8,
381 tag = empathy_text_buffer_tag_set (buffer, "fancy-header",
382 "weight", PANGO_WEIGHT_BOLD,
383 "pixels-above-lines", HEADER_PADDING,
384 "pixels-below-lines", HEADER_PADDING,
386 if (priv->header_foreground) {
388 "foreground", priv->header_foreground,
389 "paragraph-background", priv->header_background,
393 tag = empathy_text_buffer_tag_set (buffer, "fancy-header-line",
396 if (priv->header_line_background) {
398 "paragraph-background", priv->header_line_background,
402 tag = empathy_text_buffer_tag_set (buffer, "fancy-body",
403 "pixels-above-lines", 4,
405 if (priv->text_background) {
407 "paragraph-background", priv->text_background,
411 if (priv->text_foreground) {
413 "foreground", priv->text_foreground,
417 tag = empathy_text_buffer_tag_set (buffer, "fancy-action",
418 "style", PANGO_STYLE_ITALIC,
419 "pixels-above-lines", 4,
422 if (priv->text_background) {
424 "paragraph-background", priv->text_background,
428 if (priv->action_foreground) {
430 "foreground", priv->action_foreground,
434 tag = empathy_text_buffer_tag_set (buffer, "fancy-highlight",
435 "weight", PANGO_WEIGHT_BOLD,
436 "pixels-above-lines", 4,
438 if (priv->text_background) {
440 "paragraph-background", priv->text_background,
445 if (priv->highlight_foreground) {
447 "foreground", priv->highlight_foreground,
451 tag = empathy_text_buffer_tag_set (buffer, "fancy-time",
452 "justification", GTK_JUSTIFY_CENTER,
454 if (priv->time_foreground) {
456 "foreground", priv->time_foreground,
460 tag = empathy_text_buffer_tag_set (buffer, "fancy-event",
461 "justification", GTK_JUSTIFY_LEFT,
463 if (priv->event_foreground) {
465 "foreground", priv->event_foreground,
469 tag = empathy_text_buffer_tag_set (buffer, "invite", NULL);
470 if (priv->invite_foreground) {
472 "foreground", priv->invite_foreground,
476 tag = empathy_text_buffer_tag_set (buffer, "fancy-link",
477 "underline", PANGO_UNDERLINE_SINGLE,
479 if (priv->link_foreground) {
481 "foreground", priv->link_foreground,
487 theme_boxes_update_view (EmpathyTheme *theme, EmpathyChatView *view)
489 EmpathyThemeBoxesPriv *priv;
491 g_return_if_fail (EMPATHY_IS_THEME_BOXES (theme));
492 g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
494 priv = GET_PRIV (theme);
496 theme_boxes_define_theme_tags (theme, view);
498 empathy_chat_view_set_margin (view, MARGIN);
502 table_size_allocate_cb (GtkWidget *view,
503 GtkAllocation *allocation,
508 gtk_widget_get_size_request (box, NULL, &height);
510 width = allocation->width;
513 gtk_text_view_get_right_margin (GTK_TEXT_VIEW (view)) - \
514 gtk_text_view_get_left_margin (GTK_TEXT_VIEW (view));
516 width -= 2 * HEADER_PADDING;
518 gtk_widget_set_size_request (box, width, height);
522 theme_boxes_maybe_append_header (EmpathyTheme *theme,
523 EmpathyChatView *view,
526 EmpathyThemeBoxesPriv *priv;
527 EmpathyContact *contact;
528 EmpathyContact *last_contact;
529 GdkPixbuf *avatar = NULL;
530 GtkTextBuffer *buffer;
533 GtkWidget *label1, *label2;
534 GtkTextChildAnchor *anchor;
541 gboolean parse_success;
543 priv = GET_PRIV (theme);
545 contact = empathy_message_get_sender (msg);
546 name = empathy_contact_get_name (contact);
547 last_contact = empathy_chat_view_get_last_contact (view);
548 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
550 DEBUG ("Maybe add fancy header");
552 /* Only insert a header if the previously inserted block is not the same
553 * as this one. This catches all the different cases:
555 if (last_contact && empathy_contact_equal (last_contact, contact)) {
559 empathy_theme_append_spacing (theme, view);
561 gtk_text_buffer_get_end_iter (buffer, &iter);
562 gtk_text_buffer_insert_with_tags_by_name (buffer,
569 gtk_text_buffer_get_end_iter (buffer, &iter);
570 anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
572 box = gtk_hbox_new (FALSE, 0);
575 if (empathy_theme_get_show_avatars (theme)) {
576 avatar = empathy_chat_view_get_avatar_pixbuf_with_cache (contact);
580 image = gtk_image_new_from_pixbuf (avatar);
582 gtk_box_pack_start (GTK_BOX (box), image,
587 g_signal_connect_object (view, "size-allocate",
588 G_CALLBACK (table_size_allocate_cb),
591 str = g_markup_printf_escaped ("<b>%s</b>", name);
593 label1 = g_object_new (GTK_TYPE_LABEL,
599 parse_success = priv->header_foreground &&
600 gdk_color_parse (priv->header_foreground, &color);
603 gtk_widget_modify_fg (label1, GTK_STATE_NORMAL, &color);
608 time = empathy_message_get_timestamp (msg);
610 tmp = empathy_time_to_string_local (time,
611 EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
612 str = g_strdup_printf ("<i>%s</i>", tmp);
615 label2 = g_object_new (GTK_TYPE_LABEL,
622 gtk_widget_modify_fg (label2, GTK_STATE_NORMAL, &color);
627 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.5);
628 gtk_misc_set_alignment (GTK_MISC (label2), 1.0, 0.5);
630 gtk_box_pack_start (GTK_BOX (box), label1, TRUE, TRUE, 0);
631 gtk_box_pack_start (GTK_BOX (box), label2, TRUE, TRUE, 0);
633 gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view),
637 gtk_widget_show_all (box);
639 gtk_text_buffer_get_end_iter (buffer, &iter);
641 gtk_text_iter_backward_char (&start);
642 gtk_text_buffer_apply_tag_by_name (buffer,
646 gtk_text_buffer_insert_with_tags_by_name (buffer,
653 gtk_text_buffer_get_end_iter (buffer, &iter);
654 gtk_text_buffer_insert_with_tags_by_name (buffer,
663 theme_boxes_append_message (EmpathyTheme *theme,
664 EmpathyChatView *view,
665 EmpathyMessage *message)
667 EmpathyContact *sender;
669 empathy_theme_maybe_append_date_and_time (theme, view, message);
670 theme_boxes_maybe_append_header (theme, view, message);
672 sender = empathy_message_get_sender (message);
674 if (empathy_message_get_tptype (message) == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION) {
677 body = g_strdup_printf (" * %s %s",
678 empathy_contact_get_name (sender),
679 empathy_message_get_body (message));
680 empathy_theme_append_text (theme, view, body,
681 "fancy-action", "fancy-link");
683 empathy_theme_append_text (theme, view,
684 empathy_message_get_body (message),
685 "fancy-body", "fancy-link");
690 theme_boxes_append_event (EmpathyTheme *theme,
691 EmpathyChatView *view,
694 GtkTextBuffer *buffer;
698 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
700 empathy_theme_maybe_append_date_and_time (theme, view, NULL);
702 gtk_text_buffer_get_end_iter (buffer, &iter);
704 msg = g_strdup_printf (" - %s\n", str);
706 gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
714 theme_boxes_append_timestamp (EmpathyTheme *theme,
715 EmpathyChatView *view,
716 EmpathyMessage *message,
720 GtkTextBuffer *buffer;
730 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
732 date = empathy_message_get_date_and_time (message, ×tamp);
734 str = g_string_new (NULL);
736 if (show_time || show_date) {
737 empathy_theme_append_spacing (theme, view);
739 g_string_append (str, "- ");
745 g_date_strftime (buf, 256, _("%A %d %B %Y"), date);
746 g_string_append (str, buf);
749 g_string_append (str, ", ");
758 tmp = empathy_time_to_string_local (timestamp, EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
759 g_string_append (str, tmp);
763 if (show_time || show_date) {
764 g_string_append (str, " -\n");
766 gtk_text_buffer_get_end_iter (buffer, &iter);
767 gtk_text_buffer_insert_with_tags_by_name (buffer,
773 empathy_chat_view_set_last_timestamp (view, timestamp);
776 g_string_free (str, TRUE);
781 theme_boxes_append_spacing (EmpathyTheme *theme,
782 EmpathyChatView *view)
784 GtkTextBuffer *buffer;
787 g_return_if_fail (EMPATHY_IS_THEME (theme));
788 g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
790 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
792 gtk_text_buffer_get_end_iter (buffer, &iter);
793 gtk_text_buffer_insert_with_tags_by_name (buffer,