1 /* vim: set sw=4 ts=4 sts=4 et: */
3 #include "efreet_private.h"
5 #define NON_EXISTING (void *)-1
7 static char *efreet_icon_deprecated_user_dir = NULL;
8 static char *efreet_icon_user_dir = NULL;
9 static Ecore_Hash *efreet_icon_themes = NULL;
10 static Ecore_List *efreet_icon_extensions = NULL;
11 static Ecore_List *efreet_extra_icon_dirs = NULL;
12 static Ecore_Hash *efreet_icon_cache = NULL;
14 static int efreet_icon_init_count = 0;
16 typedef struct Efreet_Icon_Cache Efreet_Icon_Cache;
17 struct Efreet_Icon_Cache
24 static char *efreet_icon_remove_extension(const char *icon);
25 static Efreet_Icon_Theme *efreet_icon_find_theme_check(const char *theme_name);
28 static char *efreet_icon_find_fallback(Efreet_Icon_Theme *theme,
31 static char *efreet_icon_list_find_fallback(Efreet_Icon_Theme *theme,
34 static char *efreet_icon_find_helper(Efreet_Icon_Theme *theme,
35 const char *icon, unsigned int size);
36 static char *efreet_icon_list_find_helper(Efreet_Icon_Theme *theme,
37 Ecore_List *icons, unsigned int size);
38 static char *efreet_icon_lookup_icon(Efreet_Icon_Theme *theme,
39 const char *icon_name, unsigned int size);
40 static char *efreet_icon_fallback_icon(const char *icon_name);
41 static char *efreet_icon_fallback_dir_scan(const char *dir,
42 const char *icon_name);
44 static char *efreet_icon_lookup_directory(Efreet_Icon_Theme *theme,
45 Efreet_Icon_Theme_Directory *dir,
46 const char *icon_name);
47 static int efreet_icon_directory_size_distance(Efreet_Icon_Theme_Directory *dir,
49 static int efreet_icon_directory_size_match(Efreet_Icon_Theme_Directory *dir,
52 static Efreet_Icon *efreet_icon_new(const char *path);
53 static void efreet_icon_point_free(Efreet_Icon_Point *point);
54 static void efreet_icon_populate(Efreet_Icon *icon, const char *file);
56 static char *efreet_icon_lookup_directory_helper(Efreet_Icon_Theme_Directory *dir,
57 const char *path, const char *icon_name);
59 static Efreet_Icon_Theme *efreet_icon_theme_new(void);
60 static void efreet_icon_theme_free(Efreet_Icon_Theme *theme);
61 static void efreet_icon_theme_dir_scan_all(const char *theme_name);
62 static void efreet_icon_theme_dir_scan(const char *dir,
63 const char *theme_name);
64 static void efreet_icon_theme_dir_validity_check(void);
65 static void efreet_icon_theme_path_add(Efreet_Icon_Theme *theme,
67 static void efreet_icon_theme_index_read(Efreet_Icon_Theme *theme,
70 static Efreet_Icon_Theme_Directory *efreet_icon_theme_directory_new(Efreet_Ini *ini,
72 static void efreet_icon_theme_directory_free(Efreet_Icon_Theme_Directory *dir);
74 static void efreet_icon_theme_cache_check(Efreet_Icon_Theme *theme);
75 static int efreet_icon_theme_cache_check_dir(Efreet_Icon_Theme *theme,
78 static int efreet_icon_cache_find(Efreet_Icon_Cache *value, const char *key);
79 static void efreet_icon_cache_flush(Ecore_List *list);
80 static void efreet_icon_cache_free(Efreet_Icon_Cache *value);
81 static char *efreet_icon_cache_check(Efreet_Icon_Theme *theme, const char *icon, unsigned int size);
82 static void efreet_icon_cache_add(Efreet_Icon_Theme *theme, const char *icon, unsigned int size, const char *value);
86 * @return Returns 1 on success or 0 on failure
87 * @brief Initializes the icon system
90 efreet_icon_init(void)
92 if (efreet_icon_init_count++ > 0)
93 return efreet_icon_init_count;
95 if (!efreet_icon_themes)
97 const char *default_exts[] = {".png", ".xpm", NULL};
102 efreet_icon_init_count--;
106 /* setup the default extension list */
107 efreet_icon_extensions = ecore_list_new();
108 ecore_list_free_cb_set(efreet_icon_extensions, free);
110 for (i = 0; default_exts[i] != NULL; i++)
111 ecore_list_append(efreet_icon_extensions, strdup(default_exts[i]));
113 efreet_icon_themes = ecore_hash_new(NULL, NULL);
114 ecore_hash_free_value_cb_set(efreet_icon_themes,
115 ECORE_FREE_CB(efreet_icon_theme_free));
116 efreet_extra_icon_dirs = ecore_list_new();
118 efreet_icon_cache = ecore_hash_new(ecore_direct_hash, ecore_direct_compare);
119 ecore_hash_free_value_cb_set(efreet_icon_cache, ECORE_FREE_CB(ecore_list_destroy));
127 * @return Returns no value
128 * @brief Shuts down the icon system
131 efreet_icon_shutdown(void)
133 if (--efreet_icon_init_count)
136 IF_FREE(efreet_icon_user_dir);
137 IF_FREE(efreet_icon_deprecated_user_dir);
139 IF_FREE_LIST(efreet_icon_extensions);
140 IF_FREE_HASH(efreet_icon_themes);
141 IF_FREE_LIST(efreet_extra_icon_dirs);
143 IF_FREE_HASH(efreet_icon_cache);
146 efreet_icon_init_count = 0;
150 * @return Returns the user icon directory
151 * @brief Returns the user icon directory
154 efreet_icon_deprecated_user_dir_get(void)
159 if (efreet_icon_deprecated_user_dir) return efreet_icon_deprecated_user_dir;
161 user = efreet_home_dir_get();
162 len = strlen(user) + strlen("/.icons") + 1;
163 efreet_icon_deprecated_user_dir = malloc(sizeof(char) * len);
164 snprintf(efreet_icon_deprecated_user_dir, len, "%s/.icons", user);
166 return efreet_icon_deprecated_user_dir;
170 efreet_icon_user_dir_get(void)
175 if (efreet_icon_user_dir) return efreet_icon_user_dir;
177 user = efreet_data_home_get();
178 len = strlen(user) + strlen("/icons") + 1;
179 efreet_icon_user_dir = malloc(sizeof(char) * len);
180 snprintf(efreet_icon_user_dir, len, "%s/icons", user);
182 return efreet_icon_user_dir;
186 * @param ext: The extension to add to the list of checked extensions
187 * @return Returns no value.
188 * @brief Adds the given extension to the list of possible icon extensions
191 efreet_icon_extension_add(const char *ext)
193 ecore_list_prepend(efreet_icon_extensions, strdup(ext));
197 * @return Returns a list of strings that are paths to other icon directories
198 * @brief Gets the list of all extra directories to look for icons. These
199 * directories are used to look for icons after looking in the user icon dir
200 * and before looking in standard system directories. The order of search is
201 * from first to last directory in this list. the strings in the list should
202 * be created with ecore_string_instance().
205 efreet_icon_extra_list_get(void)
207 return efreet_extra_icon_dirs;
211 * @return Returns a list of Efreet_Icon structs for all the non-hidden icon
213 * @brief Retrieves all of the non-hidden icon themes available on the system.
214 * The returned list must be freed. Do not free the list data.
217 efreet_icon_theme_list_get(void)
219 Ecore_List *list, *theme_list;
222 /* update the list to include all icon themes */
223 efreet_icon_theme_dir_scan_all(NULL);
224 efreet_icon_theme_dir_validity_check();
226 /* create the list for the user */
227 list = ecore_list_new();
228 theme_list = ecore_hash_keys(efreet_icon_themes);
229 ecore_list_first_goto(theme_list);
230 while ((dir = ecore_list_next(theme_list)))
232 Efreet_Icon_Theme *theme;
234 theme = ecore_hash_get(efreet_icon_themes, dir);
235 if (theme->hidden || theme->fake) continue;
237 if (!theme->name.name) continue;
240 ecore_list_append(list, theme);
242 ecore_list_destroy(theme_list);
248 * @param theme_name: The theme to look for
249 * @return Returns the icon theme related to the given theme name or NULL if
251 * @brief Tries to get the icon theme structure for the given theme name
253 EAPI Efreet_Icon_Theme *
254 efreet_icon_theme_find(const char *theme_name)
257 Efreet_Icon_Theme *theme;
259 key = ecore_string_instance(theme_name);
260 theme = ecore_hash_get(efreet_icon_themes, key);
263 efreet_icon_theme_dir_scan_all(theme_name);
264 theme = ecore_hash_get(efreet_icon_themes, key);
266 ecore_string_release(key);
273 * @param icon: The icon name to strip extension
274 * @return Extension removed if in list of extensions, else untouched.
275 * @brief Removes extension from icon name if in list of extensions.
278 efreet_icon_remove_extension(const char *icon)
280 char *tmp = NULL, *ext = NULL;
283 ext = strrchr(tmp, '.');
287 ecore_list_first_goto(efreet_icon_extensions);
288 while ((ext2 = ecore_list_next(efreet_icon_extensions)))
290 if (!strcmp(ext, ext2))
293 printf("[Efreet]: Requesting an icon with an extension: %s\n",
307 * @param theme_name: The icon theme to look for
308 * @return Returns the Efreet_Icon_Theme structure representing this theme
309 * or a new blank theme if not found
310 * @brief Retrieves a theme, or creates a blank one if not found.
312 static Efreet_Icon_Theme *
313 efreet_icon_find_theme_check(const char *theme_name)
315 Efreet_Icon_Theme *theme = NULL;
316 if (theme_name) theme = efreet_icon_theme_find(theme_name);
319 theme = efreet_icon_theme_new();
321 theme->name.internal = ecore_string_instance(theme_name);
322 ecore_hash_set(efreet_icon_themes, (void *)theme->name.internal, theme);
329 * @param theme_name: The icon theme to look for
330 * @param icon: The icon to look for
331 * @param size; The icon size to look for
332 * @return Returns the path to the given icon or NULL if none found
333 * @brief Retrives the path to the given icon.
336 efreet_icon_path_find(const char *theme_name, const char *icon, unsigned int size)
339 Efreet_Icon_Theme *theme;
341 theme = efreet_icon_find_theme_check(theme_name);
347 tmp = efreet_icon_remove_extension(icon);
348 value = efreet_icon_find_helper(theme, tmp, size);
352 value = efreet_icon_find_helper(theme, icon, size);
355 /* we didn't find the icon in the theme or in the inherited directories
356 * then just look for a non theme icon
358 if (!value || (value == NON_EXISTING)) value = efreet_icon_fallback_icon(icon);
360 if (value == NON_EXISTING) value = NULL;
365 * @param theme_name: The icon theme to look for
366 * @param icons: List of icons to look for
367 * @param size; The icon size to look for
368 * @return Returns the path representing first found icon or
369 * NULL if none of the icons are found
370 * @brief Retrieves all of the information about the first found icon in
372 * @note This function will search the given theme for all icons before falling
373 * back. This is useful when searching for mimetype icons.
376 efreet_icon_list_find(const char *theme_name, Ecore_List *icons,
379 const char *icon = NULL;
381 Efreet_Icon_Theme *theme;
383 theme = efreet_icon_find_theme_check(theme_name);
385 ecore_list_first_goto(icons);
388 Ecore_List *tmps = NULL;
390 tmps = ecore_list_new();
391 ecore_list_free_cb_set(tmps, free);
392 ecore_list_first_goto(icons);
393 while ((icon = ecore_list_next(icons)))
394 ecore_list_append(tmps, efreet_icon_remove_extension(icon));
396 value = efreet_icon_list_find_helper(theme, tmps, size);
397 ecore_list_destroy(tmps);
400 value = efreet_icon_list_find_helper(theme, icons, size);
403 /* we didn't find the icons in the theme or in the inherited directories
404 * then just look for a non theme icon
406 if (!value || (value == NON_EXISTING))
408 ecore_list_first_goto(icons);
409 while ((icon = ecore_list_next(icons)))
411 value = efreet_icon_fallback_icon(icon);
412 if (value && (value != NON_EXISTING))
417 if (value == NON_EXISTING) value = NULL;
422 * @param theme: The icon theme to look for
423 * @param icon: The icon to look for
424 * @param size: The icon size to look for
425 * @return Returns the Efreet_Icon structure representing this icon or NULL
426 * if the icon is not found
427 * @brief Retrieves all of the information about the given icon.
430 efreet_icon_find(const char *theme_name, const char *icon, unsigned int size)
434 path = efreet_icon_path_find(theme_name, icon, size);
439 icon = efreet_icon_new(path);
449 * @param theme: The theme to search in
450 * @param icon: The icon to search for
451 * @param size: The size to search for
452 * @return Returns the icon matching the given information or NULL if no
454 * @brief Scans inheriting themes for the given icon
457 efreet_icon_find_fallback(Efreet_Icon_Theme *theme,
458 const char *icon, unsigned int size)
465 ecore_list_first_goto(theme->inherits);
466 while ((parent = ecore_list_next(theme->inherits)))
468 Efreet_Icon_Theme *parent_theme;
470 parent_theme = efreet_icon_theme_find(parent);
471 if ((!parent_theme) || (parent_theme == theme)) continue;
473 value = efreet_icon_find_helper(parent_theme, icon, size);
474 if (value && (value != NON_EXISTING)) break;
477 /* if this isn't the hicolor theme, and we have no other fallbacks
479 else if (strcmp(theme->name.internal, "hicolor"))
481 Efreet_Icon_Theme *parent_theme;
483 parent_theme = efreet_icon_theme_find("hicolor");
485 value = efreet_icon_find_helper(parent_theme, icon, size);
493 * @param theme: The theme to search in
494 * @param icon: The icon to search for
495 * @param size: The size to search for
496 * @return Returns the icon matching the given information or NULL if no
498 * @brief Scans the theme and any inheriting themes for the given icon
501 efreet_icon_find_helper(Efreet_Icon_Theme *theme,
502 const char *icon, unsigned int size)
505 static int recurse = 0;
507 efreet_icon_theme_cache_check(theme);
509 /* go no further if this theme is fake */
510 if (theme->fake || !theme->valid) return NULL;
512 /* limit recursion in finding themes and inherited themes to 256 levels */
513 if (recurse > 256) return NULL;
516 value = efreet_icon_lookup_icon(theme, icon, size);
518 /* we didin't find the image check the inherited themes */
519 if (!value || (value == NON_EXISTING))
520 value = efreet_icon_find_fallback(theme, icon, size);
528 * @param theme: The theme to search in
529 * @param icons: The icons to search for
530 * @param size: The size to search for
531 * @return Returns the icon matching the given information or NULL if no
533 * @brief Scans inheriting themes for the given icons
536 efreet_icon_list_find_fallback(Efreet_Icon_Theme *theme,
537 Ecore_List *icons, unsigned int size)
544 ecore_list_first_goto(theme->inherits);
545 while ((parent = ecore_list_next(theme->inherits)))
547 Efreet_Icon_Theme *parent_theme;
549 parent_theme = efreet_icon_theme_find(parent);
550 if ((!parent_theme) || (parent_theme == theme)) continue;
552 value = efreet_icon_list_find_helper(parent_theme,
554 if (value && (value != NON_EXISTING)) break;
558 /* if this isn't the hicolor theme, and we have no other fallbacks
561 else if (strcmp(theme->name.internal, "hicolor"))
563 Efreet_Icon_Theme *parent_theme;
565 parent_theme = efreet_icon_theme_find("hicolor");
567 value = efreet_icon_list_find_helper(parent_theme,
576 * @param theme: The theme to search in
577 * @param icons: The icons to search for
578 * @param size: The size to search for
579 * @return Returns the icon matching the given information or NULL if no
581 * @brief Scans the theme and any inheriting themes for the given icons
584 efreet_icon_list_find_helper(Efreet_Icon_Theme *theme,
585 Ecore_List *icons, unsigned int size)
588 const char *icon = NULL;
589 static int recurse = 0;
591 efreet_icon_theme_cache_check(theme);
593 /* go no further if this theme is fake */
594 if (theme->fake || !theme->valid) return NULL;
596 /* limit recursion in finding themes and inherited themes to 256 levels */
597 if (recurse > 256) return NULL;
600 ecore_list_first_goto(icons);
601 while ((icon = ecore_list_next(icons)))
603 value = efreet_icon_lookup_icon(theme, icon, size);
604 if (value && (value != NON_EXISTING))
608 /* we didn't find the image check the inherited themes */
609 if (!value || (value == NON_EXISTING))
610 value = efreet_icon_list_find_fallback(theme, icons, size);
618 * @param theme: The icon theme to look in
619 * @param icon_name: The icon name to look for
620 * @param size: The icon size to look for
621 * @return Returns the path for the theme/icon/size combo or NULL if
623 * @brief Looks for the @a icon in the @a theme for the @a size given.
626 efreet_icon_lookup_icon(Efreet_Icon_Theme *theme, const char *icon_name,
629 char *icon = NULL, *tmp = NULL;
630 Efreet_Icon_Theme_Directory *dir;
631 int minimal_size = INT_MAX;
633 if (!theme || (theme->paths.count == 0) || !icon_name || !size)
636 icon = efreet_icon_cache_check(theme, icon_name, size);
637 if (icon) return icon;
639 /* search for allowed size == requested size */
640 ecore_list_first_goto(theme->directories);
641 while ((dir = ecore_list_next(theme->directories)))
643 if (!efreet_icon_directory_size_match(dir, size)) continue;
644 icon = efreet_icon_lookup_directory(theme, dir,
648 efreet_icon_cache_add(theme, icon_name, size, icon);
653 /* search for any icon that matches */
654 ecore_list_first_goto(theme->directories);
655 while ((dir = ecore_list_next(theme->directories)))
659 distance = efreet_icon_directory_size_distance(dir, size);
660 if (distance >= minimal_size) continue;
662 tmp = efreet_icon_lookup_directory(theme, dir,
668 minimal_size = distance;
672 efreet_icon_cache_add(theme, icon_name, size, icon);
679 * @param theme: The theme to use
680 * @param dir: The theme directory to look in
681 * @param icon_name: The icon name to look for
682 * @return Returns the icon cloest matching the given information or NULL if
684 * @brief Tries to find the file closest matching the given icon
687 efreet_icon_lookup_directory(Efreet_Icon_Theme *theme,
688 Efreet_Icon_Theme_Directory *dir,
689 const char *icon_name)
691 if (theme->paths.count == 1)
692 return efreet_icon_lookup_directory_helper(dir, theme->paths.path, icon_name);
699 ecore_list_first_goto(theme->paths.path);
700 while ((path = ecore_list_next(theme->paths.path)))
702 icon = efreet_icon_lookup_directory_helper(dir, path, icon_name);
703 if (icon) return icon;
712 * @param dir: The theme directory to work with
713 * @param size: The size to check
714 * @return Returns true if the size matches for the given directory, 0
716 * @brief Checks if the size matches for the given directory or not
719 efreet_icon_directory_size_match(Efreet_Icon_Theme_Directory *dir,
722 if (dir->type == EFREET_ICON_SIZE_TYPE_FIXED)
723 return (dir->size.normal == size);
725 if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE)
726 return ((dir->size.min < size) && (size < dir->size.max));
728 if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD)
729 return (((dir->size.normal - dir->size.threshold) < size)
730 && (size < (dir->size.normal + dir->size.threshold)));
737 * @param dir: The directory to work with
738 * @param size: The size to check for
739 * @return Returns the distance this size is away from the desired size
740 * @brief Returns the distance the given size is away from the desired size
743 efreet_icon_directory_size_distance(Efreet_Icon_Theme_Directory *dir,
746 if (dir->type == EFREET_ICON_SIZE_TYPE_FIXED)
747 return (abs(dir->size.normal - size));
749 if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE)
751 if (size < dir->size.min)
752 return dir->size.min - size;
753 if (dir->size.max < size)
754 return size - dir->size.max;
759 if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD)
761 if (size < (dir->size.normal - dir->size.threshold))
762 return (dir->size.min - size);
763 if ((dir->size.normal + dir->size.threshold) < size)
764 return (size - dir->size.max);
774 * @param icon_name: The icon name to look for
775 * @return Returns the Efreet_Icon for the given name or NULL if none found
776 * @brief Looks for the un-themed icon in the base directories
779 efreet_icon_fallback_icon(const char *icon_name)
783 if (!icon_name) return NULL;
784 icon = efreet_icon_cache_check(NULL, icon_name, 0);
785 if (icon) return icon;
787 icon = efreet_icon_fallback_dir_scan(efreet_icon_deprecated_user_dir_get(), icon_name);
789 icon = efreet_icon_fallback_dir_scan(efreet_icon_user_dir_get(), icon_name);
792 Ecore_List *xdg_dirs;
796 ecore_list_first_goto(efreet_extra_icon_dirs);
797 while ((dir = ecore_list_next(efreet_extra_icon_dirs)))
799 icon = efreet_icon_fallback_dir_scan(dir, icon_name);
802 efreet_icon_cache_add(NULL, icon_name, 0, icon);
807 xdg_dirs = efreet_data_dirs_get();
808 ecore_list_first_goto(xdg_dirs);
809 while ((dir = ecore_list_next(xdg_dirs)))
811 snprintf(path, PATH_MAX, "%s/icons", dir);
812 icon = efreet_icon_fallback_dir_scan(path, icon_name);
815 efreet_icon_cache_add(NULL, icon_name, 0, icon);
820 icon = efreet_icon_fallback_dir_scan("/usr/share/pixmaps", icon_name);
823 efreet_icon_cache_add(NULL, icon_name, 0, icon);
829 * @param dir: The directory to scan
830 * @param icon_name: The icon to look for
831 * @return Returns the icon for the given name or NULL on failure
832 * @brief Scans the given @a dir for the given @a icon_name returning the
833 * Efreet_icon if found, NULL otherwise.
836 efreet_icon_fallback_dir_scan(const char *dir, const char *icon_name)
839 char path[PATH_MAX], *ext;
840 const char *icon_path[] = { dir, "/", icon_name, NULL };
843 if (!dir || !icon_name) return NULL;
845 size = efreet_array_cat(path, sizeof(path), icon_path);
846 ecore_list_first_goto(efreet_icon_extensions);
847 while ((ext = ecore_list_next(efreet_icon_extensions)))
849 ecore_strlcpy(path + size, ext, sizeof(path) - size);
851 if (ecore_file_exists(path))
856 *(path + size) = '\0';
858 /* This is to catch non-conforming .desktop files */
862 if (ecore_file_exists(path))
867 printf("[Efreet]: Found an icon that already has an extension: %s\n", path);
878 * @param theme: The theme to work with
879 * @param dir: The theme directory to work with
880 * @param path: The partial path to use
881 * @return Returns no value
882 * @brief Caches the icons in the given theme directory path at the given
886 efreet_icon_lookup_directory_helper(Efreet_Icon_Theme_Directory *dir,
887 const char *path, const char *icon_name)
890 char file_path[PATH_MAX];
891 const char *ext, *path_strs[] = { path, "/", dir->name, "/", icon_name, NULL };
894 len = efreet_array_cat(file_path, sizeof(file_path), path_strs);
896 ecore_list_first_goto(efreet_icon_extensions);
897 while ((ext = ecore_list_next(efreet_icon_extensions)))
899 ecore_strlcpy(file_path + len, ext, sizeof(file_path) - len);
901 if (ecore_file_exists(file_path))
903 icon = strdup(file_path);
912 * @return Returns a new Efreet_Icon struct on success or NULL on failure
913 * @brief Creates a new Efreet_Icon struct
916 efreet_icon_new(const char *path)
921 icon = NEW(Efreet_Icon, 1);
922 icon->path = strdup(path);
924 /* load the .icon file if it's available */
925 p = strrchr(icon->path, '.');
928 char ico_path[PATH_MAX];
932 snprintf(ico_path, sizeof(ico_path), "%s.icon", icon->path);
935 if (ecore_file_exists(ico_path))
936 efreet_icon_populate(icon, ico_path);
943 file = ecore_file_file_get(icon->path);
944 p = strrchr(icon->path, '.');
946 icon->name = strdup(file);
954 * @param icon: The Efreet_Icon to cleanup
955 * @return Returns no value.
956 * @brief Free's the given icon and all its internal data.
959 efreet_icon_free(Efreet_Icon *icon)
964 if (icon->ref_count > 0) return;
968 IF_FREE_LIST(icon->attach_points);
975 * @param point: The Efreet_Icon_Point to free
976 * @return Returns no value
977 * @brief Frees the given structure
980 efreet_icon_point_free(Efreet_Icon_Point *point)
989 * @param icon: The icon to populate
990 * @param file: The file to populate from
991 * @return Returns no value
992 * @brief Tries to populate the icon information from the given file
995 efreet_icon_populate(Efreet_Icon *icon, const char *file)
1000 ini = efreet_ini_new(file);
1003 efreet_ini_free(ini);
1007 efreet_ini_section_set(ini, "Icon Data");
1008 tmp = efreet_ini_localestring_get(ini, "DisplayName");
1009 if (tmp) icon->name = strdup(tmp);
1011 tmp = efreet_ini_string_get(ini, "EmbeddedTextRectangle");
1020 for (i = 0; i < 4; i++)
1027 points[i] = atoi(s);
1038 icon->has_embedded_text_rectangle = 1;
1039 icon->embedded_text_rectangle.x0 = points[0];
1040 icon->embedded_text_rectangle.y0 = points[1];
1041 icon->embedded_text_rectangle.x1 = points[2];
1042 icon->embedded_text_rectangle.y1 = points[3];
1047 tmp = efreet_ini_string_get(ini, "AttachPoints");
1052 icon->attach_points = ecore_list_new();
1053 ecore_list_free_cb_set(icon->attach_points,
1054 ECORE_FREE_CB(efreet_icon_point_free));
1061 Efreet_Icon_Point *point;
1064 /* If this happens there is something wrong with the .icon file */
1067 point = NEW(Efreet_Icon_Point, 1);
1077 ecore_list_append(icon->attach_points, point);
1085 efreet_ini_free(ini);
1090 * @return Returns a new Efreet_Icon_Theme on success or NULL on failure
1091 * @brief Creates a new Efreet_Icon_Theme structure
1093 static Efreet_Icon_Theme *
1094 efreet_icon_theme_new(void)
1096 Efreet_Icon_Theme *theme;
1098 theme = NEW(Efreet_Icon_Theme, 1);
1105 * @param theme: The theme to free
1106 * @return Returns no value
1107 * @brief Frees up the @a theme structure.
1110 efreet_icon_theme_free(Efreet_Icon_Theme *theme)
1114 IF_RELEASE(theme->name.internal);
1115 IF_RELEASE(theme->name.name);
1117 IF_FREE(theme->comment);
1118 IF_FREE(theme->example_icon);
1120 if (theme->paths.count == 1)
1121 IF_FREE(theme->paths.path);
1123 IF_FREE_LIST(theme->paths.path);
1125 IF_FREE_LIST(theme->inherits);
1126 IF_FREE_LIST(theme->directories);
1133 * @param theme: The theme to work with
1134 * @param path: The path to add
1135 * @return Returns no value
1136 * @brief This will correctly add the given path to the list of theme paths.
1137 * @Note Assumes you've already verified that @a path is a valid directory.
1140 efreet_icon_theme_path_add(Efreet_Icon_Theme *theme, const char *path)
1142 if (!theme || !path) return;
1144 if (theme->paths.count == 0)
1145 theme->paths.path = strdup(path);
1147 else if (theme->paths.count > 1)
1148 ecore_list_append(theme->paths.path, strdup(path));
1154 old = theme->paths.path;
1155 theme->paths.path = ecore_list_new();
1156 ecore_list_free_cb_set(theme->paths.path, free);
1158 ecore_list_append(theme->paths.path, old);
1159 ecore_list_append(theme->paths.path, strdup(path));
1161 theme->paths.count++;
1166 * @return Returns no value
1167 * @brief This validates that our cache is still valid.
1169 * This is checked by the following algorithm:
1170 * - if we've check less then 5 seconds ago we're good
1171 * - if the mtime on the dir is less then our last check time we're good
1172 * - otherwise, invalidate the caches
1175 efreet_icon_theme_cache_check(Efreet_Icon_Theme *theme)
1179 new_check = ecore_time_get();
1181 /* we're within 5 seconds of the last time we checked the cache */
1182 if ((new_check - 5) <= theme->last_cache_check) return;
1185 efreet_icon_theme_dir_scan_all(theme->name.internal);
1187 else if (theme->paths.count == 1)
1188 efreet_icon_theme_cache_check_dir(theme, theme->paths.path);
1190 else if (theme->paths.count > 1)
1194 ecore_list_first_goto(theme->paths.path);
1195 while ((path = ecore_list_next(theme->paths.path)))
1197 if (!efreet_icon_theme_cache_check_dir(theme, path))
1201 theme->last_cache_check = new_check;
1206 * @param theme: The icon theme to check
1207 * @param dir: The directory to check
1208 * @return Returns 1 if the cache is still valid, 0 otherwise
1209 * @brief This will check if the theme cache is still valid. If it isn't the
1210 * cache will be invalided and 0 returned.
1213 efreet_icon_theme_cache_check_dir(Efreet_Icon_Theme *theme, const char *dir)
1217 /* have we modified this directory since our last cache check? */
1218 if (stat(dir, &buf) || (buf.st_mtime > theme->last_cache_check))
1220 ecore_hash_remove(efreet_icon_cache, theme);
1229 * @param theme_name: The theme to scan for
1230 * @return Returns no value
1231 * @brief Scans the theme directories. If @a theme_name is NULL it will load
1232 * up all theme data. If @a theme_name is not NULL it will look for that
1233 * specific theme data
1236 efreet_icon_theme_dir_scan_all(const char *theme_name)
1238 Ecore_List *xdg_dirs;
1239 char path[PATH_MAX], *dir;
1241 efreet_icon_theme_dir_scan(efreet_icon_deprecated_user_dir_get(), theme_name);
1242 efreet_icon_theme_dir_scan(efreet_icon_user_dir_get(), theme_name);
1244 xdg_dirs = efreet_data_dirs_get();
1245 ecore_list_first_goto(xdg_dirs);
1246 while ((dir = ecore_list_next(xdg_dirs)))
1248 snprintf(path, sizeof(path), "%s/icons", dir);
1249 efreet_icon_theme_dir_scan(path, theme_name);
1252 efreet_icon_theme_dir_scan("/usr/share/pixmaps", theme_name);
1257 * @param search_dir: The directory to scan
1258 * @param theme_name: Scan for this specific theme, set to NULL to find all
1260 * @return Returns no value
1261 * @brief Scans the given directory and adds non-hidden icon themes to the
1262 * given list. If the theme isnt' in our cache then load the index.theme and
1266 efreet_icon_theme_dir_scan(const char *search_dir, const char *theme_name)
1271 if (!search_dir) return;
1273 dirs = opendir(search_dir);
1276 while ((dir = readdir(dirs)))
1278 Efreet_Icon_Theme *theme;
1279 char path[PATH_MAX];
1282 if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")) continue;
1284 /* only care if this is a directory or the theme name matches the
1286 snprintf(path, sizeof(path), "%s/%s", search_dir, dir->d_name);
1287 if (((theme_name != NULL) && (strcmp(theme_name, dir->d_name)))
1288 || !ecore_file_is_dir(path))
1291 key = ecore_string_instance(dir->d_name);
1292 theme = ecore_hash_get(efreet_icon_themes, key);
1296 theme = efreet_icon_theme_new();
1297 theme->name.internal = key;
1298 ecore_hash_set(efreet_icon_themes,
1299 (void *)theme->name.internal, theme);
1305 ecore_string_release(key);
1308 efreet_icon_theme_path_add(theme, path);
1310 /* we're already valid so no reason to check for an index.theme file */
1311 if (theme->valid) continue;
1313 /* if the index.theme file exists we parse it into the theme */
1314 strncat(path, "/index.theme", sizeof(path));
1315 if (ecore_file_exists(path))
1316 efreet_icon_theme_index_read(theme, path);
1320 /* if we were given a theme name we want to make sure that that given
1321 * theme is valid before finishing, unless it's a fake theme */
1324 Efreet_Icon_Theme *theme;
1326 theme = ecore_hash_get(efreet_icon_themes, theme_name);
1327 if (theme && !theme->valid && !theme->fake)
1329 ecore_hash_remove(efreet_icon_themes, theme_name);
1330 efreet_icon_theme_free(theme);
1337 * @param theme: The theme to set the values into
1338 * @param path: The path to the index.theme file for this theme
1339 * @return Returns no value
1340 * @brief This will load up the theme with the data in the index.theme file
1343 efreet_icon_theme_index_read(Efreet_Icon_Theme *theme, const char *path)
1348 if (!theme || !path) return;
1350 ini = efreet_ini_new(path);
1353 efreet_ini_free(ini);
1357 efreet_ini_section_set(ini, "Icon Theme");
1358 tmp = efreet_ini_localestring_get(ini, "Name");
1359 if (tmp) theme->name.name = ecore_string_instance(tmp);
1361 tmp = efreet_ini_localestring_get(ini, "Comment");
1362 if (tmp) theme->comment = strdup(tmp);
1364 tmp = efreet_ini_string_get(ini, "Example");
1365 if (tmp) theme->example_icon = strdup(tmp);
1367 theme->hidden = efreet_ini_boolean_get(ini, "Hidden");
1371 /* Check the inheritance. If there is none we inherit from the hicolor theme */
1372 tmp = efreet_ini_string_get(ini, "Inherits");
1377 theme->inherits = ecore_list_new();
1378 ecore_list_free_cb_set(theme->inherits, free);
1388 ecore_list_append(theme->inherits, strdup(s));
1392 ecore_list_append(theme->inherits, strdup(s));
1397 /* make sure this one is done last as setting the directory will change
1398 * the ini section ... */
1399 tmp = efreet_ini_string_get(ini, "Directories");
1404 theme->directories = ecore_list_new();
1405 ecore_list_free_cb_set(theme->directories,
1406 ECORE_FREE_CB(efreet_icon_theme_directory_free));
1418 ecore_list_append(theme->directories,
1419 efreet_icon_theme_directory_new(ini, s));
1427 efreet_ini_free(ini);
1432 * @return Returns no value
1433 * @brief Because the theme icon directories can be spread over multiple
1434 * base directories we may need to create the icon theme strucutre before
1435 * finding the index.theme file. It may also be that we never find an
1436 * index.theme file as this isn't a valid theme. This function makes sure
1437 * that everything we've got in our hash has a valid key to it.
1440 efreet_icon_theme_dir_validity_check(void)
1445 keys = ecore_hash_keys(efreet_icon_themes);
1446 ecore_list_first_goto(keys);
1447 while ((name = ecore_list_next(keys)))
1449 Efreet_Icon_Theme *theme;
1451 theme = ecore_hash_get(efreet_icon_themes, name);
1452 if (!theme->valid && !theme->fake)
1454 ecore_hash_remove(efreet_icon_themes, name);
1455 efreet_icon_theme_free(theme);
1458 ecore_list_destroy(keys);
1463 * @param ini: The ini file with information on this directory
1464 * @param name: The name of the directory
1465 * @return Returns a new Efreet_Icon_Theme_Directory based on the
1466 * information in @a ini.
1467 * @brief Creates and initialises an icon theme directory from the given ini
1470 static Efreet_Icon_Theme_Directory *
1471 efreet_icon_theme_directory_new(Efreet_Ini *ini, const char *name)
1473 Efreet_Icon_Theme_Directory *dir;
1477 if (!ini) return NULL;
1479 dir = NEW(Efreet_Icon_Theme_Directory, 1);
1480 dir->name = strdup(name);
1482 efreet_ini_section_set(ini, name);
1484 tmp = efreet_ini_string_get(ini, "Context");
1487 if (!strcasecmp(tmp, "Actions"))
1488 dir->context = EFREET_ICON_THEME_CONTEXT_ACTIONS;
1490 else if (!strcasecmp(tmp, "Devices"))
1491 dir->context = EFREET_ICON_THEME_CONTEXT_DEVICES;
1493 else if (!strcasecmp(tmp, "FileSystems"))
1494 dir->context = EFREET_ICON_THEME_CONTEXT_FILESYSTEMS;
1496 else if (!strcasecmp(tmp, "MimeTypes"))
1497 dir->context = EFREET_ICON_THEME_CONTEXT_MIMETYPES;
1500 tmp = efreet_ini_string_get(ini, "Type");
1503 if (!strcasecmp(tmp, "Fixed"))
1504 dir->type = EFREET_ICON_SIZE_TYPE_FIXED;
1506 else if (!strcasecmp(tmp, "Scalable"))
1507 dir->type = EFREET_ICON_SIZE_TYPE_SCALABLE;
1509 else if (!strcasecmp(tmp, "Threshold"))
1510 dir->type = EFREET_ICON_SIZE_TYPE_THRESHOLD;
1513 dir->size.normal = efreet_ini_int_get(ini, "Size");
1515 val = efreet_ini_int_get(ini, "MinSize");
1516 if (val < 0) dir->size.min = dir->size.normal;
1517 else dir->size.min = val;
1519 val = efreet_ini_int_get(ini, "MaxSize");
1520 if (val < 0) dir->size.max = dir->size.normal;
1521 else dir->size.max = val;
1523 val = efreet_ini_int_get(ini, "Threshold");
1524 if (val < 0) dir->size.threshold = 2;
1525 else dir->size.threshold = val;
1532 * @param dir: The Efreet_Icon_Theme_Directory to free
1533 * @return Returns no value
1534 * @brief Frees the given directory @a dir
1537 efreet_icon_theme_directory_free(Efreet_Icon_Theme_Directory *dir)
1546 efreet_icon_cache_find(Efreet_Icon_Cache *value, const char *key)
1548 if (!value || !key) return -1;
1549 return strcmp(value->key, key);
1553 efreet_icon_cache_flush(Ecore_List *list)
1556 * * Dynamic cache size
1557 * * Maybe add references to cache, so that we sort on how often a value is used
1559 while (ecore_list_count(list) > 100)
1561 Efreet_Icon_Cache *cache;
1563 cache = ecore_list_last_remove(list);
1564 efreet_icon_cache_free(cache);
1569 efreet_icon_cache_free(Efreet_Icon_Cache *value)
1573 IF_FREE(value->key);
1574 IF_FREE(value->path);
1579 efreet_icon_cache_check(Efreet_Icon_Theme *theme, const char *icon, unsigned int size)
1582 Efreet_Icon_Cache *cache;
1586 list = ecore_hash_get(efreet_icon_cache, theme);
1589 list = ecore_list_new();
1590 ecore_list_free_cb_set(list, ECORE_FREE_CB(efreet_icon_cache_free));
1591 ecore_hash_set(efreet_icon_cache, theme, list);
1595 snprintf(key, sizeof(key), "%s %d", icon, size);
1596 cache = ecore_list_find(list, ECORE_COMPARE_CB(efreet_icon_cache_find), key);
1599 ecore_list_remove(list);
1602 ecore_list_prepend(list, cache);
1603 return NON_EXISTING;
1605 else if (!stat(cache->path, &st) && st.st_mtime == cache->lasttime)
1607 ecore_list_prepend(list, cache);
1608 return strdup(cache->path);
1610 efreet_icon_cache_free(cache);
1616 efreet_icon_cache_add(Efreet_Icon_Theme *theme, const char *icon, unsigned int size, const char *value)
1619 Efreet_Icon_Cache *cache;
1623 list = ecore_hash_get(efreet_icon_cache, theme);
1626 list = ecore_list_new();
1627 ecore_list_free_cb_set(list, ECORE_FREE_CB(efreet_icon_cache_free));
1628 ecore_hash_set(efreet_icon_cache, theme, list);
1631 snprintf(key, sizeof(key), "%s %d", icon, size);
1632 cache = NEW(Efreet_Icon_Cache, 1);
1633 cache->key = strdup(key);
1634 if ((value) && !stat(value, &st))
1636 cache->path = strdup(value);
1637 cache->lasttime = st.st_mtime;
1640 cache->lasttime = ecore_time_get();
1641 ecore_list_prepend(list, cache);
1642 efreet_icon_cache_flush(list);