33a1ceade062160cea115bdcf5c79845682ff656
[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 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;
11
12 static int efreet_icon_init_count = 0;
13
14 typedef struct Efreet_Icon_Cache Efreet_Icon_Cache;
15 struct Efreet_Icon_Cache
16 {
17     char *key;
18     char *path;
19     time_t lasttime;
20 };
21
22 static char *efreet_icon_remove_extension(const char *icon);
23 static Efreet_Icon_Theme *efreet_icon_find_theme_check(const char *theme_name);
24
25
26 static char *efreet_icon_find_fallback(Efreet_Icon_Theme *theme,
27                                        const char *icon,
28                                        unsigned int size);
29 static char *efreet_icon_list_find_fallback(Efreet_Icon_Theme *theme,
30                                             Eina_List *icons,
31                                             unsigned int size);
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);
41
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,
46                                                     unsigned int size);
47 static int efreet_icon_directory_size_match(Efreet_Icon_Theme_Directory *dir,
48                                                   unsigned int size);
49
50 static Efreet_Icon *efreet_icon_new(const char *path);
51 static void efreet_icon_populate(Efreet_Icon *icon, const char *file);
52
53 static char *efreet_icon_lookup_directory_helper(Efreet_Icon_Theme_Directory *dir,
54                                     const char *path, const char *icon_name);
55
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,
63                                                 const char *path);
64 static void efreet_icon_theme_index_read(Efreet_Icon_Theme *theme,
65                                                 const char *path);
66
67 static Efreet_Icon_Theme_Directory *efreet_icon_theme_directory_new(Efreet_Ini *ini,
68                                                                 const char *name);
69
70 static void efreet_icon_theme_cache_check(Efreet_Icon_Theme *theme);
71 static int efreet_icon_theme_cache_check_dir(Efreet_Icon_Theme *theme,
72                                                         const char *dir);
73
74 static int efreet_icon_cache_find(Efreet_Icon_Cache *value, const char *key);
75 static void efreet_icon_cache_flush(Efreet_Icon_Theme *theme, Eina_List *list);
76 static void efreet_icon_cache_free(Efreet_Icon_Cache *value);
77 static char *efreet_icon_cache_check(Efreet_Icon_Theme *theme, const char *icon, unsigned int size);
78 static void efreet_icon_cache_add(Efreet_Icon_Theme *theme, const char *icon, unsigned int size, const char *value);
79
80 static Efreet_Icon_Theme *fake_null = NULL;
81
82 static void
83 _efreet_icon_cache_list_destroy(Eina_List *list)
84 {
85    Efreet_Icon_Cache *cache;
86
87    EINA_LIST_FREE(list, cache)
88      efreet_icon_cache_free(cache);
89 }
90
91 /**
92  * @internal
93  * @return Returns 1 on success or 0 on failure
94  * @brief Initializes the icon system
95  */
96 int
97 efreet_icon_init(void)
98 {
99     if (efreet_icon_init_count++ > 0)
100         return efreet_icon_init_count;
101
102     if (!efreet_icon_themes)
103     {
104         const char *default_exts[] = {".png", ".xpm", NULL};
105         int i;
106
107         if (!ecore_init())
108         {
109             efreet_icon_init_count--;
110             return 0;
111         }
112
113         /* setup the default extension list */
114         for (i = 0; default_exts[i] != NULL; i++)
115             efreet_icon_extensions = eina_list_append(efreet_icon_extensions, strdup(default_exts[i]));
116
117         efreet_icon_themes = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_icon_theme_free));
118
119         efreet_extra_icon_dirs = NULL;
120         efreet_icon_cache = eina_hash_pointer_new(EINA_FREE_CB(_efreet_icon_cache_list_destroy));
121     }
122
123     return 1;
124 }
125
126 /**
127  * @internal
128  * @return Returns no value
129  * @brief Shuts down the icon system
130  */
131 void
132 efreet_icon_shutdown(void)
133 {
134     if (--efreet_icon_init_count)
135         return;
136
137     IF_FREE(efreet_icon_user_dir);
138     IF_FREE(efreet_icon_deprecated_user_dir);
139
140     IF_FREE_LIST(efreet_icon_extensions);
141     IF_FREE_HASH(efreet_icon_themes);
142     IF_FREE_LIST(efreet_extra_icon_dirs);
143
144     IF_FREE_HASH(efreet_icon_cache);
145
146     ecore_shutdown();
147     efreet_icon_init_count = 0;
148 }
149
150 /**
151  * @return Returns the user icon directory
152  * @brief Returns the user icon directory
153  */
154 const char *
155 efreet_icon_deprecated_user_dir_get(void)
156 {
157     const char *user;
158     int len;
159
160     if (efreet_icon_deprecated_user_dir) return efreet_icon_deprecated_user_dir;
161
162     user = efreet_home_dir_get();
163     len = strlen(user) + strlen("/.icons") + 1;
164     efreet_icon_deprecated_user_dir = malloc(sizeof(char) * len);
165     snprintf(efreet_icon_deprecated_user_dir, len, "%s/.icons", user);
166
167     return efreet_icon_deprecated_user_dir;
168 }
169
170 EAPI const char *
171 efreet_icon_user_dir_get(void)
172 {
173     const char *user;
174     int len;
175
176     if (efreet_icon_user_dir) return efreet_icon_user_dir;
177
178     user = efreet_data_home_get();
179     len = strlen(user) + strlen("/icons") + 1;
180     efreet_icon_user_dir = malloc(sizeof(char) * len);
181     snprintf(efreet_icon_user_dir, len, "%s/icons", user);
182
183     return efreet_icon_user_dir;
184 }
185
186 /**
187  * @param ext: The extension to add to the list of checked extensions
188  * @return Returns no value.
189  * @brief Adds the given extension to the list of possible icon extensions
190  */
191 EAPI void
192 efreet_icon_extension_add(const char *ext)
193 {
194     efreet_icon_extensions  = eina_list_prepend(efreet_icon_extensions, strdup(ext));
195 }
196
197 /**
198  * @return Returns a list of strings that are paths to other icon directories
199  * @brief Gets the list of all extra directories to look for icons. These
200  * directories are used to look for icons after looking in the user icon dir
201  * and before looking in standard system directories. The order of search is
202  * from first to last directory in this list. the strings in the list should
203  * be created with eina_stringshare_add().
204  */
205 EAPI Eina_List **
206 efreet_icon_extra_list_get(void)
207 {
208     return &efreet_extra_icon_dirs;
209 }
210
211 static Eina_Bool
212 _hash_keys(Eina_Hash *hash __UNUSED__, const void *key, void *list)
213 {
214   *(Eina_List**)list = eina_list_append(*(Eina_List**)list, key);
215   return EINA_TRUE;
216 }
217 /**
218  * @return Returns a list of Efreet_Icon structs for all the non-hidden icon
219  * themes
220  * @brief Retrieves all of the non-hidden icon themes available on the system.
221  * The returned list must be freed. Do not free the list data.
222  */
223 EAPI Eina_List *
224 efreet_icon_theme_list_get(void)
225 {
226     Eina_List *list = NULL;
227     Eina_List *theme_list = NULL;
228     char *dir;
229     Eina_Iterator *it;
230
231     /* update the list to include all icon themes */
232     efreet_icon_theme_dir_scan_all(NULL);
233     efreet_icon_theme_dir_validity_check();
234
235     /* create the list for the user */
236     it = eina_hash_iterator_key_new(efreet_icon_themes);
237     eina_iterator_foreach(it, EINA_EACH(_hash_keys), &theme_list);
238     eina_iterator_free(it);
239
240     EINA_LIST_FREE(theme_list, dir)
241     {
242         Efreet_Icon_Theme *theme;
243
244         theme = eina_hash_find(efreet_icon_themes, dir);
245         if (theme->hidden || theme->fake) continue;
246 #ifndef STRICT_SPEC
247         if (!theme->name.name) continue;
248 #endif
249
250         list = eina_list_append(list, theme);
251     }
252
253     return list;
254 }
255
256 /**
257  * @param theme_name: The theme to look for
258  * @return Returns the icon theme related to the given theme name or NULL if
259  * none exists.
260  * @brief Tries to get the icon theme structure for the given theme name
261  */
262 EAPI Efreet_Icon_Theme *
263 efreet_icon_theme_find(const char *theme_name)
264 {
265     Efreet_Icon_Theme *theme;
266
267     if (!theme_name) return fake_null;
268
269     theme = eina_hash_find(efreet_icon_themes, theme_name);
270     if (!theme)
271     {
272         efreet_icon_theme_dir_scan_all(theme_name);
273         theme = eina_hash_find(efreet_icon_themes, theme_name);
274     }
275
276     return theme;
277 }
278
279 /**
280  * @internal
281  * @param icon: The icon name to strip extension
282  * @return Extension removed if in list of extensions, else untouched.
283  * @brief Removes extension from icon name if in list of extensions.
284  */
285 static char *
286 efreet_icon_remove_extension(const char *icon)
287 {
288     Eina_List *l;
289     char *tmp = NULL, *ext = NULL;
290
291     tmp = strdup(icon);
292     ext = strrchr(tmp, '.');
293     if (ext)
294     {
295         const char *ext2;
296         EINA_LIST_FOREACH(efreet_icon_extensions, l, ext2)
297         {
298             if (!strcmp(ext, ext2))
299             {
300 #ifdef STRICT_SPEC
301                 printf("[Efreet]: Requesting an icon with an extension: %s\n",
302                                                                         icon);
303 #endif
304                 *ext = '\0';
305                 break;
306             }
307         }
308     }
309
310     return tmp;
311 }
312
313 /**
314  * @internal
315  * @param theme_name: The icon theme to look for
316  * @return Returns the Efreet_Icon_Theme structure representing this theme
317  * or a new blank theme if not found
318  * @brief Retrieves a theme, or creates a blank one if not found.
319  */
320 static Efreet_Icon_Theme *
321 efreet_icon_find_theme_check(const char *theme_name)
322 {
323     Efreet_Icon_Theme *theme = NULL;
324     if (theme_name) theme = efreet_icon_theme_find(theme_name);
325     if (!theme)
326     {
327         theme = efreet_icon_theme_new();
328         theme->fake = 1;
329         if (theme_name)
330           {
331              theme->name.internal = eina_stringshare_add(theme_name);
332              eina_hash_del(efreet_icon_themes, (void *)theme->name.internal, NULL);
333              eina_hash_add(efreet_icon_themes, (void *)theme->name.internal, theme);
334           }
335         else
336           {
337              theme->name.internal = NULL;
338              fake_null = theme;
339           }
340     }
341
342     return theme;
343 }
344
345 /**
346  * @param theme_name: The icon theme to look for
347  * @param icon: The icon to look for
348  * @param size; The icon size to look for
349  * @return Returns the path to the given icon or NULL if none found
350  * @brief Retrives the path to the given icon.
351  */
352 EAPI char *
353 efreet_icon_path_find(const char *theme_name, const char *icon, unsigned int size)
354 {
355     const char *cached;
356     char *value;
357     Efreet_Icon_Theme *theme;
358
359     if ((cached = efreet_icon_hash_get(theme_name, icon, size)) != NULL)
360     {
361        if (cached == NON_EXISTING) return NULL;
362        return strdup(cached);
363     }
364     theme = efreet_icon_find_theme_check(theme_name);
365
366 #ifdef SLOPPY_SPEC
367     {
368         char *tmp;
369
370         tmp = efreet_icon_remove_extension(icon);
371         value = efreet_icon_find_helper(theme, tmp, size);
372         FREE(tmp);
373     }
374 #else
375     value = efreet_icon_find_helper(theme, icon, size);
376 #endif
377
378     /* we didn't find the icon in the theme or in the inherited directories
379      * then just look for a non theme icon
380      */
381     if (!value || (value == NON_EXISTING)) value = efreet_icon_fallback_icon(icon);
382
383     efreet_icon_hash_put(theme_name, icon, size, value);
384    
385     if (value == NON_EXISTING) value = NULL;
386     return value;
387 }
388
389 /**
390  * @param theme_name: The icon theme to look for
391  * @param icons: List of icons to look for
392  * @param size; The icon size to look for
393  * @return Returns the path representing first found icon or
394  * NULL if none of the icons are found
395  * @brief Retrieves all of the information about the first found icon in
396  * the list.
397  * @note This function will search the given theme for all icons before falling
398  * back. This is useful when searching for mimetype icons.
399  */
400 EAPI char *
401 efreet_icon_list_find(const char *theme_name, Eina_List *icons,
402                                                             unsigned int size)
403 {
404     Eina_List *l;
405     const char *icon = NULL;
406     char *value = NULL;
407     char *data;
408     Efreet_Icon_Theme *theme;
409
410     theme = efreet_icon_find_theme_check(theme_name);
411
412 #ifdef SLOPPY_SPEC
413     {
414         Eina_List *tmps = NULL;
415
416         EINA_LIST_FOREACH(icons, l, icon)
417             tmps = eina_list_append(tmps, efreet_icon_remove_extension(icon));
418
419         value = efreet_icon_list_find_helper(theme, tmps, size);
420         EINA_LIST_FREE(tmps, data)
421             free(data);
422     }
423 #else
424     value = efreet_icon_list_find_helper(theme, icons, size);
425 #endif
426
427     /* we didn't find the icons in the theme or in the inherited directories
428      * then just look for a non theme icon
429      */
430     if (!value || (value == NON_EXISTING))
431     {
432         EINA_LIST_FOREACH(icons, l, icon)
433         {
434             value = efreet_icon_fallback_icon(icon);
435             if (value && (value != NON_EXISTING))
436                 break;
437         }
438     }
439
440     if (value == NON_EXISTING) value = NULL;
441     return value;
442 }
443
444 /**
445  * @param theme: The icon theme to look for
446  * @param icon: The icon to look for
447  * @param size: The icon size to look for
448  * @return Returns the Efreet_Icon structure representing this icon or NULL
449  * if the icon is not found
450  * @brief Retrieves all of the information about the given icon.
451  */
452 EAPI Efreet_Icon *
453 efreet_icon_find(const char *theme_name, const char *icon, unsigned int size)
454 {
455     char *path;
456
457     path = efreet_icon_path_find(theme_name, icon, size);
458     if (path)
459     {
460         Efreet_Icon *icon;
461
462         icon = efreet_icon_new(path);
463         free(path);
464         return icon;
465     }
466
467     return NULL;
468 }
469
470 /**
471  * @internal
472  * @param theme: The theme to search in
473  * @param icon: The icon to search for
474  * @param size: The size to search for
475  * @return Returns the icon matching the given information or NULL if no
476  * icon found
477  * @brief Scans inheriting themes for the given icon
478  */
479 static char *
480 efreet_icon_find_fallback(Efreet_Icon_Theme *theme,
481                           const char *icon, unsigned int size)
482 {
483     Eina_List *l;
484     char *parent = NULL;
485     char *value = NULL;
486
487     if (theme->inherits)
488     {
489         EINA_LIST_FOREACH(theme->inherits, l, parent)
490         {
491             Efreet_Icon_Theme *parent_theme;
492
493             parent_theme = efreet_icon_theme_find(parent);
494             if ((!parent_theme) || (parent_theme == theme)) continue;
495
496             value = efreet_icon_find_helper(parent_theme, icon, size);
497             if (value && (value != NON_EXISTING)) break;
498         }
499     }
500     /* if this isn't the hicolor theme, and we have no other fallbacks
501      * check hicolor */
502     else if (strcmp(theme->name.internal, "hicolor"))
503     {
504         Efreet_Icon_Theme *parent_theme;
505
506         parent_theme = efreet_icon_theme_find("hicolor");
507         if (parent_theme)
508             value = efreet_icon_find_helper(parent_theme, icon, size);
509     }
510
511     return value;
512 }
513
514 /**
515  * @internal
516  * @param theme: The theme to search in
517  * @param icon: The icon to search for
518  * @param size: The size to search for
519  * @return Returns the icon matching the given information or NULL if no
520  * icon found
521  * @brief Scans the theme and any inheriting themes for the given icon
522  */
523 static char *
524 efreet_icon_find_helper(Efreet_Icon_Theme *theme,
525                         const char *icon, unsigned int size)
526 {
527     char *value;
528     static int recurse = 0;
529
530     efreet_icon_theme_cache_check(theme);
531
532     /* go no further if this theme is fake */
533     if (theme->fake || !theme->valid) return NULL;
534
535     /* limit recursion in finding themes and inherited themes to 256 levels */
536     if (recurse > 256) return NULL;
537     recurse++;
538
539     value = efreet_icon_lookup_icon(theme, icon, size);
540
541     /* we didin't find the image check the inherited themes */
542     if (!value || (value == NON_EXISTING))
543         value = efreet_icon_find_fallback(theme, icon, size);
544
545     recurse--;
546     return value;
547 }
548
549 /**
550  * @internal
551  * @param theme: The theme to search in
552  * @param icons: The icons to search for
553  * @param size: The size to search for
554  * @return Returns the icon matching the given information or NULL if no
555  * icon found
556  * @brief Scans inheriting themes for the given icons
557  */
558 static char *
559 efreet_icon_list_find_fallback(Efreet_Icon_Theme *theme,
560                                Eina_List *icons, unsigned int size)
561 {
562     Eina_List *l;
563     char *parent = NULL;
564     char *value = NULL;
565
566     if (theme->inherits)
567     {
568         EINA_LIST_FOREACH(theme->inherits, l, parent)
569         {
570             Efreet_Icon_Theme *parent_theme;
571
572             parent_theme = efreet_icon_theme_find(parent);
573             if ((!parent_theme) || (parent_theme == theme)) continue;
574
575             value = efreet_icon_list_find_helper(parent_theme,
576                                                         icons, size);
577             if (value && (value != NON_EXISTING)) break;
578         }
579     }
580
581     /* if this isn't the hicolor theme, and we have no other fallbacks
582      * check hicolor
583      */
584     else if (strcmp(theme->name.internal, "hicolor"))
585     {
586         Efreet_Icon_Theme *parent_theme;
587
588         parent_theme = efreet_icon_theme_find("hicolor");
589         if (parent_theme)
590             value = efreet_icon_list_find_helper(parent_theme,
591                                                         icons, size);
592     }
593
594     return value;
595 }
596
597 /**
598  * @internal
599  * @param theme: The theme to search in
600  * @param icons: The icons to search for
601  * @param size: The size to search for
602  * @return Returns the icon matching the given information or NULL if no
603  * icon found
604  * @brief Scans the theme and any inheriting themes for the given icons
605  */
606 static char *
607 efreet_icon_list_find_helper(Efreet_Icon_Theme *theme,
608                              Eina_List *icons, unsigned int size)
609 {
610     Eina_List *l;
611     char *value = NULL;
612     const char *icon = NULL;
613     static int recurse = 0;
614
615     efreet_icon_theme_cache_check(theme);
616
617     /* go no further if this theme is fake */
618     if (theme->fake || !theme->valid) return NULL;
619
620     /* limit recursion in finding themes and inherited themes to 256 levels */
621     if (recurse > 256) return NULL;
622     recurse++;
623
624     EINA_LIST_FOREACH(icons, l, icon)
625     {
626         value = efreet_icon_lookup_icon(theme, icon, size);
627         if (value && (value != NON_EXISTING))
628             break;
629     }
630
631     /* we didn't find the image check the inherited themes */
632     if (!value || (value == NON_EXISTING))
633         value = efreet_icon_list_find_fallback(theme, icons, size);
634
635     recurse--;
636     return value;
637 }
638
639 /**
640  * @internal
641  * @param theme: The icon theme to look in
642  * @param icon_name: The icon name to look for
643  * @param size: The icon size to look for
644  * @return Returns the path for the theme/icon/size combo or NULL if
645  * none found
646  * @brief Looks for the @a icon in the @a theme for the @a size given.
647  */
648 static char *
649 efreet_icon_lookup_icon(Efreet_Icon_Theme *theme, const char *icon_name,
650                                                     unsigned int size)
651 {
652     Eina_List *l;
653     char *icon = NULL, *tmp = NULL;
654     Efreet_Icon_Theme_Directory *dir;
655     int minimal_size = INT_MAX;
656
657     if (!theme || (theme->paths == NULL) || !icon_name || !size)
658         return NULL;
659
660     icon = efreet_icon_cache_check(theme, icon_name, size);
661     if (icon) return icon;
662
663     /* search for allowed size == requested size */
664     EINA_LIST_FOREACH(theme->directories, l, dir)
665     {
666         if (!efreet_icon_directory_size_match(dir, size)) continue;
667         icon = efreet_icon_lookup_directory(theme, dir,
668                                             icon_name);
669         if (icon)
670         {
671             efreet_icon_cache_add(theme, icon_name, size, icon);
672             return icon;
673         }
674     }
675
676     /* search for any icon that matches */
677     EINA_LIST_FOREACH(theme->directories, l, dir)
678     {
679         int distance;
680
681         distance = efreet_icon_directory_size_distance(dir, size);
682         if (distance >= minimal_size) continue;
683
684         tmp = efreet_icon_lookup_directory(theme, dir,
685                                            icon_name);
686         if (tmp)
687         {
688             FREE(icon);
689             icon = tmp;
690             minimal_size = distance;
691         }
692     }
693
694     efreet_icon_cache_add(theme, icon_name, size, icon);
695     return icon;
696 }
697
698
699 /**
700  * @internal
701  * @param theme: The theme to use
702  * @param dir: The theme directory to look in
703  * @param icon_name: The icon name to look for
704  * @return Returns the icon cloest matching the given information or NULL if
705  * none found
706  * @brief Tries to find the file closest matching the given icon
707  */
708 static char *
709 efreet_icon_lookup_directory(Efreet_Icon_Theme *theme,
710                              Efreet_Icon_Theme_Directory *dir,
711                              const char *icon_name)
712 {
713    Eina_List *l;
714         char *icon;
715         const char *path;
716
717    EINA_LIST_FOREACH(theme->paths, l, path)
718         {
719             icon = efreet_icon_lookup_directory_helper(dir, path, icon_name);
720             if (icon) return icon;
721         }
722
723     return NULL;
724 }
725
726 /**
727  * @internal
728  * @param dir: The theme directory to work with
729  * @param size: The size to check
730  * @return Returns true if the size matches for the given directory, 0
731  * otherwise
732  * @brief Checks if the size matches for the given directory or not
733  */
734 static int
735 efreet_icon_directory_size_match(Efreet_Icon_Theme_Directory *dir,
736                                 unsigned int size)
737 {
738     if (dir->type == EFREET_ICON_SIZE_TYPE_FIXED)
739         return (dir->size.normal == size);
740
741     if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE)
742         return ((dir->size.min < size) && (size < dir->size.max));
743
744     if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD)
745         return (((dir->size.normal - dir->size.threshold) < size)
746                 && (size < (dir->size.normal + dir->size.threshold)));
747
748     return 0;
749 }
750
751 /**
752  * @internal
753  * @param dir: The directory to work with
754  * @param size: The size to check for
755  * @return Returns the distance this size is away from the desired size
756  * @brief Returns the distance the given size is away from the desired size
757  */
758 static int
759 efreet_icon_directory_size_distance(Efreet_Icon_Theme_Directory *dir,
760                                     unsigned int size)
761 {
762     if (dir->type == EFREET_ICON_SIZE_TYPE_FIXED)
763         return (abs(dir->size.normal - size));
764
765     if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE)
766     {
767         if (size < dir->size.min)
768             return dir->size.min - size;
769         if (dir->size.max < size)
770             return size - dir->size.max;
771
772         return 0;
773     }
774
775     if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD)
776     {
777         if (size < (dir->size.normal - dir->size.threshold))
778             return (dir->size.min - size);
779         if ((dir->size.normal + dir->size.threshold) < size)
780             return (size - dir->size.max);
781
782         return 0;
783     }
784
785     return 0;
786 }
787
788 /**
789  * @internal
790  * @param icon_name: The icon name to look for
791  * @return Returns the Efreet_Icon for the given name or NULL if none found
792  * @brief Looks for the un-themed icon in the base directories
793  */
794 static char *
795 efreet_icon_fallback_icon(const char *icon_name)
796 {
797     char *icon;
798
799     if (!icon_name) return NULL;
800     icon = efreet_icon_cache_check(efreet_icon_find_theme_check(NULL), icon_name, 0);
801     if (icon) return icon;
802
803     icon = efreet_icon_fallback_dir_scan(efreet_icon_deprecated_user_dir_get(), icon_name);
804     if (!icon)
805         icon = efreet_icon_fallback_dir_scan(efreet_icon_user_dir_get(), icon_name);
806     if (!icon)
807     {
808         Eina_List *xdg_dirs, *l;
809         const char *dir;
810         char path[PATH_MAX];
811
812         EINA_LIST_FOREACH(efreet_extra_icon_dirs, l, dir)
813         {
814             icon = efreet_icon_fallback_dir_scan(dir, icon_name);
815             if (icon)
816             {
817                efreet_icon_cache_add(efreet_icon_find_theme_check(NULL), icon_name, 0, icon);
818                 return icon;
819             }
820         }
821
822         xdg_dirs = efreet_data_dirs_get();
823
824         EINA_LIST_FOREACH(xdg_dirs, l, dir)
825         {
826             snprintf(path, PATH_MAX, "%s/icons", dir);
827             icon = efreet_icon_fallback_dir_scan(path, icon_name);
828             if (icon)
829             {
830                 efreet_icon_cache_add(efreet_icon_find_theme_check(NULL), icon_name, 0, icon);
831                 return icon;
832             }
833         }
834
835         icon = efreet_icon_fallback_dir_scan("/usr/share/pixmaps", icon_name);
836     }
837
838     efreet_icon_cache_add(efreet_icon_find_theme_check(NULL), icon_name, 0, icon);
839     return icon;
840 }
841
842 /**
843  * @internal
844  * @param dir: The directory to scan
845  * @param icon_name: The icon to look for
846  * @return Returns the icon for the given name or NULL on failure
847  * @brief Scans the given @a dir for the given @a icon_name returning the
848  * Efreet_icon if found, NULL otherwise.
849  */
850 static char *
851 efreet_icon_fallback_dir_scan(const char *dir, const char *icon_name)
852 {
853     Eina_List *l;
854     char *icon = NULL;
855     char path[PATH_MAX], *ext;
856     const char *icon_path[] = { dir, "/", icon_name, NULL };
857     size_t size;
858
859     if (!dir || !icon_name) return NULL;
860
861     size = efreet_array_cat(path, sizeof(path), icon_path);
862     EINA_LIST_FOREACH(efreet_icon_extensions, l, ext)
863     {
864         ecore_strlcpy(path + size, ext, sizeof(path) - size);
865
866         if (ecore_file_exists(path))
867         {
868             icon = strdup(path);
869             if (icon) break;
870         }
871         *(path + size) = '\0';
872     }
873     /* This is to catch non-conforming .desktop files */
874 #ifdef SLOPPY_SPEC
875     if (!icon)
876     {
877         if (ecore_file_exists(path))
878         {
879             icon = strdup(path);
880 #ifdef STRICT_SPEC
881             if (icon)
882                 printf("[Efreet]: Found an icon that already has an extension: %s\n", path);
883 #endif
884         }
885     }
886 #endif
887
888     return icon;
889 }
890
891 /**
892  * @internal
893  * @param theme: The theme to work with
894  * @param dir: The theme directory to work with
895  * @param path: The partial path to use
896  * @return Returns no value
897  * @brief Caches the icons in the given theme directory path at the given
898  * size
899  */
900 static char *
901 efreet_icon_lookup_directory_helper(Efreet_Icon_Theme_Directory *dir,
902                                     const char *path, const char *icon_name)
903 {
904     Eina_List *l;
905     char *icon = NULL;
906     char file_path[PATH_MAX];
907     const char *ext, *path_strs[] = { path, "/", dir->name, "/", icon_name, NULL };
908     size_t len;
909
910     len = efreet_array_cat(file_path, sizeof(file_path), path_strs);
911
912     EINA_LIST_FOREACH(efreet_icon_extensions, l, ext)
913     {
914         ecore_strlcpy(file_path + len, ext, sizeof(file_path) - len);
915
916         if (ecore_file_exists(file_path))
917         {
918             icon = strdup(file_path);
919             break;
920         }
921     }
922     return icon;
923 }
924
925 /**
926  * @internal
927  * @return Returns a new Efreet_Icon struct on success or NULL on failure
928  * @brief Creates a new Efreet_Icon struct
929  */
930 static Efreet_Icon *
931 efreet_icon_new(const char *path)
932 {
933     Efreet_Icon *icon;
934     char *p;
935
936     icon = NEW(Efreet_Icon, 1);
937     icon->path = strdup(path);
938
939     /* load the .icon file if it's available */
940     p = strrchr(icon->path, '.');
941     if (p)
942     {
943         char ico_path[PATH_MAX];
944
945         *p = '\0';
946
947         snprintf(ico_path, sizeof(ico_path), "%s.icon", icon->path);
948         *p = '.';
949
950         if (ecore_file_exists(ico_path))
951             efreet_icon_populate(icon, ico_path);
952     }
953
954     if (!icon->name)
955     {
956         const char *file;
957
958         file = ecore_file_file_get(icon->path);
959         p = strrchr(icon->path, '.');
960         if (p) *p = '\0';
961         icon->name = strdup(file);
962         if (p) *p = '.';
963     }
964
965     return icon;
966 }
967
968 /**
969  * @param icon: The Efreet_Icon to cleanup
970  * @return Returns no value.
971  * @brief Free's the given icon and all its internal data.
972  */
973 EAPI void
974 efreet_icon_free(Efreet_Icon *icon)
975 {
976     if (!icon) return;
977
978     icon->ref_count --;
979     if (icon->ref_count > 0) return;
980
981     IF_FREE(icon->path);
982     IF_FREE(icon->name);
983     IF_FREE_LIST(icon->attach_points);
984
985     FREE(icon);
986 }
987
988 /**
989  * @internal
990  * @param icon: The icon to populate
991  * @param file: The file to populate from
992  * @return Returns no value
993  * @brief Tries to populate the icon information from the given file
994  */
995 static void
996 efreet_icon_populate(Efreet_Icon *icon, const char *file)
997 {
998     Efreet_Ini *ini;
999     const char *tmp;
1000
1001     ini = efreet_ini_new(file);
1002     if (!ini->data)
1003     {
1004         efreet_ini_free(ini);
1005         return;
1006     }
1007
1008     efreet_ini_section_set(ini, "Icon Data");
1009     tmp = efreet_ini_localestring_get(ini, "DisplayName");
1010     if (tmp) icon->name = strdup(tmp);
1011
1012     tmp = efreet_ini_string_get(ini, "EmbeddedTextRectangle");
1013     if (tmp)
1014     {
1015         int points[4];
1016         char *t, *s, *p;
1017         int i;
1018
1019         t = strdup(tmp);
1020         s = t;
1021         for (i = 0; i < 4; i++)
1022         {
1023             if (s)
1024             {
1025                 p = strchr(s, ',');
1026
1027                 if (p) *p = '\0';
1028                 points[i] = atoi(s);
1029
1030                 if (p) s = ++p;
1031                 else s = NULL;
1032             }
1033             else
1034             {
1035                 points[i] = 0;
1036             }
1037         }
1038
1039         icon->has_embedded_text_rectangle = 1;
1040         icon->embedded_text_rectangle.x0 = points[0];
1041         icon->embedded_text_rectangle.y0 = points[1];
1042         icon->embedded_text_rectangle.x1 = points[2];
1043         icon->embedded_text_rectangle.y1 = points[3];
1044
1045         FREE(t);
1046     }
1047
1048     tmp = efreet_ini_string_get(ini, "AttachPoints");
1049     if (tmp)
1050     {
1051         char *t, *s, *p;
1052
1053         t = strdup(tmp);
1054         s = t;
1055         p = t;
1056         while (s)
1057         {
1058             Efreet_Icon_Point *point;
1059
1060             p = strchr(s, ',');
1061             /* If this happens there is something wrong with the .icon file */
1062             if (!p) break;
1063
1064             point = NEW(Efreet_Icon_Point, 1);
1065
1066             *p = '\0';
1067             point->x = atoi(s);
1068
1069             s = ++p;
1070             p = strchr(s, '|');
1071             if (p) *p = '\0';
1072
1073             point->y = atoi(s);
1074
1075             icon->attach_points = eina_list_append(icon->attach_points, point);
1076
1077             if (p) s = ++p;
1078             else s = NULL;
1079         }
1080         FREE(t);
1081     }
1082
1083     efreet_ini_free(ini);
1084 }
1085
1086 /**
1087  * @internal
1088  * @return Returns a new Efreet_Icon_Theme on success or NULL on failure
1089  * @brief Creates a new Efreet_Icon_Theme structure
1090  */
1091 static Efreet_Icon_Theme *
1092 efreet_icon_theme_new(void)
1093 {
1094     Efreet_Icon_Theme *theme;
1095
1096     theme = NEW(Efreet_Icon_Theme, 1);
1097
1098     return theme;
1099 }
1100
1101 /**
1102  * @internal
1103  * @param theme: The theme to free
1104  * @return Returns no value
1105  * @brief Frees up the @a theme structure.
1106  */
1107 static void
1108 efreet_icon_theme_free(Efreet_Icon_Theme *theme)
1109 {
1110     if (!theme) return;
1111
1112     IF_RELEASE(theme->name.internal);
1113     IF_RELEASE(theme->name.name);
1114
1115     IF_FREE(theme->comment);
1116     IF_FREE(theme->example_icon);
1117
1118     IF_FREE_LIST(theme->paths);
1119     IF_FREE_LIST(theme->inherits);
1120     IF_FREE_LIST(theme->directories);
1121
1122     FREE(theme);
1123 }
1124
1125 /**
1126  * @internal
1127  * @param theme: The theme to work with
1128  * @param path: The path to add
1129  * @return Returns no value
1130  * @brief This will correctly add the given path to the list of theme paths.
1131  * @Note Assumes you've already verified that @a path is a valid directory.
1132  */
1133 static void
1134 efreet_icon_theme_path_add(Efreet_Icon_Theme *theme, const char *path)
1135 {
1136     if (!theme || !path) return;
1137
1138     theme->paths = eina_list_append(theme->paths, strdup(path));
1139 }
1140
1141 /**
1142  * @internal
1143  * @return Returns no value
1144  * @brief This validates that our cache is still valid.
1145  *
1146  * This is checked by the following algorithm:
1147  *   - if we've check less then 5 seconds ago we're good
1148  *   - if the mtime on the dir is less then our last check time we're good
1149  *   - otherwise, invalidate the caches
1150  */
1151 static void
1152 efreet_icon_theme_cache_check(Efreet_Icon_Theme *theme)
1153 {
1154     Eina_List *l;
1155     double new_check;
1156
1157     new_check = ecore_time_get();
1158
1159     /* we're within 5 seconds of the last time we checked the cache */
1160     if ((new_check - 5) <= theme->last_cache_check) return;
1161
1162     if (theme->fake)
1163         efreet_icon_theme_dir_scan_all(theme->name.internal);
1164
1165     else
1166     {
1167         char *path;
1168
1169         EINA_LIST_FOREACH(theme->paths, l, path)
1170         {
1171             if (!efreet_icon_theme_cache_check_dir(theme, path))
1172                 break;
1173         }
1174     }
1175     theme->last_cache_check = new_check;
1176 }
1177
1178 /**
1179  * @internal
1180  * @param theme: The icon theme to check
1181  * @param dir: The directory to check
1182  * @return Returns 1 if the cache is still valid, 0 otherwise
1183  * @brief This will check if the theme cache is still valid. If it isn't the
1184  * cache will be invalided and 0 returned.
1185  */
1186 static int
1187 efreet_icon_theme_cache_check_dir(Efreet_Icon_Theme *theme, const char *dir)
1188 {
1189     struct stat buf;
1190
1191     /* have we modified this directory since our last cache check? */
1192     if (stat(dir, &buf) || (buf.st_mtime > theme->last_cache_check))
1193     {
1194         eina_hash_del(efreet_icon_cache, theme, NULL);
1195         return 0;
1196     }
1197
1198     return 1;
1199 }
1200
1201 /**
1202  * @internal
1203  * @param theme_name: The theme to scan for
1204  * @return Returns no value
1205  * @brief Scans the theme directories. If @a theme_name is NULL it will load
1206  * up all theme data. If @a theme_name is not NULL it will look for that
1207  * specific theme data
1208  */
1209 static void
1210 efreet_icon_theme_dir_scan_all(const char *theme_name)
1211 {
1212     Eina_List *xdg_dirs, *l;
1213     char path[PATH_MAX], *dir;
1214
1215     efreet_icon_theme_dir_scan(efreet_icon_deprecated_user_dir_get(), theme_name);
1216     efreet_icon_theme_dir_scan(efreet_icon_user_dir_get(), theme_name);
1217
1218     xdg_dirs = efreet_data_dirs_get();
1219     EINA_LIST_FOREACH(xdg_dirs, l, dir)
1220     {
1221         snprintf(path, sizeof(path), "%s/icons", dir);
1222         efreet_icon_theme_dir_scan(path, theme_name);
1223     }
1224
1225     efreet_icon_theme_dir_scan("/usr/share/pixmaps", theme_name);
1226 }
1227
1228 /**
1229  * @internal
1230  * @param search_dir: The directory to scan
1231  * @param theme_name: Scan for this specific theme, set to NULL to find all
1232  * themes.
1233  * @return Returns no value
1234  * @brief Scans the given directory and adds non-hidden icon themes to the
1235  * given list. If the theme isnt' in our cache then load the index.theme and
1236  * add to the cache.
1237  */
1238 static void
1239 efreet_icon_theme_dir_scan(const char *search_dir, const char *theme_name)
1240 {
1241     DIR *dirs;
1242     struct dirent *dir;
1243
1244     if (!search_dir) return;
1245
1246     dirs = opendir(search_dir);
1247     if (!dirs) return;
1248
1249     while ((dir = readdir(dirs)))
1250     {
1251         Efreet_Icon_Theme *theme;
1252         char path[PATH_MAX];
1253         const char *key;
1254
1255         if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")) continue;
1256
1257         /* only care if this is a directory or the theme name matches the
1258          * given name */
1259         snprintf(path, sizeof(path), "%s/%s", search_dir, dir->d_name);
1260         if (((theme_name != NULL) && (strcmp(theme_name, dir->d_name)))
1261                 || !ecore_file_is_dir(path))
1262             continue;
1263
1264         key = eina_stringshare_add(dir->d_name);
1265         theme = eina_hash_find(efreet_icon_themes, key);
1266
1267         if (!theme)
1268         {
1269             theme = efreet_icon_theme_new();
1270             theme->name.internal = key;
1271             eina_hash_add(efreet_icon_themes,
1272                           (void *)theme->name.internal, theme);
1273         }
1274         else
1275         {
1276             if (theme->fake)
1277                 theme->fake = 0;
1278             eina_stringshare_del(key);
1279         }
1280
1281         efreet_icon_theme_path_add(theme, path);
1282
1283         /* we're already valid so no reason to check for an index.theme file */
1284         if (theme->valid) continue;
1285
1286         /* if the index.theme file exists we parse it into the theme */
1287         strncat(path, "/index.theme", sizeof(path));
1288         if (ecore_file_exists(path))
1289             efreet_icon_theme_index_read(theme, path);
1290     }
1291     closedir(dirs);
1292
1293     /* if we were given a theme name we want to make sure that that given
1294      * theme is valid before finishing, unless it's a fake theme */
1295     if (theme_name)
1296     {
1297         Efreet_Icon_Theme *theme;
1298
1299         theme = eina_hash_find(efreet_icon_themes, theme_name);
1300         if (theme && !theme->valid && !theme->fake)
1301             eina_hash_del(efreet_icon_themes, theme_name, theme);
1302     }
1303 }
1304
1305 /**
1306  * @internal
1307  * @param theme: The theme to set the values into
1308  * @param path: The path to the index.theme file for this theme
1309  * @return Returns no value
1310  * @brief This will load up the theme with the data in the index.theme file
1311  */
1312 static void
1313 efreet_icon_theme_index_read(Efreet_Icon_Theme *theme, const char *path)
1314 {
1315     Efreet_Ini *ini;
1316     const char *tmp;
1317
1318     if (!theme || !path) return;
1319
1320     ini = efreet_ini_new(path);
1321     if (!ini->data)
1322     {
1323         efreet_ini_free(ini);
1324         return;
1325     }
1326
1327     efreet_ini_section_set(ini, "Icon Theme");
1328     tmp = efreet_ini_localestring_get(ini, "Name");
1329     if (tmp) theme->name.name = eina_stringshare_add(tmp);
1330
1331     tmp = efreet_ini_localestring_get(ini, "Comment");
1332     if (tmp) theme->comment = strdup(tmp);
1333
1334     tmp = efreet_ini_string_get(ini, "Example");
1335     if (tmp) theme->example_icon = strdup(tmp);
1336
1337     theme->hidden = efreet_ini_boolean_get(ini, "Hidden");
1338
1339     theme->valid = 1;
1340
1341     /* Check the inheritance. If there is none we inherit from the hicolor theme */
1342     tmp = efreet_ini_string_get(ini, "Inherits");
1343     if (tmp)
1344     {
1345         char *t, *s, *p;
1346
1347         t = strdup(tmp);
1348         s = t;
1349         p = strchr(s, ',');
1350
1351         while (p)
1352         {
1353             *p = '\0';
1354
1355             theme->inherits = eina_list_append(theme->inherits, strdup(s));
1356             s = ++p;
1357             p = strchr(s, ',');
1358         }
1359         theme->inherits = eina_list_append(theme->inherits, strdup(s));
1360
1361         FREE(t);
1362     }
1363
1364     /* make sure this one is done last as setting the directory will change
1365      * the ini section ... */
1366     tmp = efreet_ini_string_get(ini, "Directories");
1367     if (tmp)
1368     {
1369         char *t, *s, *p;
1370
1371         t = strdup(tmp);
1372         s = t;
1373         p = s;
1374
1375         while (p)
1376         {
1377             p = strchr(s, ',');
1378
1379             if (p) *p = '\0';
1380
1381             theme->directories = eina_list_append(theme->directories,
1382                             efreet_icon_theme_directory_new(ini, s));
1383
1384             if (p) s = ++p;
1385         }
1386
1387         FREE(t);
1388     }
1389
1390     efreet_ini_free(ini);
1391 }
1392
1393 /**
1394  * @internal
1395  * @return Returns no value
1396  * @brief Because the theme icon directories can be spread over multiple
1397  * base directories we may need to create the icon theme strucutre before
1398  * finding the index.theme file. It may also be that we never find an
1399  * index.theme file as this isn't a valid theme. This function makes sure
1400  * that everything we've got in our hash has a valid key to it.
1401  */
1402 static void
1403 efreet_icon_theme_dir_validity_check(void)
1404 {
1405     Eina_List *keys;
1406     const char *name;
1407     Eina_Iterator *it;
1408
1409     keys = NULL;
1410     it = eina_hash_iterator_key_new(efreet_icon_themes);
1411     eina_iterator_foreach(it, EINA_EACH(_hash_keys), &keys);
1412     eina_iterator_free(it);
1413
1414     EINA_LIST_FREE(keys, name)
1415     {
1416         Efreet_Icon_Theme *theme;
1417
1418         theme = eina_hash_find(efreet_icon_themes, name);
1419         if (theme && !theme->valid && !theme->fake)
1420             eina_hash_del(efreet_icon_themes, name, theme);
1421     }
1422 }
1423
1424 /**
1425  * @internal
1426  * @param ini: The ini file with information on this directory
1427  * @param name: The name of the directory
1428  * @return Returns a new Efreet_Icon_Theme_Directory based on the
1429  * information in @a ini.
1430  * @brief Creates and initialises an icon theme directory from the given ini
1431  * information
1432  */
1433 static Efreet_Icon_Theme_Directory *
1434 efreet_icon_theme_directory_new(Efreet_Ini *ini, const char *name)
1435 {
1436     Efreet_Icon_Theme_Directory *dir;
1437     int val;
1438     const char *tmp;
1439
1440     if (!ini) return NULL;
1441
1442     dir = NEW(Efreet_Icon_Theme_Directory, 1);
1443     dir->name = strdup(name);
1444
1445     efreet_ini_section_set(ini, name);
1446
1447     tmp = efreet_ini_string_get(ini, "Context");
1448     if (tmp)
1449     {
1450         if (!strcasecmp(tmp, "Actions"))
1451             dir->context = EFREET_ICON_THEME_CONTEXT_ACTIONS;
1452
1453         else if (!strcasecmp(tmp, "Devices"))
1454             dir->context = EFREET_ICON_THEME_CONTEXT_DEVICES;
1455
1456         else if (!strcasecmp(tmp, "FileSystems"))
1457             dir->context = EFREET_ICON_THEME_CONTEXT_FILESYSTEMS;
1458
1459         else if (!strcasecmp(tmp, "MimeTypes"))
1460             dir->context = EFREET_ICON_THEME_CONTEXT_MIMETYPES;
1461     }
1462
1463     tmp = efreet_ini_string_get(ini, "Type");
1464     if (tmp)
1465     {
1466         if (!strcasecmp(tmp, "Fixed"))
1467             dir->type = EFREET_ICON_SIZE_TYPE_FIXED;
1468
1469         else if (!strcasecmp(tmp, "Scalable"))
1470             dir->type = EFREET_ICON_SIZE_TYPE_SCALABLE;
1471
1472         else if (!strcasecmp(tmp, "Threshold"))
1473             dir->type = EFREET_ICON_SIZE_TYPE_THRESHOLD;
1474     }
1475
1476     dir->size.normal = efreet_ini_int_get(ini, "Size");
1477
1478     val = efreet_ini_int_get(ini, "MinSize");
1479     if (val < 0) dir->size.min = dir->size.normal;
1480     else dir->size.min = val;
1481
1482     val = efreet_ini_int_get(ini, "MaxSize");
1483     if (val < 0) dir->size.max = dir->size.normal;
1484     else dir->size.max = val;
1485
1486     val = efreet_ini_int_get(ini, "Threshold");
1487     if (val < 0) dir->size.threshold = 2;
1488     else dir->size.threshold = val;
1489
1490     return dir;
1491 }
1492
1493 static int
1494 efreet_icon_cache_find(Efreet_Icon_Cache *value, const char *key)
1495 {
1496     if (!value || !key) return -1;
1497     return strcmp(value->key, key);
1498 }
1499
1500 static void
1501 efreet_icon_cache_flush(Efreet_Icon_Theme *theme, Eina_List *list)
1502 {
1503     /* TODO:
1504      * * Dynamic cache size
1505      * * Maybe add references to cache, so that we sort on how often a value is used
1506      */
1507     while (eina_list_count(list) > 100)
1508     {
1509         Efreet_Icon_Cache *cache;
1510         Eina_List *last;
1511
1512         last = eina_list_last(list);
1513         cache = eina_list_data_get(last);
1514         efreet_icon_cache_free(cache);
1515         list = eina_list_remove_list(list, last);
1516     }
1517
1518     eina_hash_modify(efreet_icon_cache, theme, list);
1519 }
1520
1521 static void
1522 efreet_icon_cache_free(Efreet_Icon_Cache *value)
1523 {
1524     if (!value) return;
1525
1526     IF_FREE(value->key);
1527     IF_FREE(value->path);
1528     free(value);
1529 }
1530
1531 static char *
1532 efreet_icon_cache_check(Efreet_Icon_Theme *theme, const char *icon, unsigned int size)
1533 {
1534     Eina_List *list;
1535     Efreet_Icon_Cache *cache;
1536     char key[4096];
1537     struct stat st;
1538
1539     list = eina_hash_find(efreet_icon_cache, theme);
1540     if (!list) return NULL;
1541
1542     snprintf(key, sizeof(key), "%s %d", icon, size);
1543     cache = eina_list_search_unsorted(list, (Eina_Compare_Cb)efreet_icon_cache_find, key);
1544     if (cache)
1545     {
1546         if (!cache->path)
1547         {
1548             list = eina_list_promote_list(list, eina_list_data_find_list(list, cache));
1549             eina_hash_modify(efreet_icon_cache, theme, list);
1550             return NON_EXISTING;
1551         }
1552         else if (!stat(cache->path, &st) && st.st_mtime == cache->lasttime)
1553         {
1554             list = eina_list_promote_list(list, eina_list_data_find_list(list, cache));
1555             eina_hash_modify(efreet_icon_cache, theme, list);
1556             return strdup(cache->path);
1557         }
1558         efreet_icon_cache_free(cache);
1559         list = eina_list_remove(list, cache);
1560         if (list != NULL) eina_hash_modify(efreet_icon_cache, theme, list);
1561         else eina_hash_del(efreet_icon_cache, theme, NULL);
1562     }
1563     return NULL;
1564 }
1565
1566 static void
1567 efreet_icon_cache_add(Efreet_Icon_Theme *theme, const char *icon, unsigned int size, const char *value)
1568 {
1569     Eina_List *list, *l;
1570     Efreet_Icon_Cache *cache;
1571     char key[4096];
1572     struct stat st;
1573
1574     list = eina_hash_find(efreet_icon_cache, theme);
1575
1576     snprintf(key, sizeof(key), "%s %d", icon, size);
1577     cache = NEW(Efreet_Icon_Cache, 1);
1578     cache->key = strdup(key);
1579     if ((value) && !stat(value, &st))
1580     {
1581         cache->path = strdup(value);
1582         cache->lasttime = st.st_mtime;
1583     }
1584     else
1585         cache->lasttime = ecore_time_get();
1586
1587     l = list;
1588     list = eina_list_prepend(list, cache);
1589
1590     if (!l) eina_hash_add(efreet_icon_cache, theme, list);
1591     else eina_hash_modify(efreet_icon_cache, theme, list);
1592
1593     efreet_icon_cache_flush(theme, list);
1594 }