1 /* vim: set sw=4 ts=4 sts=4 et: */
3 #include "efreet_private.h"
5 static char *efreet_icon_deprecated_user_dir = NULL;
6 static char *efreet_icon_user_dir = NULL;
7 static Eina_Hash *efreet_icon_themes = NULL;
8 static Eina_List *efreet_icon_extensions = NULL;
9 static Eina_List *efreet_extra_icon_dirs = NULL;
10 static Eina_Hash *efreet_icon_cache = NULL;
12 static int efreet_icon_init_count = 0;
14 typedef struct Efreet_Icon_Cache Efreet_Icon_Cache;
15 struct Efreet_Icon_Cache
22 static char *efreet_icon_remove_extension(const char *icon);
23 static Efreet_Icon_Theme *efreet_icon_find_theme_check(const char *theme_name);
26 static char *efreet_icon_find_fallback(Efreet_Icon_Theme *theme,
29 static char *efreet_icon_list_find_fallback(Efreet_Icon_Theme *theme,
32 static char *efreet_icon_find_helper(Efreet_Icon_Theme *theme,
33 const char *icon, unsigned int size);
34 static char *efreet_icon_list_find_helper(Efreet_Icon_Theme *theme,
35 Eina_List *icons, unsigned int size);
36 static char *efreet_icon_lookup_icon(Efreet_Icon_Theme *theme,
37 const char *icon_name, unsigned int size);
38 static char *efreet_icon_fallback_icon(const char *icon_name);
39 static char *efreet_icon_fallback_dir_scan(const char *dir,
40 const char *icon_name);
42 static char *efreet_icon_lookup_directory(Efreet_Icon_Theme *theme,
43 Efreet_Icon_Theme_Directory *dir,
44 const char *icon_name);
45 static int efreet_icon_directory_size_distance(Efreet_Icon_Theme_Directory *dir,
47 static int efreet_icon_directory_size_match(Efreet_Icon_Theme_Directory *dir,
50 static Efreet_Icon *efreet_icon_new(const char *path);
51 static void efreet_icon_populate(Efreet_Icon *icon, const char *file);
53 static char *efreet_icon_lookup_directory_helper(Efreet_Icon_Theme_Directory *dir,
54 const char *path, const char *icon_name);
56 static Efreet_Icon_Theme *efreet_icon_theme_new(void);
57 static void efreet_icon_theme_free(Efreet_Icon_Theme *theme);
58 static void efreet_icon_theme_dir_scan_all(const char *theme_name);
59 static void efreet_icon_theme_dir_scan(const char *dir,
60 const char *theme_name);
61 static void efreet_icon_theme_dir_validity_check(void);
62 static void efreet_icon_theme_path_add(Efreet_Icon_Theme *theme,
64 static void efreet_icon_theme_index_read(Efreet_Icon_Theme *theme,
67 static Efreet_Icon_Theme_Directory *efreet_icon_theme_directory_new(Efreet_Ini *ini,
69 static void efreet_icon_theme_directory_free(Efreet_Icon_Theme_Directory *dir);
71 static void efreet_icon_theme_cache_check(Efreet_Icon_Theme *theme);
72 static int efreet_icon_theme_cache_check_dir(Efreet_Icon_Theme *theme,
75 static int efreet_icon_cache_find(Efreet_Icon_Cache *value, const char *key);
76 static void efreet_icon_cache_flush(Efreet_Icon_Theme *theme, Eina_List *list);
77 static void efreet_icon_cache_free(Efreet_Icon_Cache *value);
78 static char *efreet_icon_cache_check(Efreet_Icon_Theme *theme, const char *icon, unsigned int size);
79 static void efreet_icon_cache_add(Efreet_Icon_Theme *theme, const char *icon, unsigned int size, const char *value);
81 static Efreet_Icon_Theme *fake_null = NULL;
84 _efreet_icon_cache_list_destroy(Eina_List *list)
86 Efreet_Icon_Cache *cache;
88 EINA_LIST_FREE(list, cache)
89 efreet_icon_cache_free(cache);
94 * @return Returns 1 on success or 0 on failure
95 * @brief Initializes the icon system
98 efreet_icon_init(void)
100 if (efreet_icon_init_count++ > 0)
101 return efreet_icon_init_count;
103 if (!efreet_icon_themes)
105 const char *default_exts[] = {".png", ".xpm", NULL};
110 efreet_icon_init_count--;
114 /* setup the default extension list */
115 for (i = 0; default_exts[i] != NULL; i++)
116 efreet_icon_extensions = eina_list_append(efreet_icon_extensions, strdup(default_exts[i]));
118 efreet_icon_themes = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_icon_theme_free));
120 efreet_extra_icon_dirs = NULL;
121 efreet_icon_cache = eina_hash_pointer_new(EINA_FREE_CB(_efreet_icon_cache_list_destroy));
129 * @return Returns no value
130 * @brief Shuts down the icon system
133 efreet_icon_shutdown(void)
137 if (--efreet_icon_init_count)
140 IF_FREE(efreet_icon_user_dir);
141 IF_FREE(efreet_icon_deprecated_user_dir);
143 IF_FREE_LIST(efreet_icon_extensions, free);
144 IF_FREE_HASH(efreet_icon_themes);
145 efreet_extra_icon_dirs = eina_list_free(efreet_extra_icon_dirs);
147 IF_FREE_HASH(efreet_icon_cache);
151 efreet_icon_theme_free(fake_null);
156 efreet_icon_init_count = 0;
160 * @return Returns the user icon directory
161 * @brief Returns the user icon directory
164 efreet_icon_deprecated_user_dir_get(void)
169 if (efreet_icon_deprecated_user_dir) return efreet_icon_deprecated_user_dir;
171 user = efreet_home_dir_get();
172 len = strlen(user) + strlen("/.icons") + 1;
173 efreet_icon_deprecated_user_dir = malloc(sizeof(char) * len);
174 snprintf(efreet_icon_deprecated_user_dir, len, "%s/.icons", user);
176 return efreet_icon_deprecated_user_dir;
180 efreet_icon_user_dir_get(void)
185 if (efreet_icon_user_dir) return efreet_icon_user_dir;
187 user = efreet_data_home_get();
188 len = strlen(user) + strlen("/icons") + 1;
189 efreet_icon_user_dir = malloc(sizeof(char) * len);
190 snprintf(efreet_icon_user_dir, len, "%s/icons", user);
192 return efreet_icon_user_dir;
196 * @param ext: The extension to add to the list of checked extensions
197 * @return Returns no value.
198 * @brief Adds the given extension to the list of possible icon extensions
201 efreet_icon_extension_add(const char *ext)
203 efreet_icon_extensions = eina_list_prepend(efreet_icon_extensions, strdup(ext));
207 * @return Returns a list of strings that are paths to other icon directories
208 * @brief Gets the list of all extra directories to look for icons. These
209 * directories are used to look for icons after looking in the user icon dir
210 * and before looking in standard system directories. The order of search is
211 * from first to last directory in this list. the strings in the list should
212 * be created with eina_stringshare_add().
215 efreet_icon_extra_list_get(void)
217 return &efreet_extra_icon_dirs;
221 _hash_keys(Eina_Hash *hash __UNUSED__, const void *key, void *list)
223 *(Eina_List**)list = eina_list_append(*(Eina_List**)list, key);
227 * @return Returns a list of Efreet_Icon structs for all the non-hidden icon
229 * @brief Retrieves all of the non-hidden icon themes available on the system.
230 * The returned list must be freed. Do not free the list data.
233 efreet_icon_theme_list_get(void)
235 Eina_List *list = NULL;
236 Eina_List *theme_list = NULL;
240 /* update the list to include all icon themes */
241 efreet_icon_theme_dir_scan_all(NULL);
242 efreet_icon_theme_dir_validity_check();
244 /* create the list for the user */
245 it = eina_hash_iterator_key_new(efreet_icon_themes);
246 eina_iterator_foreach(it, EINA_EACH(_hash_keys), &theme_list);
247 eina_iterator_free(it);
249 EINA_LIST_FREE(theme_list, dir)
251 Efreet_Icon_Theme *theme;
253 theme = eina_hash_find(efreet_icon_themes, dir);
254 if (theme->hidden || theme->fake) continue;
256 if (!theme->name.name) continue;
259 list = eina_list_append(list, theme);
266 * @param theme_name: The theme to look for
267 * @return Returns the icon theme related to the given theme name or NULL if
269 * @brief Tries to get the icon theme structure for the given theme name
271 EAPI Efreet_Icon_Theme *
272 efreet_icon_theme_find(const char *theme_name)
274 Efreet_Icon_Theme *theme;
276 if (!theme_name) return fake_null;
278 theme = eina_hash_find(efreet_icon_themes, theme_name);
281 efreet_icon_theme_dir_scan_all(theme_name);
282 theme = eina_hash_find(efreet_icon_themes, theme_name);
290 * @param icon: The icon name to strip extension
291 * @return Extension removed if in list of extensions, else untouched.
292 * @brief Removes extension from icon name if in list of extensions.
295 efreet_icon_remove_extension(const char *icon)
298 char *tmp = NULL, *ext = NULL;
301 ext = strrchr(tmp, '.');
305 EINA_LIST_FOREACH(efreet_icon_extensions, l, ext2)
307 if (!strcmp(ext, ext2))
310 printf("[Efreet]: Requesting an icon with an extension: %s\n",
324 * @param theme_name: The icon theme to look for
325 * @return Returns the Efreet_Icon_Theme structure representing this theme
326 * or a new blank theme if not found
327 * @brief Retrieves a theme, or creates a blank one if not found.
329 static Efreet_Icon_Theme *
330 efreet_icon_find_theme_check(const char *theme_name)
332 Efreet_Icon_Theme *theme = NULL;
333 if (theme_name) theme = efreet_icon_theme_find(theme_name);
336 if ((fake_null) && (!theme_name)) return fake_null;
337 theme = efreet_icon_theme_new();
341 theme->name.internal = eina_stringshare_add(theme_name);
342 eina_hash_del(efreet_icon_themes, (void *)theme->name.internal, NULL);
343 eina_hash_add(efreet_icon_themes, (void *)theme->name.internal, theme);
347 theme->name.internal = NULL;
356 * @param theme_name: The icon theme to look for
357 * @param icon: The icon to look for
358 * @param size; The icon size to look for
359 * @return Returns the path to the given icon or NULL if none found
360 * @brief Retrives the path to the given icon.
363 efreet_icon_path_find(const char *theme_name, const char *icon, unsigned int size)
367 Efreet_Icon_Theme *theme;
369 if ((cached = efreet_icon_hash_get(theme_name, icon, size)) != NULL)
371 if (cached == NON_EXISTING) return NULL;
372 return strdup(cached);
374 theme = efreet_icon_find_theme_check(theme_name);
380 tmp = efreet_icon_remove_extension(icon);
381 value = efreet_icon_find_helper(theme, tmp, size);
385 value = efreet_icon_find_helper(theme, icon, size);
388 /* we didn't find the icon in the theme or in the inherited directories
389 * then just look for a non theme icon
391 if (!value || (value == NON_EXISTING)) value = efreet_icon_fallback_icon(icon);
393 efreet_icon_hash_put(theme_name, icon, size, value);
395 if (value == NON_EXISTING) value = NULL;
400 * @param theme_name: The icon theme to look for
401 * @param icons: List of icons to look for
402 * @param size; The icon size to look for
403 * @return Returns the path representing first found icon or
404 * NULL if none of the icons are found
405 * @brief Retrieves all of the information about the first found icon in
407 * @note This function will search the given theme for all icons before falling
408 * back. This is useful when searching for mimetype icons.
411 efreet_icon_list_find(const char *theme_name, Eina_List *icons,
415 const char *icon = NULL;
418 Efreet_Icon_Theme *theme;
420 theme = efreet_icon_find_theme_check(theme_name);
424 Eina_List *tmps = NULL;
426 EINA_LIST_FOREACH(icons, l, icon)
427 tmps = eina_list_append(tmps, efreet_icon_remove_extension(icon));
429 value = efreet_icon_list_find_helper(theme, tmps, size);
430 EINA_LIST_FREE(tmps, data)
434 value = efreet_icon_list_find_helper(theme, icons, size);
437 /* we didn't find the icons in the theme or in the inherited directories
438 * then just look for a non theme icon
440 if (!value || (value == NON_EXISTING))
442 EINA_LIST_FOREACH(icons, l, icon)
444 value = efreet_icon_fallback_icon(icon);
445 if (value && (value != NON_EXISTING))
450 if (value == NON_EXISTING) value = NULL;
455 * @param theme: The icon theme to look for
456 * @param icon: The icon to look for
457 * @param size: The icon size to look for
458 * @return Returns the Efreet_Icon structure representing this icon or NULL
459 * if the icon is not found
460 * @brief Retrieves all of the information about the given icon.
463 efreet_icon_find(const char *theme_name, const char *icon, unsigned int size)
467 path = efreet_icon_path_find(theme_name, icon, size);
472 icon = efreet_icon_new(path);
482 * @param theme: The theme to search in
483 * @param icon: The icon to search for
484 * @param size: The size to search for
485 * @return Returns the icon matching the given information or NULL if no
487 * @brief Scans inheriting themes for the given icon
490 efreet_icon_find_fallback(Efreet_Icon_Theme *theme,
491 const char *icon, unsigned int size)
499 EINA_LIST_FOREACH(theme->inherits, l, parent)
501 Efreet_Icon_Theme *parent_theme;
503 parent_theme = efreet_icon_theme_find(parent);
504 if ((!parent_theme) || (parent_theme == theme)) continue;
506 value = efreet_icon_find_helper(parent_theme, icon, size);
507 if (value && (value != NON_EXISTING)) break;
510 /* if this isn't the hicolor theme, and we have no other fallbacks
512 else if (strcmp(theme->name.internal, "hicolor"))
514 Efreet_Icon_Theme *parent_theme;
516 parent_theme = efreet_icon_theme_find("hicolor");
518 value = efreet_icon_find_helper(parent_theme, icon, size);
526 * @param theme: The theme to search in
527 * @param icon: The icon to search for
528 * @param size: The size to search for
529 * @return Returns the icon matching the given information or NULL if no
531 * @brief Scans the theme and any inheriting themes for the given icon
534 efreet_icon_find_helper(Efreet_Icon_Theme *theme,
535 const char *icon, unsigned int size)
538 static int recurse = 0;
540 efreet_icon_theme_cache_check(theme);
542 /* go no further if this theme is fake */
543 if (theme->fake || !theme->valid) return NULL;
545 /* limit recursion in finding themes and inherited themes to 256 levels */
546 if (recurse > 256) return NULL;
549 value = efreet_icon_lookup_icon(theme, icon, size);
551 /* we didin't find the image check the inherited themes */
552 if (!value || (value == NON_EXISTING))
553 value = efreet_icon_find_fallback(theme, icon, size);
561 * @param theme: The theme to search in
562 * @param icons: The icons to search for
563 * @param size: The size to search for
564 * @return Returns the icon matching the given information or NULL if no
566 * @brief Scans inheriting themes for the given icons
569 efreet_icon_list_find_fallback(Efreet_Icon_Theme *theme,
570 Eina_List *icons, unsigned int size)
578 EINA_LIST_FOREACH(theme->inherits, l, parent)
580 Efreet_Icon_Theme *parent_theme;
582 parent_theme = efreet_icon_theme_find(parent);
583 if ((!parent_theme) || (parent_theme == theme)) continue;
585 value = efreet_icon_list_find_helper(parent_theme,
587 if (value && (value != NON_EXISTING)) break;
591 /* if this isn't the hicolor theme, and we have no other fallbacks
594 else if (strcmp(theme->name.internal, "hicolor"))
596 Efreet_Icon_Theme *parent_theme;
598 parent_theme = efreet_icon_theme_find("hicolor");
600 value = efreet_icon_list_find_helper(parent_theme,
609 * @param theme: The theme to search in
610 * @param icons: The icons to search for
611 * @param size: The size to search for
612 * @return Returns the icon matching the given information or NULL if no
614 * @brief Scans the theme and any inheriting themes for the given icons
617 efreet_icon_list_find_helper(Efreet_Icon_Theme *theme,
618 Eina_List *icons, unsigned int size)
622 const char *icon = NULL;
623 static int recurse = 0;
625 efreet_icon_theme_cache_check(theme);
627 /* go no further if this theme is fake */
628 if (theme->fake || !theme->valid) return NULL;
630 /* limit recursion in finding themes and inherited themes to 256 levels */
631 if (recurse > 256) return NULL;
634 EINA_LIST_FOREACH(icons, l, icon)
636 value = efreet_icon_lookup_icon(theme, icon, size);
637 if (value && (value != NON_EXISTING))
641 /* we didn't find the image check the inherited themes */
642 if (!value || (value == NON_EXISTING))
643 value = efreet_icon_list_find_fallback(theme, icons, size);
651 * @param theme: The icon theme to look in
652 * @param icon_name: The icon name to look for
653 * @param size: The icon size to look for
654 * @return Returns the path for the theme/icon/size combo or NULL if
656 * @brief Looks for the @a icon in the @a theme for the @a size given.
659 efreet_icon_lookup_icon(Efreet_Icon_Theme *theme, const char *icon_name,
663 char *icon = NULL, *tmp = NULL;
664 Efreet_Icon_Theme_Directory *dir;
665 int minimal_size = INT_MAX;
667 if (!theme || (theme->paths == NULL) || !icon_name || !size)
670 icon = efreet_icon_cache_check(theme, icon_name, size);
671 if (icon) return icon;
673 /* search for allowed size == requested size */
674 EINA_LIST_FOREACH(theme->directories, l, dir)
676 if (!efreet_icon_directory_size_match(dir, size)) continue;
677 icon = efreet_icon_lookup_directory(theme, dir,
681 efreet_icon_cache_add(theme, icon_name, size, icon);
686 /* search for any icon that matches */
687 EINA_LIST_FOREACH(theme->directories, l, dir)
691 distance = efreet_icon_directory_size_distance(dir, size);
692 if (distance >= minimal_size) continue;
694 tmp = efreet_icon_lookup_directory(theme, dir,
700 minimal_size = distance;
704 efreet_icon_cache_add(theme, icon_name, size, icon);
711 * @param theme: The theme to use
712 * @param dir: The theme directory to look in
713 * @param icon_name: The icon name to look for
714 * @return Returns the icon cloest matching the given information or NULL if
716 * @brief Tries to find the file closest matching the given icon
719 efreet_icon_lookup_directory(Efreet_Icon_Theme *theme,
720 Efreet_Icon_Theme_Directory *dir,
721 const char *icon_name)
727 EINA_LIST_FOREACH(theme->paths, l, path)
729 icon = efreet_icon_lookup_directory_helper(dir, path, icon_name);
730 if (icon) return icon;
738 * @param dir: The theme directory to work with
739 * @param size: The size to check
740 * @return Returns true if the size matches for the given directory, 0
742 * @brief Checks if the size matches for the given directory or not
745 efreet_icon_directory_size_match(Efreet_Icon_Theme_Directory *dir,
748 if (dir->type == EFREET_ICON_SIZE_TYPE_FIXED)
749 return (dir->size.normal == size);
751 if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE)
752 return ((dir->size.min < size) && (size < dir->size.max));
754 if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD)
755 return (((dir->size.normal - dir->size.threshold) < size)
756 && (size < (dir->size.normal + dir->size.threshold)));
763 * @param dir: The directory to work with
764 * @param size: The size to check for
765 * @return Returns the distance this size is away from the desired size
766 * @brief Returns the distance the given size is away from the desired size
769 efreet_icon_directory_size_distance(Efreet_Icon_Theme_Directory *dir,
772 if (dir->type == EFREET_ICON_SIZE_TYPE_FIXED)
773 return (abs(dir->size.normal - size));
775 if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE)
777 if (size < dir->size.min)
778 return dir->size.min - size;
779 if (dir->size.max < size)
780 return size - dir->size.max;
785 if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD)
787 if (size < (dir->size.normal - dir->size.threshold))
788 return (dir->size.min - size);
789 if ((dir->size.normal + dir->size.threshold) < size)
790 return (size - dir->size.max);
800 * @param icon_name: The icon name to look for
801 * @return Returns the Efreet_Icon for the given name or NULL if none found
802 * @brief Looks for the un-themed icon in the base directories
805 efreet_icon_fallback_icon(const char *icon_name)
809 if (!icon_name) return NULL;
810 icon = efreet_icon_cache_check(efreet_icon_find_theme_check(NULL), icon_name, 0);
811 if (icon) return icon;
813 icon = efreet_icon_fallback_dir_scan(efreet_icon_deprecated_user_dir_get(), icon_name);
815 icon = efreet_icon_fallback_dir_scan(efreet_icon_user_dir_get(), icon_name);
818 Eina_List *xdg_dirs, *l;
822 EINA_LIST_FOREACH(efreet_extra_icon_dirs, l, dir)
824 icon = efreet_icon_fallback_dir_scan(dir, icon_name);
827 efreet_icon_cache_add(efreet_icon_find_theme_check(NULL), icon_name, 0, icon);
832 xdg_dirs = efreet_data_dirs_get();
834 EINA_LIST_FOREACH(xdg_dirs, l, dir)
836 snprintf(path, PATH_MAX, "%s/icons", dir);
837 icon = efreet_icon_fallback_dir_scan(path, icon_name);
840 efreet_icon_cache_add(efreet_icon_find_theme_check(NULL), icon_name, 0, icon);
845 icon = efreet_icon_fallback_dir_scan("/usr/share/pixmaps", icon_name);
848 efreet_icon_cache_add(efreet_icon_find_theme_check(NULL), icon_name, 0, icon);
854 * @param dir: The directory to scan
855 * @param icon_name: The icon to look for
856 * @return Returns the icon for the given name or NULL on failure
857 * @brief Scans the given @a dir for the given @a icon_name returning the
858 * Efreet_icon if found, NULL otherwise.
861 efreet_icon_fallback_dir_scan(const char *dir, const char *icon_name)
865 char path[PATH_MAX], *ext;
866 const char *icon_path[] = { dir, "/", icon_name, NULL };
869 if (!dir || !icon_name) return NULL;
871 size = efreet_array_cat(path, sizeof(path), icon_path);
872 EINA_LIST_FOREACH(efreet_icon_extensions, l, ext)
874 ecore_strlcpy(path + size, ext, sizeof(path) - size);
876 if (ecore_file_exists(path))
881 *(path + size) = '\0';
883 /* This is to catch non-conforming .desktop files */
887 if ((ecore_file_exists(path)) && (!ecore_file_is_dir(path)))
892 printf("[Efreet]: Found an icon that already has an extension: %s\n", path);
903 * @param theme: The theme to work with
904 * @param dir: The theme directory to work with
905 * @param path: The partial path to use
906 * @return Returns no value
907 * @brief Caches the icons in the given theme directory path at the given
911 efreet_icon_lookup_directory_helper(Efreet_Icon_Theme_Directory *dir,
912 const char *path, const char *icon_name)
916 char file_path[PATH_MAX];
917 const char *ext, *path_strs[] = { path, "/", dir->name, "/", icon_name, NULL };
920 len = efreet_array_cat(file_path, sizeof(file_path), path_strs);
922 EINA_LIST_FOREACH(efreet_icon_extensions, l, ext)
924 ecore_strlcpy(file_path + len, ext, sizeof(file_path) - len);
926 if (ecore_file_exists(file_path))
928 icon = strdup(file_path);
937 * @return Returns a new Efreet_Icon struct on success or NULL on failure
938 * @brief Creates a new Efreet_Icon struct
941 efreet_icon_new(const char *path)
946 icon = NEW(Efreet_Icon, 1);
947 icon->path = strdup(path);
949 /* load the .icon file if it's available */
950 p = strrchr(icon->path, '.');
953 char ico_path[PATH_MAX];
957 snprintf(ico_path, sizeof(ico_path), "%s.icon", icon->path);
960 if (ecore_file_exists(ico_path))
961 efreet_icon_populate(icon, ico_path);
968 file = ecore_file_file_get(icon->path);
969 p = strrchr(icon->path, '.');
971 icon->name = strdup(file);
979 * @param icon: The Efreet_Icon to cleanup
980 * @return Returns no value.
981 * @brief Free's the given icon and all its internal data.
984 efreet_icon_free(Efreet_Icon *icon)
989 if (icon->ref_count > 0) return;
993 IF_FREE_LIST(icon->attach_points, free);
1000 * @param icon: The icon to populate
1001 * @param file: The file to populate from
1002 * @return Returns no value
1003 * @brief Tries to populate the icon information from the given file
1006 efreet_icon_populate(Efreet_Icon *icon, const char *file)
1011 ini = efreet_ini_new(file);
1014 efreet_ini_free(ini);
1018 efreet_ini_section_set(ini, "Icon Data");
1019 tmp = efreet_ini_localestring_get(ini, "DisplayName");
1020 if (tmp) icon->name = strdup(tmp);
1022 tmp = efreet_ini_string_get(ini, "EmbeddedTextRectangle");
1031 for (i = 0; i < 4; i++)
1038 points[i] = atoi(s);
1049 icon->has_embedded_text_rectangle = 1;
1050 icon->embedded_text_rectangle.x0 = points[0];
1051 icon->embedded_text_rectangle.y0 = points[1];
1052 icon->embedded_text_rectangle.x1 = points[2];
1053 icon->embedded_text_rectangle.y1 = points[3];
1058 tmp = efreet_ini_string_get(ini, "AttachPoints");
1067 Efreet_Icon_Point *point;
1070 /* If this happens there is something wrong with the .icon file */
1073 point = NEW(Efreet_Icon_Point, 1);
1084 icon->attach_points = eina_list_append(icon->attach_points, point);
1092 efreet_ini_free(ini);
1097 * @return Returns a new Efreet_Icon_Theme on success or NULL on failure
1098 * @brief Creates a new Efreet_Icon_Theme structure
1100 static Efreet_Icon_Theme *
1101 efreet_icon_theme_new(void)
1103 Efreet_Icon_Theme *theme;
1105 theme = NEW(Efreet_Icon_Theme, 1);
1112 * @param theme: The theme to free
1113 * @return Returns no value
1114 * @brief Frees up the @a theme structure.
1117 efreet_icon_theme_free(Efreet_Icon_Theme *theme)
1122 IF_RELEASE(theme->name.internal);
1123 IF_RELEASE(theme->name.name);
1125 IF_FREE(theme->comment);
1126 IF_FREE(theme->example_icon);
1128 IF_FREE_LIST(theme->paths, free);
1129 IF_FREE_LIST(theme->inherits, free);
1130 IF_FREE_LIST(theme->directories, efreet_icon_theme_directory_free);
1137 * @param theme: The theme to work with
1138 * @param path: The path to add
1139 * @return Returns no value
1140 * @brief This will correctly add the given path to the list of theme paths.
1141 * @Note Assumes you've already verified that @a path is a valid directory.
1144 efreet_icon_theme_path_add(Efreet_Icon_Theme *theme, const char *path)
1146 if (!theme || !path) return;
1148 theme->paths = eina_list_append(theme->paths, strdup(path));
1153 * @return Returns no value
1154 * @brief This validates that our cache is still valid.
1156 * This is checked by the following algorithm:
1157 * - if we've check less then 5 seconds ago we're good
1158 * - if the mtime on the dir is less then our last check time we're good
1159 * - otherwise, invalidate the caches
1162 efreet_icon_theme_cache_check(Efreet_Icon_Theme *theme)
1167 new_check = ecore_time_get();
1169 /* we're within 5 seconds of the last time we checked the cache */
1170 if ((new_check - 5) <= theme->last_cache_check) return;
1173 efreet_icon_theme_dir_scan_all(theme->name.internal);
1179 EINA_LIST_FOREACH(theme->paths, l, path)
1181 if (!efreet_icon_theme_cache_check_dir(theme, path))
1185 theme->last_cache_check = new_check;
1190 * @param theme: The icon theme to check
1191 * @param dir: The directory to check
1192 * @return Returns 1 if the cache is still valid, 0 otherwise
1193 * @brief This will check if the theme cache is still valid. If it isn't the
1194 * cache will be invalided and 0 returned.
1197 efreet_icon_theme_cache_check_dir(Efreet_Icon_Theme *theme, const char *dir)
1201 /* have we modified this directory since our last cache check? */
1202 if (stat(dir, &buf) || (buf.st_mtime > theme->last_cache_check))
1204 eina_hash_del(efreet_icon_cache, theme, NULL);
1213 * @param theme_name: The theme to scan for
1214 * @return Returns no value
1215 * @brief Scans the theme directories. If @a theme_name is NULL it will load
1216 * up all theme data. If @a theme_name is not NULL it will look for that
1217 * specific theme data
1220 efreet_icon_theme_dir_scan_all(const char *theme_name)
1222 Eina_List *xdg_dirs, *l;
1223 char path[PATH_MAX], *dir;
1225 efreet_icon_theme_dir_scan(efreet_icon_deprecated_user_dir_get(), theme_name);
1226 efreet_icon_theme_dir_scan(efreet_icon_user_dir_get(), theme_name);
1228 xdg_dirs = efreet_data_dirs_get();
1229 EINA_LIST_FOREACH(xdg_dirs, l, dir)
1231 snprintf(path, sizeof(path), "%s/icons", dir);
1232 efreet_icon_theme_dir_scan(path, theme_name);
1235 efreet_icon_theme_dir_scan("/usr/share/pixmaps", theme_name);
1240 * @param search_dir: The directory to scan
1241 * @param theme_name: Scan for this specific theme, set to NULL to find all
1243 * @return Returns no value
1244 * @brief Scans the given directory and adds non-hidden icon themes to the
1245 * given list. If the theme isnt' in our cache then load the index.theme and
1249 efreet_icon_theme_dir_scan(const char *search_dir, const char *theme_name)
1254 if (!search_dir) return;
1256 dirs = opendir(search_dir);
1259 while ((dir = readdir(dirs)))
1261 Efreet_Icon_Theme *theme;
1262 char path[PATH_MAX];
1265 if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")) continue;
1267 /* only care if this is a directory or the theme name matches the
1269 snprintf(path, sizeof(path), "%s/%s", search_dir, dir->d_name);
1270 if (((theme_name != NULL) && (strcmp(theme_name, dir->d_name)))
1271 || !ecore_file_is_dir(path))
1274 key = eina_stringshare_add(dir->d_name);
1275 theme = eina_hash_find(efreet_icon_themes, key);
1279 theme = efreet_icon_theme_new();
1280 theme->name.internal = key;
1281 eina_hash_add(efreet_icon_themes,
1282 (void *)theme->name.internal, theme);
1288 eina_stringshare_del(key);
1291 efreet_icon_theme_path_add(theme, path);
1293 /* we're already valid so no reason to check for an index.theme file */
1294 if (theme->valid) continue;
1296 /* if the index.theme file exists we parse it into the theme */
1297 strncat(path, "/index.theme", sizeof(path));
1298 if (ecore_file_exists(path))
1299 efreet_icon_theme_index_read(theme, path);
1303 /* if we were given a theme name we want to make sure that that given
1304 * theme is valid before finishing, unless it's a fake theme */
1307 Efreet_Icon_Theme *theme;
1309 theme = eina_hash_find(efreet_icon_themes, theme_name);
1310 if (theme && !theme->valid && !theme->fake)
1311 eina_hash_del(efreet_icon_themes, theme_name, theme);
1317 * @param theme: The theme to set the values into
1318 * @param path: The path to the index.theme file for this theme
1319 * @return Returns no value
1320 * @brief This will load up the theme with the data in the index.theme file
1323 efreet_icon_theme_index_read(Efreet_Icon_Theme *theme, const char *path)
1328 if (!theme || !path) return;
1330 ini = efreet_ini_new(path);
1333 efreet_ini_free(ini);
1337 efreet_ini_section_set(ini, "Icon Theme");
1338 tmp = efreet_ini_localestring_get(ini, "Name");
1339 if (tmp) theme->name.name = eina_stringshare_add(tmp);
1341 tmp = efreet_ini_localestring_get(ini, "Comment");
1342 if (tmp) theme->comment = strdup(tmp);
1344 tmp = efreet_ini_string_get(ini, "Example");
1345 if (tmp) theme->example_icon = strdup(tmp);
1347 theme->hidden = efreet_ini_boolean_get(ini, "Hidden");
1351 /* Check the inheritance. If there is none we inherit from the hicolor theme */
1352 tmp = efreet_ini_string_get(ini, "Inherits");
1365 theme->inherits = eina_list_append(theme->inherits, strdup(s));
1369 theme->inherits = eina_list_append(theme->inherits, strdup(s));
1374 /* make sure this one is done last as setting the directory will change
1375 * the ini section ... */
1376 tmp = efreet_ini_string_get(ini, "Directories");
1391 theme->directories = eina_list_append(theme->directories,
1392 efreet_icon_theme_directory_new(ini, s));
1400 efreet_ini_free(ini);
1405 * @return Returns no value
1406 * @brief Because the theme icon directories can be spread over multiple
1407 * base directories we may need to create the icon theme strucutre before
1408 * finding the index.theme file. It may also be that we never find an
1409 * index.theme file as this isn't a valid theme. This function makes sure
1410 * that everything we've got in our hash has a valid key to it.
1413 efreet_icon_theme_dir_validity_check(void)
1420 it = eina_hash_iterator_key_new(efreet_icon_themes);
1421 eina_iterator_foreach(it, EINA_EACH(_hash_keys), &keys);
1422 eina_iterator_free(it);
1424 EINA_LIST_FREE(keys, name)
1426 Efreet_Icon_Theme *theme;
1428 theme = eina_hash_find(efreet_icon_themes, name);
1429 if (theme && !theme->valid && !theme->fake)
1430 eina_hash_del(efreet_icon_themes, name, theme);
1436 * @param ini: The ini file with information on this directory
1437 * @param name: The name of the directory
1438 * @return Returns a new Efreet_Icon_Theme_Directory based on the
1439 * information in @a ini.
1440 * @brief Creates and initialises an icon theme directory from the given ini
1443 static Efreet_Icon_Theme_Directory *
1444 efreet_icon_theme_directory_new(Efreet_Ini *ini, const char *name)
1446 Efreet_Icon_Theme_Directory *dir;
1450 if (!ini) return NULL;
1452 dir = NEW(Efreet_Icon_Theme_Directory, 1);
1453 dir->name = strdup(name);
1455 efreet_ini_section_set(ini, name);
1457 tmp = efreet_ini_string_get(ini, "Context");
1460 if (!strcasecmp(tmp, "Actions"))
1461 dir->context = EFREET_ICON_THEME_CONTEXT_ACTIONS;
1463 else if (!strcasecmp(tmp, "Devices"))
1464 dir->context = EFREET_ICON_THEME_CONTEXT_DEVICES;
1466 else if (!strcasecmp(tmp, "FileSystems"))
1467 dir->context = EFREET_ICON_THEME_CONTEXT_FILESYSTEMS;
1469 else if (!strcasecmp(tmp, "MimeTypes"))
1470 dir->context = EFREET_ICON_THEME_CONTEXT_MIMETYPES;
1473 tmp = efreet_ini_string_get(ini, "Type");
1476 if (!strcasecmp(tmp, "Fixed"))
1477 dir->type = EFREET_ICON_SIZE_TYPE_FIXED;
1479 else if (!strcasecmp(tmp, "Scalable"))
1480 dir->type = EFREET_ICON_SIZE_TYPE_SCALABLE;
1482 else if (!strcasecmp(tmp, "Threshold"))
1483 dir->type = EFREET_ICON_SIZE_TYPE_THRESHOLD;
1486 dir->size.normal = efreet_ini_int_get(ini, "Size");
1488 val = efreet_ini_int_get(ini, "MinSize");
1489 if (val < 0) dir->size.min = dir->size.normal;
1490 else dir->size.min = val;
1492 val = efreet_ini_int_get(ini, "MaxSize");
1493 if (val < 0) dir->size.max = dir->size.normal;
1494 else dir->size.max = val;
1496 val = efreet_ini_int_get(ini, "Threshold");
1497 if (val < 0) dir->size.threshold = 2;
1498 else dir->size.threshold = val;
1505 * @param dir: The Efreet_Icon_Theme_Directory to free
1506 * @return Returns no value
1507 * @brief Frees the given directory @a dir
1510 efreet_icon_theme_directory_free(Efreet_Icon_Theme_Directory *dir)
1519 efreet_icon_cache_find(Efreet_Icon_Cache *value, const char *key)
1521 if (!value || !key) return -1;
1522 return strcmp(value->key, key);
1526 efreet_icon_cache_flush(Efreet_Icon_Theme *theme, Eina_List *list)
1529 * * Dynamic cache size
1530 * * Maybe add references to cache, so that we sort on how often a value is used
1532 while (eina_list_count(list) > 100)
1534 Efreet_Icon_Cache *cache;
1537 last = eina_list_last(list);
1538 cache = eina_list_data_get(last);
1539 efreet_icon_cache_free(cache);
1540 list = eina_list_remove_list(list, last);
1543 eina_hash_modify(efreet_icon_cache, theme, list);
1547 efreet_icon_cache_free(Efreet_Icon_Cache *value)
1551 IF_FREE(value->key);
1552 IF_FREE(value->path);
1557 efreet_icon_cache_check(Efreet_Icon_Theme *theme, const char *icon, unsigned int size)
1560 Efreet_Icon_Cache *cache;
1564 list = eina_hash_find(efreet_icon_cache, theme);
1565 if (!list) return NULL;
1567 snprintf(key, sizeof(key), "%s %d", icon, size);
1568 cache = eina_list_search_unsorted(list, EINA_COMPARE_CB(efreet_icon_cache_find), key);
1573 list = eina_list_promote_list(list, eina_list_data_find_list(list, cache));
1574 eina_hash_modify(efreet_icon_cache, theme, list);
1575 return NON_EXISTING;
1577 else if (!stat(cache->path, &st) && st.st_mtime == cache->lasttime)
1579 list = eina_list_promote_list(list, eina_list_data_find_list(list, cache));
1580 eina_hash_modify(efreet_icon_cache, theme, list);
1581 return strdup(cache->path);
1583 efreet_icon_cache_free(cache);
1584 list = eina_list_remove(list, cache);
1585 if (list != NULL) eina_hash_modify(efreet_icon_cache, theme, list);
1586 else eina_hash_del(efreet_icon_cache, theme, NULL);
1592 efreet_icon_cache_add(Efreet_Icon_Theme *theme, const char *icon, unsigned int size, const char *value)
1594 Eina_List *list, *l;
1595 Efreet_Icon_Cache *cache;
1599 list = eina_hash_find(efreet_icon_cache, theme);
1601 snprintf(key, sizeof(key), "%s %d", icon, size);
1602 cache = NEW(Efreet_Icon_Cache, 1);
1603 cache->key = strdup(key);
1604 if ((value) && !stat(value, &st))
1606 cache->path = strdup(value);
1607 cache->lasttime = st.st_mtime;
1610 cache->lasttime = ecore_time_get();
1613 list = eina_list_prepend(list, cache);
1615 if (!l) eina_hash_add(efreet_icon_cache, theme, list);
1616 else eina_hash_modify(efreet_icon_cache, theme, list);
1618 efreet_icon_cache_flush(theme, list);