elementary/theme, widget, win, toolbar, thumb, toggle, util, box - removed white...
[framework/uifw/elementary.git] / src / lib / elm_theme.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Theme
6  *
7  * Functions to modify the theme in the currently running app.
8  */
9
10 static Elm_Theme theme_default =
11 {
12    NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1
13 };
14
15 static Eina_List *themes = NULL;
16
17 static void
18 _elm_theme_clear(Elm_Theme *th)
19 {
20    const char *p;
21    EINA_LIST_FREE(th->themes, p)
22       eina_stringshare_del(p);
23    EINA_LIST_FREE(th->overlay, p)
24       eina_stringshare_del(p);
25    EINA_LIST_FREE(th->extension, p)
26       eina_stringshare_del(p);
27    if (th->cache)
28      {
29         eina_hash_free(th->cache);
30         th->cache = 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/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 Eina_Bool
120 _elm_theme_object_set(Evas_Object *parent, Evas_Object *o, const char *clas, const char *group, const char *style)
121 {
122    Elm_Theme *th = NULL;
123    if (parent) th = elm_widget_theme_get(parent);
124    return _elm_theme_set(th, o, clas, group, style);
125 }
126
127 Eina_Bool
128 _elm_theme_object_icon_set(Evas_Object *parent, Evas_Object *o, const char *group, const char *style)
129 {
130    Elm_Theme *th = NULL;
131    if (parent) th = elm_widget_theme_get(parent);
132    return _elm_theme_icon_set(th, o, group, style);
133 }
134
135 Eina_Bool
136 _elm_theme_set(Elm_Theme *th, Evas_Object *o, const char *clas, const char *group, const char *style)
137 {
138    const char *file;
139    char buf2[1024];
140    Eina_Bool ok;
141
142    if (!th) th = &(theme_default);
143    snprintf(buf2, sizeof(buf2), "elm/%s/%s/%s", clas, group, style);
144    file = _elm_theme_group_file_find(th, buf2);
145    if (file)
146      {
147         ok = edje_object_file_set(o, file, buf2);
148         if (ok) return EINA_TRUE;
149         else
150           DBG("could not set theme group '%s' from file '%s': %s",
151               buf2, file, edje_load_error_str(edje_object_load_error_get(o)));
152      }
153    snprintf(buf2, sizeof(buf2), "elm/%s/%s/default", clas, group);
154    file = _elm_theme_group_file_find(th, buf2);
155    if (!file) return EINA_FALSE;
156    ok = edje_object_file_set(o, file, buf2);
157    if (!ok)
158      DBG("could not set theme group '%s' from file '%s': %s",
159          buf2, file, edje_load_error_str(edje_object_load_error_get(o)));
160    return ok;
161 }
162
163 Eina_Bool
164 _elm_theme_icon_set(Elm_Theme *th, Evas_Object *o, const char *group, const char *style)
165 {
166    const char *file;
167    char buf2[1024];
168    int w, h;
169
170    if (!th) th = &(theme_default);
171    snprintf(buf2, sizeof(buf2), "elm/icon/%s/%s", group, style);
172    file = _elm_theme_group_file_find(th, buf2);
173    if (file)
174      {
175         _els_smart_icon_file_edje_set(o, file, buf2);
176         _els_smart_icon_size_get(o, &w, &h);
177         if (w > 0) return EINA_TRUE;
178      }
179    snprintf(buf2, sizeof(buf2), "elm/icon/%s/default", group);
180    file = _elm_theme_group_file_find(th, buf2);
181    if (!file) return EINA_FALSE;
182    _els_smart_icon_file_edje_set(o, file, buf2);
183    _els_smart_icon_size_get(o, &w, &h);
184    return (w > 0);
185 }
186
187 Eina_Bool
188 _elm_theme_parse(Elm_Theme *th, const char *theme)
189 {
190    Eina_List *names = NULL;
191    const char *p, *pe;
192
193    if (!th) th = &(theme_default);
194    if (theme)
195      {
196         p = theme;
197         pe = p;
198         for (;;)
199           {
200              if ((*pe == ':') || (!*pe))
201                { // p -> pe == 'name:'
202                   if (pe > p)
203                     {
204                        char *n = malloc(pe - p + 1);
205                        if (n)
206                          {
207                             const char *nn;
208
209                             strncpy(n, p, pe - p);
210                             n[pe - p] = 0;
211                             nn = eina_stringshare_add(n);
212                             if (nn) names = eina_list_append(names, nn);
213                             free(n);
214                          }
215                     }
216                   if (!*pe) break;
217                   p = pe + 1;
218                   pe = p;
219                }
220              else
221                pe++;
222           }
223      }
224    p = eina_list_data_get(eina_list_last(names));
225    if ((!p) || ((p) && (strcmp(p, "default"))))
226      {
227         p = eina_stringshare_add("default");
228         if (p) names = eina_list_append(names, p);
229      }
230    if (th->cache) eina_hash_free(th->cache);
231    th->cache = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
232
233    EINA_LIST_FREE(th->themes, p) eina_stringshare_del(p);
234
235    th->themes = names;
236    return EINA_TRUE;
237 }
238
239 void
240 _elm_theme_shutdown(void)
241 {
242    _elm_theme_clear(&(theme_default));
243 }
244
245 /**
246  * Create a new specific theme
247  *
248  * This creates an empty specific theme that only uses the default theme. A
249  * specific theme has its own private set of extensions and overlays too
250  * (which are empty by default). Specific themes do not fall back to themes
251  * of parent objects. They are not intended for this use. Use styles, overlays
252  * and extensions when needed, but avoid specific themes unless there is no
253  * other way (example: you want to have a preview of a new theme you are
254  * selecting in a "theme selector" window. The preview is inside a scroller
255  * and should display what the theme you selected will look like, but not
256  * actually apply it yet. The child of the scroller will have a specific
257  * theme set to show this preview before the user decides to apply it to all
258  * applications).
259  *
260  * @ingroup Theme
261  */
262 EAPI Elm_Theme *
263 elm_theme_new(void)
264 {
265    Elm_Theme *th = calloc(1, sizeof(Elm_Theme));
266    if (!th) return NULL;
267    th->ref = 1;
268    th->themes = eina_list_append(th->themes, eina_stringshare_add("default"));
269    themes = eina_list_append(themes, th);
270    return th;
271 }
272
273 /**
274  * Free a specific theme
275  *
276  * @param th The theme to free
277  *
278  * This frees a theme created with elm_theme_new().
279  *
280  * @ingroup Theme
281  */
282 EAPI void
283 elm_theme_free(Elm_Theme *th)
284 {
285    EINA_SAFETY_ON_NULL_RETURN(th);
286    th->ref--;
287    if (th->ref < 1)
288      {
289         _elm_theme_clear(th);
290         themes = eina_list_remove(themes, th);
291         free(th);
292      }
293 }
294
295 /**
296  * Copy the theme fom the source to the destination theme
297  *
298  * @param th The source theme to copy from
299  * @param thdst The destination theme to copy data to
300  *
301  * This makes a one-time static copy of all the theme config, extensions
302  * and overlays from @p th to @p thdst. If @p th references a theme, then
303  * @p thdst is also set to reference it, with all the theme settings,
304  * overlays and extensions that @p th had.
305  */
306 EAPI void
307 elm_theme_copy(Elm_Theme *th, Elm_Theme *thdst)
308 {
309    const Eina_List *l;
310    const char *f;
311
312    if (!th) th = &(theme_default);
313    if (!thdst) thdst = &(theme_default);
314    _elm_theme_clear(thdst);
315    if (th->ref_theme)
316      {
317         thdst->ref_theme = th->ref_theme;
318         thdst->ref_theme->referrers =
319            eina_list_append(thdst->ref_theme->referrers, thdst);
320         thdst->ref_theme->ref++;
321      }
322    EINA_LIST_FOREACH(th->overlay, l, f)
323      {
324         const char *s = eina_stringshare_add(f);
325         if (s) thdst->overlay = eina_list_append(thdst->overlay, s);
326      }
327    EINA_LIST_FOREACH(th->themes, l, f)
328      {
329         const char *s = eina_stringshare_add(f);
330         if (s) thdst->themes = eina_list_append(thdst->themes, s);
331      }
332    EINA_LIST_FOREACH(th->extension, l, f)
333      {
334         const char *s = eina_stringshare_add(f);
335         if (s) thdst->extension = eina_list_append(thdst->extension, s);
336      }
337    if (th->theme) thdst->theme = eina_stringshare_add(th->theme);
338    elm_theme_flush(thdst);
339 }
340
341 /**
342  * Tell the source theme to reference the ref theme
343  *
344  * @param th The theme that will do the referencing
345  * @param thref The theme that is the reference source
346  *
347  * This clears @p th to be empty and then sets it to refer to @p thref
348  * so @p th acts as an override to @p thdst, but where its overrides
349  * don't apply, it will fall through to @pthref for configuration.
350  */
351 EAPI void
352 elm_theme_ref_set(Elm_Theme *th, Elm_Theme *thref)
353 {
354    if (!th) th = &(theme_default);
355    if (!thref) thref = &(theme_default);
356    if (th->ref_theme == thref) return;
357    _elm_theme_clear(th);
358    if (thref)
359      {
360         thref->referrers = eina_list_append(thref->referrers, th);
361         thref->ref++;
362      }
363    th->ref_theme = thref;
364    elm_theme_flush(th);
365 }
366
367 /**
368  * Return the theme referred to
369  *
370  * @param th The theme to get the reference from
371  * @return The referenced theme handle
372  *
373  * This gets the theme set as the reference theme by elm_theme_ref_set().
374  * If no theme is set as a reference, NULL is returned.
375  */
376 EAPI Elm_Theme *
377 elm_theme_ref_get(Elm_Theme *th)
378 {
379    if (!th) th = &(theme_default);
380    return th->ref_theme;
381 }
382
383 /**
384  * Return the default theme
385  *
386  * @return The default theme handle
387  *
388  * This returns the internal default theme setup handle that all widgets
389  * use implicitly unless a specific theme is set. This is also often use
390  * as a shorthand of NULL.
391  */
392 EAPI Elm_Theme *
393 elm_theme_default_get(void)
394 {
395    return &theme_default;
396 }
397
398 /**
399  * Prepends a theme overlay to the list of overlays
400  *
401  * @param th The theme to add to, or if NULL, the default theme
402  * @param item The Edje file path to be used
403  *
404  * Use this if your application needs to provide some custom overlay theme
405  * (An Edje file that replaces some default styles of widgets) where adding
406  * new styles, or changing system theme configuration is not possible. Do
407  * NOT use this instead of a proper system theme configuration. Use proper
408  * configuration files, profiles, environment variables etc. to set a theme
409  * so that the theme can be altered by simple confiugration by a user. Using
410  * this call to achieve that effect is abusing the API and will create lots
411  * of trouble.
412  *
413  * @ingroup Theme
414  */
415 EAPI void
416 elm_theme_overlay_add(Elm_Theme *th, const char *item)
417 {
418    const char *f = eina_stringshare_add(item);
419
420    if (!th) th = &(theme_default);
421    if (f) th->overlay = eina_list_prepend(th->overlay, f);
422    elm_theme_flush(th);
423 }
424
425 /**
426  * Delete a theme overlay from the list of overlays
427  *
428  * @param th The theme to delete from, or if NULL, the default theme
429  * @param item The name of the theme overlay
430  *
431  * See elm_theme_overlay_add().
432  *
433  * @ingroup Theme
434  */
435 EAPI void
436 elm_theme_overlay_del(Elm_Theme *th, const char *item)
437 {
438    const Eina_List *l;
439    const char *f, *s;
440
441    if (!th) th = &(theme_default);
442    s = eina_stringshare_add(item);
443    EINA_LIST_FOREACH(th->overlay, l, f)
444       if (f == s)
445         {
446            eina_stringshare_del(f);
447            th->overlay = eina_list_remove_list(th->overlay, (Eina_List *)l);
448            break;
449         }
450    eina_stringshare_del(s);
451    elm_theme_flush(th);
452 }
453
454 /**
455  * Appends a theme extension to the list of extensions.
456  *
457  * @param th The theme to add to, or if NULL, the default theme
458  * @param item The Edje file path to be used
459  *
460  * This is intended when an application needs more styles of widgets or new
461  * widget themes that the default does not provide (or may not provide). The
462  * application has "extended" usage by coming up with new custom style names
463  * for widgets for specific uses, but as these are not "standard", they are
464  * not guaranteed to be provided by a default theme. This means the
465  * application is required to provide these extra elements itself in specific
466  * Edje files. This call adds one of those Edje files to the theme search
467  * path to be search after the default theme. The use of this call is
468  * encouraged when default styles do not meet the needs of the application.
469  * Use this call instead of elm_theme_overlay_add() for almost all cases.
470  *
471  * @ingroup Theme
472  */
473 EAPI void
474 elm_theme_extension_add(Elm_Theme *th, const char *item)
475 {
476    const char *f = eina_stringshare_add(item);
477
478    if (!th) th = &(theme_default);
479    if (f) th->extension = eina_list_append(th->extension, f);
480    elm_theme_flush(th);
481 }
482
483 /**
484  * Deletes a theme extension from the list of extensions.
485  *
486  * @param th The theme to delete from, or if NULL, the default theme
487  * @param item The name of the theme extension
488  *
489  * See elm_theme_extension_add().
490  *
491  * @ingroup Theme
492  */
493 EAPI void
494 elm_theme_extension_del(Elm_Theme *th, const char *item)
495 {
496    const Eina_List *l;
497    const char *f, *s;
498
499    if (!th) th = &(theme_default);
500    s = eina_stringshare_add(item);
501    EINA_LIST_FOREACH(th->extension, l, f)
502       if (f == s)
503         {
504            eina_stringshare_del(f);
505            th->extension = eina_list_remove_list(th->extension, (Eina_List *)l);
506            break;
507         }
508    eina_stringshare_del(s);
509    elm_theme_flush(th);
510 }
511
512 /**
513  * Set the theme search order for the given theme
514  *
515  * @param th The theme to set the search order, or if NULL, the default theme
516  * @param theme Theme search string
517  *
518  * This sets the search string for the theme in path-notation from first
519  * theme to search, to last, delimited by the : character. Example:
520  *
521  * "shiny:/path/to/file.edj:default"
522  *
523  * See the ELM_THEME environment variable for more information.
524  *
525  * @ingroup Theme
526  */
527 EAPI void
528 elm_theme_set(Elm_Theme *th, const char *theme)
529 {
530    if (!th) th = &(theme_default);
531    _elm_theme_parse(th, theme);
532    if (th->theme)
533      {
534         eina_stringshare_del(th->theme);
535         th->theme = NULL;
536      }
537    elm_theme_flush(th);
538 }
539
540 /**
541  * Return the theme search order
542  *
543  * @param th The theme to get the search order, or if NULL, the default theme
544  * @return The internal search order path
545  *
546  * See elm_theme_set() for more information.
547  *
548  * @ingroup Theme
549  */
550 EAPI const char *
551 elm_theme_get(Elm_Theme *th)
552 {
553    if (!th) th = &(theme_default);
554    if (!th->theme)
555      {
556         Eina_List *l;
557         const char *f;
558         char *tmp;
559         int len;
560
561         len = 0;
562         EINA_LIST_FOREACH(th->themes, l, f)
563           {
564              len += strlen(f);
565              if (l->next) len += 1;
566           }
567         tmp = alloca(len + 1);
568         tmp[0] = 0;
569         EINA_LIST_FOREACH(th->themes, l, f)
570           {
571              strcat(tmp, f);
572              if (l->next) strcat(tmp, ":");
573           }
574         th->theme = eina_stringshare_add(tmp);
575      }
576    return th->theme;
577 }
578
579 /**
580  * Return a list of theme elements to be used in a theme.
581  *
582  * @param th Theme to get the list of theme elements from.
583  * @return The internal list of theme elements
584  *
585  * This returns the internal list of theme elements (will only be valid as
586  * long as the theme is not modified by elm_theme_set() or theme is not
587  * freed by elm_theme_free(). This is a list of strings which must not be
588  * altered as they are also internal. If @p th is NULL, then the default
589  * theme element list is returned.
590  */
591 EAPI const Eina_List *
592 elm_theme_list_get(const Elm_Theme *th)
593 {
594    if (!th) th = &(theme_default);
595    return th->themes;
596 }
597
598 /**
599  * Return the full patrh for a theme element
600  *
601  * @param f The theme element name
602  * @param in_search_path Pointer to a boolean to indicate if item is in the search path or not
603  * @return The full path to the file found.
604  *
605  * This returns a string you should free with free() on success, NULL on
606  * failure. This will search for the given theme element, and if it is a
607  * full or relative path element or a simple searchable name. The returned
608  * path is the full path to the file, if searched, and the file exists, or it
609  * is simply the full path given in the element or a resolved path if
610  * relative to home. The @p in_search_path boolean pointed to is set to
611  * EINA_TRUE if the file was a searchable file andis in the search path,
612  * and EINA_FALSE otherwise.
613  */
614 EAPI char *
615 elm_theme_list_item_path_get(const char *f, Eina_Bool *in_search_path)
616 {
617    static const char *home = NULL;
618    char buf[PATH_MAX];
619
620    if (!f)
621      {
622         if (in_search_path) *in_search_path = EINA_FALSE;
623         return NULL;
624      }
625
626    if (!home)
627      {
628         home = getenv("HOME");
629         if (!home) home = "";
630      }
631
632    if ((f[0] == '/') || ((f[0] == '.') && (f[1] == '/')) ||
633        ((f[0] == '.') && (f[1] == '.') && (f[2] == '/')) ||
634        ((isalpha(f[0])) && (f[1] == ':')))
635      {
636         if (in_search_path) *in_search_path = EINA_FALSE;
637         return strdup(f);
638      }
639    else if (((f[0] == '~') && (f[1] == '/')))
640      {
641         if (in_search_path) *in_search_path = EINA_FALSE;
642         snprintf(buf, sizeof(buf), "%s/%s", home, f + 2);
643         return strdup(buf);
644      }
645    snprintf(buf, sizeof(buf), "%s/.elementary/themes/%s.edj", home, f);
646    if (ecore_file_exists(buf))
647      {
648         if (in_search_path) *in_search_path = EINA_TRUE;
649         return strdup(buf);
650      }
651
652    snprintf(buf, sizeof(buf), "%s/themes/%s.edj", _elm_data_dir, f);
653    if (ecore_file_exists(buf))
654      {
655         if (in_search_path) *in_search_path = EINA_TRUE;
656         return strdup(buf);
657      }
658
659    if (in_search_path) *in_search_path = EINA_FALSE;
660    return NULL;
661 }
662
663 /**
664  * Flush the current theme.
665  *
666  * @param th Theme to flush
667  *
668  * This flushes caches that let elementary know where to find theme elements
669  * in the given theme. If @p th is NULL, then the default theme is flushed.
670  * Call this call if source theme data has changed in such a way as to
671  * make any caches Elementary kept invalid.
672  *
673  * @ingroup Theme
674  */
675 EAPI void
676 elm_theme_flush(Elm_Theme *th)
677 {
678    if (!th) th = &(theme_default);
679    if (th->cache) eina_hash_free(th->cache);
680    th->cache = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
681    _elm_win_rescale(th, EINA_TRUE);
682    if (th->referrers)
683      {
684         Eina_List *l;
685         Elm_Theme *th2;
686
687         EINA_LIST_FOREACH(th->referrers, l, th2) elm_theme_flush(th2);
688      }
689 }
690
691 /**
692  * This flushes all themes (default and specific ones).
693  *
694  * This will flush all themes in the current application context, by calling
695  * elm_theme_flush() on each of them.
696  *
697  * @ingroup Theme
698  */
699 EAPI void
700 elm_theme_full_flush(void)
701 {
702    Eina_List *l;
703    Elm_Theme *th;
704
705    EINA_LIST_FOREACH(themes, l, th)
706      {
707         elm_theme_flush(th);
708      }
709    elm_theme_flush(&(theme_default));
710 }
711
712 /**
713  * Set the theme for all elementary using applications on the current display
714  *
715  * @param theme The name of the theme to use. Format same as the ELM_THEME
716  * environment variable.
717  *
718  * @ingroup Theme
719  */
720 EAPI void
721 elm_theme_all_set(const char *theme)
722 {
723 #ifdef HAVE_ELEMENTARY_X
724    static Ecore_X_Atom atom = 0;
725
726    if (!atom) atom = ecore_x_atom_get("ENLIGHTENMENT_THEME");
727    ecore_x_window_prop_string_set(ecore_x_window_root_first_get(),
728                                   atom, theme);
729 #endif
730    elm_theme_set(NULL, theme);
731 }
732
733 /**
734  * Return a list of theme elements in the theme search path
735  *
736  * @return A list of strings that are the theme element names.
737  *
738  * This lists all available theme files in the standard Elementary search path
739  * for theme elements, and returns them in alphabetical order as theme
740  * element names in a list of strings. Free this with
741  * elm_theme_name_available_list_free() when you are done with the list.
742  */
743 EAPI Eina_List *
744 elm_theme_name_available_list_new(void)
745 {
746    Eina_List *list = NULL;
747    Eina_List *dir, *l;
748    char buf[PATH_MAX], *file, *s, *th;
749    static const char *home = NULL;
750
751    if (!home)
752      {
753         home = getenv("HOME");
754         if (!home) home = "";
755      }
756
757    snprintf(buf, sizeof(buf), "%s/.elementary/themes", home);
758    dir = ecore_file_ls(buf);
759    EINA_LIST_FREE(dir, file)
760      {
761         snprintf(buf, sizeof(buf), "%s/.elementary/themes/%s", home, file);
762         if ((!ecore_file_is_dir(buf)) && (ecore_file_size(buf) > 0))
763           {
764              s = strchr(file, '.');
765              if ((s) && (!strcasecmp(s, ".edj")))
766                {
767                   th = strdup(file);
768                   s = strchr(th, '.');
769                   *s = 0;
770                   list = eina_list_append(list, th);
771                }
772           }
773         free(file);
774      }
775
776    snprintf(buf, sizeof(buf), "%s/themes", _elm_data_dir);
777    dir = ecore_file_ls(buf);
778    EINA_LIST_FREE(dir, file)
779      {
780         snprintf(buf, sizeof(buf), "%s/themes/%s", _elm_data_dir, file);
781         if ((!ecore_file_is_dir(buf)) && (ecore_file_size(buf) > 0))
782           {
783              s = strchr(file, '.');
784              if ((s) && (!strcasecmp(s, ".edj")))
785                {
786                   int dup;
787
788                   th = strdup(file);
789                   s = strchr(th, '.');
790                   *s = 0;
791                   dup = 0;
792                   EINA_LIST_FOREACH(list, l, s)
793                     {
794                        if (!strcmp(s, th))
795                          {
796                             dup = 1;
797                             break;
798                          }
799                     }
800                   if (dup) free(th);
801                   else list = eina_list_append(list, th);
802                }
803           }
804         free(file);
805      }
806    list = eina_list_sort(list, 0, EINA_COMPARE_CB(strcasecmp));
807    return list;
808 }
809
810 /**
811  * Free the list returned by elm_theme_name_available_list_new()
812  *
813  * This frees the list of themes returned by
814  * elm_theme_name_available_list_new(). Once freed the list should no longer
815  * be used. a new list mys be created.
816  */
817 EAPI void
818 elm_theme_name_available_list_free(Eina_List *list)
819 {
820    char *s;
821    EINA_LIST_FREE(list, s) free(s);
822 }
823
824 /**
825  * Set a specific theme to be used for this object and its children
826  *
827  * @param obj The object to set the theme on
828  * @param th The theme to set
829  *
830  * This sets a specific theme that will be used for the given object and any
831  * child objects it has. If @p th is NULL then the theme to be used is
832  * cleared and the object will inherit its theme from its parent (which
833  * ultimately will use the default theme if no specific themes are set).
834  *
835  * Use special themes with great care as this will annoy users and make
836  * configuration difficult. Avoid any custom themes at all if it can be
837  * helped.
838  *
839  * @ingroup Theme
840  */
841 EAPI void
842 elm_object_theme_set(Evas_Object *obj, Elm_Theme *th)
843 {
844    EINA_SAFETY_ON_NULL_RETURN(obj);
845    elm_widget_theme_set(obj, th);
846 }
847
848 /**
849  * Get the specific theme to be used
850  *
851  * @param obj The object to get the specific theme from
852  * @return The specifc theme set.
853  *
854  * This will return a specific theme set, or NULL if no specific theme is
855  * set on that object. It will not return inherited themes from parents, only
856  * the specific theme set for that specific object. See elm_object_theme_set()
857  * for more information.
858  *
859  * @ingroup Theme
860  */
861 EAPI Elm_Theme *
862 elm_object_theme_get(const Evas_Object *obj)
863 {
864    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
865    return elm_widget_theme_get(obj);
866 }