]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-theme-manager.c
Merge branch 'sasl'
[empathy.git] / libempathy-gtk / empathy-theme-manager.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2005-2007 Imendio AB
4  * Copyright (C) 2008 Collabora Ltd.
5  *
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.
10  *
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.
15  *
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
20  *
21  * Authors: Xavier Claessens <xclaesse@gmail.com>
22  */
23
24 #include "config.h"
25
26 #include <string.h>
27
28 #include <glib/gi18n-lib.h>
29 #include <telepathy-glib/dbus.h>
30 #include <gtk/gtk.h>
31
32 #include <telepathy-glib/util.h>
33
34 #include <libempathy/empathy-gsettings.h>
35 #include <libempathy/empathy-utils.h>
36
37 #include "empathy-theme-manager.h"
38 #include "empathy-chat-view.h"
39 #include "empathy-chat-text-view.h"
40 #include "empathy-theme-boxes.h"
41 #include "empathy-theme-irc.h"
42
43 #ifdef HAVE_WEBKIT
44 #include "empathy-theme-adium.h"
45 #endif
46
47 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
48 #include <libempathy/empathy-debug.h>
49
50 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyThemeManager)
51 typedef struct {
52         GSettings   *gsettings_chat;
53         gchar       *name;
54         gchar       *adium_path;
55         GtkSettings *settings;
56         GList       *boxes_views;
57 } EmpathyThemeManagerPriv;
58
59 enum {
60         THEME_CHANGED,
61         LAST_SIGNAL
62 };
63
64 static guint signals[LAST_SIGNAL] = { 0 };
65
66 static const gchar *themes[] = {
67         "classic", N_("Classic"),
68         "simple", N_("Simple"),
69         "clean", N_("Clean"),
70         "blue", N_("Blue"),
71         NULL
72 };
73
74 G_DEFINE_TYPE (EmpathyThemeManager, empathy_theme_manager, G_TYPE_OBJECT);
75
76 static void
77 theme_manager_gdk_color_to_hex (GdkColor *gdk_color, gchar *str_color)
78 {
79         g_snprintf (str_color, 10,
80                     "#%02x%02x%02x",
81                     gdk_color->red >> 8,
82                     gdk_color->green >> 8,
83                     gdk_color->blue >> 8);
84 }
85
86 static EmpathyThemeIrc *
87 theme_manager_create_irc_view (EmpathyThemeManager *manager)
88 {
89         EmpathyChatTextView *view;
90         EmpathyThemeIrc     *theme;
91
92         theme = empathy_theme_irc_new ();
93         view = EMPATHY_CHAT_TEXT_VIEW (theme);
94
95         /* Define base tags */
96         empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_SPACING,
97                                         "size", 2000,
98                                         NULL);
99         empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_TIME,
100                                         "foreground", "darkgrey",
101                                         "justification", GTK_JUSTIFY_CENTER,
102                                         NULL);
103         empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_ACTION,
104                                         "foreground", "brown4",
105                                         "style", PANGO_STYLE_ITALIC,
106                                         NULL);
107         empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_BODY,
108                                         "foreground-set", FALSE,
109                                         NULL);
110         empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_EVENT,
111                                         "foreground", "PeachPuff4",
112                                         "justification", GTK_JUSTIFY_LEFT,
113                                         NULL);
114         empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_LINK,
115                                         "foreground", "steelblue",
116                                         "underline", PANGO_UNDERLINE_SINGLE,
117                                         NULL);
118         empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_HIGHLIGHT,
119                                         "background", "yellow",
120                                         NULL);
121
122         /* Define IRC tags */
123         empathy_chat_text_view_tag_set (view, EMPATHY_THEME_IRC_TAG_NICK_SELF,
124                                         "foreground", "sea green",
125                                         NULL);
126         empathy_chat_text_view_tag_set (view, EMPATHY_THEME_IRC_TAG_NICK_OTHER,
127                                         "foreground", "skyblue4",
128                                         NULL);
129         empathy_chat_text_view_tag_set (view, EMPATHY_THEME_IRC_TAG_NICK_HIGHLIGHT,
130                                         "foreground", "indian red",
131                                         "weight", PANGO_WEIGHT_BOLD,
132                                         NULL);
133
134         return theme;
135 }
136
137 static void
138 theme_manager_boxes_weak_notify_cb (gpointer data,
139                                     GObject *where_the_object_was)
140 {
141         EmpathyThemeManagerPriv *priv = GET_PRIV (data);
142
143         priv->boxes_views = g_list_remove (priv->boxes_views, where_the_object_was);
144 }
145
146 static EmpathyThemeBoxes *
147 theme_manager_create_boxes_view (EmpathyThemeManager *manager)
148 {
149         EmpathyThemeManagerPriv *priv = GET_PRIV (manager);
150         EmpathyThemeBoxes       *theme;
151
152         theme = empathy_theme_boxes_new ();
153         priv->boxes_views = g_list_prepend (priv->boxes_views, theme);
154         g_object_weak_ref (G_OBJECT (theme),
155                            theme_manager_boxes_weak_notify_cb,
156                            manager);
157
158         return theme;
159 }
160
161 static void
162 theme_manager_update_boxes_tags (EmpathyThemeBoxes *theme,
163                                  const gchar       *header_foreground,
164                                  const gchar       *header_background,
165                                  const gchar       *header_line_background,
166                                  const gchar       *action_foreground,
167                                  const gchar       *time_foreground,
168                                  const gchar       *event_foreground,
169                                  const gchar       *link_foreground,
170                                  const gchar       *text_foreground,
171                                  const gchar       *text_background,
172                                  const gchar       *highlight_foreground)
173
174 {
175         EmpathyChatTextView *view = EMPATHY_CHAT_TEXT_VIEW (theme);
176         GtkTextTag          *tag;
177
178         DEBUG ("Update view with new colors:\n"
179                 "header_foreground = %s\n"
180                 "header_background = %s\n"
181                 "header_line_background = %s\n"
182                 "action_foreground = %s\n"
183                 "time_foreground = %s\n"
184                 "event_foreground = %s\n"
185                 "link_foreground = %s\n"
186                 "text_foreground = %s\n"
187                 "text_background = %s\n"
188                 "highlight_foreground = %s\n",
189                 header_foreground, header_background, header_line_background,
190                 action_foreground, time_foreground, event_foreground,
191                 link_foreground, text_foreground, text_background,
192                 highlight_foreground);
193
194
195         /* FIXME: GtkTextTag don't support to set color properties to NULL.
196          * See bug #542523 */
197
198         #define TAG_SET(prop, prop_set, value) \
199                 if (value != NULL) { \
200                         g_object_set (tag, prop, value, NULL); \
201                 } else { \
202                         g_object_set (tag, prop_set, FALSE, NULL); \
203                 }
204
205         /* Define base tags */
206         tag = empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_HIGHLIGHT,
207                                               "weight", PANGO_WEIGHT_BOLD,
208                                               "pixels-above-lines", 4,
209                                               NULL);
210         TAG_SET ("paragraph-background", "paragraph-background-set", text_background);
211         TAG_SET ("foreground", "foreground-set", highlight_foreground);
212
213         empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_SPACING,
214                                         "size", 3000,
215                                         "pixels-above-lines", 8,
216                                         NULL);
217         tag = empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_TIME,
218                                               "justification", GTK_JUSTIFY_CENTER,
219                                               NULL);
220         TAG_SET ("foreground", "foreground-set", time_foreground);
221         tag = empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_ACTION,
222                                               "style", PANGO_STYLE_ITALIC,
223                                               "pixels-above-lines", 4,
224                                               NULL);
225         TAG_SET ("paragraph-background", "paragraph-background-set", text_background);
226         TAG_SET ("foreground", "foreground-set", action_foreground);
227         tag = empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_BODY,
228                                               "pixels-above-lines", 4,
229                                               NULL);
230         TAG_SET ("paragraph-background", "paragraph-background-set", text_background);
231         TAG_SET ("foreground", "foreground-set", text_foreground);
232         tag = empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_EVENT,
233                                               "justification", GTK_JUSTIFY_LEFT,
234                                               NULL);
235         TAG_SET ("foreground", "foreground-set", event_foreground);
236         tag = empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_LINK,
237                                               "underline", PANGO_UNDERLINE_SINGLE,
238                                               NULL);
239         TAG_SET ("foreground", "foreground-set", link_foreground);
240
241         /* Define BOXES tags */
242         tag = empathy_chat_text_view_tag_set (view, EMPATHY_THEME_BOXES_TAG_HEADER,
243                                               "weight", PANGO_WEIGHT_BOLD,
244                                               NULL);
245         TAG_SET ("foreground", "foreground-set", header_foreground);
246         TAG_SET ("paragraph-background", "paragraph-background-set", header_background);
247         tag = empathy_chat_text_view_tag_set (view, EMPATHY_THEME_BOXES_TAG_HEADER_LINE,
248                                               "size", 1,
249                                               NULL);
250         TAG_SET ("paragraph-background", "paragraph-background-set", header_line_background);
251
252         #undef TAG_SET
253 }
254
255 static void
256 on_style_set_cb (GtkWidget *widget, GtkStyle *previous_style, gpointer data)
257 {
258         GtkStyle *style;
259         gchar     color1[10];
260         gchar     color2[10];
261         gchar     color3[10];
262         gchar     color4[10];
263
264         style = gtk_widget_get_style (GTK_WIDGET (widget));
265
266         theme_manager_gdk_color_to_hex (&style->base[GTK_STATE_SELECTED], color1);
267         theme_manager_gdk_color_to_hex (&style->bg[GTK_STATE_SELECTED], color2);
268         theme_manager_gdk_color_to_hex (&style->dark[GTK_STATE_SELECTED], color3);
269         theme_manager_gdk_color_to_hex (&style->fg[GTK_STATE_SELECTED], color4);
270
271         theme_manager_update_boxes_tags (EMPATHY_THEME_BOXES (widget),
272                                          color4,     /* header_foreground */
273                                          color2,     /* header_background */
274                                          color3,     /* header_line_background */
275                                          color1,     /* action_foreground */
276                                          "darkgrey", /* time_foreground */
277                                          "darkgrey", /* event_foreground */
278                                          color1,     /* link_foreground */
279                                          NULL,       /* text_foreground */
280                                          NULL,       /* text_background */
281                                          NULL);      /* highlight_foreground */
282 }
283
284 static void
285 theme_manager_update_boxes_theme (EmpathyThemeManager *manager,
286                                   EmpathyThemeBoxes   *theme)
287 {
288         EmpathyThemeManagerPriv *priv = GET_PRIV (manager);
289
290         if (strcmp (priv->name, "simple") == 0) {
291                 g_signal_connect (G_OBJECT (theme), "style-set",
292                                   G_CALLBACK (on_style_set_cb), theme);
293         }
294         else if (strcmp (priv->name, "clean") == 0) {
295                 theme_manager_update_boxes_tags (theme,
296                                                  "black",    /* header_foreground */
297                                                  "#efefdf",  /* header_background */
298                                                  "#e3e3d3",  /* header_line_background */
299                                                  "brown4",   /* action_foreground */
300                                                  "darkgrey", /* time_foreground */
301                                                  "darkgrey", /* event_foreground */
302                                                  "#49789e",  /* link_foreground */
303                                                  NULL,       /* text_foreground */
304                                                  NULL,       /* text_background */
305                                                  NULL);      /* highlight_foreground */
306         }
307         else if (strcmp (priv->name, "blue") == 0) {
308                 theme_manager_update_boxes_tags (theme,
309                                                  "black",    /* header_foreground */
310                                                  "#88a2b4",  /* header_background */
311                                                  "#7f96a4",  /* header_line_background */
312                                                  "brown4",   /* action_foreground */
313                                                  "darkgrey", /* time_foreground */
314                                                  "#7f96a4",  /* event_foreground */
315                                                  "#49789e",  /* link_foreground */
316                                                  "black",    /* text_foreground */
317                                                  "#adbdc8",  /* text_background */
318                                                  "black");   /* highlight_foreground */
319         }
320 }
321
322 EmpathyChatView *
323 empathy_theme_manager_create_view (EmpathyThemeManager *manager)
324 {
325         EmpathyThemeManagerPriv *priv = GET_PRIV (manager);
326         EmpathyThemeBoxes       *theme;
327
328         g_return_val_if_fail (EMPATHY_IS_THEME_MANAGER (manager), NULL);
329
330         DEBUG ("Using theme %s", priv->name);
331
332 #ifdef HAVE_WEBKIT
333         if (strcmp (priv->name, "adium") == 0)  {
334                 if (empathy_adium_path_is_valid (priv->adium_path)) {
335                         static EmpathyAdiumData *data = NULL;
336                         EmpathyThemeAdium *theme_adium;
337
338                         if (data &&
339                             !tp_strdiff (empathy_adium_data_get_path (data),
340                                          priv->adium_path)) {
341                                 /* Theme did not change, reuse data */
342                                 theme_adium = empathy_theme_adium_new (data);
343                                 return EMPATHY_CHAT_VIEW (theme_adium);
344                         }
345
346                         /* Theme changed, drop old data if any and
347                          * load a new one */
348                         if (data) {
349                                 empathy_adium_data_unref (data);
350                                 data = NULL;
351                         }
352
353                         data = empathy_adium_data_new (priv->adium_path);
354                         theme_adium = empathy_theme_adium_new (data);
355                         return EMPATHY_CHAT_VIEW (theme_adium);
356                 } else {
357                         /* The adium path is not valid, fallback to classic theme */
358                         return EMPATHY_CHAT_VIEW (theme_manager_create_irc_view (manager));
359                 }
360         }
361 #endif
362
363         if (strcmp (priv->name, "classic") == 0)  {
364                 return EMPATHY_CHAT_VIEW (theme_manager_create_irc_view (manager));
365         }
366
367         theme = theme_manager_create_boxes_view (manager);
368         theme_manager_update_boxes_theme (manager, theme);
369
370         return EMPATHY_CHAT_VIEW (theme);
371 }
372
373 static gboolean
374 theme_manager_ensure_theme_exists (const gchar *name)
375 {
376         gint i;
377
378         if (EMP_STR_EMPTY (name)) {
379                 return FALSE;
380         }
381
382         if (strcmp ("adium", name) == 0) {
383                 return TRUE;
384         }
385
386         for (i = 0; themes[i]; i += 2) {
387                 if (strcmp (themes[i], name) == 0) {
388                         return TRUE;
389                 }
390         }
391
392         return FALSE;
393 }
394
395 static void
396 theme_manager_notify_name_cb (GSettings   *gsettings_chat,
397                               const gchar *key,
398                               gpointer     user_data)
399 {
400         EmpathyThemeManager     *manager = EMPATHY_THEME_MANAGER (user_data);
401         EmpathyThemeManagerPriv *priv = GET_PRIV (manager);
402         gchar                   *name;
403
404         name = g_settings_get_string (gsettings_chat, key);
405
406         if (!theme_manager_ensure_theme_exists (name) ||
407             !tp_strdiff (priv->name, name)) {
408                 if (!priv->name) {
409                         priv->name = g_strdup ("classic");
410                 }
411
412                 g_free (name);
413                 return;
414         }
415
416         g_free (priv->name);
417         priv->name = name;
418
419         if (!tp_strdiff (priv->name, "simple") ||
420             !tp_strdiff (priv->name, "clean") ||
421             !tp_strdiff (priv->name, "blue")) {
422                 GList *l;
423
424                 /* The theme changes to a boxed one, we can update boxed views */
425                 for (l = priv->boxes_views; l; l = l->next) {
426                         theme_manager_update_boxes_theme (manager,
427                                                           EMPATHY_THEME_BOXES (l->data));
428                 }
429         }
430
431         g_signal_emit (manager, signals[THEME_CHANGED], 0, NULL);
432 }
433
434 static void
435 theme_manager_notify_adium_path_cb (GSettings   *gsettings_chat,
436                                     const gchar *key,
437                                     gpointer     user_data)
438 {
439         EmpathyThemeManager     *manager = EMPATHY_THEME_MANAGER (user_data);
440         EmpathyThemeManagerPriv *priv = GET_PRIV (manager);
441         gchar                   *adium_path = NULL;
442
443         adium_path = g_settings_get_string (gsettings_chat, key);
444
445         if (!tp_strdiff (priv->adium_path, adium_path)) {
446                 g_free (adium_path);
447                 return;
448         }
449
450         g_free (priv->adium_path);
451         priv->adium_path = adium_path;
452
453         g_signal_emit (manager, signals[THEME_CHANGED], 0, NULL);
454 }
455
456 static void
457 theme_manager_finalize (GObject *object)
458 {
459         EmpathyThemeManagerPriv *priv = GET_PRIV (object);
460         GList                   *l;
461
462         g_object_unref (priv->gsettings_chat);
463         g_free (priv->name);
464         g_free (priv->adium_path);
465
466         for (l = priv->boxes_views; l; l = l->next) {
467                 g_object_weak_unref (G_OBJECT (l->data),
468                                      theme_manager_boxes_weak_notify_cb,
469                                      object);
470         }
471         g_list_free (priv->boxes_views);
472
473         G_OBJECT_CLASS (empathy_theme_manager_parent_class)->finalize (object);
474 }
475
476 static void
477 empathy_theme_manager_class_init (EmpathyThemeManagerClass *klass)
478 {
479         GObjectClass *object_class = G_OBJECT_CLASS (klass);
480
481         signals[THEME_CHANGED] =
482                 g_signal_new ("theme-changed",
483                               G_OBJECT_CLASS_TYPE (object_class),
484                               G_SIGNAL_RUN_LAST,
485                               0,
486                               NULL, NULL,
487                               g_cclosure_marshal_VOID__VOID,
488                               G_TYPE_NONE,
489                               0);
490
491         g_type_class_add_private (object_class, sizeof (EmpathyThemeManagerPriv));
492
493         object_class->finalize = theme_manager_finalize;
494 }
495
496 static void
497 empathy_theme_manager_init (EmpathyThemeManager *manager)
498 {
499         EmpathyThemeManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
500                 EMPATHY_TYPE_THEME_MANAGER, EmpathyThemeManagerPriv);
501
502         manager->priv = priv;
503
504         priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
505
506         /* Take the theme name and track changes */
507         g_signal_connect (priv->gsettings_chat,
508                           "changed::" EMPATHY_PREFS_CHAT_THEME,
509                           G_CALLBACK (theme_manager_notify_name_cb),
510                           manager);
511         theme_manager_notify_name_cb (priv->gsettings_chat,
512                                       EMPATHY_PREFS_CHAT_THEME,
513                                       manager);
514
515         /* Take the adium path and track changes */
516         g_signal_connect (priv->gsettings_chat,
517                           "changed::" EMPATHY_PREFS_CHAT_ADIUM_PATH,
518                           G_CALLBACK (theme_manager_notify_adium_path_cb),
519                           manager);
520         theme_manager_notify_adium_path_cb (priv->gsettings_chat,
521                                             EMPATHY_PREFS_CHAT_ADIUM_PATH,
522                                             manager);
523 }
524
525 EmpathyThemeManager *
526 empathy_theme_manager_get (void)
527 {
528         static EmpathyThemeManager *manager = NULL;
529
530         if (!manager) {
531                 manager = g_object_new (EMPATHY_TYPE_THEME_MANAGER, NULL);
532         }
533
534         return manager;
535 }
536
537 const gchar **
538 empathy_theme_manager_get_themes (void)
539 {
540         return themes;
541 }
542
543 #ifdef HAVE_WEBKIT
544 static void
545 find_themes (GList **list, const gchar *dirpath)
546 {
547         GDir *dir;
548         GError *error = NULL;
549         const gchar *name = NULL;
550         GHashTable *info = NULL;
551
552         dir = g_dir_open (dirpath, 0, &error);
553         if (dir != NULL) {
554                 name = g_dir_read_name (dir);
555                 while (name != NULL) {
556                         gchar *path;
557
558                         path = g_build_path (G_DIR_SEPARATOR_S, dirpath, name, NULL);
559                         if (empathy_adium_path_is_valid (path)) {
560                                 info = empathy_adium_info_new (path);
561                                 if (info != NULL) {
562                                         *list = g_list_prepend (*list, info);
563                                 }
564                         }
565                         g_free (path);
566                         name = g_dir_read_name (dir);
567                 }
568                 g_dir_close (dir);
569         } else {
570                 DEBUG ("Error opening %s: %s\n", dirpath, error->message);
571                 g_error_free (error);
572         }
573 }
574 #endif /* HAVE_WEBKIT */
575
576 GList *
577 empathy_theme_manager_get_adium_themes (void)
578 {
579 #ifdef HAVE_WEBKIT
580         GList *themes_list = NULL;
581         gchar *userpath = NULL;
582         const gchar *const *paths = NULL;
583         gint i = 0;
584
585         userpath = g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (), "adium/message-styles", NULL);
586         find_themes (&themes_list, userpath);
587         g_free (userpath);
588
589         paths = g_get_system_data_dirs ();
590         for (i = 0; paths[i] != NULL; i++) {
591                 userpath = g_build_path (G_DIR_SEPARATOR_S, paths[i],
592                         "adium/message-styles", NULL);
593                 find_themes (&themes_list, userpath);
594                 g_free (userpath);
595         }
596
597         return themes_list;
598 #else
599         return NULL;
600 #endif /* HAVE_WEBKIT */
601 }