add null theme check
[framework/uifw/efreet.git] / src / lib / efreet_icon.c
1 /* vim: set sw=4 ts=4 sts=4 et: */
2 #include "Efreet.h"
3 #include "efreet_private.h"
4
5 #define NON_EXISTING (void *)-1
6
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;
13
14 static int efreet_icon_init_count = 0;
15
16 typedef struct Efreet_Icon_Cache Efreet_Icon_Cache;
17 struct Efreet_Icon_Cache
18 {
19     char *key;
20     char *path;
21     time_t lasttime;
22 };
23
24 static char *efreet_icon_remove_extension(const char *icon);
25 static Efreet_Icon_Theme *efreet_icon_find_theme_check(const char *theme_name);
26
27
28 static char *efreet_icon_find_fallback(Efreet_Icon_Theme *theme,
29                                        const char *icon,
30                                        unsigned int size);
31 static char *efreet_icon_list_find_fallback(Efreet_Icon_Theme *theme,
32                                             Ecore_List *icons,
33                                             unsigned int size);
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);
43
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,
48                                                     unsigned int size);
49 static int efreet_icon_directory_size_match(Efreet_Icon_Theme_Directory *dir,
50                                                   unsigned int size);
51
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);
55
56 static char *efreet_icon_lookup_directory_helper(Efreet_Icon_Theme_Directory *dir,
57                                     const char *path, const char *icon_name);
58
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,
66                                                 const char *path);
67 static void efreet_icon_theme_index_read(Efreet_Icon_Theme *theme,
68                                                 const char *path);
69
70 static Efreet_Icon_Theme_Directory *efreet_icon_theme_directory_new(Efreet_Ini *ini,
71                                                                 const char *name);
72 static void efreet_icon_theme_directory_free(Efreet_Icon_Theme_Directory *dir);
73
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,
76                                                         const char *dir);
77
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);
83
84 /**
85  * @internal
86  * @return Returns 1 on success or 0 on failure
87  * @brief Initializes the icon system
88  */
89 int
90 efreet_icon_init(void)
91 {
92     if (efreet_icon_init_count++ > 0)
93         return efreet_icon_init_count;
94
95     if (!efreet_icon_themes)
96     {
97         const char *default_exts[] = {".png", ".xpm", NULL};
98         int i;
99
100         if (!ecore_init())
101         {
102             efreet_icon_init_count--;
103             return 0;
104         }
105
106         /* setup the default extension list */
107         efreet_icon_extensions = ecore_list_new();
108         ecore_list_free_cb_set(efreet_icon_extensions, free);
109
110         for (i = 0; default_exts[i] != NULL; i++)
111             ecore_list_append(efreet_icon_extensions, strdup(default_exts[i]));
112
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();
117
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));
120     }
121
122     return 1;
123 }
124
125 /**
126  * @internal
127  * @return Returns no value
128  * @brief Shuts down the icon system
129  */
130 void
131 efreet_icon_shutdown(void)
132 {
133     if (--efreet_icon_init_count)
134         return;
135
136     IF_FREE(efreet_icon_user_dir);
137     IF_FREE(efreet_icon_deprecated_user_dir);
138
139     IF_FREE_LIST(efreet_icon_extensions);
140     IF_FREE_HASH(efreet_icon_themes);
141     IF_FREE_LIST(efreet_extra_icon_dirs);
142
143     IF_FREE_HASH(efreet_icon_cache);
144
145     ecore_shutdown();
146     efreet_icon_init_count = 0;
147 }
148
149 /**
150  * @return Returns the user icon directory
151  * @brief Returns the user icon directory
152  */
153 const char *
154 efreet_icon_deprecated_user_dir_get(void)
155 {
156     const char *user;
157     int len;
158
159     if (efreet_icon_deprecated_user_dir) return efreet_icon_deprecated_user_dir;
160
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);
165
166     return efreet_icon_deprecated_user_dir;
167 }
168
169 EAPI const char *
170 efreet_icon_user_dir_get(void)
171 {
172     const char *user;
173     int len;
174
175     if (efreet_icon_user_dir) return efreet_icon_user_dir;
176
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);
181
182     return efreet_icon_user_dir;
183 }
184
185 /**
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
189  */
190 EAPI void
191 efreet_icon_extension_add(const char *ext)
192 {
193     ecore_list_prepend(efreet_icon_extensions, strdup(ext));
194 }
195
196 /**
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().
203  */
204 EAPI Ecore_List *
205 efreet_icon_extra_list_get(void)
206 {
207     return efreet_extra_icon_dirs;
208 }
209
210 /**
211  * @return Returns a list of Efreet_Icon structs for all the non-hidden icon
212  * themes
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.
215  */
216 EAPI Ecore_List *
217 efreet_icon_theme_list_get(void)
218 {
219     Ecore_List *list, *theme_list;
220     char *dir;
221
222     /* update the list to include all icon themes */
223     efreet_icon_theme_dir_scan_all(NULL);
224     efreet_icon_theme_dir_validity_check();
225
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)))
231     {
232         Efreet_Icon_Theme *theme;
233
234         theme = ecore_hash_get(efreet_icon_themes, dir);
235         if (theme->hidden || theme->fake) continue;
236 #ifndef STRICT_SPEC
237         if (!theme->name.name) continue;
238 #endif
239
240         ecore_list_append(list, theme);
241     }
242     ecore_list_destroy(theme_list);
243
244     return list;
245 }
246
247 /**
248  * @param theme_name: The theme to look for
249  * @return Returns the icon theme related to the given theme name or NULL if
250  * none exists.
251  * @brief Tries to get the icon theme structure for the given theme name
252  */
253 EAPI Efreet_Icon_Theme *
254 efreet_icon_theme_find(const char *theme_name)
255 {
256     const char *key;
257     Efreet_Icon_Theme *theme;
258
259     key = ecore_string_instance(theme_name);
260     theme = ecore_hash_get(efreet_icon_themes, key);
261     if (!theme)
262     {
263         efreet_icon_theme_dir_scan_all(theme_name);
264         theme = ecore_hash_get(efreet_icon_themes, key);
265     }
266     ecore_string_release(key);
267
268     return theme;
269 }
270
271 /**
272  * @internal
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.
276  */
277 static char *
278 efreet_icon_remove_extension(const char *icon)
279 {
280     char *tmp = NULL, *ext = NULL;
281
282     tmp = strdup(icon);
283     ext = strrchr(tmp, '.');
284     if (ext)
285     {
286         const char *ext2;
287         ecore_list_first_goto(efreet_icon_extensions);
288         while ((ext2 = ecore_list_next(efreet_icon_extensions)))
289         {
290             if (!strcmp(ext, ext2))
291             {
292 #ifdef STRICT_SPEC
293                 printf("[Efreet]: Requesting an icon with an extension: %s\n",
294                                                                         icon);
295 #endif
296                 *ext = '\0';
297                 break;
298             }
299         }
300     }
301
302     return tmp;
303 }
304
305 /**
306  * @internal
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.
311  */
312 static Efreet_Icon_Theme *
313 efreet_icon_find_theme_check(const char *theme_name)
314 {
315     Efreet_Icon_Theme *theme = NULL;
316     if (theme_name) theme = efreet_icon_theme_find(theme_name);
317     if (!theme)
318     {
319         theme = efreet_icon_theme_new();
320         theme->fake = 1;
321         theme->name.internal = ecore_string_instance(theme_name);
322         ecore_hash_set(efreet_icon_themes, (void *)theme->name.internal, theme);
323     }
324
325     return theme;
326 }
327
328 /**
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.
334  */
335 EAPI char *
336 efreet_icon_path_find(const char *theme_name, const char *icon, unsigned int size)
337 {
338     char *value = NULL;
339     Efreet_Icon_Theme *theme;
340
341     theme = efreet_icon_find_theme_check(theme_name);
342
343 #ifdef SLOPPY_SPEC
344     {
345         char *tmp;
346
347         tmp = efreet_icon_remove_extension(icon);
348         value = efreet_icon_find_helper(theme, tmp, size);
349         FREE(tmp);
350     }
351 #else
352     value = efreet_icon_find_helper(theme, icon, size);
353 #endif
354
355     /* we didn't find the icon in the theme or in the inherited directories
356      * then just look for a non theme icon
357      */
358     if (!value || (value == NON_EXISTING)) value = efreet_icon_fallback_icon(icon);
359
360     if (value == NON_EXISTING) value = NULL;
361     return value;
362 }
363
364 /**
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
371  * the list.
372  * @note This function will search the given theme for all icons before falling
373  * back. This is useful when searching for mimetype icons.
374  */
375 EAPI char *
376 efreet_icon_list_find(const char *theme_name, Ecore_List *icons,
377                                                             unsigned int size)
378 {
379     const char *icon = NULL;
380     char *value = NULL;
381     Efreet_Icon_Theme *theme;
382
383     theme = efreet_icon_find_theme_check(theme_name);
384
385     ecore_list_first_goto(icons);
386 #ifdef SLOPPY_SPEC
387     {
388         Ecore_List *tmps = NULL;
389
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));
395
396         value = efreet_icon_list_find_helper(theme, tmps, size);
397         ecore_list_destroy(tmps);
398     }
399 #else
400     value = efreet_icon_list_find_helper(theme, icons, size);
401 #endif
402
403     /* we didn't find the icons in the theme or in the inherited directories
404      * then just look for a non theme icon
405      */
406     if (!value || (value == NON_EXISTING))
407     {
408         ecore_list_first_goto(icons);
409         while ((icon = ecore_list_next(icons)))
410         {
411             value = efreet_icon_fallback_icon(icon);
412             if (value && (value != NON_EXISTING))
413                 break;
414         }
415     }
416
417     if (value == NON_EXISTING) value = NULL;
418     return value;
419 }
420
421 /**
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.
428  */
429 EAPI Efreet_Icon *
430 efreet_icon_find(const char *theme_name, const char *icon, unsigned int size)
431 {
432     char *path;
433
434     path = efreet_icon_path_find(theme_name, icon, size);
435     if (path)
436     {
437         Efreet_Icon *icon;
438
439         icon = efreet_icon_new(path);
440         free(path);
441         return icon;
442     }
443
444     return NULL;
445 }
446
447 /**
448  * @internal
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
453  * icon found
454  * @brief Scans inheriting themes for the given icon
455  */
456 static char *
457 efreet_icon_find_fallback(Efreet_Icon_Theme *theme,
458                           const char *icon, unsigned int size)
459 {
460     char *parent = NULL;
461     char *value = NULL;
462
463     if (theme->inherits)
464     {
465         ecore_list_first_goto(theme->inherits);
466         while ((parent = ecore_list_next(theme->inherits)))
467         {
468             Efreet_Icon_Theme *parent_theme;
469
470             parent_theme = efreet_icon_theme_find(parent);
471             if ((!parent_theme) || (parent_theme == theme)) continue;
472
473             value = efreet_icon_find_helper(parent_theme, icon, size);
474             if (value && (value != NON_EXISTING)) break;
475         }
476     }
477     /* if this isn't the hicolor theme, and we have no other fallbacks
478      * check hicolor */
479     else if (strcmp(theme->name.internal, "hicolor"))
480     {
481         Efreet_Icon_Theme *parent_theme;
482
483         parent_theme = efreet_icon_theme_find("hicolor");
484         if (parent_theme)
485             value = efreet_icon_find_helper(parent_theme, icon, size);
486     }
487
488     return value;
489 }
490
491 /**
492  * @internal
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
497  * icon found
498  * @brief Scans the theme and any inheriting themes for the given icon
499  */
500 static char *
501 efreet_icon_find_helper(Efreet_Icon_Theme *theme,
502                         const char *icon, unsigned int size)
503 {
504     char *value;
505     static int recurse = 0;
506
507     efreet_icon_theme_cache_check(theme);
508
509     /* go no further if this theme is fake */
510     if (theme->fake || !theme->valid) return NULL;
511
512     /* limit recursion in finding themes and inherited themes to 256 levels */
513     if (recurse > 256) return NULL;
514     recurse++;
515
516     value = efreet_icon_lookup_icon(theme, icon, size);
517
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);
521
522     recurse--;
523     return value;
524 }
525
526 /**
527  * @internal
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
532  * icon found
533  * @brief Scans inheriting themes for the given icons
534  */
535 static char *
536 efreet_icon_list_find_fallback(Efreet_Icon_Theme *theme,
537                                Ecore_List *icons, unsigned int size)
538 {
539     char *parent = NULL;
540     char *value = NULL;
541
542     if (theme->inherits)
543     {
544         ecore_list_first_goto(theme->inherits);
545         while ((parent = ecore_list_next(theme->inherits)))
546         {
547             Efreet_Icon_Theme *parent_theme;
548
549             parent_theme = efreet_icon_theme_find(parent);
550             if ((!parent_theme) || (parent_theme == theme)) continue;
551
552             value = efreet_icon_list_find_helper(parent_theme,
553                                                         icons, size);
554             if (value && (value != NON_EXISTING)) break;
555         }
556     }
557
558     /* if this isn't the hicolor theme, and we have no other fallbacks
559      * check hicolor
560      */
561     else if (strcmp(theme->name.internal, "hicolor"))
562     {
563         Efreet_Icon_Theme *parent_theme;
564
565         parent_theme = efreet_icon_theme_find("hicolor");
566         if (parent_theme)
567             value = efreet_icon_list_find_helper(parent_theme,
568                                                         icons, size);
569     }
570
571     return value;
572 }
573
574 /**
575  * @internal
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
580  * icon found
581  * @brief Scans the theme and any inheriting themes for the given icons
582  */
583 static char *
584 efreet_icon_list_find_helper(Efreet_Icon_Theme *theme,
585                              Ecore_List *icons, unsigned int size)
586 {
587     char *value = NULL;
588     const char *icon = NULL;
589     static int recurse = 0;
590
591     efreet_icon_theme_cache_check(theme);
592
593     /* go no further if this theme is fake */
594     if (theme->fake || !theme->valid) return NULL;
595
596     /* limit recursion in finding themes and inherited themes to 256 levels */
597     if (recurse > 256) return NULL;
598     recurse++;
599
600     ecore_list_first_goto(icons);
601     while ((icon = ecore_list_next(icons)))
602     {
603         value = efreet_icon_lookup_icon(theme, icon, size);
604         if (value && (value != NON_EXISTING))
605             break;
606     }
607
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);
611
612     recurse--;
613     return value;
614 }
615
616 /**
617  * @internal
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
622  * none found
623  * @brief Looks for the @a icon in the @a theme for the @a size given.
624  */
625 static char *
626 efreet_icon_lookup_icon(Efreet_Icon_Theme *theme, const char *icon_name,
627                                                     unsigned int size)
628 {
629     char *icon = NULL, *tmp = NULL;
630     Efreet_Icon_Theme_Directory *dir;
631     int minimal_size = INT_MAX;
632
633     if (!theme || (theme->paths.count == 0) || !icon_name || !size)
634         return NULL;
635
636     icon = efreet_icon_cache_check(theme, icon_name, size);
637     if (icon) return icon;
638
639     /* search for allowed size == requested size */
640     ecore_list_first_goto(theme->directories);
641     while ((dir = ecore_list_next(theme->directories)))
642     {
643         if (!efreet_icon_directory_size_match(dir, size)) continue;
644         icon = efreet_icon_lookup_directory(theme, dir,
645                                             icon_name);
646         if (icon)
647         {
648             efreet_icon_cache_add(theme, icon_name, size, icon);
649             return icon;
650         }
651     }
652
653     /* search for any icon that matches */
654     ecore_list_first_goto(theme->directories);
655     while ((dir = ecore_list_next(theme->directories)))
656     {
657         int distance;
658
659         distance = efreet_icon_directory_size_distance(dir, size);
660         if (distance >= minimal_size) continue;
661
662         tmp = efreet_icon_lookup_directory(theme, dir,
663                                            icon_name);
664         if (tmp)
665         {
666             FREE(icon);
667             icon = tmp;
668             minimal_size = distance;
669         }
670     }
671
672     efreet_icon_cache_add(theme, icon_name, size, icon);
673     return icon;
674 }
675
676
677 /**
678  * @internal
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
683  * none found
684  * @brief Tries to find the file closest matching the given icon
685  */
686 static char *
687 efreet_icon_lookup_directory(Efreet_Icon_Theme *theme,
688                              Efreet_Icon_Theme_Directory *dir,
689                              const char *icon_name)
690 {
691     if (theme->paths.count == 1)
692         return efreet_icon_lookup_directory_helper(dir, theme->paths.path, icon_name);
693
694     else
695     {
696         char *icon;
697         const char *path;
698
699         ecore_list_first_goto(theme->paths.path);
700         while ((path = ecore_list_next(theme->paths.path)))
701         {
702             icon = efreet_icon_lookup_directory_helper(dir, path, icon_name);
703             if (icon) return icon;
704         }
705     }
706
707     return NULL;
708 }
709
710 /**
711  * @internal
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
715  * otherwise
716  * @brief Checks if the size matches for the given directory or not
717  */
718 static int
719 efreet_icon_directory_size_match(Efreet_Icon_Theme_Directory *dir,
720                                 unsigned int size)
721 {
722     if (dir->type == EFREET_ICON_SIZE_TYPE_FIXED)
723         return (dir->size.normal == size);
724
725     if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE)
726         return ((dir->size.min < size) && (size < dir->size.max));
727
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)));
731
732     return 0;
733 }
734
735 /**
736  * @internal
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
741  */
742 static int
743 efreet_icon_directory_size_distance(Efreet_Icon_Theme_Directory *dir,
744                                     unsigned int size)
745 {
746     if (dir->type == EFREET_ICON_SIZE_TYPE_FIXED)
747         return (abs(dir->size.normal - size));
748
749     if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE)
750     {
751         if (size < dir->size.min)
752             return dir->size.min - size;
753         if (dir->size.max < size)
754             return size - dir->size.max;
755
756         return 0;
757     }
758
759     if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD)
760     {
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);
765
766         return 0;
767     }
768
769     return 0;
770 }
771
772 /**
773  * @internal
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
777  */
778 static char *
779 efreet_icon_fallback_icon(const char *icon_name)
780 {
781     char *icon;
782
783     if (!icon_name) return NULL;
784     icon = efreet_icon_cache_check(NULL, icon_name, 0);
785     if (icon) return icon;
786
787     icon = efreet_icon_fallback_dir_scan(efreet_icon_deprecated_user_dir_get(), icon_name);
788     if (!icon)
789         icon = efreet_icon_fallback_dir_scan(efreet_icon_user_dir_get(), icon_name);
790     if (!icon)
791     {
792         Ecore_List *xdg_dirs;
793         const char *dir;
794         char path[PATH_MAX];
795
796         ecore_list_first_goto(efreet_extra_icon_dirs);
797         while ((dir = ecore_list_next(efreet_extra_icon_dirs)))
798         {
799             icon = efreet_icon_fallback_dir_scan(dir, icon_name);
800             if (icon)
801             {
802                 efreet_icon_cache_add(NULL, icon_name, 0, icon);
803                 return icon;
804             }
805         }
806
807         xdg_dirs = efreet_data_dirs_get();
808         ecore_list_first_goto(xdg_dirs);
809         while ((dir = ecore_list_next(xdg_dirs)))
810         {
811             snprintf(path, PATH_MAX, "%s/icons", dir);
812             icon = efreet_icon_fallback_dir_scan(path, icon_name);
813             if (icon)
814             {
815                 efreet_icon_cache_add(NULL, icon_name, 0, icon);
816                 return icon;
817             }
818         }
819
820         icon = efreet_icon_fallback_dir_scan("/usr/share/pixmaps", icon_name);
821     }
822
823     efreet_icon_cache_add(NULL, icon_name, 0, icon);
824     return icon;
825 }
826
827 /**
828  * @internal
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.
834  */
835 static char *
836 efreet_icon_fallback_dir_scan(const char *dir, const char *icon_name)
837 {
838     char *icon = NULL;
839     char path[PATH_MAX], *ext;
840     const char *icon_path[] = { dir, "/", icon_name, NULL };
841     size_t size;
842
843     if (!dir || !icon_name) return NULL;
844
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)))
848     {
849         ecore_strlcpy(path + size, ext, sizeof(path) - size);
850
851         if (ecore_file_exists(path))
852         {
853             icon = strdup(path);
854             if (icon) break;
855         }
856         *(path + size) = '\0';
857     }
858     /* This is to catch non-conforming .desktop files */
859 #ifdef SLOPPY_SPEC
860     if (!icon)
861     {
862         if (ecore_file_exists(path))
863         {
864             icon = strdup(path);
865 #ifdef STRICT_SPEC
866             if (icon)
867                 printf("[Efreet]: Found an icon that already has an extension: %s\n", path);
868 #endif
869         }
870     }
871 #endif
872
873     return icon;
874 }
875
876 /**
877  * @internal
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
883  * size
884  */
885 static char *
886 efreet_icon_lookup_directory_helper(Efreet_Icon_Theme_Directory *dir,
887                                     const char *path, const char *icon_name)
888 {
889     char *icon = NULL;
890     char file_path[PATH_MAX];
891     const char *ext, *path_strs[] = { path, "/", dir->name, "/", icon_name, NULL };
892     size_t len;
893
894     len = efreet_array_cat(file_path, sizeof(file_path), path_strs);
895
896     ecore_list_first_goto(efreet_icon_extensions);
897     while ((ext = ecore_list_next(efreet_icon_extensions)))
898     {
899         ecore_strlcpy(file_path + len, ext, sizeof(file_path) - len);
900
901         if (ecore_file_exists(file_path))
902         {
903             icon = strdup(file_path);
904             break;
905         }
906     }
907     return icon;
908 }
909
910 /**
911  * @internal
912  * @return Returns a new Efreet_Icon struct on success or NULL on failure
913  * @brief Creates a new Efreet_Icon struct
914  */
915 static Efreet_Icon *
916 efreet_icon_new(const char *path)
917 {
918     Efreet_Icon *icon;
919     char *p;
920
921     icon = NEW(Efreet_Icon, 1);
922     icon->path = strdup(path);
923
924     /* load the .icon file if it's available */
925     p = strrchr(icon->path, '.');
926     if (p)
927     {
928         char ico_path[PATH_MAX];
929
930         *p = '\0';
931
932         snprintf(ico_path, sizeof(ico_path), "%s.icon", icon->path);
933         *p = '.';
934
935         if (ecore_file_exists(ico_path))
936             efreet_icon_populate(icon, ico_path);
937     }
938
939     if (!icon->name)
940     {
941         const char *file;
942
943         file = ecore_file_file_get(icon->path);
944         p = strrchr(icon->path, '.');
945         if (p) *p = '\0';
946         icon->name = strdup(file);
947         if (p) *p = '.';
948     }
949
950     return icon;
951 }
952
953 /**
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.
957  */
958 EAPI void
959 efreet_icon_free(Efreet_Icon *icon)
960 {
961     if (!icon) return;
962
963     icon->ref_count --;
964     if (icon->ref_count > 0) return;
965
966     IF_FREE(icon->path);
967     IF_FREE(icon->name);
968     IF_FREE_LIST(icon->attach_points);
969
970     FREE(icon);
971 }
972
973 /**
974  * @internal
975  * @param point: The Efreet_Icon_Point to free
976  * @return Returns no value
977  * @brief Frees the given structure
978  */
979 static void
980 efreet_icon_point_free(Efreet_Icon_Point *point)
981 {
982     if (!point) return;
983
984     FREE(point);
985 }
986
987 /**
988  * @internal
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
993  */
994 static void
995 efreet_icon_populate(Efreet_Icon *icon, const char *file)
996 {
997     Efreet_Ini *ini;
998     const char *tmp;
999
1000     ini = efreet_ini_new(file);
1001     if (!ini->data)
1002     {
1003         efreet_ini_free(ini);
1004         return;
1005     }
1006
1007     efreet_ini_section_set(ini, "Icon Data");
1008     tmp = efreet_ini_localestring_get(ini, "DisplayName");
1009     if (tmp) icon->name = strdup(tmp);
1010
1011     tmp = efreet_ini_string_get(ini, "EmbeddedTextRectangle");
1012     if (tmp)
1013     {
1014         int points[4];
1015         char *t, *s, *p;
1016         int i;
1017
1018         t = strdup(tmp);
1019         s = t;
1020         for (i = 0; i < 4; i++)
1021         {
1022             if (s)
1023             {
1024                 p = strchr(s, ',');
1025
1026                 if (p) *p = '\0';
1027                 points[i] = atoi(s);
1028
1029                 if (p) s = ++p;
1030                 else s = NULL;
1031             }
1032             else
1033             {
1034                 points[i] = 0;
1035             }
1036         }
1037
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];
1043
1044         FREE(t);
1045     }
1046
1047     tmp = efreet_ini_string_get(ini, "AttachPoints");
1048     if (tmp)
1049     {
1050         char *t, *s, *p;
1051
1052         icon->attach_points = ecore_list_new();
1053         ecore_list_free_cb_set(icon->attach_points,
1054                             ECORE_FREE_CB(efreet_icon_point_free));
1055
1056         t = strdup(tmp);
1057         s = t;
1058         p = t;
1059         while (s)
1060         {
1061             Efreet_Icon_Point *point;
1062
1063             p = strchr(s, ',');
1064             /* If this happens there is something wrong with the .icon file */
1065             if (!p) break;
1066
1067             point = NEW(Efreet_Icon_Point, 1);
1068
1069             *p = '\0';
1070             point->x = atoi(s);
1071
1072             s = ++p;
1073             p = strchr(s, '|');
1074             if (p) *p = '\0';
1075
1076             point->y = atoi(s);
1077             ecore_list_append(icon->attach_points, point);
1078
1079             if (p) s = ++p;
1080             else s = NULL;
1081         }
1082         FREE(t);
1083     }
1084
1085     efreet_ini_free(ini);
1086 }
1087
1088 /**
1089  * @internal
1090  * @return Returns a new Efreet_Icon_Theme on success or NULL on failure
1091  * @brief Creates a new Efreet_Icon_Theme structure
1092  */
1093 static Efreet_Icon_Theme *
1094 efreet_icon_theme_new(void)
1095 {
1096     Efreet_Icon_Theme *theme;
1097
1098     theme = NEW(Efreet_Icon_Theme, 1);
1099
1100     return theme;
1101 }
1102
1103 /**
1104  * @internal
1105  * @param theme: The theme to free
1106  * @return Returns no value
1107  * @brief Frees up the @a theme structure.
1108  */
1109 static void
1110 efreet_icon_theme_free(Efreet_Icon_Theme *theme)
1111 {
1112     if (!theme) return;
1113
1114     IF_RELEASE(theme->name.internal);
1115     IF_RELEASE(theme->name.name);
1116
1117     IF_FREE(theme->comment);
1118     IF_FREE(theme->example_icon);
1119
1120     if (theme->paths.count == 1)
1121         IF_FREE(theme->paths.path);
1122     else
1123         IF_FREE_LIST(theme->paths.path);
1124
1125     IF_FREE_LIST(theme->inherits);
1126     IF_FREE_LIST(theme->directories);
1127
1128     FREE(theme);
1129 }
1130
1131 /**
1132  * @internal
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.
1138  */
1139 static void
1140 efreet_icon_theme_path_add(Efreet_Icon_Theme *theme, const char *path)
1141 {
1142     if (!theme || !path) return;
1143
1144     if (theme->paths.count == 0)
1145         theme->paths.path = strdup(path);
1146
1147     else if (theme->paths.count > 1)
1148         ecore_list_append(theme->paths.path, strdup(path));
1149
1150     else
1151     {
1152         char *old;
1153
1154         old = theme->paths.path;
1155         theme->paths.path = ecore_list_new();
1156         ecore_list_free_cb_set(theme->paths.path, free);
1157
1158         ecore_list_append(theme->paths.path, old);
1159         ecore_list_append(theme->paths.path, strdup(path));
1160     }
1161     theme->paths.count++;
1162 }
1163
1164 /**
1165  * @internal
1166  * @return Returns no value
1167  * @brief This validates that our cache is still valid.
1168  *
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
1173  */
1174 static void
1175 efreet_icon_theme_cache_check(Efreet_Icon_Theme *theme)
1176 {
1177     double new_check;
1178
1179     new_check = ecore_time_get();
1180
1181     /* we're within 5 seconds of the last time we checked the cache */
1182     if ((new_check - 5) <= theme->last_cache_check) return;
1183
1184     if (theme->fake)
1185         efreet_icon_theme_dir_scan_all(theme->name.internal);
1186
1187     else if (theme->paths.count == 1)
1188         efreet_icon_theme_cache_check_dir(theme, theme->paths.path);
1189
1190     else if (theme->paths.count > 1)
1191     {
1192         char *path;
1193
1194         ecore_list_first_goto(theme->paths.path);
1195         while ((path = ecore_list_next(theme->paths.path)))
1196         {
1197             if (!efreet_icon_theme_cache_check_dir(theme, path))
1198                 break;
1199         }
1200     }
1201     theme->last_cache_check = new_check;
1202 }
1203
1204 /**
1205  * @internal
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.
1211  */
1212 static int
1213 efreet_icon_theme_cache_check_dir(Efreet_Icon_Theme *theme, const char *dir)
1214 {
1215     struct stat buf;
1216
1217     /* have we modified this directory since our last cache check? */
1218     if (stat(dir, &buf) || (buf.st_mtime > theme->last_cache_check))
1219     {
1220         ecore_hash_remove(efreet_icon_cache, theme);
1221         return 0;
1222     }
1223
1224     return 1;
1225 }
1226
1227 /**
1228  * @internal
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
1234  */
1235 static void
1236 efreet_icon_theme_dir_scan_all(const char *theme_name)
1237 {
1238     Ecore_List *xdg_dirs;
1239     char path[PATH_MAX], *dir;
1240
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);
1243
1244     xdg_dirs = efreet_data_dirs_get();
1245     ecore_list_first_goto(xdg_dirs);
1246     while ((dir = ecore_list_next(xdg_dirs)))
1247     {
1248         snprintf(path, sizeof(path), "%s/icons", dir);
1249         efreet_icon_theme_dir_scan(path, theme_name);
1250     }
1251
1252     efreet_icon_theme_dir_scan("/usr/share/pixmaps", theme_name);
1253 }
1254
1255 /**
1256  * @internal
1257  * @param search_dir: The directory to scan
1258  * @param theme_name: Scan for this specific theme, set to NULL to find all
1259  * themes.
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
1263  * add to the cache.
1264  */
1265 static void
1266 efreet_icon_theme_dir_scan(const char *search_dir, const char *theme_name)
1267 {
1268     DIR *dirs;
1269     struct dirent *dir;
1270
1271     if (!search_dir) return;
1272
1273     dirs = opendir(search_dir);
1274     if (!dirs) return;
1275
1276     while ((dir = readdir(dirs)))
1277     {
1278         Efreet_Icon_Theme *theme;
1279         char path[PATH_MAX];
1280         const char *key;
1281
1282         if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")) continue;
1283
1284         /* only care if this is a directory or the theme name matches the
1285          * given name */
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))
1289             continue;
1290
1291         key = ecore_string_instance(dir->d_name);
1292         theme = ecore_hash_get(efreet_icon_themes, key);
1293
1294         if (!theme)
1295         {
1296             theme = efreet_icon_theme_new();
1297             theme->name.internal = key;
1298             ecore_hash_set(efreet_icon_themes,
1299                         (void *)theme->name.internal, theme);
1300         }
1301         else
1302         {
1303             if (theme->fake)
1304                 theme->fake = 0;
1305             ecore_string_release(key);
1306         }
1307
1308         efreet_icon_theme_path_add(theme, path);
1309
1310         /* we're already valid so no reason to check for an index.theme file */
1311         if (theme->valid) continue;
1312
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);
1317     }
1318     closedir(dirs);
1319
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 */
1322     if (theme_name)
1323     {
1324         Efreet_Icon_Theme *theme;
1325
1326         theme = ecore_hash_get(efreet_icon_themes, theme_name);
1327         if (theme && !theme->valid && !theme->fake)
1328         {
1329             ecore_hash_remove(efreet_icon_themes, theme_name);
1330             efreet_icon_theme_free(theme);
1331         }
1332     }
1333 }
1334
1335 /**
1336  * @internal
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
1341  */
1342 static void
1343 efreet_icon_theme_index_read(Efreet_Icon_Theme *theme, const char *path)
1344 {
1345     Efreet_Ini *ini;
1346     const char *tmp;
1347
1348     if (!theme || !path) return;
1349
1350     ini = efreet_ini_new(path);
1351     if (!ini->data)
1352     {
1353         efreet_ini_free(ini);
1354         return;
1355     }
1356
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);
1360
1361     tmp = efreet_ini_localestring_get(ini, "Comment");
1362     if (tmp) theme->comment = strdup(tmp);
1363
1364     tmp = efreet_ini_string_get(ini, "Example");
1365     if (tmp) theme->example_icon = strdup(tmp);
1366
1367     theme->hidden = efreet_ini_boolean_get(ini, "Hidden");
1368
1369     theme->valid = 1;
1370
1371     /* Check the inheritance. If there is none we inherit from the hicolor theme */
1372     tmp = efreet_ini_string_get(ini, "Inherits");
1373     if (tmp)
1374     {
1375         char *t, *s, *p;
1376
1377         theme->inherits = ecore_list_new();
1378         ecore_list_free_cb_set(theme->inherits, free);
1379
1380         t = strdup(tmp);
1381         s = t;
1382         p = strchr(s, ',');
1383
1384         while (p)
1385         {
1386             *p = '\0';
1387
1388             ecore_list_append(theme->inherits, strdup(s));
1389             s = ++p;
1390             p = strchr(s, ',');
1391         }
1392         ecore_list_append(theme->inherits, strdup(s));
1393
1394         FREE(t);
1395     }
1396
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");
1400     if (tmp)
1401     {
1402         char *t, *s, *p;
1403
1404         theme->directories = ecore_list_new();
1405         ecore_list_free_cb_set(theme->directories,
1406                             ECORE_FREE_CB(efreet_icon_theme_directory_free));
1407
1408         t = strdup(tmp);
1409         s = t;
1410         p = s;
1411
1412         while (p)
1413         {
1414             p = strchr(s, ',');
1415
1416             if (p) *p = '\0';
1417
1418             ecore_list_append(theme->directories,
1419                             efreet_icon_theme_directory_new(ini, s));
1420
1421             if (p) s = ++p;
1422         }
1423
1424         FREE(t);
1425     }
1426
1427     efreet_ini_free(ini);
1428 }
1429
1430 /**
1431  * @internal
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.
1438  */
1439 static void
1440 efreet_icon_theme_dir_validity_check(void)
1441 {
1442     Ecore_List *keys;
1443     const char *name;
1444
1445     keys = ecore_hash_keys(efreet_icon_themes);
1446     ecore_list_first_goto(keys);
1447     while ((name = ecore_list_next(keys)))
1448     {
1449         Efreet_Icon_Theme *theme;
1450
1451         theme = ecore_hash_get(efreet_icon_themes, name);
1452         if (!theme->valid && !theme->fake)
1453         {
1454             ecore_hash_remove(efreet_icon_themes, name);
1455             efreet_icon_theme_free(theme);
1456         }
1457     }
1458     ecore_list_destroy(keys);
1459 }
1460
1461 /**
1462  * @internal
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
1468  * information
1469  */
1470 static Efreet_Icon_Theme_Directory *
1471 efreet_icon_theme_directory_new(Efreet_Ini *ini, const char *name)
1472 {
1473     Efreet_Icon_Theme_Directory *dir;
1474     int val;
1475     const char *tmp;
1476
1477     if (!ini) return NULL;
1478
1479     dir = NEW(Efreet_Icon_Theme_Directory, 1);
1480     dir->name = strdup(name);
1481
1482     efreet_ini_section_set(ini, name);
1483
1484     tmp = efreet_ini_string_get(ini, "Context");
1485     if (tmp)
1486     {
1487         if (!strcasecmp(tmp, "Actions"))
1488             dir->context = EFREET_ICON_THEME_CONTEXT_ACTIONS;
1489
1490         else if (!strcasecmp(tmp, "Devices"))
1491             dir->context = EFREET_ICON_THEME_CONTEXT_DEVICES;
1492
1493         else if (!strcasecmp(tmp, "FileSystems"))
1494             dir->context = EFREET_ICON_THEME_CONTEXT_FILESYSTEMS;
1495
1496         else if (!strcasecmp(tmp, "MimeTypes"))
1497             dir->context = EFREET_ICON_THEME_CONTEXT_MIMETYPES;
1498     }
1499
1500     tmp = efreet_ini_string_get(ini, "Type");
1501     if (tmp)
1502     {
1503         if (!strcasecmp(tmp, "Fixed"))
1504             dir->type = EFREET_ICON_SIZE_TYPE_FIXED;
1505
1506         else if (!strcasecmp(tmp, "Scalable"))
1507             dir->type = EFREET_ICON_SIZE_TYPE_SCALABLE;
1508
1509         else if (!strcasecmp(tmp, "Threshold"))
1510             dir->type = EFREET_ICON_SIZE_TYPE_THRESHOLD;
1511     }
1512
1513     dir->size.normal = efreet_ini_int_get(ini, "Size");
1514
1515     val = efreet_ini_int_get(ini, "MinSize");
1516     if (val < 0) dir->size.min = dir->size.normal;
1517     else dir->size.min = val;
1518
1519     val = efreet_ini_int_get(ini, "MaxSize");
1520     if (val < 0) dir->size.max = dir->size.normal;
1521     else dir->size.max = val;
1522
1523     val = efreet_ini_int_get(ini, "Threshold");
1524     if (val < 0) dir->size.threshold = 2;
1525     else dir->size.threshold = val;
1526
1527     return dir;
1528 }
1529
1530 /**
1531  * @internal
1532  * @param dir: The Efreet_Icon_Theme_Directory to free
1533  * @return Returns no value
1534  * @brief Frees the given directory @a dir
1535  */
1536 static void
1537 efreet_icon_theme_directory_free(Efreet_Icon_Theme_Directory *dir)
1538 {
1539     if (!dir) return;
1540
1541     IF_FREE(dir->name);
1542     FREE(dir);
1543 }
1544
1545 static int
1546 efreet_icon_cache_find(Efreet_Icon_Cache *value, const char *key)
1547 {
1548     if (!value || !key) return -1;
1549     return strcmp(value->key, key);
1550 }
1551
1552 static void
1553 efreet_icon_cache_flush(Ecore_List *list)
1554 {
1555     /* TODO:
1556      * * Dynamic cache size
1557      * * Maybe add references to cache, so that we sort on how often a value is used
1558      */
1559     while (ecore_list_count(list) > 100)
1560     {
1561         Efreet_Icon_Cache *cache;
1562
1563         cache = ecore_list_last_remove(list);
1564         efreet_icon_cache_free(cache);
1565     }
1566 }
1567
1568 static void
1569 efreet_icon_cache_free(Efreet_Icon_Cache *value)
1570 {
1571     if (!value) return;
1572
1573     IF_FREE(value->key);
1574     IF_FREE(value->path);
1575     free(value);
1576 }
1577
1578 static char *
1579 efreet_icon_cache_check(Efreet_Icon_Theme *theme, const char *icon, unsigned int size)
1580 {
1581     Ecore_List *list;
1582     Efreet_Icon_Cache *cache;
1583     char key[4096];
1584     struct stat st;
1585
1586     list = ecore_hash_get(efreet_icon_cache, theme);
1587     if (!list)
1588     {
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);
1592         return NULL;
1593     }
1594
1595     snprintf(key, sizeof(key), "%s %d", icon, size);
1596     cache = ecore_list_find(list, ECORE_COMPARE_CB(efreet_icon_cache_find), key);
1597     if (cache)
1598     {
1599         ecore_list_remove(list);
1600         if (!cache->path)
1601         {
1602             ecore_list_prepend(list, cache);
1603             return NON_EXISTING;
1604         }
1605         else if (!stat(cache->path, &st) && st.st_mtime == cache->lasttime)
1606         {
1607             ecore_list_prepend(list, cache);
1608             return strdup(cache->path);
1609         }
1610         efreet_icon_cache_free(cache);
1611     }
1612     return NULL;
1613 }
1614
1615 static void
1616 efreet_icon_cache_add(Efreet_Icon_Theme *theme, const char *icon, unsigned int size, const char *value)
1617 {
1618     Ecore_List *list;
1619     Efreet_Icon_Cache *cache;
1620     char key[4096];
1621     struct stat st;
1622
1623     list = ecore_hash_get(efreet_icon_cache, theme);
1624     if (!list)
1625     {
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);
1629     }
1630
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))
1635     {
1636         cache->path = strdup(value);
1637         cache->lasttime = st.st_mtime;
1638     }
1639     else
1640         cache->lasttime = ecore_time_get();
1641     ecore_list_prepend(list, cache);
1642     efreet_icon_cache_flush(list);
1643 }