remove excution permission for source files
[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,
14      NULL, 1
15 };
16
17 static Eina_List *themes = NULL;
18
19 static void
20 _elm_theme_clear(Elm_Theme *th)
21 {
22    const char *p;
23    EINA_LIST_FREE(th->themes, p)
24       eina_stringshare_del(p);
25    EINA_LIST_FREE(th->overlay, p)
26       eina_stringshare_del(p);
27    EINA_LIST_FREE(th->extension, p)
28       eina_stringshare_del(p);
29    if (th->cache)
30      {
31         eina_hash_free(th->cache);
32         th->cache = NULL;
33      }
34    if (th->theme)
35      {
36         eina_stringshare_del(th->theme);
37         th->theme = NULL;
38      }
39 }
40
41 static const char *
42 _elm_theme_find_try(Elm_Theme *th, const char *f, const char *group)
43 {
44    const char *file;
45
46    if (edje_file_group_exists(f, group))
47      {
48         file = eina_stringshare_add(f);
49         if (file)
50           {
51              eina_hash_add(th->cache, group, file);
52              return file;
53           }
54      }
55    return NULL;
56 }
57
58 static const char *
59 _elm_theme_theme_element_try(Elm_Theme *th, const char *home, const char *f, const char *group)
60 {
61    char buf[PATH_MAX];
62    const char *file = NULL;
63
64    if ((f[0] == '/') || ((f[0] == '.') && (f[1] == '/')) ||
65        ((f[0] == '.') && (f[1] == '.') && (f[2] == '/')) ||
66        (isalpha(f[0]) && f[1] == ':'))
67      return _elm_theme_find_try(th, f, group);
68    else if (((f[0] == '~') && (f[1] == '/')))
69      {
70         snprintf(buf, sizeof(buf), "%s/%s", home, f + 2);
71         return _elm_theme_find_try(th, buf, group);
72      }
73    snprintf(buf, sizeof(buf), "%s/.elementary/themes/%s.edj", home, f);
74    file = _elm_theme_find_try(th, buf, group);
75    if (file) return file;
76    snprintf(buf, sizeof(buf), "%s/themes/%s.edj", _elm_data_dir, f);
77    file = _elm_theme_find_try(th, buf, group);
78    return file;
79 }
80
81 static const char *
82 _elm_theme_group_file_find(Elm_Theme *th, const char *group)
83 {
84    const Eina_List *l;
85    const char *f;
86    static const char *home = NULL;
87    const char *file = eina_hash_find(th->cache, group);
88
89    if (file) return file;
90    if (!home)
91      {
92         home = getenv("HOME");
93         if (!home) home = "";
94      }
95    EINA_LIST_FOREACH(th->overlay, l, f)
96      {
97         file = _elm_theme_theme_element_try(th, home, f, group);
98         if (file) return file;
99      }
100    EINA_LIST_FOREACH(th->themes, 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->extension, l, f)
106      {
107         file = _elm_theme_theme_element_try(th, home, f, group);
108         if (file) return file;
109      }
110    return NULL;
111 }
112
113 int
114 _elm_theme_object_set(Evas_Object *parent, Evas_Object *o, const char *clas, const char *group, const char *style)
115 {
116    Elm_Theme *th = NULL;
117    if (parent) th = elm_widget_theme_get(parent);
118    return _elm_theme_set(th, o, clas, group, style);
119 }
120
121 int
122 _elm_theme_object_icon_set(Evas_Object *parent, Evas_Object *o, const char *group, const char *style)
123 {
124    Elm_Theme *th = NULL;
125    if (parent) th = elm_widget_theme_get(parent);
126    return _elm_theme_icon_set(th, o, group, style);
127 }
128
129 int
130 _elm_theme_set(Elm_Theme *th, Evas_Object *o, const char *clas, const char *group, const char *style)
131 {
132    const char *file;
133    char buf2[1024];
134    int ok;
135
136    if (!th) th = &(theme_default);
137    snprintf(buf2, sizeof(buf2), "elm/%s/%s/%s", clas, group, style);
138    file = _elm_theme_group_file_find(th, buf2);
139    if (file)
140      {
141         ok = edje_object_file_set(o, file, buf2);
142         if (ok) return 1;
143      }
144    snprintf(buf2, sizeof(buf2), "elm/%s/%s/default", clas, group);
145    file = _elm_theme_group_file_find(th, buf2);
146    if (!file) return 0;
147    ok = edje_object_file_set(o, file, buf2);
148    return ok;
149 }
150
151 int
152 _elm_theme_icon_set(Elm_Theme *th, Evas_Object *o, const char *group, const char *style)
153 {
154    const char *file;
155    char buf2[1024];
156    int w, h;
157
158    if (!th) th = &(theme_default);
159    snprintf(buf2, sizeof(buf2), "elm/icon/%s/%s", group, style);
160    file = _elm_theme_group_file_find(th, buf2);
161    if (file)
162      {
163         _els_smart_icon_file_edje_set(o, file, buf2);
164         _els_smart_icon_size_get(o, &w, &h);
165         if (w > 0) return 1;
166      }
167    snprintf(buf2, sizeof(buf2), "elm/icon/%s/default", group);
168    file = _elm_theme_group_file_find(th, buf2);
169    if (!file) return 0;
170    _els_smart_icon_file_edje_set(o, file, buf2);
171    _els_smart_icon_size_get(o, &w, &h);
172    return (w > 0);
173 }
174
175 int
176 _elm_theme_parse(Elm_Theme *th, const char *theme)
177 {
178    Eina_List *names = NULL;
179    const char *p, *pe;
180
181    if (!th) th = &(theme_default);
182    if (theme)
183      {
184         p = theme;
185         pe = p;
186         for (;;)
187           {
188              if ((*pe == ':') || (*pe == 0))
189                { // p -> pe == 'name:'
190                   if (pe > p)
191                     {
192                        char *n = malloc(pe - p + 1);
193                        if (n)
194                          {
195                             const char *nn;
196                             
197                             strncpy(n, p, pe - p);
198                             n[pe - p] = 0;
199                             nn = eina_stringshare_add(n);
200                             if (nn) names = eina_list_append(names, nn);
201                             free(n);
202                          }
203                     }
204                   if (*pe == 0) break;
205                   p = pe + 1;
206                   pe = p;
207                }
208              else
209                pe++;
210           }
211      }
212    p = eina_list_data_get(eina_list_last(names));
213    if ((!p) || ((p) && (strcmp(p, "default"))))
214      {
215         p = eina_stringshare_add("default");
216         if (p) names = eina_list_append(names, p);
217      }
218    if (th->cache) eina_hash_free(th->cache);
219    th->cache = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
220
221    EINA_LIST_FREE(th->themes, p) eina_stringshare_del(p);
222
223    th->themes = names;
224    return 1;
225 }
226
227 void
228 _elm_theme_shutdown(void)
229 {
230    _elm_theme_clear(&(theme_default));
231 }
232
233 /**
234  * Create a new specific theme
235  * 
236  * This creates an empty specific theme that only uses the default theme. A
237  * specific theme has its own private set of extensions and overlays too
238  * (which are empty by default). Specific themes do not fall back to themes
239  * of parent objects. They are not intended for this use. Use styles, overlays
240  * and extensions when needed, but avoid specific themes unless there is no
241  * other way (example: you want to have a preview of a new theme you are
242  * selecting in a "theme selector" window. The preview is inside a scroller
243  * and should display what the theme you selected will look like, but not
244  * actually apply it yet. The child of the scroller will have a specific
245  * theme set to show this preview before the user decides to apply it to all
246  * applications).
247  * 
248  * @ingroup Theme
249  */
250 EAPI Elm_Theme *
251 elm_theme_new(void)
252 {
253    Elm_Theme *th = calloc(1, sizeof(Elm_Theme));
254    if (!th) return NULL;
255    th->ref = 1;
256    th->themes = eina_list_append(th->themes, eina_stringshare_add("default"));
257    themes = eina_list_append(themes, th);
258    return th;
259 }
260
261 /**
262  * Free a specific theme
263  * 
264  * @param th The theme to free
265  * 
266  * This frees a theme created with elm_theme_new().
267  * 
268  * @ingroup Theme
269  */
270 EAPI void
271 elm_theme_free(Elm_Theme *th)
272 {
273    th->ref--;
274    if (th->ref < 1)
275      {
276         _elm_theme_clear(th);
277         themes = eina_list_remove(themes, th);
278         free(th);
279      }
280 }
281
282 /**
283  * Prepends a theme overlay to the list of overlays
284  *
285  * @param th The theme to add to, or if NULL, the default theme
286  * @param item The Edje file path to be used
287  * 
288  * Use this if your application needs to provide some custom overlay theme
289  * (An Edje file that replaces some default styles of widgets) where adding
290  * new styles, or changing system theme configuration is not possible. Do
291  * NOT use this instead of a proper system theme configuration. Use proper
292  * configuration files, profiles, environment variables etc. to set a theme
293  * so that the theme can be altered by simple confiugration by a user. Using
294  * this call to achieve that effect is abusing the API and will create lots
295  * of trouble.
296  *
297  * @ingroup Theme
298  */
299 EAPI void
300 elm_theme_overlay_add(Elm_Theme *th, const char *item)
301 {
302    const char *f = eina_stringshare_add(item);
303
304    if (!th) th = &(theme_default);
305    if (f) th->overlay = eina_list_prepend(th->overlay, f);
306    elm_theme_flush(th);
307 }
308
309 /**
310  * Delete a theme overlay from the list of overlays
311  *
312  * @param th The theme to delete from, or if NULL, the default theme
313  * @param item The name of the theme overlay
314  *
315  * See elm_theme_overlay_add().
316  * 
317  * @ingroup Theme
318  */
319 EAPI void
320 elm_theme_overlay_del(Elm_Theme *th, const char *item)
321 {
322    const Eina_List *l;
323    const char *f, *s;
324
325    if (!th) th = &(theme_default);
326    s = eina_stringshare_add(item);
327    EINA_LIST_FOREACH(th->overlay, l, f)
328      if (f == s)
329        {
330           eina_stringshare_del(f);
331           th->overlay = eina_list_remove_list(th->overlay, (Eina_List *)l);
332           break;
333        }
334    eina_stringshare_del(s);
335    elm_theme_flush(th);
336 }
337
338 /**
339  * Prepends a theme extension to the list of extensions.
340  *
341  * @param th The theme to add to, or if NULL, the default theme
342  * @param item The Edje file path to be used
343  *
344  * This is intended when an application needs more styles of widgets or new
345  * widget themes that the default does not provide (or may not provide). The
346  * application has "extended" usage by coming up with new custom style names
347  * for widgets for specific uses, but as these are not "standard", they are
348  * not guaranteed to be provided by a default theme. This means the
349  * application is required to provide these extra elements itself in specific
350  * Edje files. This call adds one of those Edje files to the theme search
351  * path to be search after the default theme. The use of this call is
352  * encouraged when default styles do not meet the needs of the application.
353  * Use this call instead of elm_theme_overlay_add() for almost all cases.
354  * 
355  * @ingroup Theme
356  */
357 EAPI void
358 elm_theme_extension_add(Elm_Theme *th, const char *item)
359 {
360    const char *f = eina_stringshare_add(item);
361
362    if (!th) th = &(theme_default);
363    if (f) th->extension = eina_list_append(th->extension, f);
364    elm_theme_flush(th);
365 }
366
367 /**
368  * Deletes a theme extension from the list of extensions.
369  *
370  * @param th The theme to delete from, or if NULL, the default theme
371  * @param item The name of the theme extension
372  * 
373  * See elm_theme_extension_add().
374  *
375  * @ingroup Theme
376  */
377 EAPI void
378 elm_theme_extension_del(Elm_Theme *th, const char *item)
379 {
380    const Eina_List *l;
381    const char *f, *s;
382
383    if (!th) th = &(theme_default);
384    s = eina_stringshare_add(item);
385    EINA_LIST_FOREACH(th->extension, l, f)
386      if (f == s)
387        {
388           eina_stringshare_del(f);
389           th->extension = eina_list_remove_list(th->extension, (Eina_List *)l);
390           break;
391        }
392    eina_stringshare_del(s);
393    elm_theme_flush(th);
394 }
395
396 /**
397  * Set the theme search order for the given theme
398  * 
399  * @param th The theme to set the search order, or if NULL, the default theme
400  * @param theme Theme search string
401  * 
402  * This sets the search string for the theme in path-notation from first
403  * theme to search, to last, delimited by the : character. Example:
404  * 
405  * "shiny:/path/to/file.edj:default"
406  * 
407  * See the ELM_THEME environment variable for more information.
408  * 
409  * @ingroup Theme
410  */
411 EAPI void
412 elm_theme_set(Elm_Theme *th, const char *theme)
413 {
414    if (!th) th = &(theme_default);
415    _elm_theme_parse(th, theme);
416    if (th->theme)
417      {
418         eina_stringshare_del(th->theme);
419         th->theme = NULL;
420      }
421    elm_theme_flush(th);
422 }
423
424 /**
425  * Return the theme search order
426  * 
427  * @param th The theme to get the search order, or if NULL, the default theme
428  * @return The internal search order path
429  * 
430  * See elm_theme_set() for more information.
431  * 
432  * @ingroup Theme
433  */
434 EAPI const char *
435 elm_theme_get(Elm_Theme *th)
436 {
437    if (!th->theme)
438      {
439         Eina_List *l;
440         const char *f;
441         char *tmp;
442         int len;
443         
444         len = 0;
445         EINA_LIST_FOREACH(th->themes, l, f)
446           {
447              len += strlen(f);
448              if (l->next) len += 1;
449           }
450         tmp = alloca(len + 1);
451         tmp[0] = 0;
452         EINA_LIST_FOREACH(th->themes, l, f)
453           {
454              strcat(tmp, f);
455              if (l->next) strcat(tmp, ":");
456           }
457         th->theme = eina_stringshare_add(tmp);
458      }
459    return th->theme;
460 }
461
462 /**
463  * Flush the current theme.
464  * 
465  * @param th Theme to flush
466  * 
467  * This flushes caches that let elementary know where to find theme elements
468  * in the given theme. If @p th is NULL, then the default theme is flushed.
469  * Call this call if source theme data has changed in such a way as to
470  * make any caches Elementary kept invalid.
471  *
472  * @ingroup Theme
473  */
474 EAPI void
475 elm_theme_flush(Elm_Theme *th)
476 {
477    if (!th) th = &(theme_default);
478    if (th->cache) eina_hash_free(th->cache);
479    th->cache = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
480    _elm_win_rescale();
481 }
482
483 /**
484  * This flushes all themems (default and specific ones).
485  * 
486  * This will flush all themes in the current application context, by calling
487  * elm_theme_flush() on each of them.
488  * 
489  * @ingroup Theme
490  */
491 EAPI void
492 elm_theme_full_flush(void)
493 {
494    Eina_List *l;
495    Elm_Theme *th;
496    
497    EINA_LIST_FOREACH(themes, l, th)
498      {
499         elm_theme_flush(th);
500      }
501    elm_theme_flush(&(theme_default));
502 }
503
504 /**
505  * Set the theme for all elementary using applications on the current display
506  *
507  * @param theme The name of the theme to use. Format same as the ELM_THEME
508  * environment variable.
509  *
510  * @ingroup Theme
511  */
512 EAPI void
513 elm_theme_all_set(const char *theme)
514 {
515 #ifdef HAVE_ELEMENTARY_X
516    static Ecore_X_Atom atom = 0;
517    
518    if (!atom) atom = ecore_x_atom_get("ENLIGHTENMENT_THEME");
519    ecore_x_window_prop_string_set(ecore_x_window_root_first_get(),
520                                   atom, theme);
521 #endif
522    elm_theme_set(NULL, theme);
523 }
524
525 /**
526  * Set a specific theme to be used for this object and its children
527  * 
528  * @param obj The object to set the theme on
529  * @param th The theme to set
530  * 
531  * This sets a specific theme that will be used for the given object and any
532  * child objects it has. If @p th is NULL then the theme to be used is
533  * cleared and the object will inherit its theme from its parent (which
534  * ultimately will use the default theme if no specific themes are set).
535  * 
536  * Use special themes with great care as this will annoy users and make
537  * configuration difficult. Avoid any custom themes at all if it can be
538  * helped.
539  * 
540  * @ingroup Theme
541  */
542 EAPI void
543 elm_object_theme_set(Evas_Object *obj, Elm_Theme *th)
544 {
545    elm_widget_theme_set(obj, th);
546 }
547
548 /**
549  * Get the specific theme to be used
550  * 
551  * @param obj The object to get the specific theme from
552  * @return The specifc theme set.
553  * 
554  * This will return a specific theme set, or NULL if no specific theme is
555  * set on that object. It will not return inherited themes from parents, only
556  * the specific theme set for that specific object. See elm_object_theme_set()
557  * for more information.
558  * 
559  * @ingroup Theme
560  */
561 EAPI Elm_Theme *
562 elm_object_theme_get(Evas_Object *obj)
563 {
564    return elm_widget_theme_get(obj);
565 }