9 # define alloca __builtin_alloca
11 # define alloca __alloca
12 #elif defined _MSC_VER
14 # define alloca _alloca
20 void *alloca (size_t);
24 #include <Ecore_File.h>
26 /* define macros and variable for using the eina logging system */
27 #define EFREET_MODULE_LOG_DOM _efreet_icon_log_dom
28 static int _efreet_icon_log_dom = -1;
31 #include "efreet_private.h"
33 static const char *efreet_icon_deprecated_user_dir = NULL;
34 static const char *efreet_icon_user_dir = NULL;
35 static Eina_List *efreet_icon_extensions = NULL;
36 static Eina_List *efreet_extra_icon_dirs = NULL;
38 static Eina_Hash *change_monitors = NULL;
40 typedef struct Efreet_Icon_Cache Efreet_Icon_Cache;
41 struct Efreet_Icon_Cache
48 static char *efreet_icon_remove_extension(const char *icon);
50 static Efreet_Icon *efreet_icon_new(const char *path);
51 static void efreet_icon_populate(Efreet_Icon *icon, const char *file);
53 static const char *efreet_icon_lookup_icon(Efreet_Cache_Icon *icon, unsigned int size);
54 static const char *efreet_icon_list_lookup_icon(Efreet_Icon_Theme *theme, Eina_List *icons, unsigned int size);
55 static int efreet_icon_size_match(Efreet_Cache_Icon_Element *elem, unsigned int size);
56 static double efreet_icon_size_distance(Efreet_Cache_Icon_Element *elem, unsigned int size);
57 static const char *efreet_icon_lookup_path(Efreet_Cache_Icon_Element *elem);
58 static const char *efreet_icon_lookup_path_path(Efreet_Cache_Icon_Element *elem, const char *path);
59 static const char *efreet_icon_fallback_lookup_path(Efreet_Cache_Fallback_Icon *icon);
60 static const char *efreet_icon_fallback_lookup_path_path(Efreet_Cache_Fallback_Icon *icon,
63 static void efreet_icon_changes_listen(void);
64 static void efreet_icon_changes_monitor_add(const char *path);
65 static void efreet_icon_changes_cb(void *data, Ecore_File_Monitor *em,
66 Ecore_File_Event event, const char *path);
71 * @return Returns 1 on success or 0 on failure
72 * @brief Initializes the icon system
75 efreet_icon_init(void)
77 const char *default_exts[] = {".png", ".xpm", ".svg", NULL};
80 _efreet_icon_log_dom = eina_log_domain_register
81 ("efreet_icon", EFREET_DEFAULT_LOG_COLOR);
82 if (_efreet_icon_log_dom < 0)
85 /* setup the default extension list */
86 for (i = 0; default_exts[i]; i++)
87 efreet_icon_extensions = eina_list_append(efreet_icon_extensions, eina_stringshare_add(default_exts[i]));
89 efreet_icon_changes_listen();
91 efreet_extra_icon_dirs = NULL;
98 * @return Returns no value
99 * @brief Shuts down the icon system
102 efreet_icon_shutdown(void)
104 IF_RELEASE(efreet_icon_user_dir);
105 IF_RELEASE(efreet_icon_deprecated_user_dir);
107 IF_FREE_LIST(efreet_icon_extensions, eina_stringshare_del);
108 efreet_extra_icon_dirs = eina_list_free(efreet_extra_icon_dirs);
110 eina_log_domain_unregister(_efreet_icon_log_dom);
111 _efreet_icon_log_dom = -1;
112 IF_FREE_HASH(change_monitors);
116 efreet_icon_deprecated_user_dir_get(void)
122 if (efreet_icon_deprecated_user_dir) return efreet_icon_deprecated_user_dir;
124 user = efreet_home_dir_get();
125 len = strlen(user) + strlen("/.icons") + 1;
127 snprintf(tmp, len, "%s/.icons", user);
129 efreet_icon_deprecated_user_dir = eina_stringshare_add_length(tmp, len - 1);
131 return efreet_icon_deprecated_user_dir;
135 efreet_icon_user_dir_get(void)
141 if (efreet_icon_user_dir) return efreet_icon_user_dir;
143 user = efreet_data_home_get();
144 len = strlen(user) + strlen("/icons") + 1;
146 snprintf(tmp, len, "%s/icons", user);
148 efreet_icon_user_dir = eina_stringshare_add_length(tmp, len - 1);
150 return efreet_icon_user_dir;
154 efreet_icon_extension_add(const char *ext)
158 EINA_SAFETY_ON_NULL_RETURN(ext);
160 ext = eina_stringshare_add(ext);
162 if ((l = eina_list_data_find_list(efreet_icon_extensions, ext)))
164 efreet_icon_extensions = eina_list_promote_list(efreet_icon_extensions, l);
165 eina_stringshare_del(ext);
168 efreet_icon_extensions = eina_list_prepend(efreet_icon_extensions, ext);
172 efreet_icon_extra_list_get(void)
174 return &efreet_extra_icon_dirs;
178 efreet_icon_extensions_list_get(void)
180 return efreet_icon_extensions;
184 efreet_icon_theme_list_get(void)
186 return efreet_cache_icon_theme_list();
189 EAPI Efreet_Icon_Theme *
190 efreet_icon_theme_find(const char *theme_name)
192 if (!theme_name) return NULL;
194 return efreet_cache_icon_theme_find(theme_name);
199 * @param icon The icon name to strip extension
200 * @return Extension removed if in list of extensions, else untouched.
201 * @brief Removes extension from icon name if in list of extensions.
204 efreet_icon_remove_extension(const char *icon)
207 char *tmp = NULL, *ext = NULL;
209 if (!icon) return NULL;
212 ext = strrchr(tmp, '.');
216 EINA_LIST_FOREACH(efreet_icon_extensions, l, ext2)
218 if (!strcmp(ext, ext2))
221 WRN("[Efreet]: Requesting an icon with an extension: %s",
234 efreet_icon_path_find(const char *theme_name, const char *icon, unsigned int size)
237 const char *value = NULL;
238 Efreet_Icon_Theme *theme;
240 EINA_SAFETY_ON_NULL_RETURN_VAL(icon, NULL);
242 theme = efreet_icon_theme_find(theme_name);
245 tmp = efreet_icon_remove_extension(icon);
246 if (!tmp) return NULL;
253 Efreet_Cache_Icon *cache;
254 cache = efreet_cache_icon_find(theme, tmp);
255 value = efreet_icon_lookup_icon(cache, size);
256 if (!value) INF("lookup for `%s` failed in theme `%s` with %p.", icon, theme_name, cache);
259 /* we didn't find the icon in the theme or in the inherited directories
260 * then just look for a non theme icon
264 Efreet_Cache_Fallback_Icon *cache;
266 cache = efreet_cache_icon_fallback_find(tmp);
267 value = efreet_icon_fallback_lookup_path(cache);
268 if (!value) INF("lookup for `%s` failed in fallback too with %p.", icon, cache);
278 efreet_icon_list_find(const char *theme_name, Eina_List *icons,
282 Eina_List *tmps = NULL;
283 const char *icon = NULL;
284 const char *value = NULL;
286 Efreet_Icon_Theme *theme;
288 EINA_SAFETY_ON_NULL_RETURN_VAL(icons, NULL);
290 theme = efreet_icon_theme_find(theme_name);
293 EINA_LIST_FOREACH(icons, l, icon)
295 data = efreet_icon_remove_extension(icon);
296 if (!data) return NULL;
297 tmps = eina_list_append(tmps, data);
305 Eina_List *tmps2 = NULL;
306 Efreet_Cache_Icon *cache;
308 EINA_LIST_FOREACH(tmps, l, icon)
310 cache = efreet_cache_icon_find(theme, icon);
313 /* If the icon is in the asked for theme, return it */
314 if (!strcmp(cache->theme, theme->name.internal))
316 value = efreet_icon_lookup_icon(cache, size);
320 tmps2 = eina_list_append(tmps2, cache);
326 value = efreet_icon_list_lookup_icon(theme, tmps2, size);
327 eina_list_free(tmps2);
331 /* we didn't find the icons in the theme or in the inherited directories
332 * then just look for a non theme icon
336 Efreet_Cache_Fallback_Icon *cache;
337 EINA_LIST_FOREACH(tmps, l, icon)
339 cache = efreet_cache_icon_fallback_find(icon);
340 value = efreet_icon_fallback_lookup_path(cache);
347 EINA_LIST_FREE(tmps, data)
355 efreet_icon_find(const char *theme_name, const char *icon, unsigned int size)
359 EINA_SAFETY_ON_NULL_RETURN_VAL(icon, NULL);
361 path = efreet_icon_path_find(theme_name, icon, size);
366 ic = efreet_icon_new(path);
375 * @return Returns a new Efreet_Icon struct on success or NULL on failure
376 * @brief Creates a new Efreet_Icon struct
379 efreet_icon_new(const char *path)
384 icon = NEW(Efreet_Icon, 1);
385 if (!icon) return NULL;
386 icon->path = eina_stringshare_add(path);
388 /* load the .icon file if it's available */
389 p = strrchr(icon->path, '.');
392 char ico_path[PATH_MAX];
396 snprintf(ico_path, sizeof(ico_path), "%s.icon", icon->path);
399 if (ecore_file_exists(ico_path))
400 efreet_icon_populate(icon, ico_path);
407 file = ecore_file_file_get(icon->path);
408 p = strrchr(icon->path, '.');
410 icon->name = eina_stringshare_add(file);
418 efreet_icon_free(Efreet_Icon *icon)
423 if (icon->ref_count > 0) return;
425 IF_RELEASE(icon->path);
426 IF_RELEASE(icon->name);
427 IF_FREE_LIST(icon->attach_points, free);
434 * @param icon The icon to populate
435 * @param file The file to populate from
436 * @return Returns no value
437 * @brief Tries to populate the icon information from the given file
440 efreet_icon_populate(Efreet_Icon *icon, const char *file)
445 ini = efreet_ini_new(file);
449 efreet_ini_free(ini);
453 efreet_ini_section_set(ini, "Icon Data");
454 tmp = efreet_ini_localestring_get(ini, "DisplayName");
455 if (tmp) icon->name = eina_stringshare_add(tmp);
457 tmp = efreet_ini_string_get(ini, "EmbeddedTextRectangle");
465 len = strlen(tmp) + 1;
469 for (i = 0; i < 4; i++)
487 icon->has_embedded_text_rectangle = 1;
488 icon->embedded_text_rectangle.x0 = points[0];
489 icon->embedded_text_rectangle.y0 = points[1];
490 icon->embedded_text_rectangle.x1 = points[2];
491 icon->embedded_text_rectangle.y1 = points[3];
494 tmp = efreet_ini_string_get(ini, "AttachPoints");
500 len = strlen(tmp) + 1;
506 Efreet_Icon_Point *point;
509 /* If this happens there is something wrong with the .icon file */
512 point = NEW(Efreet_Icon_Point, 1);
513 if (!point) goto error;
524 icon->attach_points = eina_list_append(icon->attach_points, point);
532 efreet_ini_free(ini);
536 efreet_icon_lookup_icon(Efreet_Cache_Icon *icon, unsigned int size)
538 const char *path = NULL;
539 double minimal_distance = INT_MAX;
540 unsigned int ret_size = 0;
543 if (!icon) return NULL;
545 /* search for allowed size == requested size */
546 for (i = 0; i < icon->icons_count; ++i)
548 if (!efreet_icon_size_match(icon->icons[i], size)) continue;
549 path = efreet_icon_lookup_path(icon->icons[i]);
550 if (path) return path;
553 /* search for any icon that matches */
554 for (i = 0; i < icon->icons_count; ++i)
556 const char *tmp = NULL;
559 distance = efreet_icon_size_distance(icon->icons[i], size);
560 if (distance > minimal_distance) continue;
562 if ((distance == minimal_distance) && (icon->icons[i]->normal < ret_size)) continue;
564 tmp = efreet_icon_lookup_path(icon->icons[i]);
569 minimal_distance = distance;
570 ret_size = icon->icons[i]->normal;
578 efreet_icon_list_lookup_icon(Efreet_Icon_Theme *theme, Eina_List *icons, unsigned int size)
580 const char *value = NULL;
581 Efreet_Cache_Icon *cache;
584 EINA_LIST_FOREACH(icons, l, cache)
586 if (!strcmp(cache->theme, theme->name.internal))
588 value = efreet_icon_lookup_icon(cache, size);
592 if (value) return value;
596 EINA_LIST_FOREACH(theme->inherits, l, parent)
598 Efreet_Icon_Theme *parent_theme;
600 parent_theme = efreet_icon_theme_find(parent);
601 if ((!parent_theme) || (parent_theme == theme)) continue;
603 value = efreet_icon_list_lookup_icon(parent_theme, icons, size);
607 /* if this isn't the hicolor theme, and we have no other fallbacks
609 else if (strcmp(theme->name.internal, "hicolor"))
611 Efreet_Icon_Theme *parent_theme;
613 parent_theme = efreet_icon_theme_find("hicolor");
615 value = efreet_icon_list_lookup_icon(parent_theme, icons, size);
621 efreet_icon_size_match(Efreet_Cache_Icon_Element *elem, unsigned int size)
623 if (elem->type == EFREET_ICON_SIZE_TYPE_FIXED)
624 return (elem->normal == size);
626 if ((elem->type == EFREET_ICON_SIZE_TYPE_SCALABLE) ||
627 (elem->type == EFREET_ICON_SIZE_TYPE_THRESHOLD))
628 return ((elem->min < size) && (size < elem->max));
634 efreet_icon_size_distance(Efreet_Cache_Icon_Element *elem, unsigned int size)
636 if (elem->type == EFREET_ICON_SIZE_TYPE_FIXED)
637 return (abs(elem->normal - size));
639 if ((elem->type == EFREET_ICON_SIZE_TYPE_SCALABLE) ||
640 (elem->type == EFREET_ICON_SIZE_TYPE_THRESHOLD))
643 if (size < elem->min)
644 return (elem->min - size);
645 if (elem->max < size)
646 return (size - elem->max);
648 if (size < elem->min)
649 return (elem->min / (double)size);
650 if (elem->max < size)
651 return (size / (double)elem->max);
659 efreet_icon_lookup_path(Efreet_Cache_Icon_Element *elem)
661 Eina_List *xdg_dirs, *l;
666 if (elem->paths_count == 1)
668 const char *pp, *ext;
670 pp = strrchr(elem->paths[0], '.');
671 if (!pp) return NULL;
673 EINA_LIST_FOREACH(efreet_icon_extensions, l, ext)
674 if (!strcmp(pp, ext))
675 return elem->paths[0];
679 path = efreet_icon_lookup_path_path(elem, efreet_icon_deprecated_user_dir_get());
680 if (path) return path;
682 path = efreet_icon_lookup_path_path(elem, efreet_icon_user_dir_get());
683 if (path) return path;
686 EINA_LIST_FOREACH(efreet_extra_icon_dirs, l, dir)
688 path = efreet_icon_lookup_path_path(elem, dir);
689 if (path) return path;
693 xdg_dirs = efreet_data_dirs_get();
695 EINA_LIST_FOREACH(xdg_dirs, l, dir)
697 snprintf(buf, sizeof(buf), "%s/icons", dir);
699 path = efreet_icon_lookup_path_path(elem, buf);
700 if (path) return path;
707 efreet_icon_lookup_path_path(Efreet_Cache_Icon_Element *elem, const char *path)
710 const char *ext, *pp;
716 for (i = 0; i < elem->paths_count; ++i)
718 if (strncmp(path, elem->paths[i], len)) continue;
719 pp = strrchr(elem->paths[i], '.');
722 EINA_LIST_FOREACH(efreet_icon_extensions, ll, ext)
723 if (!strcmp(pp, ext))
724 return elem->paths[i];
731 efreet_icon_fallback_lookup_path(Efreet_Cache_Fallback_Icon *icon)
734 Eina_List *xdg_dirs, *l;
738 if (!icon) return NULL;
740 if (icon->icons_count == 1)
742 const char *pp, *ext;
744 pp = strrchr(icon->icons[0], '.');
745 if (!pp) return NULL;
747 EINA_LIST_FOREACH(efreet_icon_extensions, l, ext)
748 if (!strcmp(pp, ext))
749 return icon->icons[0];
753 path = efreet_icon_fallback_lookup_path_path(icon, efreet_icon_deprecated_user_dir_get());
754 if (path) return path;
756 path = efreet_icon_fallback_lookup_path_path(icon, efreet_icon_user_dir_get());
757 if (path) return path;
759 EINA_LIST_FOREACH(efreet_extra_icon_dirs, l, dir)
761 path = efreet_icon_fallback_lookup_path_path(icon, dir);
762 if (path) return path;
765 xdg_dirs = efreet_data_dirs_get();
767 EINA_LIST_FOREACH(xdg_dirs, l, dir)
769 snprintf(buf, sizeof(buf), "%s/icons", dir);
771 path = efreet_icon_fallback_lookup_path_path(icon, buf);
772 if (path) return path;
776 EINA_LIST_FOREACH(xdg_dirs, l, dir)
778 snprintf(buf, sizeof(buf), "%s/pixmaps", dir);
780 path = efreet_icon_fallback_lookup_path_path(icon, buf);
781 if (path) return path;
785 path = efreet_icon_fallback_lookup_path_path(icon, "/usr/share/pixmaps");
786 if (path) return path;
792 efreet_icon_fallback_lookup_path_path(Efreet_Cache_Fallback_Icon *icon, const char *path)
795 const char *ext, *pp;
801 for (i = 0; i < icon->icons_count; ++i)
803 if (strncmp(path, icon->icons[i], len)) continue;
805 pp = strrchr(icon->icons[i], '.');
808 EINA_LIST_FOREACH(efreet_icon_extensions, ll, ext)
809 if (!strcmp(pp, ext))
810 return icon->icons[i];
817 efreet_icon_changes_listen(void)
824 if (!efreet_cache_update) return;
826 change_monitors = eina_hash_string_superfast_new(EINA_FREE_CB(ecore_file_monitor_del));
827 if (!change_monitors) return;
829 efreet_icon_changes_monitor_add(efreet_icon_deprecated_user_dir_get());
830 efreet_icon_changes_monitor_add(efreet_icon_user_dir_get());
831 EINA_LIST_FOREACH(efreet_extra_icon_dirs, l, dir)
832 efreet_icon_changes_monitor_add(dir);
834 xdg_dirs = efreet_data_dirs_get();
835 EINA_LIST_FOREACH(xdg_dirs, l, dir)
837 snprintf(buf, sizeof(buf), "%s/icons", dir);
838 efreet_icon_changes_monitor_add(buf);
842 EINA_LIST_FOREACH(xdg_dirs, l, dir)
844 snprintf(buf, sizeof(buf), "%s/pixmaps", dir);
845 efreet_icon_changes_monitor_add(buf);
849 efreet_icon_changes_monitor_add("/usr/share/pixmaps");
853 efreet_icon_changes_monitor_add(const char *path)
857 if (!realpath(path, rp)) return;
858 if (!ecore_file_is_dir(rp)) return;
859 if (eina_hash_find(change_monitors, rp)) return;
860 eina_hash_add(change_monitors, rp,
861 ecore_file_monitor_add(rp,
862 efreet_icon_changes_cb,
865 if (ecore_file_is_dir(rp))
870 it = eina_file_ls(rp);
872 EINA_ITERATOR_FOREACH(it, ent)
874 if (!realpath(ent, rp)) continue;
875 if (!ecore_file_is_dir(rp)) continue;
876 eina_hash_add(change_monitors, rp,
877 ecore_file_monitor_add(rp,
878 efreet_icon_changes_cb,
881 eina_iterator_free(it);
886 efreet_icon_changes_cb(void *data __UNUSED__, Ecore_File_Monitor *em __UNUSED__,
887 Ecore_File_Event event, const char *path)
889 /* TODO: If we get a stale symlink, we need to rerun cache creation */
892 case ECORE_FILE_EVENT_NONE:
896 case ECORE_FILE_EVENT_CREATED_FILE:
897 case ECORE_FILE_EVENT_DELETED_FILE:
898 case ECORE_FILE_EVENT_MODIFIED:
899 case ECORE_FILE_EVENT_CLOSED:
900 case ECORE_FILE_EVENT_DELETED_DIRECTORY:
901 case ECORE_FILE_EVENT_CREATED_DIRECTORY:
902 efreet_cache_icon_update();
905 case ECORE_FILE_EVENT_DELETED_SELF:
906 eina_hash_del_by_key(change_monitors, path);
907 efreet_cache_icon_update();