add empathy-auto-salut-account-helper.c to POTFILES.in
[empathy-mirror.git] / libempathy-gtk / empathy-theme-boxes.c
blobe6a606de186ea4b5d3d08d1a704c661c65d16066
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
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>
24 #include <config.h>
26 #include <string.h>
28 #include <glib/gi18n-lib.h>
29 #include <gtk/gtk.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>
41 #define MARGIN 4
42 #define HEADER_PADDING 2
44 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyThemeBoxes)
45 typedef struct {
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);
52 static void
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,
62 NULL);
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.
70 static GdkPixbuf *
71 theme_boxes_pad_to_size (GdkPixbuf *pixbuf,
72 gint width,
73 gint height,
74 gint extra_padding_right)
76 gint src_width, src_height;
77 GdkPixbuf *padded;
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),
87 TRUE, /* alpha */
88 gdk_pixbuf_get_bits_per_sample (pixbuf),
89 width + extra_padding_right,
90 height);
92 gdk_pixbuf_fill (padded, 0);
94 gdk_pixbuf_copy_area (pixbuf,
95 0, /* source coords */
97 src_width,
98 src_height,
99 padded,
100 x_offset, /* dest coords */
101 y_offset);
103 return padded;
106 typedef struct {
107 GdkPixbuf *pixbuf;
108 gchar *token;
109 } AvatarData;
111 static void
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);
121 static GdkPixbuf *
122 theme_boxes_get_avatar_pixbuf_with_cache (EmpathyContact *contact)
124 AvatarData *data;
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");
132 if (data) {
133 if (avatar && !tp_strdiff (avatar->token, data->token)) {
134 /* We have the avatar in cache */
135 return data->pixbuf;
139 /* Avatar not in cache, create pixbuf */
140 tmp_pixbuf = empathy_pixbuf_avatar_from_contact_scaled (contact, 32, 32);
141 if (tmp_pixbuf) {
142 pixbuf = theme_boxes_pad_to_size (tmp_pixbuf, 32, 32, 6);
143 g_object_unref (tmp_pixbuf);
145 if (!pixbuf) {
146 return NULL;
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);
157 return data->pixbuf;
160 static void
161 table_size_allocate_cb (GtkWidget *view,
162 GtkAllocation *allocation,
163 GtkWidget *box)
165 gint width, height;
167 gtk_widget_get_size_request (box, NULL, &height);
169 width = allocation->width;
171 width -= \
172 gtk_text_view_get_right_margin (GTK_TEXT_VIEW (view)) - \
173 gtk_text_view_get_left_margin (GTK_TEXT_VIEW (view));
174 width -= 2 * MARGIN;
175 width -= 2 * HEADER_PADDING;
177 gtk_widget_set_size_request (box, width, height);
180 static void
181 theme_boxes_maybe_append_header (EmpathyThemeBoxes *theme,
182 EmpathyMessage *msg)
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;
190 const gchar *name;
191 GtkTextIter iter;
192 GtkWidget *label1, *label2;
193 GtkTextChildAnchor *anchor;
194 GtkWidget *box;
195 gchar *str;
196 time_t time_;
197 gchar *tmp;
198 GtkTextIter start;
199 gboolean color_set;
200 GtkTextTagTable *table;
201 GtkTextTag *tag;
202 GString *str_obj;
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
212 * as this one.
214 if (empathy_contact_equal (last_contact, contact)) {
215 return;
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,
223 &iter,
224 "\n",
226 EMPATHY_THEME_BOXES_TAG_HEADER_LINE,
227 NULL);
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
233 * changes */
234 box = gtk_hbox_new (FALSE, 0);
235 g_signal_connect_object (view, "size-allocate",
236 G_CALLBACK (table_size_allocate_cb),
237 box, 0);
239 /* Add avatar to the box if needed */
240 if (priv->show_avatars) {
241 avatar = theme_boxes_get_avatar_pixbuf_with_cache (contact);
242 if (avatar) {
243 GtkWidget *image;
245 image = gtk_image_new_from_pixbuf (avatar);
247 gtk_box_pack_start (GTK_BOX (box), image,
248 FALSE, TRUE, 2);
252 /* Add contact alias */
253 str = g_markup_printf_escaped ("<b>%s</b>", name);
254 label1 = g_object_new (GTK_TYPE_LABEL,
255 "label", str,
256 "use-markup", TRUE,
257 "xalign", 0.0,
258 NULL);
259 g_free (str);
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,
267 "label", str,
268 "use-markup", TRUE,
269 "xalign", 1.0,
270 NULL);
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, " -");
277 g_free (tmp);
278 g_free (str);
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);
284 if (color_set) {
285 GdkColor *color;
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),
301 "str_obj",
302 g_string_free (str_obj, FALSE),
303 g_free);
304 gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view),
305 box,
306 anchor);
307 gtk_widget_show_all (box);
309 /* Insert a header line */
310 gtk_text_buffer_get_end_iter (buffer, &iter);
311 start = iter;
312 gtk_text_iter_backward_char (&start);
313 gtk_text_buffer_apply_tag_by_name (buffer,
314 EMPATHY_THEME_BOXES_TAG_HEADER,
315 &start, &iter);
316 gtk_text_buffer_insert_with_tags_by_name (buffer,
317 &iter,
318 "\n",
320 EMPATHY_THEME_BOXES_TAG_HEADER,
321 NULL);
322 gtk_text_buffer_get_end_iter (buffer, &iter);
323 gtk_text_buffer_insert_with_tags_by_name (buffer,
324 &iter,
325 "\n",
327 EMPATHY_THEME_BOXES_TAG_HEADER_LINE,
328 NULL);
331 static void
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) {
342 gchar *body;
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),
348 body,
349 EMPATHY_CHAT_TEXT_VIEW_TAG_ACTION);
350 } else {
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);
357 static void
358 theme_boxes_notify_show_avatars_cb (EmpathyConf *conf,
359 const gchar *key,
360 gpointer user_data)
362 EmpathyThemeBoxesPriv *priv = GET_PRIV (user_data);
364 empathy_conf_get_bool (conf, key, &priv->show_avatars);
367 static void
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);
378 static void
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));
393 static void
394 empathy_theme_boxes_init (EmpathyThemeBoxes *theme)
396 EmpathyThemeBoxesPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (theme,
397 EMPATHY_TYPE_THEME_BOXES, EmpathyThemeBoxesPriv);
399 theme->priv = priv;
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,
407 theme);
409 empathy_conf_get_bool (empathy_conf_get (),
410 EMPATHY_PREFS_UI_SHOW_AVATARS,
411 &priv->show_avatars);
413 /* Define margin */
414 g_object_set (theme,
415 "left-margin", MARGIN,
416 "right-margin", MARGIN,
417 NULL);
420 EmpathyThemeBoxes *
421 empathy_theme_boxes_new (void)
423 return g_object_new (EMPATHY_TYPE_THEME_BOXES,
424 "only-if-date", TRUE,
425 NULL);