1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4
5 #include <Elementary.h>
6
7 #include "elm_priv.h"
8 #include "elm_icon_eo.h"
9
10 #include "efl_ui_theme.eo.h"
11
12 static Elm_Theme *theme_default = NULL;
13
14 static Eina_List *themes = NULL;
15
16 static Eina_File *
_elm_theme_find_try(Elm_Theme * th,Elm_Theme_File * etf,const char * group,Eina_Bool force)17 _elm_theme_find_try(Elm_Theme *th, Elm_Theme_File *etf, const char *group, Eina_Bool force)
18 {
19 if (edje_mmap_group_exists(etf->handle, group))
20 {
21 if (etf->match_theme && (!force)) // overlay or extension
22 {
23 Elm_Theme_File *base_etf;
24 Eina_Bool found = EINA_FALSE;
25
26 EINA_INLIST_FOREACH(th->themes, base_etf)
27 {
28 if (base_etf->base_theme != etf->match_theme) continue;
29 found = EINA_TRUE;
30 break;
31 }
32 if (!found) return NULL;
33 }
34 eina_hash_add(th->cache, group, eina_file_dup(etf->handle));
35 return etf->handle;
36 }
37 return NULL;
38 }
39
40 static inline void
_elm_theme_item_finalize(Eina_Inlist ** files,const char * item,Eina_File * f,Eina_Bool prepend,Eina_Bool istheme)41 _elm_theme_item_finalize(Eina_Inlist **files,
42 const char *item,
43 Eina_File *f,
44 Eina_Bool prepend,
45 Eina_Bool istheme)
46 {
47 Elm_Theme_File *etf;
48 char *name;
49 /* Theme version history:
50 * <110: legacy, had no version tag
51 * 110: first supported version
52 * 119: switched windows to always use border
53 * win group has no menu, no blocker
54 * border group has all required swallows (conformant, bg, win)
55 * data: "elm_bg_version" matches "version" ("119")
56 */
57 if (!f) return;
58 if (istheme)
59 {
60 char *version;
61 int v;
62
63 if (!(version = edje_mmap_data_get(f, "version")))
64 {
65 eina_file_close(f); // close matching open (finalize expected to consume eina file) OK
66 return;
67 }
68 v = atoi(version);
69 if (v < 110) // bump this version number when we need to
70 {
71 WRN("Selected theme is too old (version = %d, needs >= 110)", v);
72 }
73 free(version);
74 }
75 etf = calloc(1, sizeof(Elm_Theme_File));
76 EINA_SAFETY_ON_NULL_RETURN(etf);
77 etf->item = eina_stringshare_add(item);
78 etf->handle = f; // now own/consume the file handle
79 if (istheme)
80 {
81 name = edje_mmap_data_get(f, "efl_theme_base");
82 etf->base_theme = eina_stringshare_add(name);
83 }
84 else
85 {
86 name = edje_mmap_data_get(f, "efl_theme_match");
87 etf->match_theme = eina_stringshare_add(name);
88 }
89 free(name);
90 if (prepend)
91 {
92 *files = eina_inlist_prepend(*files, EINA_INLIST_GET(etf));
93 }
94 else
95 {
96 *files = eina_inlist_append(*files, EINA_INLIST_GET(etf));
97 }
98 }
99
100 static void
_elm_theme_file_item_add(Eina_Inlist ** files,const char * item,Eina_Bool prepend,Eina_Bool istheme)101 _elm_theme_file_item_add(Eina_Inlist **files, const char *item, Eina_Bool prepend, Eina_Bool istheme)
102 {
103 Eina_Strbuf *buf = NULL;
104 Eina_File *f = NULL;
105 const char *home;
106
107 home = eina_environment_home_get();
108 buf = eina_strbuf_new();
109
110 if ((item[0] == '/') ||
111 ((item[0] == '.') && (item[1] == '/')) ||
112 ((item[0] == '.') && (item[1] == '.') && (item[2] == '/')) ||
113 ((isalpha(item[0])) && (item[1] == ':')))
114 {
115 f = eina_file_open(item, EINA_FALSE);
116 if (!f) goto on_error;
117 }
118 else if (((item[0] == '~') && (item[1] == '/')))
119 {
120 eina_strbuf_append_printf(buf, "%s/%s", home, item + 2);
121
122 f = eina_file_open(eina_strbuf_string_get(buf), EINA_FALSE);
123 if (!f) goto on_error;
124 }
125 else
126 {
127 eina_strbuf_append_printf(buf,
128 "%s/"ELEMENTARY_BASE_DIR"/themes/%s.edj",
129 home, item);
130 f = eina_file_open(eina_strbuf_string_get(buf), EINA_FALSE);
131 _elm_theme_item_finalize(files, item, f, prepend, istheme);
132
133 eina_strbuf_reset(buf);
134
135 eina_strbuf_append_printf(buf,
136 "%s/themes/%s.edj",
137 _elm_data_dir, item);
138 f = eina_file_open(eina_strbuf_string_get(buf), EINA_FALSE);
139 /* Finalize will be done by the common one */
140 }
141
142 _elm_theme_item_finalize(files, item, f, prepend, istheme);
143
144 on_error:
145 if (buf) eina_strbuf_free(buf);
146 }
147
148 static void
_elm_theme_file_item_del(Eina_Inlist ** files,const char * str)149 _elm_theme_file_item_del(Eina_Inlist **files, const char *str)
150 {
151 Eina_Inlist *l;
152 Elm_Theme_File *item;
153
154 str = eina_stringshare_add(str);
155
156 EINA_INLIST_FOREACH_SAFE(*files, l, item)
157 {
158 if (item->item != str) continue;
159 eina_file_close(item->handle); // close matching open (file consumed in finalize by putting it in etf) OK
160 eina_stringshare_del(item->item);
161 *files = eina_inlist_remove(*files, EINA_INLIST_GET(item));
162 free(item);
163 }
164
165 eina_stringshare_del(str);
166 }
167
168 static void
_elm_theme_file_mmap_del(Eina_Inlist ** files,const Eina_File * file)169 _elm_theme_file_mmap_del(Eina_Inlist **files, const Eina_File *file)
170 {
171 Eina_Inlist *l;
172 Elm_Theme_File *item;
173
174 EINA_INLIST_FOREACH_SAFE(*files, l, item)
175 {
176 if (item->handle != file) continue;
177 eina_file_close(item->handle); // close matching open (file consumed in finalize by putting it in etf) OK
178 eina_stringshare_del(item->item);
179 *files = eina_inlist_remove(*files, EINA_INLIST_GET(item));
180 free(item);
181 }
182 }
183
184 static void
_elm_theme_file_clean(Eina_Inlist ** files)185 _elm_theme_file_clean(Eina_Inlist **files)
186 {
187 while (*files)
188 {
189 Elm_Theme_File *etf = EINA_INLIST_CONTAINER_GET(*files, Elm_Theme_File);
190
191 eina_stringshare_del(etf->item);
192 eina_file_close(etf->handle); // close matching open (file consumed in finalize by putting it in etf) OK
193 eina_stringshare_del(etf->match_theme);
194 *files = eina_inlist_remove(*files, *files);
195 free(etf);
196 }
197 }
198
199 static void
_elm_theme_clear(Elm_Theme * th)200 _elm_theme_clear(Elm_Theme *th)
201 {
202 Efl_Ui_Theme_Data *td;
203
204 _elm_theme_file_clean(&th->themes);
205 _elm_theme_file_clean(&th->overlay);
206 _elm_theme_file_clean(&th->extension);
207
208 ELM_SAFE_FREE(th->overlay_items, eina_list_free);
209 ELM_SAFE_FREE(th->theme_items, eina_list_free);
210 ELM_SAFE_FREE(th->extension_items, eina_list_free);
211
212 ELM_SAFE_FREE(th->cache, eina_hash_free);
213 ELM_SAFE_FREE(th->cache_data, eina_hash_free);
214 ELM_SAFE_FREE(th->cache_style_load_failed, eina_hash_free);
215 ELM_SAFE_FREE(th->theme, eina_stringshare_del);
216 if (th->ref_theme)
217 {
218 th->ref_theme->referrers =
219 eina_list_remove(th->ref_theme->referrers, th);
220 elm_theme_free(th->ref_theme);
221 th->ref_theme = NULL;
222 }
223
224 td = efl_data_scope_get(th->eo_theme, EFL_UI_THEME_CLASS);
225 td->th = NULL;
226 th->eo_theme = NULL;
227 }
228
229 static Eina_File *
_elm_theme_group_file_find_internal(Elm_Theme * th,const char * group,Eina_Bool force)230 _elm_theme_group_file_find_internal(Elm_Theme *th, const char *group, Eina_Bool force)
231 {
232 Elm_Theme_File *etf;
233 Eina_File *file = eina_hash_find(th->cache, group);
234
235 if (file) return file;
236
237 EINA_INLIST_FOREACH(th->overlay, etf)
238 {
239 file = _elm_theme_find_try(th, etf, group, force);
240 if (file) return file;
241 }
242 EINA_INLIST_FOREACH(th->themes, etf)
243 {
244 file = _elm_theme_find_try(th, etf, group, force);
245 if (file) return file;
246 }
247 EINA_INLIST_FOREACH(th->extension, etf)
248 {
249 file = _elm_theme_find_try(th, etf, group, force);
250 if (file) return file;
251 }
252 if (th->ref_theme) return _elm_theme_group_file_find_internal(th->ref_theme, group, force);
253 return NULL;
254 }
255
256 Eina_File *
_elm_theme_group_file_find(Elm_Theme * th,const char * group)257 _elm_theme_group_file_find(Elm_Theme *th, const char *group)
258 {
259 Eina_File *file = _elm_theme_group_file_find_internal(th, group, EINA_FALSE);
260 if (file) return file;
261 file = _elm_theme_group_file_find_internal(th, group, EINA_TRUE);
262 return file;
263 }
264
265 static const char *
_elm_theme_find_data_try(Elm_Theme * th,const Eina_File * f,const char * key)266 _elm_theme_find_data_try(Elm_Theme *th, const Eina_File *f, const char *key)
267 {
268 char *data;
269 const char *t;
270
271 data = edje_mmap_data_get(f, key);
272 t = eina_stringshare_add(data);
273 free(data);
274 if (t)
275 {
276 eina_hash_add(th->cache_data, key, t);
277 return t;
278 }
279 return NULL;
280 }
281
282 static const char *
_elm_theme_data_find(Elm_Theme * th,const char * key)283 _elm_theme_data_find(Elm_Theme *th, const char *key)
284 {
285 Elm_Theme_File *etf;
286
287 const char *data = eina_hash_find(th->cache_data, key);
288 if (data) return data;
289
290 EINA_INLIST_FOREACH(th->overlay, etf)
291 {
292 data = _elm_theme_find_data_try(th, etf->handle, key);
293 if (data) return data;
294 }
295 EINA_INLIST_FOREACH(th->themes, etf)
296 {
297 data = _elm_theme_find_data_try(th, etf->handle, key);
298 if (data) return data;
299 }
300 EINA_INLIST_FOREACH(th->extension, etf)
301 {
302 data = _elm_theme_find_data_try(th, etf->handle, key);
303 if (data) return data;
304 }
305 if (th->ref_theme) return _elm_theme_data_find(th->ref_theme, key);
306 return NULL;
307 }
308
309 Eina_Error
_elm_theme_object_set(Evas_Object * parent,Evas_Object * o,const char * clas,const char * group,const char * style)310 _elm_theme_object_set(Evas_Object *parent, Evas_Object *o, const char *clas, const char *group, const char *style)
311 {
312 Elm_Theme *th = NULL;
313
314 if (parent) th = elm_widget_theme_get(parent);
315
316 return _elm_theme_set(th, o, clas, group, style, elm_widget_is_legacy(parent));
317 }
318
319 /* only issued by elm_icon.c */
320 Eina_Bool
_elm_theme_object_icon_set(Evas_Object * o,const char * group,const char * style)321 _elm_theme_object_icon_set(Evas_Object *o,
322 const char *group,
323 const char *style)
324 {
325 Elm_Theme *th = elm_widget_theme_get(o);
326
327 return _elm_theme_icon_set(th, o, group, style);
328 }
329
330 Eina_Error
_elm_theme_set(Elm_Theme * th,Evas_Object * o,const char * clas,const char * group,const char * style,Eina_Bool is_legacy)331 _elm_theme_set(Elm_Theme *th, Evas_Object *o, const char *clas, const char *group, const char *style, Eina_Bool is_legacy)
332 {
333 Eina_File *file;
334 char buf2[1024];
335 const char *group_sep = "/";
336 const char *style_sep = ":";
337
338 if ((!clas) || !o) return EFL_UI_THEME_APPLY_ERROR_GENERIC;
339 if (!th) th = theme_default;
340 if (!th) return EFL_UI_THEME_APPLY_ERROR_GENERIC;
341
342 if (eina_streq(style, "default")) style = NULL;
343
344 if (is_legacy)
345 snprintf(buf2, sizeof(buf2), "elm/%s/%s/%s", clas, (group) ? group : "base", (style) ? style : "default");
346 else
347 snprintf(buf2, sizeof(buf2), "efl/%s%s%s%s%s", clas,
348 ((group) ? group_sep : "\0"), ((group) ? group : "\0"),
349 ((style) ? style_sep : "\0"), ((style) ? style : "\0"));
350 if (!eina_hash_find(th->cache_style_load_failed, buf2))
351 {
352 file = _elm_theme_group_file_find(th, buf2);
353 if (file)
354 {
355 if (edje_object_mmap_set(o, file, buf2)) return EFL_UI_THEME_APPLY_ERROR_NONE;
356 else
357 {
358 ERR("could not set theme group '%s' from file '%s': %s",
359 buf2,
360 eina_file_filename_get(file),
361 edje_load_error_str(edje_object_load_error_get(o)));
362 }
363 }
364 //style not found, add to the not found list
365 eina_hash_add(th->cache_style_load_failed, buf2, (void *)1);
366 }
367
368 if (!style)
369 return EFL_UI_THEME_APPLY_ERROR_GENERIC;
370
371 // Use the elementary default style.
372 if (_elm_theme_set(th, o, clas, group, NULL, is_legacy) == EFL_UI_THEME_APPLY_ERROR_NONE)
373 return EFL_UI_THEME_APPLY_ERROR_DEFAULT;
374 return EFL_UI_THEME_APPLY_ERROR_GENERIC;
375 }
376
377 Eina_Bool
_elm_theme_icon_set(Elm_Theme * th,Evas_Object * o,const char * group,const char * style)378 _elm_theme_icon_set(Elm_Theme *th,
379 Evas_Object *o,
380 const char *group,
381 const char *style)
382 {
383 Eina_File *file;
384 char buf2[1024];
385 int w, h;
386
387 if (efl_isa((o), ELM_ICON_CLASS) && elm_icon_standard_get(o) &&
388 strcmp(elm_config_icon_theme_get(), ELM_CONFIG_ICON_THEME_ELEMENTARY))
389 {
390 elm_icon_standard_set(o, elm_icon_standard_get(o));
391 return EINA_TRUE;
392 }
393
394 if (!th) th = theme_default;
395 if (!th) return EINA_FALSE;
396
397 snprintf(buf2, sizeof(buf2), "elm/icon/%s/%s", group, style);
398 file = _elm_theme_group_file_find(th, buf2);
399 if (file)
400 {
401 elm_image_mmap_set(o, file, buf2);
402 elm_image_object_size_get(o, &w, &h);
403 if (w > 0) return EINA_TRUE;
404 }
405 snprintf(buf2, sizeof(buf2), "elm/icon/%s/default", group);
406 file = _elm_theme_group_file_find(th, buf2);
407
408 if (!file) return EINA_FALSE;
409
410 elm_image_mmap_set(o, file, buf2);
411 elm_image_object_size_get(o, &w, &h);
412
413 return w > 0;
414 }
415
416 void
_elm_theme_parse(Elm_Theme * th,const char * theme)417 _elm_theme_parse(Elm_Theme *th, const char *theme)
418 {
419 Eina_List *names = NULL;
420 const char *p, *pe;
421
422 if (!th) th = theme_default;
423 if (!th) return;
424
425 if (theme)
426 {
427 Eina_Strbuf *buf;
428
429 buf = eina_strbuf_new();
430
431 p = theme;
432 pe = p;
433 for (;;)
434 {
435 if ((pe[0] == '\\') && (pe[1] == ':'))
436 {
437 eina_strbuf_append_char(buf, ':');
438 pe += 2;
439 }
440 #ifdef HAVE_ELEMENTARY_WIN32
441 else if (isalpha(pe[0]) && (pe[1] == ':') &&
442 ((pe[2] == '/') || (pe[2] == '\\')))
443 {
444 // Correct processing file path on Windows OS "<disk>:/" or "<disk>:\"
445 eina_strbuf_append_char(buf, *pe);
446 pe++;
447 eina_strbuf_append_char(buf, *pe);
448 pe++;
449 }
450 #endif
451 else if ((*pe == ':') || (!*pe))
452 { // p -> pe == 'name:'
453 if (pe > p)
454 {
455 const char *nn;
456
457 nn = eina_stringshare_add(eina_strbuf_string_get(buf));
458 if (nn) names = eina_list_append(names, nn);
459 eina_strbuf_reset(buf);
460 }
461 if (!*pe) break;
462 p = pe + 1;
463 pe = p;
464 }
465 else
466 {
467 eina_strbuf_append_char(buf, *pe);
468 pe++;
469 }
470 }
471
472 eina_strbuf_free(buf);
473 }
474 p = eina_list_data_get(eina_list_last(names));
475 if ((!p) || (strcmp(p, "default")))
476 {
477 p = eina_stringshare_add("default");
478 if (p) names = eina_list_append(names, p);
479 }
480 if (th->cache) eina_hash_free(th->cache);
481 th->cache = eina_hash_string_superfast_new(EINA_FREE_CB(eina_file_close));
482 if (th->cache_data) eina_hash_free(th->cache_data);
483 th->cache_data = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
484 if (th->cache_style_load_failed) eina_hash_free(th->cache_style_load_failed);
485 th->cache_style_load_failed = eina_hash_string_superfast_new(NULL);
486 _elm_theme_file_clean(&th->themes);
487 th->theme_items = eina_list_free(th->theme_items);
488
489 EINA_LIST_FREE(names, p)
490 _elm_theme_file_item_add(&th->themes, p, EINA_FALSE, EINA_TRUE);
491 elm_theme_get(th);
492 }
493
494 void
_elm_theme_init(void)495 _elm_theme_init(void)
496 {
497 Eo *theme_default_obj;
498 Efl_Ui_Theme_Data *td;
499
500 if (theme_default) return;
501
502 theme_default_obj = efl_add(EFL_UI_THEME_CLASS, efl_main_loop_get());
503 td = efl_data_scope_get(theme_default_obj, EFL_UI_THEME_CLASS);
504 theme_default = td->th;
505 }
506
507 void
_elm_theme_shutdown(void)508 _elm_theme_shutdown(void)
509 {
510 Elm_Theme *th;
511
512 while (themes)
513 {
514 th = eina_list_data_get(themes);
515
516 /* In theme object destructor, theme is deallocated and the theme is
517 * removed from themes list in _elm_theme_free_internal().
518 * Consequently, themes list is updated.
519 */
520 efl_del(th->eo_theme);
521 }
522 }
523
524 EAPI Elm_Theme *
elm_theme_new(void)525 elm_theme_new(void)
526 {
527 Eo *obj = efl_add(EFL_UI_THEME_CLASS, efl_main_loop_get());
528 Efl_Ui_Theme_Data *td = efl_data_scope_get(obj, EFL_UI_THEME_CLASS);
529 return td->th;
530 }
531
532 EAPI void
elm_theme_free(Elm_Theme * th)533 elm_theme_free(Elm_Theme *th)
534 {
535 EINA_SAFETY_ON_NULL_RETURN(th);
536
537 /* Destructs theme object and theme is deallocated in
538 * _elm_theme_free_internal() in theme object desctructor.
539 */
540 if (efl_ref_count(th->eo_theme) > 1)
541 efl_unref(th->eo_theme);
542 else
543 efl_del(th->eo_theme);
544 }
545
546 static void
elm_theme_files_copy(Eina_Inlist ** dst,Eina_Inlist ** src)547 elm_theme_files_copy(Eina_Inlist **dst, Eina_Inlist **src)
548 {
549 Elm_Theme_File *etf, *cpy;
550
551 EINA_INLIST_FOREACH(*src, etf)
552 {
553 cpy = malloc(sizeof(Elm_Theme_File));
554 EINA_SAFETY_ON_NULL_RETURN(cpy);
555 cpy->item = eina_stringshare_ref(etf->item);
556 cpy->handle = eina_file_dup(etf->handle);
557 *dst = eina_inlist_append(*dst, EINA_INLIST_GET(cpy));
558 }
559 }
560
561 EAPI void
elm_theme_copy(Elm_Theme * th,Elm_Theme * thdst)562 elm_theme_copy(Elm_Theme *th, Elm_Theme *thdst)
563 {
564 Eo *thdst_obj;
565 Efl_Ui_Theme_Data *thdst_td;
566
567 if (!th) th = theme_default;
568 if (!th) return;
569 if (!thdst) thdst = theme_default;
570 if (!thdst) return;
571
572 //Clear the given theme and restore the relationship with theme object.
573 thdst_obj = thdst->eo_theme;
574 _elm_theme_clear(thdst);
575 thdst->eo_theme = thdst_obj;
576 thdst_td = efl_data_scope_get(thdst_obj, EFL_UI_THEME_CLASS);
577 thdst_td->th = thdst;
578
579 if (th->ref_theme)
580 {
581 thdst->ref_theme = th->ref_theme;
582 thdst->ref_theme->referrers =
583 eina_list_append(thdst->ref_theme->referrers, thdst);
584 efl_ref(thdst->ref_theme->eo_theme);
585 }
586 elm_theme_files_copy(&thdst->overlay, &th->overlay);
587 elm_theme_files_copy(&thdst->themes, &th->themes);
588 elm_theme_files_copy(&thdst->extension, &th->extension);
589
590 if (th->theme) thdst->theme = eina_stringshare_add(th->theme);
591 elm_theme_flush(thdst);
592 }
593
594 EAPI void
elm_theme_ref_set(Elm_Theme * th,Elm_Theme * thref)595 elm_theme_ref_set(Elm_Theme *th, Elm_Theme *thref)
596 {
597 Eo *th_obj;
598 Efl_Ui_Theme_Data *th_td;
599
600 if (!th) th = theme_default;
601 if (!th) return;
602 if (!thref) thref = theme_default;
603 if (!thref) return;
604 if (th->ref_theme == thref) return;
605
606 //Clear the given theme and restore the relationship with theme object.
607 th_obj = th->eo_theme;
608 _elm_theme_clear(th);
609 th->eo_theme = th_obj;
610 th_td = efl_data_scope_get(th_obj, EFL_UI_THEME_CLASS);
611 th_td->th = th;
612
613 if (thref)
614 {
615 thref->referrers = eina_list_append(thref->referrers, th);
616 efl_ref(thref->eo_theme);
617 }
618 th->ref_theme = thref;
619 elm_theme_flush(th);
620 }
621
622 EAPI Elm_Theme *
elm_theme_ref_get(const Elm_Theme * th)623 elm_theme_ref_get(const Elm_Theme *th)
624 {
625 if (!th) th = theme_default;
626 if (!th) return NULL;
627 return th->ref_theme;
628 }
629
630 EAPI Elm_Theme *
elm_theme_default_get(void)631 elm_theme_default_get(void)
632 {
633 return theme_default;
634 }
635
636 EAPI void
elm_theme_overlay_add(Elm_Theme * th,const char * item)637 elm_theme_overlay_add(Elm_Theme *th, const char *item)
638 {
639 if (!th) th = theme_default;
640 if (!th) return;
641 efl_ui_theme_overlay_add(th->eo_theme, item);
642 }
643
644 EAPI void
elm_theme_overlay_del(Elm_Theme * th,const char * item)645 elm_theme_overlay_del(Elm_Theme *th, const char *item)
646 {
647 if (!th) th = theme_default;
648 if (!th) return;
649 efl_ui_theme_overlay_del(th->eo_theme, item);
650 }
651
652 EAPI void
elm_theme_overlay_mmap_add(Elm_Theme * th,const Eina_File * f)653 elm_theme_overlay_mmap_add(Elm_Theme *th, const Eina_File *f)
654 {
655 Eina_File *file = eina_file_dup(f);
656
657 if (!th) th = theme_default;
658 if (!th)
659 {
660 eina_file_close(file); // close matching open (finalize expected to consume eina file) OK
661 return;
662 }
663 th->overlay_items = eina_list_free(th->overlay_items);
664 _elm_theme_item_finalize(&th->overlay, eina_file_filename_get(file), file, EINA_TRUE, EINA_FALSE);
665 elm_theme_flush(th);
666 }
667
668 EAPI void
elm_theme_overlay_mmap_del(Elm_Theme * th,const Eina_File * f)669 elm_theme_overlay_mmap_del(Elm_Theme *th, const Eina_File *f)
670 {
671 if (!f) return;
672 if (!th) th = theme_default;
673 if (!th) return;
674 th->overlay_items = eina_list_free(th->overlay_items);
675 _elm_theme_file_mmap_del(&th->overlay, f);
676 elm_theme_flush(th);
677 }
678
679 EAPI const Eina_List *
elm_theme_overlay_list_get(const Elm_Theme * th)680 elm_theme_overlay_list_get(const Elm_Theme *th)
681 {
682 if (!th) th = theme_default;
683 if (!th) return NULL;
684 if (!th->overlay_items)
685 {
686 Elm_Theme_File *etf;
687
688 EINA_INLIST_FOREACH(th->overlay, etf)
689 ((Elm_Theme*)th)->overlay_items = eina_list_append(th->overlay_items, etf->item);
690 }
691 return th->overlay_items;
692 }
693
694 EAPI void
elm_theme_extension_add(Elm_Theme * th,const char * item)695 elm_theme_extension_add(Elm_Theme *th, const char *item)
696 {
697 if (!th) th = theme_default;
698 if (!th) return;
699 efl_ui_theme_extension_add(th->eo_theme, item);
700 }
701
702 EAPI void
elm_theme_extension_del(Elm_Theme * th,const char * item)703 elm_theme_extension_del(Elm_Theme *th, const char *item)
704 {
705 if (!th) th = theme_default;
706 if (!th) return;
707 efl_ui_theme_extension_del(th->eo_theme, item);
708 }
709
710 EAPI void
elm_theme_extension_mmap_add(Elm_Theme * th,const Eina_File * f)711 elm_theme_extension_mmap_add(Elm_Theme *th, const Eina_File *f)
712 {
713 Eina_File *file;
714
715 if (!f) return;
716 if (!th) th = theme_default;
717 if (!th) return;
718 file = eina_file_dup(f);
719 th->extension_items = eina_list_free(th->extension_items);
720 _elm_theme_item_finalize(&th->extension, eina_file_filename_get(file), file, EINA_FALSE, EINA_FALSE);
721 elm_theme_flush(th);
722 }
723
724 EAPI void
elm_theme_extension_mmap_del(Elm_Theme * th,const Eina_File * f)725 elm_theme_extension_mmap_del(Elm_Theme *th, const Eina_File *f)
726 {
727 if (!f) return;
728 if (!th) th = theme_default;
729 if (!th) return;
730 th->extension_items = eina_list_free(th->extension_items);
731 _elm_theme_file_mmap_del(&th->extension, f);
732 elm_theme_flush(th);
733 }
734
735 EAPI const Eina_List *
elm_theme_extension_list_get(const Elm_Theme * th)736 elm_theme_extension_list_get(const Elm_Theme *th)
737 {
738 if (!th) th = theme_default;
739 if (!th) return NULL;
740 if (!th->extension_items)
741 {
742 Elm_Theme_File *etf;
743
744 EINA_INLIST_FOREACH(th->extension, etf)
745 ((Elm_Theme*)th)->extension_items = eina_list_append(th->extension_items, etf->item);
746 }
747 return th->extension_items;
748 }
749
750 EAPI void
elm_theme_set(Elm_Theme * th,const char * theme)751 elm_theme_set(Elm_Theme *th, const char *theme)
752 {
753 if (!th) th = theme_default;
754 if (!th) return;
755 _elm_theme_parse(th, theme);
756 ELM_SAFE_FREE(th->theme, eina_stringshare_del);
757 elm_theme_flush(th);
758 if (th == theme_default)
759 eina_stringshare_replace(&_elm_config->theme, theme);
760 }
761
762 EAPI const char *
elm_theme_get(Elm_Theme * th)763 elm_theme_get(Elm_Theme *th)
764 {
765 if (!th) th = theme_default;
766 if (!th) return NULL;
767 if (!th->theme)
768 {
769 Eina_Strbuf *buf;
770 Elm_Theme_File *etf;
771 const char *f;
772
773 buf = eina_strbuf_new();
774 EINA_INLIST_FOREACH(th->themes, etf)
775 {
776 f = etf->item;
777 while (*f)
778 {
779 if (*f == ':')
780 eina_strbuf_append_char(buf, '\\');
781 eina_strbuf_append_char(buf, *f);
782
783 f++;
784 }
785 if (EINA_INLIST_GET(etf)->next)
786 eina_strbuf_append_char(buf, ':');
787 }
788 th->theme = eina_stringshare_add(eina_strbuf_string_get(buf));
789 eina_strbuf_free(buf);
790 }
791 return th->theme;
792 }
793
794 EAPI const Eina_List *
elm_theme_list_get(const Elm_Theme * th)795 elm_theme_list_get(const Elm_Theme *th)
796 {
797 if (!th) th = theme_default;
798 if (!th) return NULL;
799 if (!th->theme_items)
800 {
801 Elm_Theme_File *etf;
802
803 EINA_INLIST_FOREACH(th->themes, etf)
804 ((Elm_Theme*)th)->theme_items = eina_list_append(th->theme_items, etf->item);
805 }
806 return th->theme_items;
807 }
808
809 EAPI char *
elm_theme_list_item_path_get(const char * f,Eina_Bool * in_search_path)810 elm_theme_list_item_path_get(const char *f, Eina_Bool *in_search_path)
811 {
812 static const char *home = NULL;
813 char buf[PATH_MAX];
814
815 if (!f)
816 {
817 if (in_search_path) *in_search_path = EINA_FALSE;
818 return NULL;
819 }
820
821 if (!home)
822 {
823 home = eina_environment_home_get();
824 if (!home) home = "";
825 }
826
827 if ((f[0] == '/') || ((f[0] == '.') && (f[1] == '/')) ||
828 ((f[0] == '.') && (f[1] == '.') && (f[2] == '/')) ||
829 ((isalpha(f[0])) && (f[1] == ':')))
830 {
831 if (in_search_path) *in_search_path = EINA_FALSE;
832 return strdup(f);
833 }
834 else if (((f[0] == '~') && (f[1] == '/')))
835 {
836 if (in_search_path) *in_search_path = EINA_FALSE;
837 snprintf(buf, sizeof(buf), "%s/%s", home, f + 2);
838 return strdup(buf);
839 }
840 snprintf(buf, sizeof(buf), "%s/"ELEMENTARY_BASE_DIR"/themes/%s.edj", home, f);
841 if (ecore_file_exists(buf))
842 {
843 if (in_search_path) *in_search_path = EINA_TRUE;
844 return strdup(buf);
845 }
846
847 snprintf(buf, sizeof(buf), "%s/themes/%s.edj", _elm_data_dir, f);
848 if (ecore_file_exists(buf))
849 {
850 if (in_search_path) *in_search_path = EINA_TRUE;
851 return strdup(buf);
852 }
853
854 if (in_search_path) *in_search_path = EINA_FALSE;
855 return NULL;
856 }
857
858 EAPI void
elm_theme_flush(Elm_Theme * th)859 elm_theme_flush(Elm_Theme *th)
860 {
861 if (!th) th = theme_default;
862 if (!th) return;
863 if (th->cache) eina_hash_free(th->cache);
864 th->cache = eina_hash_string_superfast_new(EINA_FREE_CB(eina_file_close));
865 if (th->cache_data) eina_hash_free(th->cache_data);
866 th->cache_data = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
867 if (th->cache_style_load_failed) eina_hash_free(th->cache_style_load_failed);
868 th->cache_style_load_failed = eina_hash_string_superfast_new(NULL);
869 _elm_win_rescale(th, EINA_TRUE);
870 _elm_ews_wm_rescale(th, EINA_TRUE);
871 if (th->referrers)
872 {
873 Eina_List *l;
874 Elm_Theme *th2;
875
876 EINA_LIST_FOREACH(th->referrers, l, th2) elm_theme_flush(th2);
877 }
878 }
879
880 EAPI void
elm_theme_full_flush(void)881 elm_theme_full_flush(void)
882 {
883 Eina_List *l;
884 Elm_Theme *th;
885
886 EINA_LIST_FOREACH(themes, l, th)
887 {
888 elm_theme_flush(th);
889 }
890 elm_theme_flush(theme_default);
891 }
892
893 EAPI Eina_List *
elm_theme_name_available_list_new(void)894 elm_theme_name_available_list_new(void)
895 {
896 Eina_List *list = NULL;
897 Eina_List *dir, *l;
898 char buf[PATH_MAX], *file, *s, *th;
899 static const char *home = NULL;
900
901 if (!home)
902 {
903 home = eina_environment_home_get();
904 if (!home) home = "";
905 }
906
907 snprintf(buf, sizeof(buf), "%s/"ELEMENTARY_BASE_DIR"/themes", home);
908 dir = ecore_file_ls(buf);
909 EINA_LIST_FREE(dir, file)
910 {
911 snprintf(buf, sizeof(buf), "%s/"ELEMENTARY_BASE_DIR"/themes/%s", home, file);
912 if ((!ecore_file_is_dir(buf)) && (ecore_file_size(buf) > 0))
913 {
914 if (eina_str_has_extension(file, ".edj"))
915 {
916 th = strdup(file);
917 s = strrchr(th, '.');
918 *s = 0;
919 list = eina_list_append(list, th);
920 }
921 }
922 free(file);
923 }
924
925 snprintf(buf, sizeof(buf), "%s/themes", _elm_data_dir);
926 dir = ecore_file_ls(buf);
927 EINA_LIST_FREE(dir, file)
928 {
929 snprintf(buf, sizeof(buf), "%s/themes/%s", _elm_data_dir, file);
930 if ((!ecore_file_is_dir(buf)) && (ecore_file_size(buf) > 0))
931 {
932 if (eina_str_has_extension(file, ".edj"))
933 {
934 int dupp;
935
936 th = strdup(file);
937 s = strrchr(th, '.');
938 *s = 0;
939 dupp = 0;
940 EINA_LIST_FOREACH(list, l, s)
941 {
942 if (!strcmp(s, th))
943 {
944 dupp = 1;
945 break;
946 }
947 }
948 if (dupp) free(th);
949 else list = eina_list_append(list, th);
950 }
951 }
952 free(file);
953 }
954 list = eina_list_sort(list, 0, EINA_COMPARE_CB(strcasecmp));
955 return list;
956 }
957
958 EAPI void
elm_theme_name_available_list_free(Eina_List * list)959 elm_theme_name_available_list_free(Eina_List *list)
960 {
961 char *s;
962 EINA_LIST_FREE(list, s) free(s);
963 }
964
965 EAPI void
elm_object_theme_set(Evas_Object * obj,Elm_Theme * th)966 elm_object_theme_set(Evas_Object *obj, Elm_Theme *th)
967 {
968 EINA_SAFETY_ON_NULL_RETURN(obj);
969 elm_widget_theme_set(obj, th);
970 }
971
972 EAPI Elm_Theme *
elm_object_theme_get(const Evas_Object * obj)973 elm_object_theme_get(const Evas_Object *obj)
974 {
975 EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
976 return elm_widget_theme_get(obj);
977 }
978
979 EAPI const char *
elm_theme_data_get(Elm_Theme * th,const char * key)980 elm_theme_data_get(Elm_Theme *th, const char *key)
981 {
982 if (!th) th = theme_default;
983 if (!th) return NULL;
984 return _elm_theme_data_find(th, key);
985 }
986
987 EAPI const char *
elm_theme_group_path_find(Elm_Theme * th,const char * group)988 elm_theme_group_path_find(Elm_Theme *th, const char *group)
989 {
990 EINA_SAFETY_ON_NULL_RETURN_VAL(group, NULL);
991 Eina_File *th_file = NULL;
992 if (!th) th = theme_default;
993 if (!th) return NULL;
994
995 th_file = _elm_theme_group_file_find(th, group);
996 if (th_file)
997 return eina_file_filename_get(th_file);
998 return NULL;
999 }
1000
1001 static Eina_List *
_elm_theme_file_group_base_list(Eina_List * list,Eina_Inlist * handles,const char * base,int len)1002 _elm_theme_file_group_base_list(Eina_List *list,
1003 Eina_Inlist *handles,
1004 const char *base, int len)
1005 {
1006 Eina_Stringshare *c, *c2;
1007 Eina_List *coll;
1008 Eina_List *in, *ll;
1009 Elm_Theme_File *etf;
1010
1011 EINA_INLIST_FOREACH(handles, etf)
1012 {
1013 coll = edje_mmap_collection_list(etf->handle);
1014 EINA_LIST_FOREACH(coll, ll, c)
1015 {
1016 // if base == start of collection str
1017 if (!strncmp(c, base, len))
1018 {
1019 // check if already in list
1020 EINA_LIST_FOREACH(list, in, c2)
1021 if (c == c2)
1022 break;
1023
1024 // if not already in list append shared str to list
1025 if (!in)
1026 list = eina_list_append(list, eina_stringshare_ref(c));
1027 }
1028 }
1029 edje_mmap_collection_list_free(coll);
1030 }
1031
1032 return list;
1033 }
1034
1035 EAPI Eina_List *
elm_theme_group_base_list(Elm_Theme * th,const char * base)1036 elm_theme_group_base_list(Elm_Theme *th, const char *base)
1037 {
1038 Eina_List *list;
1039 int len;
1040
1041 EINA_SAFETY_ON_NULL_RETURN_VAL(base, NULL);
1042 if (!th) th = theme_default;
1043 if (!th) return NULL;
1044
1045 // XXX: look results up in a hash for speed
1046 len = strlen(base);
1047 // go through all possible theme files and find collections that match
1048 list = _elm_theme_file_group_base_list(NULL, th->overlay,
1049 base, len);
1050 list = _elm_theme_file_group_base_list(list, th->themes,
1051 base, len);
1052 list = _elm_theme_file_group_base_list(list, th->extension,
1053 base, len);
1054
1055 // sort the list nicely at the end
1056 list = eina_list_sort(list, 0, EINA_COMPARE_CB(strcmp));
1057 // XXX: store results in hash for fast lookup...
1058 return list;
1059 }
1060
1061 EAPI const char *
elm_theme_system_dir_get(void)1062 elm_theme_system_dir_get(void)
1063 {
1064 static char *path = NULL;
1065 char buf[PATH_MAX];
1066
1067 if (path) return path;
1068
1069 snprintf(buf, sizeof(buf), "%s/themes", _elm_data_dir);
1070 path = strdup(buf);
1071
1072 return path;
1073 }
1074
1075 EAPI const char *
elm_theme_user_dir_get(void)1076 elm_theme_user_dir_get(void)
1077 {
1078 static char *path = NULL;
1079 char buf[PATH_MAX];
1080 const char *home;
1081
1082 if (path) return path;
1083
1084 home = eina_environment_home_get();
1085 snprintf(buf, sizeof(buf), "%s/"ELEMENTARY_BASE_DIR"/themes", home);
1086 path = strdup(buf);
1087
1088 return path;
1089 }
1090
1091 /* Allocates memory for theme and appends the theme to themes list. */
1092 static Elm_Theme *
_elm_theme_new_internal(Eo * obj)1093 _elm_theme_new_internal(Eo *obj)
1094 {
1095 Elm_Theme *th = calloc(1, sizeof(Elm_Theme));
1096 if (!th) return NULL;
1097 _elm_theme_file_item_add(&th->themes, "default", EINA_FALSE, EINA_TRUE);
1098 themes = eina_list_append(themes, th);
1099 th->eo_theme = obj;
1100 return th;
1101 }
1102
1103 EOLIAN static Eo *
_efl_ui_theme_efl_object_constructor(Eo * obj,Efl_Ui_Theme_Data * pd)1104 _efl_ui_theme_efl_object_constructor(Eo *obj, Efl_Ui_Theme_Data *pd)
1105 {
1106 obj = efl_constructor(efl_super(obj, EFL_UI_THEME_CLASS));
1107
1108 pd->th = _elm_theme_new_internal(obj);
1109
1110 return obj;
1111 }
1112
1113 /* Removes the given theme from themes list and deallocates the given theme. */
1114 static void
_elm_theme_free_internal(Elm_Theme * th)1115 _elm_theme_free_internal(Elm_Theme *th)
1116 {
1117 _elm_theme_clear(th);
1118 themes = eina_list_remove(themes, th);
1119 free(th);
1120 }
1121
1122 EOLIAN static void
_efl_ui_theme_efl_object_destructor(Eo * obj,Efl_Ui_Theme_Data * pd)1123 _efl_ui_theme_efl_object_destructor(Eo *obj, Efl_Ui_Theme_Data *pd)
1124 {
1125 Eina_Bool is_theme_default = EINA_FALSE;
1126
1127 if (pd->th == theme_default)
1128 is_theme_default = EINA_TRUE;
1129
1130 _elm_theme_free_internal(pd->th);
1131
1132 if (is_theme_default) theme_default = NULL;
1133
1134 efl_destructor(efl_super(obj, EFL_UI_THEME_CLASS));
1135 }
1136
1137 EOLIAN static Efl_Ui_Theme *
_efl_ui_theme_default_get(void)1138 _efl_ui_theme_default_get(void)
1139 {
1140 if (theme_default)
1141 return theme_default->eo_theme;
1142
1143 return NULL;
1144 }
1145
1146 EOLIAN static void
_efl_ui_theme_extension_add(Eo * obj EINA_UNUSED,Efl_Ui_Theme_Data * pd,const char * item)1147 _efl_ui_theme_extension_add(Eo *obj EINA_UNUSED, Efl_Ui_Theme_Data *pd, const char *item)
1148 {
1149 if (!item) return;
1150 pd->th->extension_items = eina_list_free(pd->th->extension_items);
1151 _elm_theme_file_item_add(&pd->th->extension, item, EINA_FALSE, EINA_FALSE);
1152 elm_theme_flush(pd->th);
1153 }
1154
1155 EOLIAN static void
_efl_ui_theme_extension_del(Eo * obj EINA_UNUSED,Efl_Ui_Theme_Data * pd,const char * item)1156 _efl_ui_theme_extension_del(Eo *obj EINA_UNUSED, Efl_Ui_Theme_Data *pd, const char *item)
1157 {
1158 if (!item) return;
1159 pd->th->extension_items = eina_list_free(pd->th->extension_items);
1160 _elm_theme_file_item_del(&pd->th->extension, item);
1161 elm_theme_flush(pd->th);
1162 }
1163
1164 EOLIAN static void
_efl_ui_theme_overlay_add(Eo * obj EINA_UNUSED,Efl_Ui_Theme_Data * pd,const char * item)1165 _efl_ui_theme_overlay_add(Eo *obj EINA_UNUSED, Efl_Ui_Theme_Data *pd, const char *item)
1166 {
1167 if (!item) return;
1168 pd->th->overlay_items = eina_list_free(pd->th->overlay_items);
1169 _elm_theme_file_item_add(&pd->th->overlay, item, EINA_TRUE, EINA_FALSE);
1170 elm_theme_flush(pd->th);
1171 }
1172
1173 EOLIAN static void
_efl_ui_theme_overlay_del(Eo * obj EINA_UNUSED,Efl_Ui_Theme_Data * pd,const char * item)1174 _efl_ui_theme_overlay_del(Eo *obj EINA_UNUSED, Efl_Ui_Theme_Data *pd, const char *item)
1175 {
1176 if (!item) return;
1177 pd->th->overlay_items = eina_list_free(pd->th->overlay_items);
1178 _elm_theme_file_item_del(&pd->th->overlay, item);
1179 elm_theme_flush(pd->th);
1180 }
1181
1182 #include "efl_ui_theme.eo.c"
1183