2 * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
7 #include "Ecore_Desktop.h"
8 #include "ecore_desktop_private.h"
9 #include "ecore_private.h"
13 static char *_ecore_desktop_icon_find0(const char *icon,
14 const char *icon_size,
15 const char *icon_theme,
18 static int _ecore_desktop_icon_theme_list_add(void *data,
20 static void _ecore_desktop_icon_theme_destroy(Ecore_Desktop_Icon_Theme *
23 _ecore_desktop_icon_theme_directory_destroy(Ecore_Desktop_Icon_Theme_Directory *
24 icon_theme_directory);
26 _ecore_desktop_icon_theme_cache_check(Ecore_Desktop_Icon_Theme *icon_theme);
28 /* FIXME: We need a way for the client to disable searching for any of these that they don't support. */
29 static const char *ext[] =
30 { "", ".edj", ".png", ".svgz", ".svg", ".xpm", NULL }; /* "" is in case the icon already has an extension, search for that first. */
31 static int init_count = 0;
32 static Ecore_Hash *icon_theme_cache = NULL;
35 * @defgroup Ecore_Desktop_Icon_Group icon theme Functions
37 * Functions that deal with freedesktop.org icon themes.
39 * This conforms with the freedesktop.org XDG Icon Theme Specification version 0.11
43 * Find the path to an icon.
45 * Using the search algorithm specified by freedesktop.org,
46 * search for an icon in the currently installed set of icon themes.
48 * The returned string needs to be freed eventually.
50 * @param icon The name of the required icon.
51 * @param icon_size The size of the required icon.
52 * @param icon_theme The theme of the required icon.
53 * @return The full path to an icon file, or NULL.
54 * @ingroup Ecore_Desktop_Icon_Group
58 ecore_desktop_icon_find(const char *icon, const char *icon_size,
59 const char *icon_theme)
61 char *result = NULL, *icn;
66 begin = ecore_time_get();
69 /* Easy check first, was a full path supplied? */
70 if ((icon[0] == '/') && (ecore_file_exists(icon)))
71 result = strdup(icon);
74 icons = ecore_desktop_paths_to_list(icon);
78 if (icon_size == NULL)
80 if (icon_theme == NULL)
81 icon_theme = "hicolor";
82 ecore_list_first_goto(icons);
83 while ((icn = ecore_list_next(icons)))
87 fprintf(stderr, "\tTrying To Find Icon %s\n", icn);
89 ext = strrchr(icn, '.');
90 /* Check for unsupported extension */
91 if ((ext) && (!strcmp(ext, ".ico")))
94 result = _ecore_desktop_icon_find0(icn, icon_size, icon_theme, &in_cache);
98 ecore_list_destroy(icons);
101 } /* if ((icon[0] == '/') && (ecore_file_exists(icon))) ; else */
108 instrumentation.icons_in_cache_time += ecore_time_get() - begin;
109 instrumentation.icons_in_cache++;
113 instrumentation.icons_time += ecore_time_get() - begin;
114 instrumentation.icons++;
119 instrumentation.icons_not_found_time += ecore_time_get() - begin;
120 instrumentation.icons_not_found++;
126 /** Search for an icon the fdo way.
128 * This complies with the freedesktop.org Icon Theme Specification version 0.7
130 * @param icon The icon to search for.
131 * @param icon_size The icon size to search for.
132 * @param icon_theme The icon theme to search in.
133 * @return The full path to the found icon.
136 _ecore_desktop_icon_find0(const char *icon, const char *icon_size,
137 const char *icon_theme, int *in_cache)
139 Ecore_Desktop_Icon_Theme *theme;
143 int minimal_size = INT_MAX;
145 int has_icon_ext = 0;
147 char *closest = NULL;
148 Ecore_Desktop_Icon_Theme_Directory *directory;
150 if ((icon == NULL) || (icon[0] == '\0'))
153 /* Check the file extension, if any. */
154 found = strrchr(icon, '.');
158 for (i = 0; ext[i] != NULL; i++)
160 if (strcmp(found, ext[i]) == 0)
170 fprintf(stderr, "\tTrying To Find Icon %s (%s) in theme %s\n", icon,
171 icon_size, icon_theme);
174 /* Get the theme description file. */
175 theme = ecore_desktop_icon_theme_get(icon_theme, NULL);
177 printf("SEARCHING FOR %s\n", icon_theme);
180 if (!theme) return NULL;
181 if (!theme->Directories) goto done;
183 wanted_size = atoi(icon_size);
185 /* Loop through the themes directories. */
186 ecore_list_first_goto(theme->Directories);
187 while ((directory = ecore_list_next(theme->Directories)) != NULL)
194 /* Does this theme directory match the required icon size? */
195 switch (directory->type[0])
197 case 'F': /* Fixed. */
198 match = (wanted_size == directory->size);
199 result_size = abs(directory->size - wanted_size);
201 case 'S': /* Scaled. */
202 match = ((directory->minimum <= wanted_size) &&
203 (wanted_size <= directory->maximum));
204 if (wanted_size < directory->minimum)
205 result_size = directory->minimum - wanted_size;
206 if (wanted_size > directory->maximum)
207 result_size = wanted_size - directory->maximum;
209 default: /* Threshold. */
210 match = (((directory->size - directory->threshold) <= wanted_size) &&
211 (wanted_size <= (directory->size + directory->threshold)));
212 if (wanted_size < (directory->size - directory->threshold))
213 result_size = directory->minimum - wanted_size;
214 if (wanted_size > (directory->size + directory->threshold))
215 result_size = wanted_size - directory->maximum;
219 /* Do we need to check this directory? */
220 if ((match) || (result_size < minimal_size))
222 /* Look for icon with all extensions. */
223 for (i = 0; ext[i] != NULL; i++)
225 /* Check if there will be an extension to check. */
226 if ((ext[i][0] == '\0') && (!has_ext))
228 if ((ext[i][0] != '\0') && (has_icon_ext))
230 if (directory->icons)
232 snprintf(path, PATH_MAX, "%s%s", icon, ext[i]);
234 printf("FDO icon = %s\n", path);
236 found = ecore_hash_get(directory->icons, path);
239 found = strdup(found);
246 snprintf(path, PATH_MAX, "%s/%s%s", directory->full_path, icon, ext[i]);
248 printf("FDO icon = %s\n", path);
250 if (ecore_file_exists(path))
251 found = strdup(path);
255 if (ecore_file_is_dir(found))
260 else if (match) /* If there is a match in sizes, return the icon. */
262 else if (result_size < minimal_size) /* While we are here, figure out our next fallback strategy. */
264 minimal_size = result_size;
265 if (closest) free(closest);
275 } /* for (i = 0; ext[i] != NULL; i++) */
276 } /* if ((match) || (result_size < minimal_size)) */
277 } /* if (directory->size) */
278 } /* while ((directory = ecore_list_next(directory_paths)) != NULL) */
282 /* Fall back strategy #1, look for closest size in this theme. */
290 /* Fall back strategy #2, Try again with the parent themes. */
297 ecore_list_first_goto(theme->Inherits);
298 while ((inherits = ecore_list_next(theme->Inherits)) != NULL)
300 found = _ecore_desktop_icon_find0(icon, icon_size, inherits, in_cache);
301 if (found) goto done;
304 else /* Fall back strategy #3, Try the default hicolor theme. */
306 found = _ecore_desktop_icon_find0(icon, icon_size, "hicolor", in_cache);
307 if (found) goto done;
311 /* Fall back strategy #4, Just search in the base of the icon directories. */
312 for (i = 0; ext[i] != NULL; i++)
314 /* Check if there will be an extension to check. */
315 if ((ext[i][0] == '\0') && (!has_ext))
317 if ((ext[i][0] != '\0') && (has_icon_ext))
319 snprintf(path, PATH_MAX, "%s%s", icon, ext[i]);
321 printf("FDO icon = %s\n", path);
323 found = ecore_desktop_paths_file_find(ecore_desktop_paths_icons, path, 0, NULL, NULL);
326 if (ecore_file_is_dir(found))
338 if (closest) free(closest);
339 ecore_desktop_icon_theme_destroy(theme);
345 ecore_desktop_icon_theme_list(void)
347 static int loaded = 0;
351 tmp = ecore_desktop_paths_file_find(ecore_desktop_paths_icons, "index.theme", 2,
352 _ecore_desktop_icon_theme_list_add, NULL);
356 return icon_theme_cache;
360 _ecore_desktop_icon_theme_list_add(void *data __UNUSED__, const char *path)
364 snprintf(icn, PATH_MAX, "%sindex.theme", path);
365 if (ecore_desktop_icon_theme_get(icn, NULL))
366 return 1; /* Should stop it from recursing this directory, but let it continue searching the next. */
371 * Setup what ever needs to be setup to support ecore_desktop_icon.
373 * There are internal structures that are needed for ecore_desktop_icon
374 * functions to operate, this sets them up.
376 * @ingroup Ecore_Desktop_Icon_Group
379 ecore_desktop_icon_init()
381 if (++init_count != 1)
384 if (!icon_theme_cache)
386 icon_theme_cache = ecore_hash_new(ecore_str_hash, ecore_str_compare);
387 if (icon_theme_cache)
389 ecore_hash_free_key_cb_set(icon_theme_cache, free);
390 ecore_hash_free_value_cb_set(icon_theme_cache,
391 ECORE_FREE_CB(_ecore_desktop_icon_theme_destroy));
399 * Tear down what ever needs to be torn down to support ecore_desktop_ycon.
401 * There are internal structures that are needed for ecore_desktop_icon
402 * functions to operate, this tears them down.
404 * @ingroup Ecore_Desktop_Icon_Group
407 ecore_desktop_icon_shutdown()
409 if (--init_count != 0)
412 if (icon_theme_cache)
414 ecore_hash_destroy(icon_theme_cache);
415 icon_theme_cache = NULL;
422 * Get the contents of an index.theme file.
424 * Everything that is in the index.theme file is returned in the
425 * data member of the Ecore_Desktop_Icon_Theme structure, it's an Ecore_Hash
426 * as returned by ecore_desktop_ini_get(). Some of the data in the
427 * index.theme file is decoded into specific members of the returned
430 * Use ecore_desktop_icon_theme_destroy() to free this structure.
432 * @param icon_theme Name of the icon theme, or full path to the index.theme file.
433 * @param lang Language to use, or NULL for default.
434 * @return An Ecore_Desktop_Icon_Theme containing the files contents.
435 * @ingroup Ecore_Desktop_Icon_Group
437 Ecore_Desktop_Icon_Theme *
438 ecore_desktop_icon_theme_get(const char *icon_theme, const char *lang __UNUSED__)
440 Ecore_Desktop_Icon_Theme *result = NULL;
441 char *theme_path = NULL, *theme_dir = NULL;
443 Ecore_List *Directories;
446 if (icon_theme[0] == '/')
448 theme_path = strdup(icon_theme);
449 theme_dir = ecore_file_dir_get(theme_path);
451 icon_theme = ecore_file_file_get(theme_dir);
453 printf("LOADING THEME %s - %s\n", icon_theme, theme_path);
457 result = ecore_hash_get(icon_theme_cache, icon_theme);
458 if (result) goto done;
463 snprintf(icn, PATH_MAX, "%s/index.theme", icon_theme);
465 printf("SEARCHING FOR %s\n", icn);
467 theme_path = ecore_desktop_paths_file_find(ecore_desktop_paths_icons, icn,
469 if (!theme_path) goto error;
470 theme_dir = ecore_file_dir_get(theme_path);
472 if (!theme_path) goto error;
473 result = calloc(1, sizeof(Ecore_Desktop_Icon_Theme));
474 if (!result) goto error;
475 result->data = ecore_desktop_ini_get(theme_path);
476 if (!result->data) goto error;
477 result->group = ecore_hash_get(result->data, "Icon Theme");
478 if (!result->group) goto error;
481 if ((strcmp(icon_theme, "hicolor") == 0))
484 /* According to the spec, name and comment are required, but we can fake those easily enough. */
485 value = ecore_hash_get(result->group, "Name");
486 if (!value) value = icon_theme;
487 result->name = strdup(value);
488 value = ecore_hash_get(result->group, "Comment");
489 if (!value) value = "No comment provided.";
490 result->comment = strdup(value);
491 value = ecore_hash_get(result->group, "Inherits");
494 result->inherits = strdup(value);
495 if (result->inherits)
496 result->Inherits = ecore_desktop_paths_to_list(result->inherits);
498 value = ecore_hash_get(result->group, "Example");
499 if (!value) value = "exec";
500 result->example = strdup(value);
501 value = ecore_hash_get(result->group, "Directories");
502 /* FIXME: Directories is also required, don't feel like faking it for now. */
503 if (!value) goto error;
504 result->directories = strdup(value);
505 Directories = ecore_desktop_paths_to_list(result->directories);
506 if (!Directories) goto error;
507 result->Directories = ecore_list_new();
508 if (!result->Directories) goto error;
509 ecore_list_free_cb_set(result->Directories,
510 ECORE_FREE_CB(_ecore_desktop_icon_theme_directory_destroy));
511 ecore_list_first_goto(Directories);
512 while ((directory = ecore_list_next(Directories)) != NULL)
514 Ecore_Hash *sub_group;
515 Ecore_Desktop_Icon_Theme_Directory *dir;
517 /* Get the details for this theme directory. */
518 sub_group = ecore_hash_get(result->data, directory);
519 dir = calloc(1, sizeof (Ecore_Desktop_Icon_Theme_Directory));
520 if ((dir) && (sub_group))
522 const char *size, *minsize, *maxsize, *threshold;
523 char full_path[PATH_MAX];
525 dir->path = strdup(directory);
526 snprintf(full_path, PATH_MAX, "%s/%s", theme_dir, directory);
527 dir->full_path = strdup(full_path);
528 value = ecore_hash_get(sub_group, "Type");
531 dir->type = strdup(value);
532 size = ecore_hash_get(sub_group, "Size");
533 minsize = ecore_hash_get(sub_group, "MinSize");
534 maxsize = ecore_hash_get(sub_group, "MaxSize");
535 threshold = ecore_hash_get(sub_group, "Threshold");
544 dir->minimum = atoi(minsize);
545 dir->maximum = atoi(maxsize);
546 dir->threshold = atoi(threshold);
548 dir->size = atoi(size);
549 ecore_list_append(result->Directories, dir);
552 _ecore_desktop_icon_theme_directory_destroy(dir);
555 _ecore_desktop_icon_theme_directory_destroy(dir);
557 ecore_list_destroy(Directories);
559 /* This passes the basic validation tests, mark it as real and cache it. */
560 result->path = strdup(theme_path);
561 ecore_hash_set(icon_theme_cache, strdup(icon_theme), result);
562 ecore_hash_destroy(result->data);
564 result->group = NULL;
567 if (theme_dir) free(theme_dir);
568 if (theme_path) free(theme_path);
570 /* Cache the directories. */
571 _ecore_desktop_icon_theme_cache_check(result);
575 if (theme_dir) free(theme_dir);
576 if (theme_path) free(theme_path);
579 if (result->data) ecore_hash_destroy(result->data);
580 _ecore_desktop_icon_theme_destroy(result);
586 * Free whatever resources are used by an Ecore_Desktop_Icon_Theme.
588 * There are internal resources used by each Ecore_Desktop_Icon_Theme
589 * This releases those resources.
591 * @param icon_theme An Ecore_Desktop_Icon_Theme.
592 * @ingroup Ecore_Desktop_Icon_Group
595 ecore_desktop_icon_theme_destroy(Ecore_Desktop_Icon_Theme * icon_theme)
597 /* This is just a dummy, because these structures are cached. */
598 /* Later versions of the cache may reference count, then this will be useful. */
604 _ecore_desktop_icon_theme_destroy(Ecore_Desktop_Icon_Theme * icon_theme)
606 if (icon_theme->path)
607 free(icon_theme->path);
608 if (icon_theme->name)
609 free(icon_theme->name);
610 if (icon_theme->comment)
611 free(icon_theme->comment);
612 if (icon_theme->example)
613 free(icon_theme->example);
614 if (icon_theme->inherits)
615 free(icon_theme->inherits);
616 if (icon_theme->directories)
617 free(icon_theme->directories);
618 if (icon_theme->Directories)
619 ecore_list_destroy(icon_theme->Directories);
620 if (icon_theme->Inherits)
621 ecore_list_destroy(icon_theme->Inherits);
626 _ecore_desktop_icon_theme_directory_destroy(Ecore_Desktop_Icon_Theme_Directory *
627 icon_theme_directory)
629 if (icon_theme_directory->path)
630 free(icon_theme_directory->path);
631 if (icon_theme_directory->full_path)
632 free(icon_theme_directory->full_path);
633 if (icon_theme_directory->type)
634 free(icon_theme_directory->type);
635 if (icon_theme_directory->icons)
636 ecore_hash_destroy(icon_theme_directory->icons);
637 free(icon_theme_directory);
641 _ecore_desktop_icon_theme_cache_check(Ecore_Desktop_Icon_Theme *icon_theme)
643 /* The spec has this to say -
645 * "The algorithm as described in this document works by always looking up
646 * filenames in directories (a stat in unix terminology). A good
647 * implementation is expected to read the directories once, and do all
648 * lookups in memory using that information.
650 * "This caching can make it impossible for users to add icons without having
651 * to restart applications. In order to handle this, any implementation that
652 * does caching is required to look at the mtime of the toplevel icon
653 * directories when doing a cache lookup, unless it already did so less than
654 * 5 seconds ago. This means that any icon editor or theme installation
655 * program need only to change the mtime of the the toplevel directory where
656 * it changed the theme to make sure that the new icons will eventually get
659 * The phrase "toplevel icon directories" is ambigous, but I guess they mean
660 * the directory where the index.theme file lives.
666 if (ecore_time_get() > (icon_theme->last_checked + 5.0))
668 if (stat(icon_theme->path, &st) >= 0)
670 icon_theme->last_checked = ecore_time_get();
671 if (st.st_mtime > icon_theme->mtime)
674 icon_theme->mtime = st.st_mtime;
681 Ecore_Desktop_Icon_Theme_Directory *dir;
682 char full_path[PATH_MAX];
684 ecore_list_first_goto(icon_theme->Directories);
685 while ((dir = ecore_list_next(icon_theme->Directories)) != NULL)
689 ecore_hash_destroy(dir->icons);
692 dir->icons = ecore_hash_new(ecore_str_hash, ecore_str_compare);
697 ecore_hash_free_key_cb_set(dir->icons, free);
698 ecore_hash_free_value_cb_set(dir->icons, free);
699 files = ecore_file_ls(dir->full_path);
704 while ((file = ecore_list_next(files)))
706 snprintf(full_path, PATH_MAX, "%s/%s", dir->full_path, file);
707 ecore_hash_set(dir->icons, strdup(file), strdup(full_path));
709 ecore_list_destroy(files);