elementary/elm_entry : Fix a bug in filter callback. There can be a
[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 const Eina_List *
439 elm_theme_overlay_list_get(const Elm_Theme *th)
440 {
441    if (!th) th = &(theme_default);
442    return th->overlay;
443 }
444
445 EAPI void
446 elm_theme_extension_add(Elm_Theme *th, const char *item)
447 {
448    const char *f = eina_stringshare_add(item);
449
450    if (!th) th = &(theme_default);
451    if (f) th->extension = eina_list_append(th->extension, f);
452    elm_theme_flush(th);
453 }
454
455 EAPI void
456 elm_theme_extension_del(Elm_Theme *th, const char *item)
457 {
458    const Eina_List *l;
459    const char *f, *s;
460
461    if (!th) th = &(theme_default);
462    s = eina_stringshare_add(item);
463    EINA_LIST_FOREACH(th->extension, l, f)
464       if (f == s)
465         {
466            eina_stringshare_del(f);
467            th->extension = eina_list_remove_list(th->extension, (Eina_List *)l);
468            break;
469         }
470    eina_stringshare_del(s);
471    elm_theme_flush(th);
472 }
473
474 EAPI const Eina_List *
475 elm_theme_extension_list_get(const Elm_Theme *th)
476 {
477    if (!th) th = &(theme_default);
478    return th->extension;
479 }
480
481 EAPI void
482 elm_theme_set(Elm_Theme *th, const char *theme)
483 {
484    if (!th) th = &(theme_default);
485    _elm_theme_parse(th, theme);
486    if (th->theme)
487      {
488         eina_stringshare_del(th->theme);
489         th->theme = NULL;
490      }
491    elm_theme_flush(th);
492    if (th == &(theme_default))
493      eina_stringshare_replace(&_elm_config->theme, theme);
494 }
495
496 EAPI const char *
497 elm_theme_get(Elm_Theme *th)
498 {
499    if (!th) th = &(theme_default);
500    if (!th->theme)
501      {
502         Eina_List *l;
503         const char *f;
504         char *tmp;
505         int len;
506
507         len = 0;
508         EINA_LIST_FOREACH(th->themes, l, f)
509           {
510              len += strlen(f);
511              if (l->next) len += 1;
512           }
513         tmp = alloca(len + 1);
514         tmp[0] = 0;
515         EINA_LIST_FOREACH(th->themes, l, f)
516           {
517              strcat(tmp, f);
518              if (l->next) strcat(tmp, ":");
519           }
520         th->theme = eina_stringshare_add(tmp);
521      }
522    return th->theme;
523 }
524
525 EAPI const Eina_List *
526 elm_theme_list_get(const Elm_Theme *th)
527 {
528    if (!th) th = &(theme_default);
529    return th->themes;
530 }
531
532 EAPI char *
533 elm_theme_list_item_path_get(const char *f, Eina_Bool *in_search_path)
534 {
535    static const char *home = NULL;
536    char buf[PATH_MAX];
537
538    if (!f)
539      {
540         if (in_search_path) *in_search_path = EINA_FALSE;
541         return NULL;
542      }
543
544    if (!home)
545      {
546         home = getenv("HOME");
547         if (!home) home = "";
548      }
549
550    if ((f[0] == '/') || ((f[0] == '.') && (f[1] == '/')) ||
551        ((f[0] == '.') && (f[1] == '.') && (f[2] == '/')) ||
552        ((isalpha(f[0])) && (f[1] == ':')))
553      {
554         if (in_search_path) *in_search_path = EINA_FALSE;
555         return strdup(f);
556      }
557    else if (((f[0] == '~') && (f[1] == '/')))
558      {
559         if (in_search_path) *in_search_path = EINA_FALSE;
560         snprintf(buf, sizeof(buf), "%s/%s", home, f + 2);
561         return strdup(buf);
562      }
563    snprintf(buf, sizeof(buf), "%s/"ELEMENTARY_BASE_DIR"/themes/%s.edj", home, f);
564    if (ecore_file_exists(buf))
565      {
566         if (in_search_path) *in_search_path = EINA_TRUE;
567         return strdup(buf);
568      }
569
570    snprintf(buf, sizeof(buf), "%s/themes/%s.edj", _elm_data_dir, f);
571    if (ecore_file_exists(buf))
572      {
573         if (in_search_path) *in_search_path = EINA_TRUE;
574         return strdup(buf);
575      }
576
577    if (in_search_path) *in_search_path = EINA_FALSE;
578    return NULL;
579 }
580
581 EAPI void
582 elm_theme_flush(Elm_Theme *th)
583 {
584    if (!th) th = &(theme_default);
585    if (th->cache) eina_hash_free(th->cache);
586    th->cache = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
587    if (th->cache_data) eina_hash_free(th->cache_data);
588    th->cache_data = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
589    _elm_win_rescale(th, EINA_TRUE);
590    _elm_ews_wm_rescale(th, EINA_TRUE);
591    if (th->referrers)
592      {
593         Eina_List *l;
594         Elm_Theme *th2;
595
596         EINA_LIST_FOREACH(th->referrers, l, th2) elm_theme_flush(th2);
597      }
598 }
599
600 EAPI void
601 elm_theme_full_flush(void)
602 {
603    Eina_List *l;
604    Elm_Theme *th;
605
606    EINA_LIST_FOREACH(themes, l, th)
607      {
608         elm_theme_flush(th);
609      }
610    elm_theme_flush(&(theme_default));
611 }
612
613 EAPI Eina_List *
614 elm_theme_name_available_list_new(void)
615 {
616    Eina_List *list = NULL;
617    Eina_List *dir, *l;
618    char buf[PATH_MAX], *file, *s, *th;
619    static const char *home = NULL;
620
621    if (!home)
622      {
623         home = getenv("HOME");
624         if (!home) home = "";
625      }
626
627    snprintf(buf, sizeof(buf), "%s/"ELEMENTARY_BASE_DIR"/themes", home);
628    dir = ecore_file_ls(buf);
629    EINA_LIST_FREE(dir, file)
630      {
631         snprintf(buf, sizeof(buf), "%s/"ELEMENTARY_BASE_DIR"/themes/%s", home, file);
632         if ((!ecore_file_is_dir(buf)) && (ecore_file_size(buf) > 0))
633           {
634              s = strchr(file, '.');
635              if ((s) && (!strcasecmp(s, ".edj")))
636                {
637                   th = strdup(file);
638                   s = strchr(th, '.');
639                   *s = 0;
640                   list = eina_list_append(list, th);
641                }
642           }
643         free(file);
644      }
645
646    snprintf(buf, sizeof(buf), "%s/themes", _elm_data_dir);
647    dir = ecore_file_ls(buf);
648    EINA_LIST_FREE(dir, file)
649      {
650         snprintf(buf, sizeof(buf), "%s/themes/%s", _elm_data_dir, file);
651         if ((!ecore_file_is_dir(buf)) && (ecore_file_size(buf) > 0))
652           {
653              s = strchr(file, '.');
654              if ((s) && (!strcasecmp(s, ".edj")))
655                {
656                   int dupp;
657
658                   th = strdup(file);
659                   s = strchr(th, '.');
660                   *s = 0;
661                   dupp = 0;
662                   EINA_LIST_FOREACH(list, l, s)
663                     {
664                        if (!strcmp(s, th))
665                          {
666                             dupp = 1;
667                             break;
668                          }
669                     }
670                   if (dupp) free(th);
671                   else list = eina_list_append(list, th);
672                }
673           }
674         free(file);
675      }
676    list = eina_list_sort(list, 0, EINA_COMPARE_CB(strcasecmp));
677    return list;
678 }
679
680 EAPI void
681 elm_theme_name_available_list_free(Eina_List *list)
682 {
683    char *s;
684    EINA_LIST_FREE(list, s) free(s);
685 }
686
687 EAPI void
688 elm_object_theme_set(Evas_Object *obj, Elm_Theme *th)
689 {
690    EINA_SAFETY_ON_NULL_RETURN(obj);
691    elm_widget_theme_set(obj, th);
692 }
693
694 EAPI Elm_Theme *
695 elm_object_theme_get(const Evas_Object *obj)
696 {
697    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
698    return elm_widget_theme_get(obj);
699 }
700
701 EAPI const char *
702 elm_theme_data_get(Elm_Theme *th, const char *key)
703 {
704    if (!th) th = &(theme_default);
705    return _elm_theme_data_find(th, key);
706 }