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