]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-theme.c
merge git work
[empathy.git] / libempathy-gtk / empathy-theme.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 #include <glib/gi18n.h>
25 #include <gtk/gtk.h>
26
27 #include <libempathy/empathy-conf.h>
28 #include <libempathy/empathy-debug.h>
29 #include <libempathy/empathy-utils.h>
30 #include <libempathy/empathy-marshal.h>
31
32 #include "empathy-chat.h"
33 #include "empathy-preferences.h"
34 #include "empathy-theme-utils.h"
35 #include "empathy-theme.h"
36 #include "empathy-smiley-manager.h"
37
38 #define DEBUG_DOMAIN "Theme"
39
40 /* Number of seconds between timestamps when using normal mode, 5 minutes. */
41 #define TIMESTAMP_INTERVAL 300
42
43 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_THEME, EmpathyThemePriv))
44
45 typedef struct _EmpathyThemePriv EmpathyThemePriv;
46
47 struct _EmpathyThemePriv {
48         EmpathySmileyManager *smiley_manager;
49         gboolean show_avatars;
50 };
51
52 static void         theme_finalize            (GObject            *object);
53 static void         theme_get_property        (GObject            *object,
54                                                guint               param_id,
55                                                GValue             *value,
56                                                GParamSpec         *pspec);
57 static void         theme_set_property        (GObject            *object,
58                                                guint               param_id,
59                                                const GValue       *value,
60                                                GParamSpec         *pspec);
61
62
63 G_DEFINE_TYPE (EmpathyTheme, empathy_theme, G_TYPE_OBJECT);
64
65 enum {
66         PROP_0,
67         PROP_SHOW_AVATARS
68 };
69
70 enum {
71         UPDATED,
72         LAST_SIGNAL
73 };
74
75 static guint signals[LAST_SIGNAL] = { 0 };
76
77 static void
78 empathy_theme_class_init (EmpathyThemeClass *class)
79 {
80         GObjectClass *object_class;
81
82         object_class = G_OBJECT_CLASS (class);
83
84         object_class->finalize     = theme_finalize;
85         object_class->get_property = theme_get_property;
86         object_class->set_property = theme_set_property;
87
88         class->setup_with_view  = NULL;
89         class->view_cleared     = NULL;
90         class->append_message   = NULL;
91         class->append_event     = NULL;
92         class->append_timestamp = NULL;
93         class->append_spacing   = NULL;
94
95         g_object_class_install_property (object_class,
96                                          PROP_SHOW_AVATARS,
97                                          g_param_spec_boolean ("show-avatars",
98                                                                "", "",
99                                                                TRUE,
100                                                                G_PARAM_READWRITE));
101
102         signals[UPDATED] =
103                 g_signal_new ("updated",
104                               G_TYPE_FROM_CLASS (class),
105                               G_SIGNAL_RUN_LAST,
106                               0,
107                               NULL, NULL,
108                               empathy_marshal_VOID__VOID,
109                               G_TYPE_NONE, 
110                               0);
111
112         g_type_class_add_private (object_class, sizeof (EmpathyThemePriv));
113 }
114
115 static void
116 empathy_theme_init (EmpathyTheme *presence)
117 {
118         EmpathyThemePriv *priv;
119
120         priv = GET_PRIV (presence);
121
122         priv->smiley_manager = empathy_smiley_manager_new ();
123 }
124
125 static void
126 theme_finalize (GObject *object)
127 {
128         EmpathyThemePriv *priv;
129
130         priv = GET_PRIV (object);
131
132         if (priv->smiley_manager) {
133                 g_object_unref (priv->smiley_manager);
134         }
135
136         (G_OBJECT_CLASS (empathy_theme_parent_class)->finalize) (object);
137 }
138
139 static void
140 theme_get_property (GObject    *object,
141                     guint       param_id,
142                     GValue     *value,
143                     GParamSpec *pspec)
144 {
145         EmpathyThemePriv *priv;
146
147         priv = GET_PRIV (object);
148
149         switch (param_id) {
150         case PROP_SHOW_AVATARS:
151                 g_value_set_boolean (value, priv->show_avatars);
152                 break;
153         default:
154                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
155                 break;
156         }
157 }
158
159 static void
160 theme_set_property (GObject      *object,
161                     guint         param_id,
162                     const GValue *value,
163                     GParamSpec   *pspec)
164 {
165         EmpathyThemePriv *priv;
166
167         priv = GET_PRIV (object);
168
169         switch (param_id) {
170         case PROP_SHOW_AVATARS:
171                 priv->show_avatars = g_value_get_boolean (value);
172                 break;
173         default:
174                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
175                 break;
176         }
177 }
178
179 void
180 empathy_theme_maybe_append_date_and_time (EmpathyTheme        *theme,
181                                          EmpathyThemeContext *context,
182                                          EmpathyChatView     *view,
183                                          EmpathyMessage      *message)
184 {
185         time_t    timestamp;
186         GDate    *date, *last_date;
187         gboolean  append_date, append_time;
188
189         date = empathy_message_get_date_and_time (message, &timestamp);
190
191         last_date = g_date_new ();
192         g_date_set_time (last_date, empathy_chat_view_get_last_timestamp (view));
193
194         append_date = FALSE;
195         append_time = FALSE;
196
197         if (g_date_compare (date, last_date) > 0) {
198                 append_date = TRUE;
199                 append_time = TRUE;
200         }
201         
202         g_date_free (last_date);
203         g_date_free (date);
204
205         if (empathy_chat_view_get_last_timestamp (view) + TIMESTAMP_INTERVAL < timestamp) {
206                 append_time = TRUE;
207         }
208
209         if (append_time || append_date) {
210                 empathy_theme_append_timestamp (theme, context,
211                                                view, message,
212                                                append_date, append_time);
213         }
214 }
215
216 EmpathyTheme *
217 empathy_theme_new (void)
218 {
219         EmpathyTheme     *theme;
220
221         theme = g_object_new (EMPATHY_TYPE_THEME, NULL);
222
223         return theme;
224 }
225
226 EmpathyThemeContext *
227 empathy_theme_setup_with_view (EmpathyTheme    *theme,
228                               EmpathyChatView *view)
229 {
230         if (!EMPATHY_THEME_GET_CLASS(theme)->setup_with_view) {
231                 g_error ("Theme must override setup_with_view");
232         }
233
234         return EMPATHY_THEME_GET_CLASS(theme)->setup_with_view (theme, view);
235 }
236
237 void
238 empathy_theme_detach_from_view (EmpathyTheme        *theme,
239                                EmpathyThemeContext *context,
240                                EmpathyChatView     *view)
241 {
242         if (!EMPATHY_THEME_GET_CLASS(theme)->detach_from_view) {
243                 g_error ("Theme must override detach_from_view");
244         }
245
246         return EMPATHY_THEME_GET_CLASS(theme)->detach_from_view (theme, context,
247                                                                 view);
248 }
249
250 void
251 empathy_theme_view_cleared (EmpathyTheme        *theme,
252                            EmpathyThemeContext *context,
253                            EmpathyChatView     *view)
254 {
255         if (!EMPATHY_THEME_GET_CLASS(theme)->view_cleared) {
256                 return;
257         }
258
259         EMPATHY_THEME_GET_CLASS(theme)->view_cleared (theme, context, view);
260 }
261
262 void
263 empathy_theme_append_message (EmpathyTheme        *theme,
264                              EmpathyThemeContext *context,
265                              EmpathyChatView     *view,
266                              EmpathyMessage      *message)
267 {
268         if (!EMPATHY_THEME_GET_CLASS(theme)->append_message) {
269                 g_warning ("Theme should override append_message");
270                 return;
271         }
272
273         EMPATHY_THEME_GET_CLASS(theme)->append_message (theme, context, view,
274                                                        message);
275 }
276
277 static void
278 theme_insert_text_with_emoticons (GtkTextBuffer *buf,
279                                   GtkTextIter   *iter,
280                                   const gchar   *str,
281                                   EmpathySmileyManager *smiley_manager)
282 {
283         gboolean             use_smileys = FALSE;
284         GSList              *smileys, *l;
285
286         empathy_conf_get_bool (empathy_conf_get (),
287                               EMPATHY_PREFS_CHAT_SHOW_SMILEYS,
288                               &use_smileys);
289
290         if (!use_smileys) {
291                 gtk_text_buffer_insert (buf, iter, str, -1);
292                 return;
293         }
294
295         smileys = empathy_smiley_manager_parse (smiley_manager, str);
296         for (l = smileys; l; l = l->next) {
297                 EmpathySmiley *smiley;
298
299                 smiley = l->data;
300                 if (smiley->pixbuf) {
301                         gtk_text_buffer_insert_pixbuf (buf, iter, smiley->pixbuf);
302                 } else {
303                         gtk_text_buffer_insert (buf, iter, smiley->str, -1);
304                 }
305                 empathy_smiley_free (smiley);
306         }
307         g_slist_free (smileys);
308 }
309
310 void
311 empathy_theme_append_text (EmpathyTheme        *theme,
312                           EmpathyThemeContext *context,
313                           EmpathyChatView     *view,
314                           const gchar        *body,
315                           const gchar        *tag,
316                           const gchar        *link_tag)
317 {
318         EmpathyThemePriv *priv;
319         GtkTextBuffer   *buffer;
320         GtkTextIter      start_iter, end_iter;
321         GtkTextMark     *mark;
322         GtkTextIter      iter;
323         gint             num_matches, i;
324         GArray          *start, *end;
325
326         priv = GET_PRIV (theme);
327         buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
328
329         gtk_text_buffer_get_end_iter (buffer, &start_iter);
330         mark = gtk_text_buffer_create_mark (buffer, NULL, &start_iter, TRUE);
331
332         start = g_array_new (FALSE, FALSE, sizeof (gint));
333         end = g_array_new (FALSE, FALSE, sizeof (gint));
334
335         num_matches = empathy_regex_match (EMPATHY_REGEX_ALL, body, start, end);
336
337         if (num_matches == 0) {
338                 gtk_text_buffer_get_end_iter (buffer, &iter);
339                 theme_insert_text_with_emoticons (buffer, &iter, body, priv->smiley_manager);
340         } else {
341                 gint   last = 0;
342                 gint   s = 0, e = 0;
343                 gchar *tmp;
344
345                 for (i = 0; i < num_matches; i++) {
346                         s = g_array_index (start, gint, i);
347                         e = g_array_index (end, gint, i);
348
349                         if (s > last) {
350                                 tmp = empathy_substring (body, last, s);
351
352                                 gtk_text_buffer_get_end_iter (buffer, &iter);
353                                 theme_insert_text_with_emoticons (buffer,
354                                                                   &iter,
355                                                                   tmp,
356                                                                   priv->smiley_manager);
357                                 g_free (tmp);
358                         }
359
360                         tmp = empathy_substring (body, s, e);
361
362                         gtk_text_buffer_get_end_iter (buffer, &iter);
363                         if (!link_tag) {
364                                 gtk_text_buffer_insert (buffer, &iter,
365                                                         tmp, -1);
366                         } {
367                                 gtk_text_buffer_insert_with_tags_by_name (buffer,
368                                                                           &iter,
369                                                                           tmp,
370                                                                           -1,
371                                                                           link_tag,
372                                                                           "link",
373                                                                           NULL);
374                         }
375
376                         g_free (tmp);
377
378                         last = e;
379                 }
380
381                 if (e < strlen (body)) {
382                         tmp = empathy_substring (body, e, strlen (body));
383
384                         gtk_text_buffer_get_end_iter (buffer, &iter);
385                         theme_insert_text_with_emoticons (buffer,
386                                                           &iter,
387                                                           tmp,
388                                                           priv->smiley_manager);
389                         g_free (tmp);
390                 }
391         }
392
393         g_array_free (start, TRUE);
394         g_array_free (end, TRUE);
395
396         gtk_text_buffer_get_end_iter (buffer, &iter);
397         gtk_text_buffer_insert (buffer, &iter, "\n", 1);
398
399         /* Apply the style to the inserted text. */
400         gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, mark);
401         gtk_text_buffer_get_end_iter (buffer, &end_iter);
402
403         gtk_text_buffer_apply_tag_by_name (buffer,
404                                            tag,
405                                            &start_iter,
406                                            &end_iter);
407
408         gtk_text_buffer_delete_mark (buffer, mark);
409 }
410
411 void 
412 empathy_theme_append_event (EmpathyTheme        *theme,
413                            EmpathyThemeContext *context,
414                            EmpathyChatView     *view,
415                            const gchar        *str)
416 {
417         if (!EMPATHY_THEME_GET_CLASS(theme)->append_event) {
418                 return;
419         }
420
421         EMPATHY_THEME_GET_CLASS(theme)->append_event (theme, context, view, str);
422 }
423
424 void
425 empathy_theme_append_spacing (EmpathyTheme        *theme, 
426                              EmpathyThemeContext *context,
427                              EmpathyChatView     *view)
428 {
429         if (!EMPATHY_THEME_GET_CLASS(theme)->append_spacing) {
430                 return;
431         }
432
433         EMPATHY_THEME_GET_CLASS(theme)->append_spacing (theme, context, view);
434 }
435
436
437 void 
438 empathy_theme_append_timestamp (EmpathyTheme        *theme,
439                                EmpathyThemeContext *context,
440                                EmpathyChatView     *view,
441                                EmpathyMessage      *message,
442                                gboolean            show_date,
443                                gboolean            show_time)
444 {
445         if (!EMPATHY_THEME_GET_CLASS(theme)->append_timestamp) {
446                 return;
447         }
448
449         EMPATHY_THEME_GET_CLASS(theme)->append_timestamp (theme, context, view,
450                                                          message, show_date,
451                                                          show_time);
452 }
453
454 gboolean
455 empathy_theme_get_show_avatars (EmpathyTheme *theme)
456 {
457         EmpathyThemePriv *priv;
458
459         g_return_val_if_fail (EMPATHY_IS_THEME (theme), FALSE);
460
461         priv = GET_PRIV (theme);
462
463         return priv->show_avatars;
464 }
465
466 void
467 empathy_theme_set_show_avatars (EmpathyTheme *theme, gboolean show)
468 {
469         EmpathyThemePriv *priv;
470
471         g_return_if_fail (EMPATHY_IS_THEME (theme));
472
473         priv = GET_PRIV (theme);
474
475         priv->show_avatars = show;
476
477         g_object_notify (G_OBJECT (theme), "show-avatars");
478 }
479