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