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