]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-theme-manager.c
1f6611663afb290b010ac85e56347692fb4207c5
[empathy.git] / libempathy-gtk / empathy-theme-manager.c
1 /*
2  * Copyright (C) 2005-2007 Imendio AB
3  * Copyright (C) 2008-2012 Collabora Ltd.
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., 51 Franklin St, Fifth Floor,
18  * Boston, MA  02110-1301  USA
19  *
20  * Authors: Xavier Claessens <xclaesse@gmail.com>
21  */
22
23 #include "config.h"
24
25 #include <string.h>
26
27 #include <glib/gi18n-lib.h>
28 #include <telepathy-glib/dbus.h>
29 #include <gtk/gtk.h>
30
31 #include <telepathy-glib/util.h>
32
33 #include <libempathy/empathy-gsettings.h>
34 #include <libempathy/empathy-utils.h>
35
36 #include "empathy-theme-manager.h"
37 #include "empathy-chat-view.h"
38 #include "empathy-chat-text-view.h"
39 #include "empathy-theme-adium.h"
40
41 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
42 #include <libempathy/empathy-debug.h>
43
44 struct _EmpathyThemeManagerPriv
45 {
46   GSettings   *gsettings_chat;
47   guint        emit_changed_idle;
48   gboolean     in_constructor;
49
50   EmpathyAdiumData *adium_data;
51   gchar *adium_variant;
52   /* list of weakref to EmpathyThemeAdium objects */
53   GList *adium_views;
54 };
55
56 enum
57 {
58   THEME_CHANGED,
59   LAST_SIGNAL
60 };
61
62 static guint signals[LAST_SIGNAL] = { 0 };
63
64 G_DEFINE_TYPE (EmpathyThemeManager, empathy_theme_manager, G_TYPE_OBJECT);
65
66 static gboolean
67 theme_manager_emit_changed_idle_cb (gpointer manager)
68 {
69   EmpathyThemeManager *self = manager;
70   const gchar *adium_path = NULL;
71
72   if (self->priv->adium_data)
73     adium_path = empathy_adium_data_get_path (self->priv->adium_data);
74
75   DEBUG ("Emit theme-changed with: adium_path='%s' "
76       "adium_variant='%s'", adium_path, self->priv->adium_variant);
77
78   g_signal_emit (self, signals[THEME_CHANGED], 0, NULL);
79   self->priv->emit_changed_idle = 0;
80
81   return FALSE;
82 }
83
84 static void
85 theme_manager_emit_changed (EmpathyThemeManager *self)
86 {
87   /* We emit the signal in idle callback to be sure we emit it only once
88    * in the case both the name and adium_path changed */
89   if (self->priv->emit_changed_idle == 0 && !self->priv->in_constructor)
90     {
91       self->priv->emit_changed_idle = g_idle_add (
92         theme_manager_emit_changed_idle_cb, self);
93     }
94 }
95
96 static void
97 theme_manager_view_weak_notify_cb (gpointer data,
98     GObject *where_the_object_was)
99 {
100   GList **list = data;
101
102   *list = g_list_remove (*list, where_the_object_was);
103 }
104
105 static void
106 clear_list_of_views (GList **views)
107 {
108   while (*views)
109     {
110       g_object_weak_unref ((*views)->data,
111                theme_manager_view_weak_notify_cb,
112                views);
113
114       *views = g_list_delete_link (*views, *views);
115     }
116 }
117
118 static EmpathyThemeAdium *
119 theme_manager_create_adium_view (EmpathyThemeManager *self)
120 {
121   EmpathyThemeAdium *theme;
122
123   theme = empathy_theme_adium_new (self->priv->adium_data, self->priv->adium_variant);
124
125   self->priv->adium_views = g_list_prepend (self->priv->adium_views, theme);
126
127   g_object_weak_ref (G_OBJECT (theme),
128          theme_manager_view_weak_notify_cb,
129          &self->priv->adium_views);
130
131   return theme;
132 }
133
134 static void
135 theme_manager_notify_adium_path_cb (GSettings *gsettings_chat,
136     const gchar *key,
137     gpointer user_data)
138 {
139   EmpathyThemeManager *self = EMPATHY_THEME_MANAGER (user_data);
140   const gchar *current_path = NULL;
141   gchar *new_path;
142
143   new_path = g_settings_get_string (gsettings_chat, key);
144
145   if (self->priv->adium_data != NULL)
146     current_path = empathy_adium_data_get_path (self->priv->adium_data);
147
148   /* If path did not really changed, ignore */
149   if (!tp_strdiff (current_path, new_path))
150     goto finally;
151
152   /* If path does not really contains an adium path, ignore */
153   if (empathy_adium_path_is_valid (new_path))
154     {
155       /* pass */
156     }
157   else if (empathy_theme_manager_find_theme (new_path) != NULL)
158     {
159       new_path = empathy_theme_manager_find_theme (new_path);
160     }
161   else
162     {
163       g_warning ("Do not understand theme: %s", new_path);
164       goto finally;
165     }
166
167   /* Load new theme data, we can stop tracking existing views since we
168    * won't be able to change them live anymore */
169   clear_list_of_views (&self->priv->adium_views);
170   tp_clear_pointer (&self->priv->adium_data, empathy_adium_data_unref);
171   self->priv->adium_data = empathy_adium_data_new (new_path);
172
173   theme_manager_emit_changed (self);
174
175 finally:
176   g_free (new_path);
177 }
178
179 static void
180 theme_manager_notify_adium_variant_cb (GSettings *gsettings_chat,
181     const gchar *key,
182     gpointer user_data)
183 {
184   EmpathyThemeManager *self = EMPATHY_THEME_MANAGER (user_data);
185   gchar *new_variant;
186   GList *l;
187
188   new_variant = g_settings_get_string (gsettings_chat, key);
189   if (!tp_strdiff (self->priv->adium_variant, new_variant))
190     {
191       g_free (new_variant);
192       return;
193     }
194
195   g_free (self->priv->adium_variant);
196   self->priv->adium_variant = new_variant;
197
198   for (l = self->priv->adium_views; l; l = l->next)
199     {
200       empathy_theme_adium_set_variant (EMPATHY_THEME_ADIUM (l->data),
201         self->priv->adium_variant);
202     }
203 }
204
205 EmpathyChatView *
206 empathy_theme_manager_create_view (EmpathyThemeManager *self)
207 {
208   g_return_val_if_fail (EMPATHY_IS_THEME_MANAGER (self), NULL);
209
210   if (self->priv->adium_data != NULL)
211     return EMPATHY_CHAT_VIEW (theme_manager_create_adium_view (self));
212
213   g_return_val_if_reached (NULL);
214 }
215
216 static void
217 theme_manager_finalize (GObject *object)
218 {
219   EmpathyThemeManager *self = (EmpathyThemeManager *) object;
220
221   g_object_unref (self->priv->gsettings_chat);
222
223   if (self->priv->emit_changed_idle != 0)
224     g_source_remove (self->priv->emit_changed_idle);
225
226   clear_list_of_views (&self->priv->adium_views);
227   g_free (self->priv->adium_variant);
228   tp_clear_pointer (&self->priv->adium_data, empathy_adium_data_unref);
229
230   G_OBJECT_CLASS (empathy_theme_manager_parent_class)->finalize (object);
231 }
232
233 static void
234 empathy_theme_manager_class_init (EmpathyThemeManagerClass *klass)
235 {
236   GObjectClass *object_class = G_OBJECT_CLASS (klass);
237
238   signals[THEME_CHANGED] = g_signal_new ("theme-changed",
239       G_OBJECT_CLASS_TYPE (object_class),
240       G_SIGNAL_RUN_LAST,
241       0,
242       NULL, NULL,
243       g_cclosure_marshal_generic,
244       G_TYPE_NONE,
245       0);
246
247   g_type_class_add_private (object_class, sizeof (EmpathyThemeManagerPriv));
248
249   object_class->finalize = theme_manager_finalize;
250 }
251
252 static void
253 empathy_theme_manager_init (EmpathyThemeManager *self)
254 {
255   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
256     EMPATHY_TYPE_THEME_MANAGER, EmpathyThemeManagerPriv);
257
258   self->priv->in_constructor = TRUE;
259
260   self->priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
261
262   /* Take the adium path/variant and track changes */
263   g_signal_connect (self->priv->gsettings_chat,
264       "changed::" EMPATHY_PREFS_CHAT_ADIUM_PATH,
265       G_CALLBACK (theme_manager_notify_adium_path_cb), self);
266
267   theme_manager_notify_adium_path_cb (self->priv->gsettings_chat,
268       EMPATHY_PREFS_CHAT_ADIUM_PATH, self);
269
270   g_signal_connect (self->priv->gsettings_chat,
271       "changed::" EMPATHY_PREFS_CHAT_THEME_VARIANT,
272       G_CALLBACK (theme_manager_notify_adium_variant_cb), self);
273
274   theme_manager_notify_adium_variant_cb (self->priv->gsettings_chat,
275       EMPATHY_PREFS_CHAT_THEME_VARIANT, self);
276
277   self->priv->in_constructor = FALSE;
278 }
279
280 EmpathyThemeManager *
281 empathy_theme_manager_dup_singleton (void)
282 {
283   static EmpathyThemeManager *manager = NULL;
284
285   if (manager == NULL)
286     {
287       manager = g_object_new (EMPATHY_TYPE_THEME_MANAGER, NULL);
288       g_object_add_weak_pointer (G_OBJECT (manager), (gpointer *) &manager);
289
290       return manager;
291     }
292
293   return g_object_ref (manager);
294 }
295
296 static void
297 find_themes (GList **list, const gchar *dirpath)
298 {
299   GDir *dir;
300   GError *error = NULL;
301   const gchar *name = NULL;
302   GHashTable *info = NULL;
303
304   dir = g_dir_open (dirpath, 0, &error);
305   if (dir != NULL)
306     {
307       name = g_dir_read_name (dir);
308
309       while (name != NULL)
310         {
311           gchar *path;
312
313           path = g_build_path (G_DIR_SEPARATOR_S, dirpath, name, NULL);
314           if (empathy_adium_path_is_valid (path))
315             {
316               info = empathy_adium_info_new (path);
317
318               if (info != NULL)
319                 *list = g_list_prepend (*list, info);
320             }
321
322           g_free (path);
323           name = g_dir_read_name (dir);
324         }
325
326       g_dir_close (dir);
327     }
328   else
329     {
330       DEBUG ("Error opening %s: %s\n", dirpath, error->message);
331       g_error_free (error);
332     }
333 }
334
335 GList *
336 empathy_theme_manager_get_adium_themes (void)
337 {
338   GList *themes_list = NULL;
339   gchar *userpath = NULL;
340   const gchar *const *paths = NULL;
341   gint i = 0;
342
343   userpath = g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (),
344       "adium/message-styles", NULL);
345   find_themes (&themes_list, userpath);
346   g_free (userpath);
347
348   paths = g_get_system_data_dirs ();
349   for (i = 0; paths[i] != NULL; i++)
350     {
351       userpath = g_build_path (G_DIR_SEPARATOR_S, paths[i],
352         "adium/message-styles", NULL);
353       find_themes (&themes_list, userpath);
354       g_free (userpath);
355     }
356
357   return themes_list;
358 }
359
360 gchar *
361 empathy_theme_manager_find_theme (const gchar *name)
362 {
363   gchar *path;
364   const gchar * const *paths;
365   gint i;
366
367   /* look in EMPATHY_SRCDIR */
368   path = g_strjoin (NULL,
369       g_getenv ("EMPATHY_SRCDIR"),
370       "/data/themes/",
371       name,
372       ".AdiumMessageStyle",
373       NULL);
374
375   DEBUG ("Trying '%s'", path);
376
377   if (empathy_adium_path_is_valid (path))
378     return path;
379
380   g_free (path);
381
382   /* look in user dir */
383   path = g_strjoin (NULL,
384       g_get_user_data_dir (),
385       "/adium/message-styles/",
386       name,
387       ".AdiumMessageStyle",
388       NULL);
389
390   DEBUG ("Trying '%s'", path);
391
392   if (empathy_adium_path_is_valid (path))
393     return path;
394
395   g_free (path);
396
397   /* look in system dirs */
398   paths = g_get_system_data_dirs ();
399
400   for (i = 0; paths[i] != NULL; i++)
401     {
402       path = g_strjoin (NULL,
403           paths[i],
404           "/adium/message-styles/",
405           name,
406           ".AdiumMessageStyle",
407           NULL);
408
409       DEBUG ("Trying '%s'", path);
410
411       if (empathy_adium_path_is_valid (path))
412         return path;
413
414       g_free (path);
415     }
416
417   return NULL;
418 }
419
420 gchar *
421 empathy_theme_manager_dup_theme_name_from_path (const gchar *path)
422 {
423   gchar *fullname, *result;
424   gchar **tmp;
425
426   if (path == NULL)
427     return NULL;
428
429   fullname = g_path_get_basename (path);
430   if (!g_str_has_suffix (fullname, ".AdiumMessageStyle"))
431     return NULL;
432
433   tmp = g_strsplit (fullname, ".AdiumMessageStyle", 0);
434   result = g_strdup (tmp[0]);
435
436   g_strfreev (tmp);
437   return result;
438 }