]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-theme-boxes.c
Move chat_view_get_avatar_pixbuf_with_cache to EmpathyThemeBoxes where it makes more...
[empathy.git] / libempathy-gtk / empathy-theme-boxes.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007 Imendio AB
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 #include <config.h>
22
23 #include <string.h>
24
25 #include <glib/gi18n-lib.h>
26 #include <gtk/gtk.h>
27
28 #include <telepathy-glib/util.h>
29 #include <libempathy/empathy-utils.h>
30 #include "empathy-ui-utils.h"
31 #include "empathy-theme-boxes.h"
32
33 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
34 #include <libempathy/empathy-debug.h>
35
36 #define MARGIN 4
37 #define HEADER_PADDING 2
38
39 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyThemeBoxes)
40 typedef struct {
41         gchar *header_foreground;
42         gchar *header_background;
43         gchar *header_line_background;
44         gchar *text_foreground;
45         gchar *text_background;
46         gchar *action_foreground;
47         gchar *highlight_foreground;
48         gchar *time_foreground;
49         gchar *event_foreground;
50         gchar *invite_foreground;
51         gchar *link_foreground;
52 } EmpathyThemeBoxesPriv;
53
54 static void     theme_boxes_finalize          (GObject            *object);
55 static void     theme_boxes_get_property      (GObject            *object,
56                                                guint               param_id,
57                                                GValue             *value,
58                                                GParamSpec         *pspec);
59 static void     theme_boxes_set_property      (GObject            *object,
60                                                guint               param_id,
61                                                const GValue       *value,
62                                                GParamSpec         *pspec);
63 static void     theme_boxes_define_theme_tags (EmpathyTheme        *theme,
64                                                EmpathyChatView     *view);
65 static void     theme_boxes_update_view       (EmpathyTheme        *theme,
66                                                EmpathyChatView     *view);
67 static void     theme_boxes_append_message    (EmpathyTheme        *theme,
68                                                EmpathyChatView     *view,
69                                                EmpathyMessage      *message);
70 static void     theme_boxes_append_event      (EmpathyTheme        *theme,
71                                                EmpathyChatView     *view,
72                                                const gchar        *str);
73 static void     theme_boxes_append_timestamp  (EmpathyTheme        *theme,
74                                                EmpathyChatView     *view,
75                                                EmpathyMessage      *message,
76                                                gboolean            show_date,
77                                                gboolean            show_time);
78 static void     theme_boxes_append_spacing    (EmpathyTheme        *theme,
79                                                EmpathyChatView     *view);
80
81 enum {
82         PROP_0,
83         PROP_HEADER_FOREGROUND,
84         PROP_HEADER_BACKGROUND,
85         PROP_HEADER_LINE_BACKGROUND,
86         PROP_TEXT_FOREGROUND,
87         PROP_TEXT_BACKGROUND,
88         PROP_ACTION_FOREGROUND,
89         PROP_HIGHLIGHT_FOREGROUND,
90         PROP_TIME_FOREGROUND,
91         PROP_EVENT_FOREGROUND,
92         PROP_INVITE_FOREGROUND,
93         PROP_LINK_FOREGROUND
94 };
95
96 enum {
97         PROP_FLOP,
98         PROP_MY_PROP
99 };
100
101 G_DEFINE_TYPE (EmpathyThemeBoxes, empathy_theme_boxes, EMPATHY_TYPE_THEME);
102
103 static void
104 empathy_theme_boxes_class_init (EmpathyThemeBoxesClass *class)
105 {
106         GObjectClass     *object_class;
107         EmpathyThemeClass *theme_class;
108
109         object_class = G_OBJECT_CLASS (class);
110         theme_class  = EMPATHY_THEME_CLASS (class);
111
112         object_class->finalize       = theme_boxes_finalize;
113         object_class->get_property   = theme_boxes_get_property;
114         object_class->set_property   = theme_boxes_set_property;
115
116         theme_class->update_view      = theme_boxes_update_view;
117         theme_class->append_message   = theme_boxes_append_message;
118         theme_class->append_event     = theme_boxes_append_event;
119         theme_class->append_timestamp = theme_boxes_append_timestamp;
120         theme_class->append_spacing   = theme_boxes_append_spacing;
121
122         g_object_class_install_property (object_class,
123                                          PROP_HEADER_FOREGROUND,
124                                          g_param_spec_string ("header-foreground",
125                                                               "",
126                                                               "",
127                                                               NULL,
128                                                               G_PARAM_READWRITE));
129
130         g_object_class_install_property (object_class,
131                                          PROP_HEADER_BACKGROUND,
132                                          g_param_spec_string ("header-background",
133                                                               "",
134                                                               "",
135                                                               NULL,
136                                                               G_PARAM_READWRITE));
137
138         g_object_class_install_property (object_class,
139                                          PROP_HEADER_LINE_BACKGROUND,
140                                          g_param_spec_string ("header-line-background",
141                                                               "",
142                                                               "",
143                                                               NULL,
144                                                               G_PARAM_READWRITE));
145
146
147         g_object_class_install_property (object_class,
148                                          PROP_TEXT_FOREGROUND,
149                                          g_param_spec_string ("text-foreground",
150                                                               "",
151                                                               "",
152                                                               NULL,
153                                                               G_PARAM_READWRITE));
154
155         g_object_class_install_property (object_class,
156                                          PROP_TEXT_BACKGROUND,
157                                          g_param_spec_string ("text-background",
158                                                               "",
159                                                               "",
160                                                               NULL,
161                                                               G_PARAM_READWRITE));
162
163         g_object_class_install_property (object_class,
164                                          PROP_ACTION_FOREGROUND,
165                                          g_param_spec_string ("action-foreground",
166                                                               "",
167                                                               "",
168                                                               NULL,
169                                                               G_PARAM_READWRITE));
170
171         g_object_class_install_property (object_class,
172                                          PROP_HIGHLIGHT_FOREGROUND,
173                                          g_param_spec_string ("highlight-foreground",
174                                                               "",
175                                                               "",
176                                                               NULL,
177                                                               G_PARAM_READWRITE));
178
179         g_object_class_install_property (object_class,
180                                          PROP_TIME_FOREGROUND,
181                                          g_param_spec_string ("time-foreground",
182                                                               "",
183                                                               "",
184                                                               NULL,
185                                                               G_PARAM_READWRITE));
186
187         g_object_class_install_property (object_class,
188                                          PROP_EVENT_FOREGROUND,
189                                          g_param_spec_string ("event-foreground",
190                                                               "",
191                                                               "",
192                                                               NULL,
193                                                               G_PARAM_READWRITE));
194
195         g_object_class_install_property (object_class,
196                                          PROP_INVITE_FOREGROUND,
197                                          g_param_spec_string ("invite-foreground",
198                                                               "",
199                                                               "",
200                                                               NULL,
201                                                               G_PARAM_READWRITE));
202
203         g_object_class_install_property (object_class,
204                                          PROP_LINK_FOREGROUND,
205                                          g_param_spec_string ("link-foreground",
206                                                               "",
207                                                               "",
208                                                               NULL,
209                                                               G_PARAM_READWRITE));
210
211         g_type_class_add_private (object_class, sizeof (EmpathyThemeBoxesPriv));
212 }
213
214 static void
215 empathy_theme_boxes_init (EmpathyThemeBoxes *theme)
216 {
217         EmpathyThemeBoxesPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (theme,
218                 EMPATHY_TYPE_THEME_BOXES, EmpathyThemeBoxesPriv);
219
220         theme->priv = priv;
221 }
222
223 static void
224 theme_boxes_finalize (GObject *object)
225 {
226         EmpathyThemeBoxesPriv *priv;
227
228         priv = GET_PRIV (object);
229
230         g_free (priv->header_foreground);
231         g_free (priv->header_background);
232         g_free (priv->header_line_background);
233         g_free (priv->text_foreground);
234         g_free (priv->text_background);
235         g_free (priv->action_foreground);
236         g_free (priv->highlight_foreground);
237         g_free (priv->time_foreground);
238         g_free (priv->event_foreground);
239         g_free (priv->invite_foreground);
240         g_free (priv->link_foreground);
241         
242         (G_OBJECT_CLASS (empathy_theme_boxes_parent_class)->finalize) (object);
243 }
244
245 static void
246 theme_boxes_get_property (GObject    *object,
247                           guint       param_id,
248                           GValue     *value,
249                           GParamSpec *pspec)
250 {
251         EmpathyThemeBoxesPriv *priv;
252
253         priv = GET_PRIV (object);
254
255         switch (param_id) {
256         case PROP_HEADER_FOREGROUND:
257                 g_value_set_string (value, priv->header_foreground);
258                 break;
259         case PROP_HEADER_BACKGROUND:
260                 g_value_set_string (value, priv->header_background);
261                 break;
262         case PROP_HEADER_LINE_BACKGROUND:
263                 g_value_set_string (value, priv->header_line_background);
264                 break;
265         case PROP_TEXT_FOREGROUND:
266                 g_value_set_string (value, priv->text_foreground);
267                 break;
268         case PROP_TEXT_BACKGROUND:
269                 g_value_set_string (value, priv->text_background);
270                 break;
271         case PROP_ACTION_FOREGROUND:
272                 g_value_set_string (value, priv->action_foreground);
273                 break;
274         case PROP_HIGHLIGHT_FOREGROUND:
275                 g_value_set_string (value, priv->highlight_foreground);
276                 break;
277         case PROP_TIME_FOREGROUND:
278                 g_value_set_string (value, priv->time_foreground);
279                 break;
280         case PROP_EVENT_FOREGROUND:
281                 g_value_set_string (value, priv->event_foreground);
282                 break;
283         case PROP_INVITE_FOREGROUND:
284                 g_value_set_string (value, priv->invite_foreground);
285                 break;
286         case PROP_LINK_FOREGROUND:
287                 g_value_set_string (value, priv->link_foreground);
288                 break;
289         default:
290                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
291                 break;
292         }
293 }
294 static void
295 theme_boxes_set_property (GObject      *object,
296                           guint         param_id,
297                           const GValue *value,
298                           GParamSpec   *pspec)
299 {
300         EmpathyThemeBoxesPriv *priv;
301
302         priv = GET_PRIV (object);
303
304         switch (param_id) {
305         case PROP_HEADER_FOREGROUND:
306                 g_free (priv->header_foreground);
307                 priv->header_foreground = g_value_dup_string (value);
308                 g_object_notify (object, "header-foreground");
309                 break;
310         case PROP_HEADER_BACKGROUND:
311                 g_free (priv->header_background);
312                 priv->header_background = g_value_dup_string (value);
313                 g_object_notify (object, "header-background");
314                 break;
315         case PROP_HEADER_LINE_BACKGROUND:
316                 g_free (priv->header_line_background);
317                 priv->header_line_background = g_value_dup_string (value);
318                 g_object_notify (object, "header-line_background");
319                 break;
320         case PROP_TEXT_FOREGROUND:
321                 g_free (priv->text_foreground);
322                 priv->text_foreground = g_value_dup_string (value);
323                 g_object_notify (object, "text-foreground");
324                 break;
325         case PROP_TEXT_BACKGROUND:
326                 g_free (priv->text_background);
327                 priv->text_background = g_value_dup_string (value);
328                 g_object_notify (object, "text-background");
329                 break;
330         case PROP_ACTION_FOREGROUND:
331                 g_free (priv->action_foreground);
332                 priv->action_foreground = g_value_dup_string (value);
333                 g_object_notify (object, "action-foreground");
334                 break;
335         case PROP_HIGHLIGHT_FOREGROUND:
336                 g_free (priv->highlight_foreground);
337                 priv->highlight_foreground = g_value_dup_string (value);
338                 g_object_notify (object, "highlight-foreground");
339                 break;
340         case PROP_TIME_FOREGROUND:
341                 g_free (priv->time_foreground);
342                 priv->time_foreground = g_value_dup_string (value);
343                 g_object_notify (object, "time-foreground");
344                 break;
345         case PROP_EVENT_FOREGROUND:
346                 g_free (priv->event_foreground);
347                 priv->event_foreground = g_value_dup_string (value);
348                 g_object_notify (object, "event-foreground");
349                 break;
350         case PROP_INVITE_FOREGROUND:
351                 g_free (priv->invite_foreground);
352                 priv->invite_foreground = g_value_dup_string (value);
353                 g_object_notify (object, "invite-foreground");
354                 break;
355         case PROP_LINK_FOREGROUND:
356                 g_free (priv->link_foreground);
357                 priv->link_foreground = g_value_dup_string (value);
358                 g_object_notify (object, "link-foreground");
359                 break;
360         default:
361                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
362                 break;
363         }
364 }
365
366 static void
367 theme_boxes_define_theme_tags (EmpathyTheme *theme, EmpathyChatView *view)
368 {
369         EmpathyThemeBoxesPriv *priv;
370         GtkTextBuffer         *buffer;
371         GtkTextTag            *tag;
372
373         priv = GET_PRIV (theme);
374
375         buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
376
377         empathy_text_buffer_tag_set (buffer, "fancy-spacing",
378                                      "size", 3000,
379                                      "pixels-above-lines", 8,
380                                      NULL);
381
382         tag = empathy_text_buffer_tag_set (buffer, "fancy-header",
383                                            "weight", PANGO_WEIGHT_BOLD,
384                                            "pixels-above-lines", HEADER_PADDING,
385                                            "pixels-below-lines", HEADER_PADDING,
386                                            NULL);
387         if (priv->header_foreground) {
388                 g_object_set (tag,
389                               "foreground", priv->header_foreground,
390                               "paragraph-background", priv->header_background,
391                               NULL);
392         }
393
394         tag = empathy_text_buffer_tag_set (buffer, "fancy-header-line",
395                                            "size", 1,
396                                            NULL);
397         if (priv->header_line_background) {
398                 g_object_set (tag,
399                               "paragraph-background", priv->header_line_background,
400                               NULL);
401         }
402
403         tag = empathy_text_buffer_tag_set (buffer, "fancy-body",
404                                            "pixels-above-lines", 4,
405                                            NULL);
406         if (priv->text_background) {
407                 g_object_set (tag,
408                               "paragraph-background", priv->text_background,
409                               NULL);
410         }
411
412         if (priv->text_foreground) {
413                 g_object_set (tag,
414                               "foreground", priv->text_foreground,
415                               NULL);
416         }
417
418         tag = empathy_text_buffer_tag_set (buffer, "fancy-action",
419                                            "style", PANGO_STYLE_ITALIC,
420                                            "pixels-above-lines", 4,
421                                            NULL);
422
423         if (priv->text_background) {
424                 g_object_set (tag,
425                               "paragraph-background", priv->text_background,
426                               NULL);
427         }
428
429         if (priv->action_foreground) {
430                 g_object_set (tag,
431                               "foreground", priv->action_foreground,
432                               NULL);
433         }
434
435         tag = empathy_text_buffer_tag_set (buffer, "fancy-highlight",
436                                            "weight", PANGO_WEIGHT_BOLD,
437                                            "pixels-above-lines", 4,
438                                            NULL);
439         if (priv->text_background) {
440                 g_object_set (tag,
441                               "paragraph-background", priv->text_background,
442                               NULL);
443         }
444
445
446         if (priv->highlight_foreground) {
447                 g_object_set (tag,
448                               "foreground", priv->highlight_foreground,
449                               NULL);
450         }
451
452         tag = empathy_text_buffer_tag_set (buffer, "fancy-time",
453                                            "justification", GTK_JUSTIFY_CENTER,
454                                            NULL);
455         if (priv->time_foreground) {
456                 g_object_set (tag,
457                               "foreground", priv->time_foreground,
458                               NULL);
459         }
460
461         tag = empathy_text_buffer_tag_set (buffer, "fancy-event",
462                                            "justification", GTK_JUSTIFY_LEFT,
463                                            NULL);
464         if (priv->event_foreground) {
465                 g_object_set (tag,
466                               "foreground", priv->event_foreground,
467                               NULL);
468         }
469
470         tag = empathy_text_buffer_tag_set (buffer, "invite", NULL);
471         if (priv->invite_foreground) {
472                 g_object_set (tag,
473                               "foreground", priv->invite_foreground,
474                               NULL);
475         }
476
477         tag = empathy_text_buffer_tag_set (buffer, "fancy-link",
478                                            "underline", PANGO_UNDERLINE_SINGLE,
479                                            NULL);
480         if (priv->link_foreground) {
481                 g_object_set (tag,
482                               "foreground", priv->link_foreground,
483                               NULL);
484         } 
485 }
486
487 static void
488 theme_boxes_update_view (EmpathyTheme *theme, EmpathyChatView *view)
489 {
490         EmpathyThemeBoxesPriv *priv;
491
492         g_return_if_fail (EMPATHY_IS_THEME_BOXES (theme));
493         g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
494
495         priv = GET_PRIV (theme);
496
497         theme_boxes_define_theme_tags (theme, view);
498         
499         empathy_chat_view_set_margin (view, MARGIN);
500 }
501
502 static void
503 table_size_allocate_cb (GtkWidget     *view,
504                         GtkAllocation *allocation,
505                         GtkWidget     *box)
506 {
507         gint width, height;
508
509         gtk_widget_get_size_request (box, NULL, &height);
510
511         width = allocation->width;
512         
513         width -= \
514                 gtk_text_view_get_right_margin (GTK_TEXT_VIEW (view)) - \
515                 gtk_text_view_get_left_margin (GTK_TEXT_VIEW (view));
516         width -= 2 * MARGIN;
517         width -= 2 * HEADER_PADDING;
518
519         gtk_widget_set_size_request (box, width, height);
520 }
521
522 /* Pads a pixbuf to the specified size, by centering it in a larger transparent
523  * pixbuf. Returns a new ref.
524  */
525 static GdkPixbuf *
526 theme_boxes_pad_to_size (GdkPixbuf *pixbuf,
527                          gint       width,
528                          gint       height,
529                          gint       extra_padding_right)
530 {
531         gint       src_width, src_height;
532         GdkPixbuf *padded;
533         gint       x_offset, y_offset;
534
535         src_width = gdk_pixbuf_get_width (pixbuf);
536         src_height = gdk_pixbuf_get_height (pixbuf);
537
538         x_offset = (width - src_width) / 2;
539         y_offset = (height - src_height) / 2;
540
541         padded = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (pixbuf),
542                                  TRUE, /* alpha */
543                                  gdk_pixbuf_get_bits_per_sample (pixbuf),
544                                  width + extra_padding_right,
545                                  height);
546
547         gdk_pixbuf_fill (padded, 0);
548
549         gdk_pixbuf_copy_area (pixbuf,
550                               0, /* source coords */
551                               0,
552                               src_width,
553                               src_height,
554                               padded,
555                               x_offset, /* dest coords */
556                               y_offset);
557
558         return padded;
559 }
560
561 typedef struct {
562         GdkPixbuf *pixbuf;
563         gchar     *token;
564 } AvatarData;
565
566 static void
567 theme_boxes_avatar_cache_data_free (gpointer ptr)
568 {
569         AvatarData *data = ptr;
570
571         g_object_unref (data->pixbuf);
572         g_free (data->token);
573         g_slice_free (AvatarData, data);
574 }
575
576 static GdkPixbuf *
577 theme_boxes_get_avatar_pixbuf_with_cache (EmpathyContact *contact)
578 {
579         AvatarData        *data;
580         EmpathyAvatar     *avatar;
581         GdkPixbuf         *tmp_pixbuf;
582         GdkPixbuf         *pixbuf = NULL;
583
584         /* Check if avatar is in cache and if it's up to date */
585         avatar = empathy_contact_get_avatar (contact);
586         data = g_object_get_data (G_OBJECT (contact), "chat-view-avatar-cache");
587         if (data) {
588                 if (avatar && !tp_strdiff (avatar->token, data->token)) {
589                         /* We have the avatar in cache */
590                         return data->pixbuf;
591                 }
592         }
593
594         /* Avatar not in cache, create pixbuf */
595         tmp_pixbuf = empathy_pixbuf_avatar_from_contact_scaled (contact, 32, 32);
596         if (tmp_pixbuf) {
597                 pixbuf = theme_boxes_pad_to_size (tmp_pixbuf, 32, 32, 6);
598                 g_object_unref (tmp_pixbuf);
599         }
600         if (!pixbuf) {
601                 return NULL;
602         }
603
604         /* Insert new pixbuf in cache */
605         data = g_slice_new0 (AvatarData);
606         data->token = g_strdup (avatar->token);
607         data->pixbuf = pixbuf;
608
609         g_object_set_data_full (G_OBJECT (contact), "chat-view-avatar-cache",
610                                 data, theme_boxes_avatar_cache_data_free);
611
612         return data->pixbuf;
613 }
614
615 static void
616 theme_boxes_maybe_append_header (EmpathyTheme        *theme,
617                                  EmpathyChatView     *view,
618                                  EmpathyMessage      *msg)
619 {
620         EmpathyThemeBoxesPriv *priv;
621         EmpathyContact        *contact;
622         EmpathyContact        *last_contact;
623         GdkPixbuf            *avatar = NULL;
624         GtkTextBuffer        *buffer;
625         const gchar          *name;
626         GtkTextIter           iter;
627         GtkWidget            *label1, *label2;
628         GtkTextChildAnchor   *anchor;
629         GtkWidget            *box;
630         gchar                *str;
631         time_t                time;
632         gchar                *tmp;
633         GtkTextIter           start;
634         GdkColor              color;
635         gboolean              parse_success;
636
637         priv = GET_PRIV (theme);
638
639         contact = empathy_message_get_sender (msg);
640         name = empathy_contact_get_name (contact);
641         last_contact = empathy_chat_view_get_last_contact (view);
642         buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
643
644         DEBUG ("Maybe add fancy header");
645
646         /* Only insert a header if the previously inserted block is not the same
647          * as this one. This catches all the different cases:
648          */
649         if (last_contact && empathy_contact_equal (last_contact, contact)) {
650                 return;
651         }
652
653         empathy_theme_append_spacing (theme, view);
654
655         gtk_text_buffer_get_end_iter (buffer, &iter);
656         gtk_text_buffer_insert_with_tags_by_name (buffer,
657                                                   &iter,
658                                                   "\n",
659                                                   -1,
660                                                   "fancy-header-line",
661                                                   NULL);
662
663         gtk_text_buffer_get_end_iter (buffer, &iter);
664         anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
665
666         box = gtk_hbox_new (FALSE, 0);
667
668
669         if (empathy_theme_get_show_avatars (theme)) {
670                 avatar = theme_boxes_get_avatar_pixbuf_with_cache (contact);
671                 if (avatar) {
672                         GtkWidget *image;
673
674                         image = gtk_image_new_from_pixbuf (avatar);
675
676                         gtk_box_pack_start (GTK_BOX (box), image,
677                                             FALSE, TRUE, 2);
678                 }
679         }
680
681         g_signal_connect_object (view, "size-allocate",
682                                  G_CALLBACK (table_size_allocate_cb),
683                                  box, 0);
684
685         str = g_markup_printf_escaped ("<b>%s</b>", name);
686
687         label1 = g_object_new (GTK_TYPE_LABEL,
688                                "label", str,
689                                "use-markup", TRUE,
690                                "xalign", 0.0,
691                                NULL);
692
693         parse_success = priv->header_foreground &&
694                         gdk_color_parse (priv->header_foreground, &color);
695
696         if (parse_success) {
697                 gtk_widget_modify_fg (label1, GTK_STATE_NORMAL, &color);
698         }
699
700         g_free (str);
701
702         time = empathy_message_get_timestamp (msg);
703
704         tmp = empathy_time_to_string_local (time, 
705                                            EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
706         str = g_strdup_printf ("<i>%s</i>", tmp);
707         g_free (tmp);
708
709         label2 = g_object_new (GTK_TYPE_LABEL,
710                                "label", str,
711                                "use-markup", TRUE,
712                                "xalign", 1.0,
713                                NULL);
714         
715         if (parse_success) {
716                 gtk_widget_modify_fg (label2, GTK_STATE_NORMAL, &color);
717         }
718
719         g_free (str);
720
721         gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.5);
722         gtk_misc_set_alignment (GTK_MISC (label2), 1.0, 0.5);
723
724         gtk_box_pack_start (GTK_BOX (box), label1, TRUE, TRUE, 0);
725         gtk_box_pack_start (GTK_BOX (box), label2, TRUE, TRUE, 0);
726
727         gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view),
728                                            box,
729                                            anchor);
730
731         gtk_widget_show_all (box);
732
733         gtk_text_buffer_get_end_iter (buffer, &iter);
734         start = iter;
735         gtk_text_iter_backward_char (&start);
736         gtk_text_buffer_apply_tag_by_name (buffer,
737                                            "fancy-header",
738                                            &start, &iter);
739
740         gtk_text_buffer_insert_with_tags_by_name (buffer,
741                                                   &iter,
742                                                   "\n",
743                                                   -1,
744                                                   "fancy-header",
745                                                   NULL);
746
747         gtk_text_buffer_get_end_iter (buffer, &iter);
748         gtk_text_buffer_insert_with_tags_by_name (buffer,
749                                                   &iter,
750                                                   "\n",
751                                                   -1,
752                                                   "fancy-header-line",
753                                                   NULL);
754 }
755
756 static void
757 theme_boxes_append_message (EmpathyTheme        *theme,
758                             EmpathyChatView     *view,
759                             EmpathyMessage      *message)
760 {
761         EmpathyContact *sender;
762
763         empathy_theme_maybe_append_date_and_time (theme, view, message);
764         theme_boxes_maybe_append_header (theme, view, message);
765
766         sender = empathy_message_get_sender (message);
767
768         if (empathy_message_get_tptype (message) == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION) {
769                 gchar *body;
770
771                 body = g_strdup_printf (" * %s %s", 
772                                         empathy_contact_get_name (sender),
773                                         empathy_message_get_body (message));
774                 empathy_theme_append_text (theme, view, body,
775                                            "fancy-action", "fancy-link");
776         } else {
777                 empathy_theme_append_text (theme, view,
778                                            empathy_message_get_body (message),
779                                            "fancy-body", "fancy-link");
780         }
781 }
782
783 static void
784 theme_boxes_append_event (EmpathyTheme        *theme,
785                           EmpathyChatView     *view,
786                           const gchar        *str)
787 {
788         GtkTextBuffer *buffer;
789         GtkTextIter    iter;
790         gchar         *msg;
791
792         buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
793
794         empathy_theme_maybe_append_date_and_time (theme, view, NULL);
795
796         gtk_text_buffer_get_end_iter (buffer, &iter);
797
798         msg = g_strdup_printf (" - %s\n", str);
799
800         gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
801                                                   msg, -1,
802                                                   "fancy-event",
803                                                   NULL);
804         g_free (msg);
805 }
806
807 static void
808 theme_boxes_append_timestamp (EmpathyTheme        *theme,
809                               EmpathyChatView     *view,
810                               EmpathyMessage      *message,
811                               gboolean            show_date,
812                               gboolean            show_time)
813 {
814         GtkTextBuffer *buffer;
815         time_t         timestamp;
816         GDate         *date;
817         GtkTextIter    iter;
818         GString       *str;
819         
820         if (!show_date) {
821                 return;
822         }
823
824         buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
825
826         date = empathy_message_get_date_and_time (message, &timestamp);
827
828         str = g_string_new (NULL);
829
830         if (show_time || show_date) {
831                 empathy_theme_append_spacing (theme, view);
832
833                 g_string_append (str, "- ");
834         }
835
836         if (show_date) {
837                 gchar buf[256];
838
839                 g_date_strftime (buf, 256, _("%A %d %B %Y"), date);
840                 g_string_append (str, buf);
841
842                 if (show_time) {
843                         g_string_append (str, ", ");
844                 }
845         }
846
847         g_date_free (date);
848
849         if (show_time) {
850                 gchar *tmp;
851
852                 tmp = empathy_time_to_string_local (timestamp, EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
853                 g_string_append (str, tmp);
854                 g_free (tmp);
855         }
856
857         if (show_time || show_date) {
858                 g_string_append (str, " -\n");
859
860                 gtk_text_buffer_get_end_iter (buffer, &iter);
861                 gtk_text_buffer_insert_with_tags_by_name (buffer,
862                                                           &iter,
863                                                           str->str, -1,
864                                                           "fancy-time",
865                                                           NULL);
866
867                 empathy_chat_view_set_last_timestamp (view, timestamp);
868         }
869
870         g_string_free (str, TRUE);
871         
872 }
873
874 static void
875 theme_boxes_append_spacing (EmpathyTheme        *theme,
876                             EmpathyChatView     *view)
877 {
878         GtkTextBuffer *buffer;
879         GtkTextIter    iter;
880
881         g_return_if_fail (EMPATHY_IS_THEME (theme));
882         g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view));
883
884         buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
885
886         gtk_text_buffer_get_end_iter (buffer, &iter);
887         gtk_text_buffer_insert_with_tags_by_name (buffer,
888                                                   &iter,
889                                                   "\n",
890                                                   -1,
891                                                   "cut",
892                                                   "fancy-spacing",
893                                                   NULL);
894 }
895