Merge branch 'master' into svn_merge
[framework/uifw/elementary.git] / src / lib / elm_theme.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Theme Theme
6  * @ingroup Elementary
7  *
8  * Functions to modify the theme in the currently running app.
9  */
10
11 static Elm_Theme theme_default =
12 {
13    NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1
14 };
15
16 static Eina_List *themes = NULL;
17
18 static void
19 _elm_theme_clear(Elm_Theme *th)
20 {
21    const char *p;
22    EINA_LIST_FREE(th->themes, p)
23       eina_stringshare_del(p);
24    EINA_LIST_FREE(th->overlay, p)
25       eina_stringshare_del(p);
26    EINA_LIST_FREE(th->extension, p)
27       eina_stringshare_del(p);
28    if (th->cache)
29      {
30         eina_hash_free(th->cache);
31         th->cache = NULL;
32      }
33    if (th->theme)
34      {
35         eina_stringshare_del(th->theme);
36         th->theme = NULL;
37      }
38    if (th->ref_theme)
39      {
40         th->ref_theme->referrers =
41            eina_list_remove(th->ref_theme->referrers, th);
42         elm_theme_free(th->ref_theme);
43         th->ref_theme = NULL;
44      }
45 }
46
47 static const char *
48 _elm_theme_find_try(Elm_Theme *th, const char *f, const char *group)
49 {
50    const char *file;
51
52    if (edje_file_group_exists(f, group))
53      {
54         file = eina_stringshare_add(f);
55         if (file)
56           {
57              eina_hash_add(th->cache, group, file);
58              return file;
59           }
60      }
61    return NULL;
62 }
63
64 static const char *
65 _elm_theme_theme_element_try(Elm_Theme *th, const char *home, const char *f, const char *group)
66 {
67    char buf[PATH_MAX];
68    const char *file = NULL;
69
70    if ((f[0] == '/') || ((f[0] == '.') && (f[1] == '/')) ||
71        ((f[0] == '.') && (f[1] == '.') && (f[2] == '/')) ||
72        ((isalpha(f[0])) && (f[1] == ':')))
73      return _elm_theme_find_try(th, f, group);
74    else if (((f[0] == '~') && (f[1] == '/')))
75      {
76         snprintf(buf, sizeof(buf), "%s/%s", home, f + 2);
77         return _elm_theme_find_try(th, buf, group);
78      }
79    snprintf(buf, sizeof(buf), "%s/.elementary/themes/%s.edj", home, f);
80    file = _elm_theme_find_try(th, buf, group);
81    if (file) return file;
82    snprintf(buf, sizeof(buf), "%s/themes/%s.edj", _elm_data_dir, f);
83    file = _elm_theme_find_try(th, buf, group);
84    return file;
85 }
86
87 static const char *
88 _elm_theme_group_file_find(Elm_Theme *th, const char *group)
89 {
90    const Eina_List *l;
91    const char *f;
92    static const char *home = NULL;
93    const char *file = eina_hash_find(th->cache, group);
94
95    if (file) return file;
96    if (!home)
97      {
98         home = getenv("HOME");
99         if (!home) home = "";
100      }
101    EINA_LIST_FOREACH(th->overlay, l, f)
102      {
103         file = _elm_theme_theme_element_try(th, home, f, group);
104         if (file) return file;
105      }
106    EINA_LIST_FOREACH(th->themes, l, f)
107      {
108         file = _elm_theme_theme_element_try(th, home, f, group);
109         if (file) return file;
110      }
111    EINA_LIST_FOREACH(th->extension, l, f)
112      {
113         file = _elm_theme_theme_element_try(th, home, f, group);
114         if (file) return file;
115      }
116    if (th->ref_theme) return _elm_theme_group_file_find(th->ref_theme, group);
117    return NULL;
118 }
119
120 Eina_Bool
121 _elm_theme_object_set(Evas_Object *parent, Evas_Object *o, const char *clas, const char *group, const char *style)
122 {
123    Elm_Theme *th = NULL;
124    if (parent) th = elm_widget_theme_get(parent);
125    return _elm_theme_set(th, o, clas, group, style);
126 }
127
128 Eina_Bool
129 _elm_theme_object_icon_set(Evas_Object *parent, Evas_Object *o, const char *group, const char *style)
130 {
131    Elm_Theme *th = NULL;
132    if (parent) th = elm_widget_theme_get(parent);
133    return _elm_theme_icon_set(th, o, group, style);
134 }
135
136 Eina_Bool
137 _elm_theme_set(Elm_Theme *th, Evas_Object *o, const char *clas, const char *group, const char *style)
138 {
139    const char *file;
140    char buf2[1024];
141    Eina_Bool ok;
142
143    if (!th) th = &(theme_default);
144    snprintf(buf2, sizeof(buf2), "elm/%s/%s/%s", clas, group, style);
145    file = _elm_theme_group_file_find(th, buf2);
146    if (file)
147      {
148         ok = edje_object_file_set(o, file, buf2);
149         if (ok) return EINA_TRUE;
150         else
151           DBG("could not set theme group '%s' from file '%s': %s",
152               buf2, file, edje_load_error_str(edje_object_load_error_get(o)));
153      }
154    snprintf(buf2, sizeof(buf2), "elm/%s/%s/default", clas, group);
155    file = _elm_theme_group_file_find(th, buf2);
156    if (!file) return EINA_FALSE;
157    ok = edje_object_file_set(o, file, buf2);
158    if (!ok)
159      DBG("could not set theme group '%s' from file '%s': %s",
160          buf2, file, edje_load_error_str(edje_object_load_error_get(o)));
161    return ok;
162 }
163
164 Eina_Bool
165 _elm_theme_icon_set(Elm_Theme *th, Evas_Object *o, const char *group, const char *style)
166 {
167    const char *file;
168    char buf2[1024];
169    int w, h;
170
171    if (!th) th = &(theme_default);
172    snprintf(buf2, sizeof(buf2), "elm/icon/%s/%s", group, style);
173    file = _elm_theme_group_file_find(th, buf2);
174    if (file)
175      {
176         _els_smart_icon_file_edje_set(o, file, buf2);
177         _els_smart_icon_size_get(o, &w, &h);
178         if (w > 0) return EINA_TRUE;
179      }
180    snprintf(buf2, sizeof(buf2), "elm/icon/%s/default", group);
181    file = _elm_theme_group_file_find(th, buf2);
182    if (!file) return EINA_FALSE;
183    _els_smart_icon_file_edje_set(o, file, buf2);
184    _els_smart_icon_size_get(o, &w, &h);
185    return (w > 0);
186 }
187
188 Eina_Bool
189 _elm_theme_parse(Elm_Theme *th, const char *theme)
190 {
191    Eina_List *names = NULL;
192    const char *p, *pe;
193
194    if (!th) th = &(theme_default);
195    if (theme)
196      {
197         p = theme;
198         pe = p;
199         for (;;)
200           {
201              if ((*pe == ':') || (!*pe))
202                { // p -> pe == 'name:'
203                   if (pe > p)
204                     {
205                        char *n = malloc(pe - p + 1);
206                        if (n)
207                          {
208                             const char *nn;
209
210                             strncpy(n, p, pe - p);
211                             n[pe - p] = 0;
212                             nn = eina_stringshare_add(n);
213                             if (nn) names = eina_list_append(names, nn);
214                             free(n);
215                          }
216                     }
217                   if (!*pe) break;
218                   p = pe + 1;
219                   pe = p;
220                }
221              else
222                pe++;
223           }
224      }
225    p = eina_list_data_get(eina_list_last(names));
226    if ((!p) || ((p) && (strcmp(p, "default"))))
227      {
228         p = eina_stringshare_add("default");
229         if (p) names = eina_list_append(names, p);
230      }
231    if (th->cache) eina_hash_free(th->cache);
232    th->cache = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
233
234    EINA_LIST_FREE(th->themes, p) eina_stringshare_del(p);
235
236    th->themes = names;
237    return EINA_TRUE;
238 }
239
240 void
241 _elm_theme_shutdown(void)
242 {
243    _elm_theme_clear(&(theme_default));
244 }
245
246 EAPI Elm_Theme *
247 elm_theme_new(void)
248 {
249    Elm_Theme *th = calloc(1, sizeof(Elm_Theme));
250    if (!th) return NULL;
251    th->ref = 1;
252    th->themes = eina_list_append(th->themes, eina_stringshare_add("default"));
253    themes = eina_list_append(themes, th);
254    return th;
255 }
256
257 EAPI void
258 elm_theme_free(Elm_Theme *th)
259 {
260    EINA_SAFETY_ON_NULL_RETURN(th);
261    th->ref--;
262    if (th->ref < 1)
263      {
264         _elm_theme_clear(th);
265         themes = eina_list_remove(themes, th);
266         free(th);
267      }
268 }
269
270 EAPI void
271 elm_theme_ref_set(Elm_Theme *th, Elm_Theme *thref)
272 {
273    if (!th) th = &(theme_default);
274    if (!thref) thref = &(theme_default);
275    if (th->ref_theme == thref) return;
276    _elm_theme_clear(th);
277    if (thref)
278      {
279         thref->referrers = eina_list_append(thref->referrers, th);
280         thref->ref++;
281      }
282    th->ref_theme = thref;
283    elm_theme_flush(th);
284 }
285
286 EAPI Elm_Theme *
287 elm_theme_ref_get(Elm_Theme *th)
288 {
289    if (!th) th = &(theme_default);
290    return th->ref_theme;
291 }
292
293 EAPI Elm_Theme *
294 elm_theme_default_get(void)
295 {
296    return &theme_default;
297 }
298
299 EAPI void
300 elm_theme_overlay_add(Elm_Theme *th, const char *item)
301 {
302    const char *f = eina_stringshare_add(item);
303
304    if (!th) th = &(theme_default);
305    if (f) th->overlay = eina_list_prepend(th->overlay, f);
306    elm_theme_flush(th);
307 }
308
309 EAPI void
310 elm_theme_extension_add(Elm_Theme *th, const char *item)
311 {
312    const char *f = eina_stringshare_add(item);
313
314    if (!th) th = &(theme_default);
315    if (f) th->extension = eina_list_append(th->extension, f);
316    elm_theme_flush(th);
317 }
318
319 EAPI void
320 elm_theme_extension_del(Elm_Theme *th, const char *item)
321 {
322    const Eina_List *l;
323    const char *f, *s;
324
325    if (!th) th = &(theme_default);
326    s = eina_stringshare_add(item);
327    EINA_LIST_FOREACH(th->extension, l, f)
328       if (f == s)
329         {
330            eina_stringshare_del(f);
331            th->extension = eina_list_remove_list(th->extension, (Eina_List *)l);
332            break;
333         }
334    eina_stringshare_del(s);
335    elm_theme_flush(th);
336 }
337
338 EAPI void
339 elm_theme_set(Elm_Theme *th, const char *theme)
340 {
341    if (!th) th = &(theme_default);
342    _elm_theme_parse(th, theme);
343    if (th->theme)
344      {
345         eina_stringshare_del(th->theme);
346         th->theme = NULL;
347      }
348    elm_theme_flush(th);
349 }
350
351 EAPI const char *
352 elm_theme_get(Elm_Theme *th)
353 {
354    if (!th) th = &(theme_default);
355    if (!th->theme)
356      {
357         Eina_List *l;
358         const char *f;
359         char *tmp;
360         int len;
361
362         len = 0;
363         EINA_LIST_FOREACH(th->themes, l, f)
364           {
365              len += strlen(f);
366              if (l->next) len += 1;
367           }
368         tmp = alloca(len + 1);
369         tmp[0] = 0;
370         EINA_LIST_FOREACH(th->themes, l, f)
371           {
372              strcat(tmp, f);
373              if (l->next) strcat(tmp, ":");
374           }
375         th->theme = eina_stringshare_add(tmp);
376      }
377    return th->theme;
378 }
379
380 EAPI const Eina_List *
381 elm_theme_list_get(const Elm_Theme *th)
382 {
383    if (!th) th = &(theme_default);
384    return th->themes;
385 }
386
387 EAPI char *
388 elm_theme_list_item_path_get(const char *f, Eina_Bool *in_search_path)
389 {
390    static const char *home = NULL;
391    char buf[PATH_MAX];
392
393    if (!f)
394      {
395         if (in_search_path) *in_search_path = EINA_FALSE;
396         return NULL;
397      }
398
399    if (!home)
400      {
401         home = getenv("HOME");
402         if (!home) home = "";
403      }
404
405    if ((f[0] == '/') || ((f[0] == '.') && (f[1] == '/')) ||
406        ((f[0] == '.') && (f[1] == '.') && (f[2] == '/')) ||
407        ((isalpha(f[0])) && (f[1] == ':')))
408      {
409         if (in_search_path) *in_search_path = EINA_FALSE;
410         return strdup(f);
411      }
412    else if (((f[0] == '~') && (f[1] == '/')))
413      {
414         if (in_search_path) *in_search_path = EINA_FALSE;
415         snprintf(buf, sizeof(buf), "%s/%s", home, f + 2);
416         return strdup(buf);
417      }
418    snprintf(buf, sizeof(buf), "%s/.elementary/themes/%s.edj", home, f);
419    if (ecore_file_exists(buf))
420      {
421         if (in_search_path) *in_search_path = EINA_TRUE;
422         return strdup(buf);
423      }
424
425    snprintf(buf, sizeof(buf), "%s/themes/%s.edj", _elm_data_dir, f);
426    if (ecore_file_exists(buf))
427      {
428         if (in_search_path) *in_search_path = EINA_TRUE;
429         return strdup(buf);
430      }
431
432    if (in_search_path) *in_search_path = EINA_FALSE;
433    return NULL;
434 }
435
436 EAPI void
437 elm_theme_flush(Elm_Theme *th)
438 {
439    if (!th) th = &(theme_default);
440    if (th->cache) eina_hash_free(th->cache);
441    th->cache = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
442    _elm_win_rescale(th, EINA_TRUE);
443    if (th->referrers)
444      {
445         Eina_List *l;
446         Elm_Theme *th2;
447
448         EINA_LIST_FOREACH(th->referrers, l, th2) elm_theme_flush(th2);
449      }
450 }
451
452 EAPI void
453 elm_theme_full_flush(void)
454 {
455    Eina_List *l;
456    Elm_Theme *th;
457
458    EINA_LIST_FOREACH(themes, l, th)
459      {
460         elm_theme_flush(th);
461      }
462    elm_theme_flush(&(theme_default));
463 }
464
465 EAPI void
466 elm_theme_all_set(const char *theme)
467 {
468 #ifdef HAVE_ELEMENTARY_X
469    static Ecore_X_Atom atom = 0;
470
471    if (!atom) atom = ecore_x_atom_get("ENLIGHTENMENT_THEME");
472    ecore_x_window_prop_string_set(ecore_x_window_root_first_get(),
473                                   atom, theme);
474    //FIXME - ecore_x_window_prop_string_set() works properly only after ecore_x_window_prop_string_get() is called.
475    ecore_x_window_prop_string_get(ecore_x_window_root_first_get(), atom);
476 #endif
477    elm_theme_set(NULL, theme);
478 }
479
480 EAPI Eina_List *
481 elm_theme_name_available_list_new(void)
482 {
483    Eina_List *list = NULL;
484    Eina_List *dir, *l;
485    char buf[PATH_MAX], *file, *s, *th;
486    static const char *home = NULL;
487
488    if (!home)
489      {
490         home = getenv("HOME");
491         if (!home) home = "";
492      }
493
494    snprintf(buf, sizeof(buf), "%s/.elementary/themes", home);
495    dir = ecore_file_ls(buf);
496    EINA_LIST_FREE(dir, file)
497      {
498         snprintf(buf, sizeof(buf), "%s/.elementary/themes/%s", home, file);
499         if ((!ecore_file_is_dir(buf)) && (ecore_file_size(buf) > 0))
500           {
501              s = strchr(file, '.');
502              if ((s) && (!strcasecmp(s, ".edj")))
503                {
504                   th = strdup(file);
505                   s = strchr(th, '.');
506                   *s = 0;
507                   list = eina_list_append(list, th);
508                }
509           }
510         free(file);
511      }
512
513    snprintf(buf, sizeof(buf), "%s/themes", _elm_data_dir);
514    dir = ecore_file_ls(buf);
515    EINA_LIST_FREE(dir, file)
516      {
517         snprintf(buf, sizeof(buf), "%s/themes/%s", _elm_data_dir, file);
518         if ((!ecore_file_is_dir(buf)) && (ecore_file_size(buf) > 0))
519           {
520              s = strchr(file, '.');
521              if ((s) && (!strcasecmp(s, ".edj")))
522                {
523                   int dup;
524
525                   th = strdup(file);
526                   s = strchr(th, '.');
527                   *s = 0;
528                   dup = 0;
529                   EINA_LIST_FOREACH(list, l, s)
530                     {
531                        if (!strcmp(s, th))
532                          {
533                             dup = 1;
534                             break;
535                          }
536                     }
537                   if (dup) free(th);
538                   else list = eina_list_append(list, th);
539                }
540           }
541         free(file);
542      }
543    list = eina_list_sort(list, 0, EINA_COMPARE_CB(strcasecmp));
544    return list;
545 }
546
547 EAPI void
548 elm_theme_name_available_list_free(Eina_List *list)
549 {
550    char *s;
551    EINA_LIST_FREE(list, s) free(s);
552 }
553
554 EAPI void
555 elm_object_theme_set(Evas_Object *obj, Elm_Theme *th)
556 {
557    EINA_SAFETY_ON_NULL_RETURN(obj);
558    elm_widget_theme_set(obj, th);
559 }
560
561 EAPI Elm_Theme *
562 elm_object_theme_get(const Evas_Object *obj)
563 {
564    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
565    return elm_widget_theme_get(obj);
566 }