-if closest_filename set
- return closest_filename
-
-// Stage 3: look in unthemed icons
-for each directory in $(basename list) { // <---- $HOME/.icons, /usr/share/icons, /usr/share/pixmaps
- for extension in ("png", "svg", "xpm") {
- if exists directory/iconname.extension
- return directory/iconname.extension
- }
- }
-
- return failed icon lookup
-
-// With the following helper functions:
-
-DirectoryMatchesSize(subdir, iconsize):
-read Type and size data from subdir
-if Type is Fixed
-return Size == iconsize
-if Type is Scaled
-return MinSize <= iconsize <= MaxSize
-if Type is Threshold
-return Size - Threshold <= iconsize <= Size + Threshold
-
-DirectorySizeDistance(subdir, size):
-read Type and size data from subdir
-if Type is Fixed
-return abs(Size - iconsize)
-if Type is Scaled
-if iconsize < MinSize
-return MinSize - iconsize
-if iconsize > MaxSize
-return iconsize - MaxSize
-return 0
-if Type is Threshold
-if iconsize < Size - Threshold
-return MinSize - iconsize
-if iconsize > Size + Threshold
-return iconsize - MaxSize
-return 0
- */
+
+// Returns the full path to an icon file (or NULL) given the icon name
+char *icon_path(Launcher *launcher, const char *icon_name, int size)
+{
+ if (icon_name == NULL)
+ return NULL;
+
+ // If the icon_name is already a path and the file exists, return it
+ if (strstr(icon_name, "/") == icon_name) {
+ if (g_file_test(icon_name, G_FILE_TEST_EXISTS))
+ return strdup(icon_name);
+ else
+ return NULL;
+ }
+
+ GSList *basenames = NULL;
+ basenames = g_slist_append(basenames, "~/.icons");
+ basenames = g_slist_append(basenames, "/usr/share/icons");
+ basenames = g_slist_append(basenames, "/usr/share/pixmaps");
+
+ GSList *extensions = NULL;
+ extensions = g_slist_append(extensions, "png");
+ extensions = g_slist_append(extensions, "xpm");
+
+ // Stage 1: exact size match
+ GSList *theme;
+ for (theme = launcher->icon_themes; theme; theme = g_slist_next(theme)) {
+ GSList *dir;
+ for (dir = ((IconTheme*)theme->data)->list_directories; dir; dir = g_slist_next(dir)) {
+ if (directory_matches_size((IconThemeDir*)dir->data, size)) {
+ GSList *base;
+ for (base = basenames; base; base = g_slist_next(base)) {
+ GSList *ext;
+ for (ext = extensions; ext; ext = g_slist_next(ext)) {
+ char *base_name = (char*) base->data;
+ char *theme_name = ((IconTheme*)theme->data)->name;
+ char *dir_name = ((IconThemeDir*)dir->data)->name;
+ char *extension = (char*) ext->data;
+ char *file_name = malloc(strlen(base_name) + strlen(theme_name) +
+ strlen(dir_name) + strlen(icon_name) + strlen(extension) + 100);
+ // filename = directory/$(themename)/subdirectory/iconname.extension
+ sprintf(file_name, "%s/%s/%s/%s.%s", base_name, theme_name, dir_name, icon_name, extension);
+ //printf("checking %s\n", file_name);
+ if (g_file_test(file_name, G_FILE_TEST_EXISTS)) {
+ g_slist_free(basenames);
+ g_slist_free(extensions);
+ return file_name;
+ } else {
+ free(file_name);
+ file_name = NULL;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Stage 2: best size match
+ // Contrary to the freedesktop spec, we are not choosing the closest icon in size, but the next larger icon
+ // otherwise the quality is usually crap (for size 22, if you can choose 16 or 32, you're better with 32)
+ // We do fallback to the closest size if we cannot find a larger or equal icon
+ int minimal_size = INT_MAX;
+ char *best_file_name = NULL;
+ int next_larger_size = -1;
+ char *next_larger = NULL;
+ for (theme = launcher->icon_themes; theme; theme = g_slist_next(theme)) {
+ GSList *dir;
+ for (dir = ((IconTheme*)theme->data)->list_directories; dir; dir = g_slist_next(dir)) {
+ GSList *base;
+ for (base = basenames; base; base = g_slist_next(base)) {
+ GSList *ext;
+ for (ext = extensions; ext; ext = g_slist_next(ext)) {
+ char *base_name = (char*) base->data;
+ char *theme_name = ((IconTheme*)theme->data)->name;
+ char *dir_name = ((IconThemeDir*)dir->data)->name;
+ char *extension = (char*) ext->data;
+ char *file_name = malloc(strlen(base_name) + strlen(theme_name) +
+ strlen(dir_name) + strlen(icon_name) + strlen(extension) + 100);
+ // filename = directory/$(themename)/subdirectory/iconname.extension
+ sprintf(file_name, "%s/%s/%s/%s.%s", base_name, theme_name, dir_name, icon_name, extension);
+ if (g_file_test(file_name, G_FILE_TEST_EXISTS)) {
+ if (directory_size_distance((IconThemeDir*)dir->data, size) < minimal_size) {
+ if (best_file_name) {
+ free(best_file_name);
+ best_file_name = NULL;
+ }
+ best_file_name = strdup(file_name);
+ minimal_size = directory_size_distance((IconThemeDir*)dir->data, size);
+ }
+ if (((IconThemeDir*)dir->data)->size >= size && (next_larger_size == -1 || ((IconThemeDir*)dir->data)->size < next_larger_size)) {
+ if (next_larger) {
+ free(next_larger);
+ next_larger = NULL;
+ }
+ next_larger = strdup(file_name);
+ next_larger_size = ((IconThemeDir*)dir->data)->size;
+ }
+ }
+ free(file_name);
+ }
+ }
+ }
+ }
+ if (next_larger) {
+ g_slist_free(basenames);
+ g_slist_free(extensions);
+ free(best_file_name);
+ return next_larger;
+ }
+ if (best_file_name) {
+ g_slist_free(basenames);
+ g_slist_free(extensions);
+ return best_file_name;
+ }
+
+ // Stage 3: look in unthemed icons
+ {
+ GSList *base;
+ for (base = basenames; base; base = g_slist_next(base)) {
+ GSList *ext;
+ for (ext = extensions; ext; ext = g_slist_next(ext)) {
+ char *base_name = (char*) base->data;
+ char *extension = (char*) ext->data;
+ char *file_name = malloc(strlen(base_name) + strlen(icon_name) +
+ strlen(extension) + 100);
+ // filename = directory/iconname.extension
+ sprintf(file_name, "%s/%s.%s", base_name, icon_name, extension);
+ //printf("checking %s\n", file_name);
+ if (g_file_test(file_name, G_FILE_TEST_EXISTS)) {
+ g_slist_free(basenames);
+ g_slist_free(extensions);
+ return file_name;
+ } else {
+ free(file_name);
+ file_name = NULL;
+ }
+ }
+ }
+ }
+
+ fprintf(stderr, "Could not find icon %s\n", icon_name);
+
+ return NULL;