1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2007 Imendio AB
4 * Copyright (C) 2008 Collabora Ltd.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301 USA
21 * Authors: Xavier Claessens <xclaesse@gmail.com>
28 #include <glib/gi18n-lib.h>
31 #include <telepathy-glib/util.h>
33 #include <libempathy/empathy-utils.h>
34 #include "empathy-theme-boxes.h"
35 #include "empathy-ui-utils.h"
36 #include "empathy-conf.h"
38 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
39 #include <libempathy/empathy-debug.h>
42 #define HEADER_PADDING 2
44 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyThemeBoxes)
46 gboolean show_avatars
;
47 guint notify_show_avatars_id
;
48 } EmpathyThemeBoxesPriv
;
50 G_DEFINE_TYPE (EmpathyThemeBoxes
, empathy_theme_boxes
, EMPATHY_TYPE_CHAT_TEXT_VIEW
);
53 theme_boxes_create_tags (EmpathyThemeBoxes
*theme
)
55 GtkTextBuffer
*buffer
;
57 buffer
= gtk_text_view_get_buffer (GTK_TEXT_VIEW (theme
));
59 gtk_text_buffer_create_tag (buffer
, EMPATHY_THEME_BOXES_TAG_HEADER
,
60 "pixels-above-lines", HEADER_PADDING
,
61 "pixels-below-lines", HEADER_PADDING
,
64 gtk_text_buffer_create_tag (buffer
, EMPATHY_THEME_BOXES_TAG_HEADER_LINE
, NULL
);
67 /* Pads a pixbuf to the specified size, by centering it in a larger transparent
68 * pixbuf. Returns a new ref.
71 theme_boxes_pad_to_size (GdkPixbuf
*pixbuf
,
74 gint extra_padding_right
)
76 gint src_width
, src_height
;
78 gint x_offset
, y_offset
;
80 src_width
= gdk_pixbuf_get_width (pixbuf
);
81 src_height
= gdk_pixbuf_get_height (pixbuf
);
83 x_offset
= (width
- src_width
) / 2;
84 y_offset
= (height
- src_height
) / 2;
86 padded
= gdk_pixbuf_new (gdk_pixbuf_get_colorspace (pixbuf
),
88 gdk_pixbuf_get_bits_per_sample (pixbuf
),
89 width
+ extra_padding_right
,
92 gdk_pixbuf_fill (padded
, 0);
94 gdk_pixbuf_copy_area (pixbuf
,
95 0, /* source coords */
100 x_offset
, /* dest coords */
112 theme_boxes_avatar_cache_data_free (gpointer ptr
)
114 AvatarData
*data
= ptr
;
116 g_object_unref (data
->pixbuf
);
117 g_free (data
->token
);
118 g_slice_free (AvatarData
, data
);
122 theme_boxes_get_avatar_pixbuf_with_cache (EmpathyContact
*contact
)
125 EmpathyAvatar
*avatar
;
126 GdkPixbuf
*tmp_pixbuf
;
127 GdkPixbuf
*pixbuf
= NULL
;
129 /* Check if avatar is in cache and if it's up to date */
130 avatar
= empathy_contact_get_avatar (contact
);
131 data
= g_object_get_data (G_OBJECT (contact
), "chat-view-avatar-cache");
133 if (avatar
&& !tp_strdiff (avatar
->token
, data
->token
)) {
134 /* We have the avatar in cache */
139 /* Avatar not in cache, create pixbuf */
140 tmp_pixbuf
= empathy_pixbuf_avatar_from_contact_scaled (contact
, 32, 32);
142 pixbuf
= theme_boxes_pad_to_size (tmp_pixbuf
, 32, 32, 6);
143 g_object_unref (tmp_pixbuf
);
149 /* Insert new pixbuf in cache */
150 data
= g_slice_new0 (AvatarData
);
151 data
->token
= g_strdup (avatar
->token
);
152 data
->pixbuf
= pixbuf
;
154 g_object_set_data_full (G_OBJECT (contact
), "chat-view-avatar-cache",
155 data
, theme_boxes_avatar_cache_data_free
);
161 table_size_allocate_cb (GtkWidget
*view
,
162 GtkAllocation
*allocation
,
167 gtk_widget_get_size_request (box
, NULL
, &height
);
169 width
= allocation
->width
;
172 gtk_text_view_get_right_margin (GTK_TEXT_VIEW (view
)) - \
173 gtk_text_view_get_left_margin (GTK_TEXT_VIEW (view
));
175 width
-= 2 * HEADER_PADDING
;
177 gtk_widget_set_size_request (box
, width
, height
);
181 theme_boxes_maybe_append_header (EmpathyThemeBoxes
*theme
,
184 EmpathyChatTextView
*view
= EMPATHY_CHAT_TEXT_VIEW (theme
);
185 EmpathyThemeBoxesPriv
*priv
= GET_PRIV (theme
);
186 EmpathyContact
*contact
;
187 EmpathyContact
*last_contact
;
188 GdkPixbuf
*avatar
= NULL
;
189 GtkTextBuffer
*buffer
;
192 GtkWidget
*label1
, *label2
;
193 GtkTextChildAnchor
*anchor
;
200 GtkTextTagTable
*table
;
204 contact
= empathy_message_get_sender (msg
);
205 name
= empathy_contact_get_name (contact
);
206 last_contact
= empathy_chat_text_view_get_last_contact (view
);
207 buffer
= gtk_text_view_get_buffer (GTK_TEXT_VIEW (theme
));
209 DEBUG ("Maybe add fancy header");
211 /* Only insert a header if the previously inserted block is not the same
214 if (empathy_contact_equal (last_contact
, contact
)) {
218 empathy_chat_text_view_append_spacing (view
);
220 /* Insert header line */
221 gtk_text_buffer_get_end_iter (buffer
, &iter
);
222 gtk_text_buffer_insert_with_tags_by_name (buffer
,
226 EMPATHY_THEME_BOXES_TAG_HEADER_LINE
,
229 gtk_text_buffer_get_end_iter (buffer
, &iter
);
230 anchor
= gtk_text_buffer_create_child_anchor (buffer
, &iter
);
232 /* Create a hbox for the header and resize it when the view allocation
234 box
= gtk_hbox_new (FALSE
, 0);
235 g_signal_connect_object (view
, "size-allocate",
236 G_CALLBACK (table_size_allocate_cb
),
239 /* Add avatar to the box if needed */
240 if (priv
->show_avatars
) {
241 avatar
= theme_boxes_get_avatar_pixbuf_with_cache (contact
);
245 image
= gtk_image_new_from_pixbuf (avatar
);
247 gtk_box_pack_start (GTK_BOX (box
), image
,
252 /* Add contact alias */
253 str
= g_markup_printf_escaped ("<b>%s</b>", name
);
254 label1
= g_object_new (GTK_TYPE_LABEL
,
261 /* Add the message receive time */
262 time_
= empathy_message_get_timestamp (msg
);
263 tmp
= empathy_time_to_string_local (time_
,
264 EMPATHY_TIME_FORMAT_DISPLAY_SHORT
);
265 str
= g_strdup_printf ("<i>%s</i>", tmp
);
266 label2
= g_object_new (GTK_TYPE_LABEL
,
272 str_obj
= g_string_new ("\n- ");
273 g_string_append (str_obj
, name
);
274 g_string_append (str_obj
, ", ");
275 g_string_append (str_obj
, tmp
);
276 g_string_append (str_obj
, " -");
280 /* Set foreground color of labels to the same color than the header tag. */
281 table
= gtk_text_buffer_get_tag_table (buffer
);
282 tag
= gtk_text_tag_table_lookup (table
, EMPATHY_THEME_BOXES_TAG_HEADER
);
283 g_object_get (tag
, "foreground-set", &color_set
, NULL
);
287 g_object_get (tag
, "foreground-gdk", &color
, NULL
);
288 gtk_widget_modify_fg (label1
, GTK_STATE_NORMAL
, color
);
289 gtk_widget_modify_fg (label2
, GTK_STATE_NORMAL
, color
);
290 gdk_color_free (color
);
293 /* Pack labels into the box */
294 gtk_misc_set_alignment (GTK_MISC (label1
), 0.0, 0.5);
295 gtk_misc_set_alignment (GTK_MISC (label2
), 1.0, 0.5);
296 gtk_box_pack_start (GTK_BOX (box
), label1
, TRUE
, TRUE
, 0);
297 gtk_box_pack_start (GTK_BOX (box
), label2
, TRUE
, TRUE
, 0);
299 /* Add the header box to the text view */
300 g_object_set_data_full (G_OBJECT (box
),
302 g_string_free (str_obj
, FALSE
),
304 gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view
),
307 gtk_widget_show_all (box
);
309 /* Insert a header line */
310 gtk_text_buffer_get_end_iter (buffer
, &iter
);
312 gtk_text_iter_backward_char (&start
);
313 gtk_text_buffer_apply_tag_by_name (buffer
,
314 EMPATHY_THEME_BOXES_TAG_HEADER
,
316 gtk_text_buffer_insert_with_tags_by_name (buffer
,
320 EMPATHY_THEME_BOXES_TAG_HEADER
,
322 gtk_text_buffer_get_end_iter (buffer
, &iter
);
323 gtk_text_buffer_insert_with_tags_by_name (buffer
,
327 EMPATHY_THEME_BOXES_TAG_HEADER_LINE
,
332 theme_boxes_append_message (EmpathyChatTextView
*view
,
333 EmpathyMessage
*message
)
335 EmpathyContact
*sender
;
337 theme_boxes_maybe_append_header (EMPATHY_THEME_BOXES (view
), message
);
339 sender
= empathy_message_get_sender (message
);
340 if (empathy_message_get_tptype (message
) ==
341 TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION
) {
344 body
= g_strdup_printf (" * %s %s",
345 empathy_contact_get_name (sender
),
346 empathy_message_get_body (message
));
347 empathy_chat_text_view_append_body (EMPATHY_CHAT_TEXT_VIEW (view
),
349 EMPATHY_CHAT_TEXT_VIEW_TAG_ACTION
);
351 empathy_chat_text_view_append_body (EMPATHY_CHAT_TEXT_VIEW (view
),
352 empathy_message_get_body (message
),
353 EMPATHY_CHAT_TEXT_VIEW_TAG_BODY
);
358 theme_boxes_notify_show_avatars_cb (EmpathyConf
*conf
,
362 EmpathyThemeBoxesPriv
*priv
= GET_PRIV (user_data
);
364 empathy_conf_get_bool (conf
, key
, &priv
->show_avatars
);
368 theme_boxes_finalize (GObject
*object
)
370 EmpathyThemeBoxesPriv
*priv
= GET_PRIV (object
);
372 empathy_conf_notify_remove (empathy_conf_get (),
373 priv
->notify_show_avatars_id
);
375 G_OBJECT_CLASS (empathy_theme_boxes_parent_class
)->finalize (object
);
379 empathy_theme_boxes_class_init (EmpathyThemeBoxesClass
*class)
381 GObjectClass
*object_class
;
382 EmpathyChatTextViewClass
*chat_text_view_class
;
384 object_class
= G_OBJECT_CLASS (class);
385 chat_text_view_class
= EMPATHY_CHAT_TEXT_VIEW_CLASS (class);
387 object_class
->finalize
= theme_boxes_finalize
;
388 chat_text_view_class
->append_message
= theme_boxes_append_message
;
390 g_type_class_add_private (object_class
, sizeof (EmpathyThemeBoxesPriv
));
394 empathy_theme_boxes_init (EmpathyThemeBoxes
*theme
)
396 EmpathyThemeBoxesPriv
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (theme
,
397 EMPATHY_TYPE_THEME_BOXES
, EmpathyThemeBoxesPriv
);
401 theme_boxes_create_tags (theme
);
403 priv
->notify_show_avatars_id
=
404 empathy_conf_notify_add (empathy_conf_get (),
405 EMPATHY_PREFS_UI_SHOW_AVATARS
,
406 theme_boxes_notify_show_avatars_cb
,
409 empathy_conf_get_bool (empathy_conf_get (),
410 EMPATHY_PREFS_UI_SHOW_AVATARS
,
411 &priv
->show_avatars
);
415 "left-margin", MARGIN
,
416 "right-margin", MARGIN
,
421 empathy_theme_boxes_new (void)
423 return g_object_new (EMPATHY_TYPE_THEME_BOXES
,
424 "only-if-date", TRUE
,