r3020: Get file icons using freedesktop.org icon theme system.
[rox-filer.git] / ROX-Filer / src / gtkicontheme.c
blob2c51b8b4fddefcae0c8d7a7f73458830d2866ad1
1 /* GtkIconTheme - a loader for icon themes
2 * gtk-icon-theme.c Copyright (C) 2002, 2003 Red Hat, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 #include "config.h"
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #ifdef HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27 #include <string.h>
28 #include <stdlib.h>
29 #include <glib.h>
31 #ifdef G_OS_WIN32
32 #ifndef S_ISDIR
33 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
34 #endif
35 #endif /* G_OS_WIN32 */
37 #include "gtkicontheme.h"
38 #include "gtkiconthemeparser.h"
39 /* #include "gtkintl.h" */
40 #include <gtk/gtksettings.h>
41 #include <gtk/gtkprivate.h>
43 #define DEFAULT_THEME_NAME "hicolor"
45 typedef struct _GtkIconData GtkIconData;
47 typedef enum
49 ICON_THEME_DIR_FIXED,
50 ICON_THEME_DIR_SCALABLE,
51 ICON_THEME_DIR_THRESHOLD,
52 ICON_THEME_DIR_UNTHEMED
53 } IconThemeDirType;
55 /* In reverse search order: */
56 typedef enum
58 ICON_SUFFIX_NONE = 0,
59 ICON_SUFFIX_XPM = 1 << 0,
60 ICON_SUFFIX_SVG = 1 << 1,
61 ICON_SUFFIX_PNG = 1 << 2,
62 } IconSuffix;
64 struct _GtkIconThemePrivate
66 guint custom_theme : 1;
67 guint is_screen_singleton : 1;
68 guint pixbuf_supports_svg : 1;
70 char *current_theme;
71 char **search_path;
72 int search_path_len;
74 gboolean themes_valid;
75 /* A list of all the themes needed to look up icons.
76 * In search order, without duplicates
78 GList *themes;
79 GHashTable *unthemed_icons;
81 /* Note: The keys of this hashtable are owned by the
82 * themedir and unthemed hashtables.
84 GHashTable *all_icons;
86 /* GdkScreen for the icon theme (may be NULL)
88 GdkScreen *screen;
90 /* time when we last stat:ed for theme changes */
91 long last_stat_time;
92 GList *dir_mtimes;
95 struct _GtkIconInfo
97 guint ref_count;
99 /* Information about the source
101 gchar *filename;
102 GdkPixbuf *builtin_pixbuf;
104 GtkIconData *data;
106 /* Information about the directory where
107 * the source was found
109 IconThemeDirType dir_type;
110 gint dir_size;
111 gint threshold;
113 /* Parameters influencing the scaled icon
115 gint desired_size;
116 gboolean raw_coordinates;
118 /* Cached information if we go ahead and try to load
119 * the icon.
121 GdkPixbuf *pixbuf;
122 GError *load_error;
123 gdouble scale;
126 typedef struct
128 char *name;
129 char *display_name;
130 char *comment;
131 char *example;
133 /* In search order */
134 GList *dirs;
135 } IconTheme;
137 struct _GtkIconData
139 gboolean has_embedded_rect;
140 gint x0, y0, x1, y1;
142 GdkPoint *attach_points;
143 gint n_attach_points;
145 gchar *display_name;
148 typedef struct
150 IconThemeDirType type;
151 GQuark context;
153 int size;
154 int min_size;
155 int max_size;
156 int threshold;
158 char *dir;
160 GHashTable *icons;
161 GHashTable *icon_data;
162 } IconThemeDir;
164 typedef struct
166 char *svg_filename;
167 char *no_svg_filename;
168 } UnthemedIcon;
170 typedef struct
172 gint size;
173 GdkPixbuf *pixbuf;
174 } BuiltinIcon;
176 typedef struct
178 char *dir;
179 time_t mtime; /* 0 == not existing or not a dir */
180 } IconThemeDirMtime;
182 static void gtk_icon_theme_class_init (GtkIconThemeClass *klass);
183 static void gtk_icon_theme_init (GtkIconTheme *icon_theme);
184 static void gtk_icon_theme_finalize (GObject *object);
185 static void theme_dir_destroy (IconThemeDir *dir);
187 static void theme_destroy (IconTheme *theme);
188 static GtkIconInfo *theme_lookup_icon (IconTheme *theme,
189 const char *icon_name,
190 int size,
191 gboolean allow_svg,
192 gboolean use_default_icons);
193 static void theme_list_icons (IconTheme *theme,
194 GHashTable *icons,
195 GQuark context);
196 static void theme_subdir_load (GtkIconTheme *icon_theme,
197 IconTheme *theme,
198 GtkIconThemeFile *theme_file,
199 char *subdir);
200 static void do_theme_change (GtkIconTheme *icon_theme);
202 static void blow_themes (GtkIconTheme *icon_themes);
204 static void icon_data_free (GtkIconData *icon_data);
206 static GtkIconInfo *icon_info_new (void);
207 static GtkIconInfo *icon_info_new_builtin (BuiltinIcon *icon);
209 static IconSuffix suffix_from_name (const char *name);
211 static BuiltinIcon *find_builtin_icon (const gchar *icon_name,
212 gint size,
213 gint *min_difference_p,
214 gboolean *has_larger_p);
216 static guint signal_changed = 0;
218 static GHashTable *icon_theme_builtin_icons;
220 GType
221 gtk_icon_theme_get_type (void)
223 static GType type = 0;
225 if (type == 0)
227 static const GTypeInfo info =
229 sizeof (GtkIconThemeClass),
230 NULL, /* base_init */
231 NULL, /* base_finalize */
232 (GClassInitFunc) gtk_icon_theme_class_init,
233 NULL, /* class_finalize */
234 NULL, /* class_data */
235 sizeof (GtkIconTheme),
236 0, /* n_preallocs */
237 (GInstanceInitFunc) gtk_icon_theme_init,
240 type = g_type_register_static (G_TYPE_OBJECT, "GtkIconTheme", &info, 0);
243 return type;
247 * gtk_icon_theme_new:
249 * Creates a new icon theme object. Icon theme objects are used
250 * to lookup up an icon by name in a particular icon theme.
251 * Usually, you'll want to use gtk_icon_theme_get_default()
252 * or gtk_icon_theme_get_for_screen() rather than creating
253 * a new icon theme object for scratch.
255 * Return value: the newly created #GtkIconTheme object.
257 GtkIconTheme *
258 gtk_icon_theme_new (void)
260 return g_object_new (GTK_TYPE_ICON_THEME, NULL);
264 * gtk_icon_theme_get_default:
266 * Gets the icon theme for the default screen. See
267 * gtk_icon_theme_get_for_screen().
269 * Return value: A unique #GtkIconTheme associated with
270 * the default screen. This icon theme is associated with
271 * the screen and can be used as long as the screen
272 * is open.
274 GtkIconTheme *
275 gtk_icon_theme_get_default (void)
277 return gtk_icon_theme_get_for_screen (gdk_screen_get_default ());
281 * gtk_icon_theme_get_for_screen:
282 * @screen: a #GdkScreen
284 * Gets the icon theme object associated with @screen; if this
285 * function has not previously been called for the given
286 * screen, a new icon theme object will be created and
287 * associated with the screen. Icon theme objects are
288 * fairly expensive to create, so using this function
289 * is usually a better choice than calling than gtk_icon_theme_new()
290 * and setting the screen yourself; by using this function
291 * a single icon theme object will be shared between users.
293 * Return value: A unique #GtkIconTheme associated with
294 * the given screen. This icon theme is associated with
295 * the screen and can be used as long as the screen
296 * is open.
298 GtkIconTheme *
299 gtk_icon_theme_get_for_screen (GdkScreen *screen)
301 GtkIconTheme *icon_theme;
303 g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
304 g_return_val_if_fail (!screen->closed, NULL);
306 icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
307 if (!icon_theme)
309 GtkIconThemePrivate *priv;
311 icon_theme = gtk_icon_theme_new ();
312 gtk_icon_theme_set_screen (icon_theme, screen);
314 priv = icon_theme->priv;
315 priv->is_screen_singleton = TRUE;
317 g_object_set_data (G_OBJECT (screen), "gtk-icon-theme", icon_theme);
320 return icon_theme;
323 static void
324 gtk_icon_theme_class_init (GtkIconThemeClass *klass)
326 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
328 gobject_class->finalize = gtk_icon_theme_finalize;
331 * GtkIconTheme::changed
332 * @icon_theme: the icon theme
334 * Emitted when the current icon theme is switched or GTK+ detects
335 * that a change has occurred in the contents of the current
336 * icon theme.
338 signal_changed = g_signal_new ("changed",
339 G_TYPE_FROM_CLASS (klass),
340 G_SIGNAL_RUN_LAST,
341 G_STRUCT_OFFSET (GtkIconThemeClass, changed),
342 NULL, NULL,
343 g_cclosure_marshal_VOID__VOID,
344 G_TYPE_NONE, 0);
346 /* g_type_class_add_private (klass, sizeof (GtkIconThemePrivate)); */
350 /* Callback when the display that the icon theme is attached to
351 * is closed; unset the screen, and if it's the unique theme
352 * for the screen, drop the reference
354 static void
355 display_closed (GdkDisplay *display,
356 gboolean is_error,
357 GtkIconTheme *icon_theme)
359 GtkIconThemePrivate *priv = icon_theme->priv;
360 GdkScreen *screen = priv->screen;
361 gboolean was_screen_singleton = priv->is_screen_singleton;
363 if (was_screen_singleton)
365 g_object_set_data (G_OBJECT (screen), "gtk-icon-theme", NULL);
366 priv->is_screen_singleton = FALSE;
369 gtk_icon_theme_set_screen (icon_theme, NULL);
371 if (was_screen_singleton)
373 g_object_unref (icon_theme);
377 static void
378 update_current_theme (GtkIconTheme *icon_theme)
380 GtkIconThemePrivate *priv = icon_theme->priv;
382 if (!priv->custom_theme)
384 gchar *theme = NULL;
386 if (0 && priv->screen)
388 GtkSettings *settings = gtk_settings_get_for_screen (priv->screen);
389 g_object_get (settings, "gtk-icon-theme-name", &theme, NULL);
392 if (!theme)
393 theme = g_strdup (DEFAULT_THEME_NAME);
395 if (strcmp (priv->current_theme, theme) != 0)
397 g_free (priv->current_theme);
398 priv->current_theme = theme;
400 do_theme_change (icon_theme);
402 else
403 g_free (theme);
407 /* Callback when the icon theme GtkSetting changes
409 static void
410 theme_changed (GtkSettings *settings,
411 GParamSpec *pspec,
412 GtkIconTheme *icon_theme)
414 update_current_theme (icon_theme);
417 static void
418 unset_screen (GtkIconTheme *icon_theme)
420 GtkIconThemePrivate *priv = icon_theme->priv;
421 GtkSettings *settings;
422 GdkDisplay *display;
424 if (priv->screen)
426 settings = gtk_settings_get_for_screen (priv->screen);
427 display = gdk_screen_get_display (priv->screen);
429 g_signal_handlers_disconnect_by_func (display,
430 (gpointer) display_closed,
431 icon_theme);
432 g_signal_handlers_disconnect_by_func (settings,
433 (gpointer) theme_changed,
434 icon_theme);
436 priv->screen = NULL;
441 * gtk_icon_theme_set_screen:
442 * @icon_theme: a #GtkIconTheme
443 * @screen: a #GdkScreen
445 * Sets the screen for an icon theme; the screen is used
446 * to track the user's currently configured icon theme,
447 * which might be different for different screens.
449 void
450 gtk_icon_theme_set_screen (GtkIconTheme *icon_theme,
451 GdkScreen *screen)
453 GtkIconThemePrivate *priv;
454 GtkSettings *settings;
455 GdkDisplay *display;
457 g_return_if_fail (GTK_ICON_THEME (icon_theme));
458 g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
460 priv = icon_theme->priv;
462 unset_screen (icon_theme);
464 if (screen)
466 display = gdk_screen_get_display (screen);
467 settings = gtk_settings_get_for_screen (screen);
469 priv->screen = screen;
471 g_signal_connect (display, "closed",
472 G_CALLBACK (display_closed), icon_theme);
473 g_signal_connect (settings, "notify::gtk-icon-theme-name",
474 G_CALLBACK (theme_changed), icon_theme);
477 update_current_theme (icon_theme);
480 /* Checks whether a loader for SVG files has been registered
481 * with GdkPixbuf.
483 static gboolean
484 pixbuf_supports_svg ()
486 GSList *formats = gdk_pixbuf_get_formats ();
487 GSList *tmp_list;
488 gboolean found_svg = FALSE;
490 for (tmp_list = formats; tmp_list && !found_svg; tmp_list = tmp_list->next)
492 gchar **mime_types = gdk_pixbuf_format_get_mime_types (tmp_list->data);
493 gchar **mime_type;
495 for (mime_type = mime_types; *mime_type && !found_svg; mime_type++)
497 if (strcmp (*mime_type, "image/svg") == 0)
498 found_svg = TRUE;
501 g_strfreev (mime_types);
504 g_slist_free (formats);
506 return found_svg;
509 #define GTK_DATA_PREFIX "/tmp/"
510 static void
511 gtk_icon_theme_init (GtkIconTheme *icon_theme)
513 GtkIconThemePrivate *priv;
515 priv = g_new0(GtkIconThemePrivate, 1);
516 icon_theme->priv = priv;
518 priv->custom_theme = FALSE;
519 priv->current_theme = g_strdup (DEFAULT_THEME_NAME);
520 priv->search_path = g_new (char *, 5);
523 priv->search_path[0] = g_build_filename (g_get_home_dir (),
524 ".icons",
525 NULL);
526 priv->search_path[1] = g_build_filename (GTK_DATA_PREFIX, "pixmaps", NULL);
527 priv->search_path[2] = g_build_filename (GTK_DATA_PREFIX, "icons", NULL);
528 priv->search_path[3] = g_strdup ("/usr/share/icons");
529 priv->search_path[4] = g_strdup ("/usr/share/pixmaps");
530 priv->search_path_len = 5;
532 priv->themes_valid = FALSE;
533 priv->themes = NULL;
534 priv->unthemed_icons = NULL;
536 priv->pixbuf_supports_svg = pixbuf_supports_svg ();
539 static void
540 free_dir_mtime (IconThemeDirMtime *dir_mtime)
542 g_free (dir_mtime->dir);
543 g_free (dir_mtime);
546 static void
547 do_theme_change (GtkIconTheme *icon_theme)
549 GtkIconThemePrivate *priv = icon_theme->priv;
551 blow_themes (icon_theme);
552 g_signal_emit (G_OBJECT (icon_theme), signal_changed, 0);
554 if (priv->screen && priv->is_screen_singleton)
556 GtkSettings *settings = gtk_settings_get_for_screen (priv->screen);
557 _gtk_rc_reset_styles (settings);
561 static void
562 blow_themes (GtkIconTheme *icon_theme)
564 GtkIconThemePrivate *priv = icon_theme->priv;
566 if (priv->themes_valid)
568 g_hash_table_destroy (priv->all_icons);
569 g_list_foreach (priv->themes, (GFunc)theme_destroy, NULL);
570 g_list_free (priv->themes);
571 g_list_foreach (priv->dir_mtimes, (GFunc)free_dir_mtime, NULL);
572 g_list_free (priv->dir_mtimes);
573 g_hash_table_destroy (priv->unthemed_icons);
575 priv->themes = NULL;
576 priv->unthemed_icons = NULL;
577 priv->dir_mtimes = NULL;
578 priv->all_icons = NULL;
579 priv->themes_valid = FALSE;
582 static void
583 gtk_icon_theme_finalize (GObject *object)
585 GtkIconTheme *icon_theme;
586 GtkIconThemePrivate *priv;
587 int i;
589 icon_theme = GTK_ICON_THEME (object);
590 priv = icon_theme->priv;
592 unset_screen (icon_theme);
594 g_free (priv->current_theme);
595 priv->current_theme = NULL;
597 for (i=0; i < priv->search_path_len; i++)
598 g_free (priv->search_path[i]);
600 g_free (priv->search_path);
601 priv->search_path = NULL;
603 blow_themes (icon_theme);
607 * gtk_icon_theme_set_search_path:
608 * @icon_theme: a #GtkIconTheme
609 * @path: array of directories that are searched for icon themes
610 * @n_elements: number of elements in @path.
612 * Sets the search path for the icon theme object. When looking
613 * for an icon theme, GTK+ will search for a subdirectory of
614 * one or more of the directories in @path with the same name
615 * as the icon theme. (Themes from multiple of the path elements
616 * are combined to allow themes to be extended by adding icons
617 * in the user's home directory.)
619 * In addition if an icon found isn't found either in the current
620 * icon theme or the default icon theme, and an image file with
621 * the right name is found directly in one of the elements of
622 * @path, then that image will be used for the icon name.
623 * (This is legacy feature, and new icons should be put
624 * into the default icon theme, which is called "hicolor", rather than
625 * directly on the icon path.)
627 void
628 gtk_icon_theme_set_search_path (GtkIconTheme *icon_theme,
629 const gchar *path[],
630 gint n_elements)
632 GtkIconThemePrivate *priv;
633 gint i;
635 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
637 priv = icon_theme->priv;
638 for (i = 0; i < priv->search_path_len; i++)
639 g_free (priv->search_path[i]);
641 g_free (priv->search_path);
643 priv->search_path = g_new (gchar *, n_elements);
644 priv->search_path_len = n_elements;
645 for (i = 0; i < priv->search_path_len; i++)
646 priv->search_path[i] = g_strdup (path[i]);
648 do_theme_change (icon_theme);
653 * gtk_icon_theme_get_search_path:
654 * @icon_theme: a #GtkIconTheme
655 * @path: location to store a list of icon theme path directories or %NULL
656 * The stored value should be freed with g_strfreev().
657 * @n_elements: location to store number of elements
658 * in @path, or %NULL
660 * Gets the current search path. See gtk_icon_theme_set_search_path().
662 void
663 gtk_icon_theme_get_search_path (GtkIconTheme *icon_theme,
664 gchar **path[],
665 gint *n_elements)
667 GtkIconThemePrivate *priv;
668 int i;
670 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
672 priv = icon_theme->priv;
674 if (n_elements)
675 *n_elements = priv->search_path_len;
677 if (path)
679 *path = g_new (gchar *, priv->search_path_len + 1);
680 for (i = 0; i < priv->search_path_len; i++)
681 (*path)[i] = g_strdup (priv->search_path[i] + 1);
682 (*path)[i] = NULL;
687 * gtk_icon_theme_append_search_path:
688 * @icon_theme: a #GtkIconTheme
689 * @path: directory name to append to the icon path
691 * Appends a directory to the search path. See gtk_icon_theme_set_search_path().
693 void
694 gtk_icon_theme_append_search_path (GtkIconTheme *icon_theme,
695 const gchar *path)
697 GtkIconThemePrivate *priv;
699 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
700 g_return_if_fail (path != NULL);
702 priv = icon_theme->priv;
704 priv->search_path_len++;
705 priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
706 priv->search_path[priv->search_path_len-1] = g_strdup (path);
708 do_theme_change (icon_theme);
712 * gtk_icon_theme_prepend_search_path:
713 * @icon_theme: a #GtkIconTheme
714 * @path: directory name to prepend to the icon path
716 * Prepends a directory to the search path. See gtk_icon_theme_set_search_path().
718 void
719 gtk_icon_theme_prepend_search_path (GtkIconTheme *icon_theme,
720 const gchar *path)
722 GtkIconThemePrivate *priv;
723 int i;
725 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
726 g_return_if_fail (path != NULL);
728 priv = icon_theme->priv;
730 priv->search_path_len++;
731 priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
733 for (i = 0; i < priv->search_path_len - 1; i++)
734 priv->search_path[i+1] = priv->search_path[i];
736 priv->search_path[0] = g_strdup (path);
738 do_theme_change (icon_theme);
742 * gtk_icon_theme_set_custom_theme:
743 * @icon_theme: a #GtkIconTheme
744 * @theme_name: name of icon theme to use instead of configured theme
746 * Sets the name of the icon theme that the #GtkIconTheme object uses
747 * overriding system configuration. This function cannot be called
748 * on the icon theme objects returned from gtk_icon_theme_get_default()
749 * and gtk_icon_theme_get_default().
751 void
752 gtk_icon_theme_set_custom_theme (GtkIconTheme *icon_theme,
753 const gchar *theme_name)
755 GtkIconThemePrivate *priv;
757 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
759 priv = icon_theme->priv;
761 g_return_if_fail (!priv->is_screen_singleton);
763 if (theme_name != NULL)
765 priv->custom_theme = TRUE;
766 if (strcmp (theme_name, priv->current_theme) != 0)
768 g_free (priv->current_theme);
769 priv->current_theme = g_strdup (theme_name);
771 do_theme_change (icon_theme);
774 else
776 priv->custom_theme = FALSE;
778 update_current_theme (icon_theme);
782 static void
783 insert_theme (GtkIconTheme *icon_theme, const char *theme_name)
785 int i;
786 GList *l;
787 char **dirs;
788 char **themes;
789 GtkIconThemePrivate *priv;
790 IconTheme *theme;
791 char *path;
792 char *contents;
793 char *directories;
794 char *inherits;
795 GtkIconThemeFile *theme_file;
796 IconThemeDirMtime *dir_mtime;
797 struct stat stat_buf;
799 priv = icon_theme->priv;
801 for (l = priv->themes; l != NULL; l = l->next)
803 theme = l->data;
804 if (strcmp (theme->name, theme_name) == 0)
805 return;
808 for (i = 0; i < priv->search_path_len; i++)
810 path = g_build_filename (priv->search_path[i],
811 theme_name,
812 NULL);
813 dir_mtime = g_new (IconThemeDirMtime, 1);
814 dir_mtime->dir = path;
815 if (stat (path, &stat_buf) == 0 && S_ISDIR (stat_buf.st_mode))
816 dir_mtime->mtime = stat_buf.st_mtime;
817 else
818 dir_mtime->mtime = 0;
820 priv->dir_mtimes = g_list_prepend (priv->dir_mtimes, dir_mtime);
823 theme_file = NULL;
824 for (i = 0; i < priv->search_path_len; i++)
826 path = g_build_filename (priv->search_path[i],
827 theme_name,
828 "index.theme",
829 NULL);
830 if (g_file_test (path, G_FILE_TEST_IS_REGULAR)) {
831 if (g_file_get_contents (path, &contents, NULL, NULL)) {
832 theme_file = _gtk_icon_theme_file_new_from_string (contents, NULL);
833 g_free (contents);
834 g_free (path);
835 break;
838 g_free (path);
841 if (theme_file == NULL)
842 return;
844 theme = g_new (IconTheme, 1);
845 if (!_gtk_icon_theme_file_get_locale_string (theme_file,
846 "Icon Theme",
847 "Name",
848 &theme->display_name))
850 g_warning ("Theme file for %s has no name\n", theme_name);
851 g_free (theme);
852 _gtk_icon_theme_file_free (theme_file);
853 return;
856 if (!_gtk_icon_theme_file_get_string (theme_file,
857 "Icon Theme",
858 "Directories",
859 &directories))
861 g_warning ("Theme file for %s has no directories\n", theme_name);
862 g_free (theme->display_name);
863 g_free (theme);
864 _gtk_icon_theme_file_free (theme_file);
865 return;
868 theme->name = g_strdup (theme_name);
869 _gtk_icon_theme_file_get_locale_string (theme_file,
870 "Icon Theme",
871 "Comment",
872 &theme->comment);
873 _gtk_icon_theme_file_get_string (theme_file,
874 "Icon Theme",
875 "Example",
876 &theme->example);
878 dirs = g_strsplit (directories, ",", 0);
880 theme->dirs = NULL;
881 for (i = 0; dirs[i] != NULL; i++)
882 theme_subdir_load (icon_theme, theme, theme_file, dirs[i]);
884 g_strfreev (dirs);
886 theme->dirs = g_list_reverse (theme->dirs);
888 g_free (directories);
890 /* Prepend the finished theme */
891 priv->themes = g_list_prepend (priv->themes, theme);
893 if (_gtk_icon_theme_file_get_string (theme_file,
894 "Icon Theme",
895 "Inherits",
896 &inherits))
898 themes = g_strsplit (inherits, ",", 0);
900 for (i = 0; themes[i] != NULL; i++)
901 insert_theme (icon_theme, themes[i]);
903 g_strfreev (themes);
905 g_free (inherits);
908 _gtk_icon_theme_file_free (theme_file);
911 static void
912 free_unthemed_icon (UnthemedIcon *unthemed_icon)
914 if (unthemed_icon->svg_filename)
915 g_free (unthemed_icon->svg_filename);
916 if (unthemed_icon->svg_filename)
917 g_free (unthemed_icon->no_svg_filename);
918 g_free (unthemed_icon);
921 static void
922 load_themes (GtkIconTheme *icon_theme)
924 GtkIconThemePrivate *priv;
925 GDir *gdir;
926 int base;
927 char *dir, *base_name, *dot;
928 const char *file;
929 char *abs_file;
930 UnthemedIcon *unthemed_icon;
931 IconSuffix old_suffix, new_suffix;
932 GTimeVal tv;
934 priv = icon_theme->priv;
936 priv->all_icons = g_hash_table_new (g_str_hash, g_str_equal);
938 insert_theme (icon_theme, priv->current_theme);
940 /* Always look in the "default" icon theme */
941 insert_theme (icon_theme, DEFAULT_THEME_NAME);
942 priv->themes = g_list_reverse (priv->themes);
944 priv->unthemed_icons = g_hash_table_new_full (g_str_hash, g_str_equal,
945 g_free, (GDestroyNotify)free_unthemed_icon);
947 for (base = 0; base < icon_theme->priv->search_path_len; base++)
949 dir = icon_theme->priv->search_path[base];
950 gdir = g_dir_open (dir, 0, NULL);
952 if (gdir == NULL)
953 continue;
955 while ((file = g_dir_read_name (gdir)))
957 new_suffix = suffix_from_name (file);
959 if (new_suffix != ICON_SUFFIX_NONE)
961 abs_file = g_build_filename (dir, file, NULL);
963 base_name = g_strdup (file);
965 dot = strrchr (base_name, '.');
966 if (dot)
967 *dot = 0;
969 if ((unthemed_icon = g_hash_table_lookup (priv->unthemed_icons,
970 base_name)))
972 if (new_suffix == ICON_SUFFIX_SVG)
974 if (unthemed_icon->no_svg_filename)
975 g_free (abs_file);
976 else
977 unthemed_icon->svg_filename = abs_file;
979 else
981 if (unthemed_icon->no_svg_filename)
983 old_suffix = suffix_from_name (unthemed_icon->no_svg_filename);
984 if (new_suffix > old_suffix)
986 g_free (unthemed_icon->no_svg_filename);
987 unthemed_icon->no_svg_filename = abs_file;
989 else
990 g_free (abs_file);
992 else
993 unthemed_icon->no_svg_filename = abs_file;
996 g_free (base_name);
998 else
1000 unthemed_icon = g_new0 (UnthemedIcon, 1);
1002 if (new_suffix == ICON_SUFFIX_SVG)
1003 unthemed_icon->svg_filename = abs_file;
1004 else
1005 unthemed_icon->svg_filename = abs_file;
1007 g_hash_table_insert (priv->unthemed_icons,
1008 base_name,
1009 unthemed_icon);
1010 g_hash_table_insert (priv->all_icons,
1011 base_name, NULL);
1015 g_dir_close (gdir);
1018 priv->themes_valid = TRUE;
1020 g_get_current_time(&tv);
1021 priv->last_stat_time = tv.tv_sec;
1024 static void
1025 ensure_valid_themes (GtkIconTheme *icon_theme)
1027 GtkIconThemePrivate *priv = icon_theme->priv;
1028 GTimeVal tv;
1030 if (priv->themes_valid)
1032 g_get_current_time(&tv);
1034 if (ABS (tv.tv_sec - priv->last_stat_time) > 5)
1035 gtk_icon_theme_rescan_if_needed (icon_theme);
1038 if (!priv->themes_valid)
1039 load_themes (icon_theme);
1043 * gtk_icon_theme_lookup_icon:
1044 * @icon_theme: a #GtkIconTheme
1045 * @icon_name: the name of the icon to lookup
1046 * @size: desired icon size
1047 * @flags: flags modifying the behavior of the icon lookup
1049 * Looks up a named icon and returns a structure containing
1050 * information such as the filename of the icon. The icon
1051 * can then be rendered into a pixbuf using
1052 * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
1053 * combines these two steps if all you need is the pixbuf.)
1055 * Return value: a #GtkIconInfo structure containing information
1056 * about the icon, or %NULL if the icon wasn't found. Free with
1057 * gtk_icon_info_free()
1059 GtkIconInfo *
1060 gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme,
1061 const gchar *icon_name,
1062 gint size,
1063 GtkIconLookupFlags flags)
1065 GtkIconThemePrivate *priv;
1066 GList *l;
1067 GtkIconInfo *icon_info = NULL;
1068 UnthemedIcon *unthemed_icon;
1069 gboolean allow_svg;
1070 gboolean use_builtin;
1071 gboolean found_default;
1073 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1074 g_return_val_if_fail (icon_name != NULL, NULL);
1075 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1076 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1078 priv = icon_theme->priv;
1080 if (flags & GTK_ICON_LOOKUP_NO_SVG)
1081 allow_svg = FALSE;
1082 else if (flags & GTK_ICON_LOOKUP_FORCE_SVG)
1083 allow_svg = TRUE;
1084 else
1085 allow_svg = priv->pixbuf_supports_svg;
1087 use_builtin = (flags & GTK_ICON_LOOKUP_USE_BUILTIN);
1089 ensure_valid_themes (icon_theme);
1091 found_default = FALSE;
1092 l = priv->themes;
1093 while (l != NULL)
1095 IconTheme *icon_theme = l->data;
1097 if (strcmp (icon_theme->name, DEFAULT_THEME_NAME) == 0)
1098 found_default = TRUE;
1100 icon_info = theme_lookup_icon (icon_theme, icon_name, size, allow_svg, use_builtin);
1101 if (icon_info)
1102 goto out;
1104 l = l->next;
1107 if (!found_default)
1109 BuiltinIcon *builtin = find_builtin_icon (icon_name, size, NULL, NULL);
1110 if (builtin)
1112 icon_info = icon_info_new_builtin (builtin);
1113 goto out;
1117 unthemed_icon = g_hash_table_lookup (priv->unthemed_icons, icon_name);
1118 if (unthemed_icon)
1120 icon_info = icon_info_new ();
1122 /* A SVG icon, when allowed, beats out a XPM icon, but not
1123 * a PNG icon
1125 if (allow_svg &&
1126 unthemed_icon->svg_filename &&
1127 (!unthemed_icon->no_svg_filename ||
1128 suffix_from_name (unthemed_icon->no_svg_filename) != ICON_SUFFIX_PNG))
1129 icon_info->filename = g_strdup (unthemed_icon->svg_filename);
1130 else if (unthemed_icon->no_svg_filename)
1131 icon_info->filename = g_strdup (unthemed_icon->no_svg_filename);
1133 icon_info->dir_type = ICON_THEME_DIR_UNTHEMED;
1136 out:
1137 if (icon_info)
1138 icon_info->desired_size = size;
1140 return icon_info;
1143 /* Error quark */
1144 GQuark
1145 gtk_icon_theme_error_quark (void)
1147 static GQuark q = 0;
1148 if (q == 0)
1149 q = g_quark_from_static_string ("gtk-icon-theme-error-quark");
1151 return q;
1155 * gtk_icon_theme_load_icon:
1156 * @icon_theme: a #GtkIconTheme
1157 * @icon_name: the name of the icon to lookup
1158 * @size: the desired icon size. The resulting icon may not be
1159 * exactly this size; see gtk_icon_info_load_icon().
1160 * @flags: flags modifying the behavior of the icon lookup
1161 * @error: Location to store error information on failure, or %NULL.
1163 * Looks up an icon in an icon theme, scales it to the given size
1164 * and renders it into a pixbuf. This is a convenience function;
1165 * if more details about the icon are needed, use
1166 * gtk_icon_theme_lookup_icon() followed by gtk_icon_info_load_icon().
1168 * Return value: the rendered icon; this may be a newly created icon
1169 * or a new reference to an internal icon, so you must not modify
1170 * the icon. Use g_object_unref() to release your reference to the
1171 * icon. %NULL if the icon isn't found.
1173 GdkPixbuf *
1174 gtk_icon_theme_load_icon (GtkIconTheme *icon_theme,
1175 const gchar *icon_name,
1176 gint size,
1177 GtkIconLookupFlags flags,
1178 GError **error)
1180 GtkIconInfo *icon_info;
1181 GdkPixbuf *pixbuf = NULL;
1183 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1184 g_return_val_if_fail (icon_name != NULL, NULL);
1185 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1186 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1187 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1189 icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size,
1190 flags | GTK_ICON_LOOKUP_USE_BUILTIN);
1191 if (!icon_info)
1193 g_set_error (error, GTK_ICON_THEME_ERROR, GTK_ICON_THEME_NOT_FOUND,
1194 _("Icon '%s' not present in theme"), icon_name);
1195 return NULL;
1198 pixbuf = gtk_icon_info_load_icon (icon_info, error);
1199 gtk_icon_info_free (icon_info);
1201 return pixbuf;
1205 * gtk_icon_theme_has_icon:
1206 * @icon_theme: a #GtkIconTheme
1207 * @icon_name: the name of an icon
1209 * Checks whether an icon theme includes an icon
1210 * for a particular name.
1212 * Return value: %TRUE if @icon_theme includes an
1213 * icon for @icon_name.
1215 gboolean
1216 gtk_icon_theme_has_icon (GtkIconTheme *icon_theme,
1217 const char *icon_name)
1219 GtkIconThemePrivate *priv;
1221 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
1223 priv = icon_theme->priv;
1225 ensure_valid_themes (icon_theme);
1227 if (g_hash_table_lookup_extended (priv->all_icons,
1228 icon_name, NULL, NULL))
1229 return TRUE;
1230 if (g_hash_table_lookup_extended (icon_theme_builtin_icons,
1231 icon_name, NULL, NULL))
1232 return TRUE;
1234 return FALSE;
1238 static void
1239 add_key_to_hash (gpointer key,
1240 gpointer value,
1241 gpointer user_data)
1243 GHashTable *hash = user_data;
1245 g_hash_table_insert (hash, key, NULL);
1248 static void
1249 add_key_to_list (gpointer key,
1250 gpointer value,
1251 gpointer user_data)
1253 GList **list = user_data;
1255 *list = g_list_prepend (*list, g_strdup (key));
1259 * gtk_icon_theme_list_icons:
1260 * @icon_theme: a #GtkIconTheme
1261 * @context: a string identifying a particular type of icon,
1262 * or %NULL to list all icons.
1264 * Lists the icons in the current icon theme. Only a subset
1265 * of the icons can be listed by providing a context string.
1266 * The set of values for the context string is system dependent,
1267 * but will typically include such values as 'apps' and
1268 * 'mimetypes'.
1270 * Return value: a #GList list holding the names of all the
1271 * icons in the theme. You must first free each element
1272 * in the list with g_free(), then free the list itself
1273 * with g_list_free().
1275 GList *
1276 gtk_icon_theme_list_icons (GtkIconTheme *icon_theme,
1277 const char *context)
1279 GtkIconThemePrivate *priv;
1280 GHashTable *icons;
1281 GList *list, *l;
1282 GQuark context_quark;
1284 priv = icon_theme->priv;
1286 ensure_valid_themes (icon_theme);
1288 if (context)
1290 context_quark = g_quark_try_string (context);
1292 if (!context_quark)
1293 return NULL;
1295 else
1296 context_quark = 0;
1298 icons = g_hash_table_new (g_str_hash, g_str_equal);
1300 l = priv->themes;
1301 while (l != NULL)
1303 theme_list_icons (l->data, icons, context_quark);
1304 l = l->next;
1307 if (context_quark == 0)
1308 g_hash_table_foreach (priv->unthemed_icons,
1309 add_key_to_hash,
1310 icons);
1312 list = 0;
1314 g_hash_table_foreach (icons,
1315 add_key_to_list,
1316 &list);
1318 g_hash_table_destroy (icons);
1320 return list;
1324 * gtk_icon_theme_get_example_icon_name:
1325 * @icon_theme: a #GtkIconTheme
1327 * Gets the name of an icon that is representative of the
1328 * current theme (for instance, to use when presenting
1329 * a list of themes to the user.)
1331 * Return value: the name of an example icon or %NULL.
1332 * Free with g_free().
1334 char *
1335 gtk_icon_theme_get_example_icon_name (GtkIconTheme *icon_theme)
1337 GtkIconThemePrivate *priv;
1338 GList *l;
1339 IconTheme *theme;
1341 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1343 priv = icon_theme->priv;
1345 ensure_valid_themes (icon_theme);
1347 l = priv->themes;
1348 while (l != NULL)
1350 theme = l->data;
1351 if (theme->example)
1352 return g_strdup (theme->example);
1354 l = l->next;
1357 return NULL;
1361 * gtk_icon_theme_rescan_if_needed:
1362 * @icon_theme: a #GtkIconTheme
1364 * Checks to see if the icon theme has changed; if it has, any
1365 * currently cached information is discarded and will be reloaded
1366 * next time @icon_theme is accessed.
1368 * Return value: %TRUE if the icon theme has changed and needed
1369 * to be reloaded.
1371 gboolean
1372 gtk_icon_theme_rescan_if_needed (GtkIconTheme *icon_theme)
1374 GtkIconThemePrivate *priv;
1375 IconThemeDirMtime *dir_mtime;
1376 GList *d;
1377 int stat_res;
1378 struct stat stat_buf;
1379 GTimeVal tv;
1381 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
1383 priv = icon_theme->priv;
1385 for (d = priv->dir_mtimes; d != NULL; d = d->next)
1387 dir_mtime = d->data;
1389 stat_res = stat (dir_mtime->dir, &stat_buf);
1391 /* dir mtime didn't change */
1392 if (stat_res == 0 &&
1393 S_ISDIR (stat_buf.st_mode) &&
1394 dir_mtime->mtime == stat_buf.st_mtime)
1395 continue;
1396 /* didn't exist before, and still doesn't */
1397 if (dir_mtime->mtime == 0 &&
1398 (stat_res != 0 || !S_ISDIR (stat_buf.st_mode)))
1399 continue;
1401 do_theme_change (icon_theme);
1402 return TRUE;
1405 g_get_current_time (&tv);
1406 priv->last_stat_time = tv.tv_sec;
1408 return FALSE;
1411 static void
1412 theme_destroy (IconTheme *theme)
1414 g_free (theme->display_name);
1415 g_free (theme->comment);
1416 g_free (theme->name);
1417 g_free (theme->example);
1419 g_list_foreach (theme->dirs, (GFunc)theme_dir_destroy, NULL);
1420 g_list_free (theme->dirs);
1421 g_free (theme);
1424 static void
1425 theme_dir_destroy (IconThemeDir *dir)
1427 g_hash_table_destroy (dir->icons);
1428 if (dir->icon_data)
1429 g_hash_table_destroy (dir->icon_data);
1430 g_free (dir->dir);
1431 g_free (dir);
1434 static int
1435 theme_dir_size_difference (IconThemeDir *dir, int size, gboolean *smaller)
1437 int min, max;
1438 switch (dir->type)
1440 case ICON_THEME_DIR_FIXED:
1441 *smaller = size < dir->size;
1442 return abs (size - dir->size);
1443 break;
1444 case ICON_THEME_DIR_SCALABLE:
1445 *smaller = size < dir->min_size;
1446 if (size < dir->min_size)
1447 return dir->min_size - size;
1448 if (size > dir->max_size)
1449 return size - dir->max_size;
1450 return 0;
1451 break;
1452 case ICON_THEME_DIR_THRESHOLD:
1453 min = dir->size - dir->threshold;
1454 max = dir->size + dir->threshold;
1455 *smaller = size < min;
1456 if (size < min)
1457 return min - size;
1458 if (size > max)
1459 return size - max;
1460 return 0;
1461 break;
1462 case ICON_THEME_DIR_UNTHEMED:
1463 g_assert_not_reached ();
1464 break;
1466 g_assert_not_reached ();
1467 return 1000;
1470 static const char *
1471 string_from_suffix (IconSuffix suffix)
1473 switch (suffix)
1475 case ICON_SUFFIX_XPM:
1476 return ".xpm";
1477 case ICON_SUFFIX_SVG:
1478 return ".svg";
1479 case ICON_SUFFIX_PNG:
1480 return ".png";
1481 default:
1482 g_assert_not_reached();
1484 return NULL;
1487 static IconSuffix
1488 suffix_from_name (const char *name)
1490 IconSuffix retval;
1492 if (g_str_has_suffix (name, ".png"))
1493 retval = ICON_SUFFIX_PNG;
1494 else if (g_str_has_suffix (name, ".svg"))
1495 retval = ICON_SUFFIX_SVG;
1496 else if (g_str_has_suffix (name, ".xpm"))
1497 retval = ICON_SUFFIX_XPM;
1498 else
1499 retval = ICON_SUFFIX_NONE;
1501 return retval;
1504 static IconSuffix
1505 best_suffix (IconSuffix suffix,
1506 gboolean allow_svg)
1508 if ((suffix & ICON_SUFFIX_PNG) != 0)
1509 return ICON_SUFFIX_PNG;
1510 else if (allow_svg && ((suffix & ICON_SUFFIX_SVG) != 0))
1511 return ICON_SUFFIX_SVG;
1512 else if ((suffix & ICON_SUFFIX_XPM) != 0)
1513 return ICON_SUFFIX_XPM;
1514 else
1515 return ICON_SUFFIX_NONE;
1518 static GtkIconInfo *
1519 theme_lookup_icon (IconTheme *theme,
1520 const char *icon_name,
1521 int size,
1522 gboolean allow_svg,
1523 gboolean use_builtin)
1525 GList *l;
1526 IconThemeDir *dir, *min_dir;
1527 char *file;
1528 int min_difference, difference;
1529 BuiltinIcon *closest_builtin = NULL;
1530 gboolean smaller, has_larger;
1531 IconSuffix suffix;
1533 min_difference = G_MAXINT;
1534 min_dir = NULL;
1535 has_larger = FALSE;
1537 /* Builtin icons are logically part of the default theme and
1538 * are searched before other subdirectories of the default theme.
1540 if (strcmp (theme->name, DEFAULT_THEME_NAME) == 0 && use_builtin)
1542 closest_builtin = find_builtin_icon (icon_name, size,
1543 &min_difference,
1544 &has_larger);
1546 if (min_difference == 0)
1547 return icon_info_new_builtin (closest_builtin);
1550 l = theme->dirs;
1551 while (l != NULL)
1553 dir = l->data;
1555 suffix = GPOINTER_TO_UINT (g_hash_table_lookup (dir->icons, icon_name));
1557 if (suffix != ICON_SUFFIX_NONE &&
1558 (allow_svg || suffix != ICON_SUFFIX_SVG))
1560 difference = theme_dir_size_difference (dir, size, &smaller);
1562 if (difference == 0)
1564 min_dir = dir;
1565 break;
1568 if (!has_larger)
1570 if (difference < min_difference || smaller)
1572 min_difference = difference;
1573 min_dir = dir;
1574 closest_builtin = NULL;
1575 has_larger = smaller;
1578 else
1580 if (difference < min_difference && smaller)
1582 min_difference = difference;
1583 min_dir = dir;
1584 closest_builtin = NULL;
1590 l = l->next;
1593 if (closest_builtin)
1594 return icon_info_new_builtin (closest_builtin);
1596 if (min_dir)
1598 GtkIconInfo *icon_info = icon_info_new ();
1600 suffix = GPOINTER_TO_UINT (g_hash_table_lookup (min_dir->icons, icon_name));
1601 suffix = best_suffix (suffix, allow_svg);
1602 g_assert (suffix != ICON_SUFFIX_NONE);
1604 file = g_strconcat (icon_name, string_from_suffix (suffix), NULL);
1605 icon_info->filename = g_build_filename (min_dir->dir, file, NULL);
1606 g_free (file);
1608 if (min_dir->icon_data != NULL)
1609 icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
1611 icon_info->dir_type = min_dir->type;
1612 icon_info->dir_size = min_dir->size;
1613 icon_info->threshold = min_dir->threshold;
1615 return icon_info;
1618 return NULL;
1621 static void
1622 theme_list_icons (IconTheme *theme, GHashTable *icons,
1623 GQuark context)
1625 GList *l = theme->dirs;
1626 IconThemeDir *dir;
1628 while (l != NULL)
1630 dir = l->data;
1632 if (context == dir->context ||
1633 context == 0)
1634 g_hash_table_foreach (dir->icons,
1635 add_key_to_hash,
1636 icons);
1638 l = l->next;
1642 static void
1643 load_icon_data (IconThemeDir *dir, const char *path, const char *name)
1645 GtkIconThemeFile *icon_file;
1646 char *base_name;
1647 char **split;
1648 char *contents;
1649 char *dot;
1650 char *str;
1651 char *split_point;
1652 int i;
1654 GtkIconData *data;
1656 if (g_file_get_contents (path, &contents, NULL, NULL))
1658 icon_file = _gtk_icon_theme_file_new_from_string (contents, NULL);
1660 if (icon_file)
1662 base_name = g_strdup (name);
1663 dot = strrchr (base_name, '.');
1664 *dot = 0;
1666 data = g_new0 (GtkIconData, 1);
1667 g_hash_table_replace (dir->icon_data, base_name, data);
1669 if (_gtk_icon_theme_file_get_string (icon_file, "Icon Data",
1670 "EmbeddedTextRectangle",
1671 &str))
1673 split = g_strsplit (str, ",", 4);
1675 i = 0;
1676 while (split[i] != NULL)
1677 i++;
1679 if (i==4)
1681 data->has_embedded_rect = TRUE;
1682 data->x0 = atoi (split[0]);
1683 data->y0 = atoi (split[1]);
1684 data->x1 = atoi (split[2]);
1685 data->y1 = atoi (split[3]);
1688 g_strfreev (split);
1689 g_free (str);
1693 if (_gtk_icon_theme_file_get_string (icon_file, "Icon Data",
1694 "AttachPoints",
1695 &str))
1697 split = g_strsplit (str, "|", -1);
1699 i = 0;
1700 while (split[i] != NULL)
1701 i++;
1703 data->n_attach_points = i;
1704 data->attach_points = g_malloc (sizeof (GdkPoint) * i);
1706 i = 0;
1707 while (split[i] != NULL && i < data->n_attach_points)
1709 split_point = strchr (split[i], ',');
1710 if (split_point)
1712 *split_point = 0;
1713 split_point++;
1714 data->attach_points[i].x = atoi (split[i]);
1715 data->attach_points[i].y = atoi (split_point);
1717 i++;
1720 g_strfreev (split);
1721 g_free (str);
1724 _gtk_icon_theme_file_get_locale_string (icon_file, "Icon Data",
1725 "DisplayName",
1726 &data->display_name);
1728 _gtk_icon_theme_file_free (icon_file);
1730 g_free (contents);
1735 static void
1736 scan_directory (GtkIconThemePrivate *icon_theme,
1737 IconThemeDir *dir, char *full_dir)
1739 GDir *gdir;
1740 const char *name;
1741 char *base_name, *dot;
1742 char *path;
1743 IconSuffix suffix, hash_suffix;
1745 dir->icons = g_hash_table_new_full (g_str_hash, g_str_equal,
1746 g_free, NULL);
1748 gdir = g_dir_open (full_dir, 0, NULL);
1750 if (gdir == NULL)
1751 return;
1753 while ((name = g_dir_read_name (gdir)))
1755 if (g_str_has_suffix (name, ".icon"))
1757 if (dir->icon_data == NULL)
1758 dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
1759 g_free, (GDestroyNotify)icon_data_free);
1761 path = g_build_filename (full_dir, name, NULL);
1762 if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
1763 load_icon_data (dir, path, name);
1765 g_free (path);
1767 continue;
1770 suffix = suffix_from_name (name);
1771 if (suffix == ICON_SUFFIX_NONE)
1772 continue;
1774 base_name = g_strdup (name);
1775 dot = strrchr (base_name, '.');
1776 *dot = 0;
1778 hash_suffix = GPOINTER_TO_INT (g_hash_table_lookup (dir->icons, base_name));
1779 g_hash_table_replace (dir->icons, base_name, GUINT_TO_POINTER (hash_suffix| suffix));
1780 g_hash_table_insert (icon_theme->all_icons, base_name, NULL);
1783 g_dir_close (gdir);
1786 static void
1787 theme_subdir_load (GtkIconTheme *icon_theme,
1788 IconTheme *theme,
1789 GtkIconThemeFile *theme_file,
1790 char *subdir)
1792 int base;
1793 char *type_string;
1794 IconThemeDir *dir;
1795 IconThemeDirType type;
1796 char *context_string;
1797 GQuark context;
1798 int size;
1799 int min_size;
1800 int max_size;
1801 int threshold;
1802 char *full_dir;
1804 if (!_gtk_icon_theme_file_get_integer (theme_file,
1805 subdir,
1806 "Size",
1807 &size))
1809 g_warning ("Theme directory %s of theme %s has no size field\n", subdir, theme->name);
1810 return;
1813 type = ICON_THEME_DIR_THRESHOLD;
1814 if (_gtk_icon_theme_file_get_string (theme_file, subdir, "Type", &type_string))
1816 if (strcmp (type_string, "Fixed") == 0)
1817 type = ICON_THEME_DIR_FIXED;
1818 else if (strcmp (type_string, "Scalable") == 0)
1819 type = ICON_THEME_DIR_SCALABLE;
1820 else if (strcmp (type_string, "Threshold") == 0)
1821 type = ICON_THEME_DIR_THRESHOLD;
1823 g_free (type_string);
1826 context = 0;
1827 if (_gtk_icon_theme_file_get_string (theme_file, subdir, "Context", &context_string))
1829 context = g_quark_from_string (context_string);
1830 g_free (context_string);
1833 if (!_gtk_icon_theme_file_get_integer (theme_file,
1834 subdir,
1835 "MaxSize",
1836 &max_size))
1837 max_size = size;
1839 if (!_gtk_icon_theme_file_get_integer (theme_file,
1840 subdir,
1841 "MinSize",
1842 &min_size))
1843 min_size = size;
1845 if (!_gtk_icon_theme_file_get_integer (theme_file,
1846 subdir,
1847 "Threshold",
1848 &threshold))
1849 threshold = 2;
1851 for (base = 0; base < icon_theme->priv->search_path_len; base++)
1853 full_dir = g_build_filename (icon_theme->priv->search_path[base],
1854 theme->name,
1855 subdir,
1856 NULL);
1857 if (g_file_test (full_dir, G_FILE_TEST_IS_DIR))
1859 dir = g_new (IconThemeDir, 1);
1860 dir->type = type;
1861 dir->context = context;
1862 dir->size = size;
1863 dir->min_size = min_size;
1864 dir->max_size = max_size;
1865 dir->threshold = threshold;
1866 dir->dir = full_dir;
1867 dir->icon_data = NULL;
1869 scan_directory (icon_theme->priv, dir, full_dir);
1871 theme->dirs = g_list_append (theme->dirs, dir);
1873 else
1874 g_free (full_dir);
1878 static void
1879 icon_data_free (GtkIconData *icon_data)
1881 g_free (icon_data->attach_points);
1882 g_free (icon_data->display_name);
1883 g_free (icon_data);
1887 * GtkIconInfo
1889 GType
1890 gtk_icon_info_get_type (void)
1892 static GType our_type = 0;
1894 if (our_type == 0)
1895 our_type = g_boxed_type_register_static ("GtkIconInfo",
1896 (GBoxedCopyFunc) gtk_icon_info_copy,
1897 (GBoxedFreeFunc) gtk_icon_info_free);
1899 return our_type;
1902 static GtkIconInfo *
1903 icon_info_new (void)
1905 GtkIconInfo *icon_info = g_new0 (GtkIconInfo, 1);
1907 icon_info->ref_count = 1;
1908 icon_info->scale = -1.;
1910 return icon_info;
1913 static GtkIconInfo *
1914 icon_info_new_builtin (BuiltinIcon *icon)
1916 GtkIconInfo *icon_info = icon_info_new ();
1918 icon_info->builtin_pixbuf = g_object_ref (icon->pixbuf);
1919 icon_info->dir_type = ICON_THEME_DIR_THRESHOLD;
1920 icon_info->dir_size = icon->size;
1921 icon_info->threshold = 2;
1923 return icon_info;
1927 * gtk_icon_info_copy:
1928 * @icon_info: a #GtkIconInfo
1930 * Make a copy of a #GtkIconInfo.
1932 * Return value: the new GtkIconInfo
1934 GtkIconInfo *
1935 gtk_icon_info_copy (GtkIconInfo *icon_info)
1937 GtkIconInfo *copy;
1939 g_return_val_if_fail (icon_info != NULL, NULL);
1941 copy = g_memdup (icon_info, sizeof (GtkIconInfo));
1942 if (copy->builtin_pixbuf)
1943 g_object_ref (copy->builtin_pixbuf);
1944 if (copy->pixbuf)
1945 g_object_ref (copy->pixbuf);
1946 if (copy->load_error)
1947 copy->load_error = g_error_copy (copy->load_error);
1948 if (copy->filename)
1949 copy->filename = g_strdup (copy->filename);
1951 return copy;
1955 * gtk_icon_info_free:
1956 * @icon_info: a #GtkIconInfo
1958 * Free a #GtkIconInfo and associated information
1960 void
1961 gtk_icon_info_free (GtkIconInfo *icon_info)
1963 g_return_if_fail (icon_info != NULL);
1965 if (icon_info->filename)
1966 g_free (icon_info->filename);
1967 if (icon_info->builtin_pixbuf)
1968 g_object_unref (icon_info->builtin_pixbuf);
1969 if (icon_info->pixbuf)
1970 g_object_unref (icon_info->pixbuf);
1972 g_free (icon_info);
1976 * gtk_icon_info_get_base_size:
1977 * @icon_info: a #GtkIconInfo
1979 * Gets the base size for the icon. The base size
1980 * is a size for the icon that was specified by
1981 * the icon theme creator. This may be different
1982 * than the actual size of image; an example of
1983 * this is small emblem icons that can be attached
1984 * to a larger icon. These icons will be given
1985 * the same base size as the larger icons to which
1986 * they are attached.
1988 * Return value: the base size, or 0, if no base
1989 * size is known for the icon.
1991 gint
1992 gtk_icon_info_get_base_size (GtkIconInfo *icon_info)
1994 g_return_val_if_fail (icon_info != NULL, 0);
1996 return icon_info->dir_size;
2000 * gtk_icon_info_get_filename:
2001 * @icon_info: a #GtkIconInfo
2003 * Gets the filename for the icon. If the
2004 * %GTK_ICON_LOOKUP_USE_BUILTIN flag was passed
2005 * to gtk_icon_theme_lookup_icon(), there may be
2006 * no filename if a builtin icon is returned; in this
2007 * case, you should use gtk_icon_info_get_builtin_pixbuf().
2009 * Return value: the filename for the icon, or %NULL
2010 * if gtk_icon_info_get_builtin_pixbuf() should
2011 * be used instead. The return value is owned by
2012 * GTK+ and should not be modified or freed.
2014 G_CONST_RETURN gchar *
2015 gtk_icon_info_get_filename (GtkIconInfo *icon_info)
2017 g_return_val_if_fail (icon_info != NULL, NULL);
2019 return icon_info->filename;
2023 * gtk_icon_info_get_builtin_pixbuf:
2024 * @icon_info: a #GtkIconInfo structure
2026 * Gets the built-in image for this icon, if any. To allow
2027 * GTK+ to use built in icon images, you must pass the
2028 * %GTK_ICON_LOOKUP_USE_BUILTIN to
2029 * gtk_icon_theme_lookup_icon().
2031 * Return value: the built-in image pixbuf, or %NULL. No
2032 * extra reference is added to the returned pixbuf, so if
2033 * you want to keep it around, you must use g_object_ref().
2034 * The returned image must not be modified.
2036 GdkPixbuf *
2037 gtk_icon_info_get_builtin_pixbuf (GtkIconInfo *icon_info)
2039 g_return_val_if_fail (icon_info != NULL, NULL);
2041 return icon_info->builtin_pixbuf;
2044 static GdkPixbuf *
2045 load_svg_at_size (const gchar *filename,
2046 gint size,
2047 GError **error)
2049 GdkPixbuf *pixbuf = NULL;
2050 GdkPixbufLoader *loader = NULL;
2051 gchar *contents;
2052 gsize length;
2054 if (!g_file_get_contents (filename,
2055 &contents, &length, error))
2056 goto bail;
2058 loader = gdk_pixbuf_loader_new ();
2059 gdk_pixbuf_loader_set_size (loader, size, size);
2061 if (!gdk_pixbuf_loader_write (loader, contents, length, error))
2063 gdk_pixbuf_loader_close (loader, NULL);
2064 goto bail;
2067 if (!gdk_pixbuf_loader_close (loader, error))
2068 goto bail;
2070 pixbuf = g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader));
2072 bail:
2073 if (loader)
2074 g_object_unref (loader);
2076 return pixbuf;
2079 /* This function contains the complicatd logic for deciding
2080 * on the size at which to load the icon and loading it at
2081 * that size.
2083 static gboolean
2084 icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info,
2085 gboolean scale_only)
2087 int image_width, image_height;
2088 GdkPixbuf *source_pixbuf;
2090 /* First check if we already succeeded have the necessary
2091 * information (or failed earlier)
2093 if (scale_only && icon_info->scale >= 0)
2094 return TRUE;
2096 if (icon_info->pixbuf)
2097 return TRUE;
2099 if (icon_info->load_error)
2100 return FALSE;
2102 /* SVG icons are a special case - we just immediately scale them
2103 * to the desired size
2105 if (icon_info->filename && g_str_has_suffix (icon_info->filename, ".svg"))
2107 icon_info->scale = icon_info->desired_size / 1000.;
2109 if (scale_only)
2110 return TRUE;
2112 icon_info->pixbuf = load_svg_at_size (icon_info->filename,
2113 icon_info->desired_size,
2114 &icon_info->load_error);
2116 return icon_info->pixbuf != NULL;
2119 /* In many cases, the scale can be determined without actual access
2120 * to the icon file. This is generally true when we have a size
2121 * for the directory where the icon is; the image size doesn't
2122 * matter in that case.
2124 if (icon_info->dir_type == ICON_THEME_DIR_FIXED)
2125 icon_info->scale = 1.0;
2126 else if (icon_info->dir_type == ICON_THEME_DIR_THRESHOLD)
2128 if (icon_info->desired_size >= icon_info->dir_size - icon_info->threshold &&
2129 icon_info->desired_size <= icon_info->dir_size + icon_info->threshold)
2130 icon_info->scale = 1.0;
2131 else if (icon_info->dir_size > 0)
2132 icon_info->scale =(gdouble) icon_info->desired_size / icon_info->dir_size;
2134 else if (icon_info->dir_type == ICON_THEME_DIR_SCALABLE)
2136 if (icon_info->dir_size > 0)
2137 icon_info->scale = (gdouble) icon_info->desired_size / icon_info->dir_size;
2140 if (icon_info->scale >= 0. && scale_only)
2141 return TRUE;
2143 /* At this point, we need to actually get the icon; either from the
2144 * builting image or by loading the file
2146 if (icon_info->builtin_pixbuf)
2147 source_pixbuf = g_object_ref (icon_info->builtin_pixbuf);
2148 else
2150 source_pixbuf = gdk_pixbuf_new_from_file (icon_info->filename,
2151 &icon_info->load_error);
2152 if (!source_pixbuf)
2153 return FALSE;
2156 /* Do scale calculations that depend on the image size
2158 image_width = gdk_pixbuf_get_width (source_pixbuf);
2159 image_height = gdk_pixbuf_get_height (source_pixbuf);
2161 if (icon_info->scale < 0.0)
2163 gint image_size = MAX (image_width, image_height);
2164 if (image_size > 0)
2165 icon_info->scale = icon_info->desired_size / image_size;
2166 else
2167 icon_info->scale = 1.0;
2169 if (icon_info->dir_type == ICON_THEME_DIR_UNTHEMED)
2170 icon_info->scale = MAX (icon_info->scale, 1.0);
2173 /* We don't short-circuit out here for scale_only, since, now
2174 * we've loaded the icon, we might as well go ahead and finish
2175 * the job. This is a bit of a waste when we scale here
2176 * and never get the final pixbuf; at the cost of a bit of
2177 * extra complexity, we could keep the source pixbuf around
2178 * but not actually scale it until neede.
2181 if (icon_info->scale == 1.0)
2182 icon_info->pixbuf = source_pixbuf;
2183 else
2185 icon_info->pixbuf = gdk_pixbuf_scale_simple (source_pixbuf,
2186 0.5 + image_width * icon_info->scale,
2187 0.5 + image_height * icon_info->scale,
2188 GDK_INTERP_BILINEAR);
2189 g_object_unref (source_pixbuf);
2192 return TRUE;
2196 * gtk_icon_info_load_icon:
2197 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
2198 * @error:
2200 * Renders an icon previously looked up in an icon theme using
2201 * gtk_icon_theme_lookup_icon(); the size will be based on the size
2202 * pssed to gtk_icon_theme_lookup_icon(). Note that the resulting
2203 * pixbuf may not be exactly this size; an icon theme may have icons
2204 * that differ slightly from their nominal sizes, and in addition GTK+
2205 * will avoid scaling icons that it considers sufficiently close to the
2206 * requested size. (This maintains sharpness.)
2208 * Return value: the rendered icon; this may be a newly created icon
2209 * or a new reference to an internal icon, so you must not modify
2210 * the icon. Use g_object_unref() to release your reference to the
2211 * icon.
2213 GdkPixbuf *
2214 gtk_icon_info_load_icon (GtkIconInfo *icon_info,
2215 GError **error)
2217 g_return_val_if_fail (icon_info != NULL, NULL);
2219 g_return_val_if_fail (icon_info != NULL, NULL);
2220 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2222 icon_info_ensure_scale_and_pixbuf (icon_info, FALSE);
2224 if (icon_info->load_error)
2226 g_propagate_error (error, icon_info->load_error);
2227 return NULL;
2230 return g_object_ref (icon_info->pixbuf);
2234 * gtk_icon_info_set_raw_coordinates:
2235 * @icon_info: a #GtkIconInfo
2236 * @raw_coordinates: whether the coordinates of embedded rectangles
2237 * and attached points should be returned in their original
2238 * (unscaled) form.
2240 * Sets whether the coordinates returned by gtk_icon_info_get_embedded_rect()
2241 * and gtk_icon_info_get_attach_points() should be returned in their
2242 * original form as specified in the icon theme, instead of scaled
2243 * appropriately for the pixbuf returned by gtk_icon_info_load_icon().
2245 * Raw coordinates are somewhat strange; they are specified to be with
2246 * respect to the unscaled pixmap for PNG and XPM icons, but for SVG
2247 * icons, they are in a 1000x1000 coordinate space that is scaled
2248 * to the final size of the icon. You can determine if the icon is an SVG
2249 * icon by using gtk_icon_info_get_filename(), and seeing if it is non-%NULL
2250 * and ends in '.svg'.
2252 * This function is provided primarily to allow compatibility wrappers
2253 * for older API's, and is not expected to be useful for applications.
2255 void
2256 gtk_icon_info_set_raw_coordinates (GtkIconInfo *icon_info,
2257 gboolean raw_coordinates)
2259 g_return_if_fail (icon_info != NULL);
2261 icon_info->raw_coordinates = raw_coordinates != FALSE;
2264 /* Scale coordinates from the icon data prior to returning
2265 * them to the user.
2267 static gboolean
2268 icon_info_scale_point (GtkIconInfo *icon_info,
2269 gint x,
2270 gint y,
2271 gint *x_out,
2272 gint *y_out)
2274 if (icon_info->raw_coordinates)
2276 *x_out = x;
2277 *y_out = y;
2279 else
2281 if (!icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
2282 return FALSE;
2284 *x_out = 0.5 + x * icon_info->scale;
2285 *y_out = 0.5 + y * icon_info->scale;
2288 return TRUE;
2292 * gtk_icon_info_get_embedded_rect:
2293 * @icon_info: a #GtkIconInfo
2294 * @rectangle: #GdkRectangle in which to store embedded
2295 * rectangle coordinates; coordinates are only stored
2296 * when this function returns %TRUE.
2298 * Gets the coordinates of a rectangle within the icon
2299 * that can be used for display of information such
2300 * as a preview of the contents of a text file.
2301 * See gtk_icon_info_set_raw_coordinates() for further
2302 * information about the coordinate system.
2304 * Return value: %TRUE if the icon has an embedded rectangle
2306 gboolean
2307 gtk_icon_info_get_embedded_rect (GtkIconInfo *icon_info,
2308 GdkRectangle *rectangle)
2310 g_return_val_if_fail (icon_info != NULL, FALSE);
2312 if (icon_info->data && icon_info->data->has_embedded_rect &&
2313 icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
2315 gint scaled_x0, scaled_y0;
2316 gint scaled_x1, scaled_y1;
2318 if (rectangle)
2320 icon_info_scale_point (icon_info,
2321 icon_info->data->x0, icon_info->data->y0,
2322 &scaled_x0, &scaled_y0);
2323 icon_info_scale_point (icon_info,
2324 icon_info->data->x0, icon_info->data->y0,
2325 &scaled_x1, &scaled_y1);
2327 rectangle->x = scaled_x0;
2328 rectangle->y = scaled_y0;
2329 rectangle->width = scaled_x1 - rectangle->x;
2330 rectangle->height = scaled_y1 - rectangle->y;
2333 return TRUE;
2335 else
2336 return FALSE;
2340 * gtk_icon_info_get_attach_points:
2341 * @icon_info: a #GtkIconInfo
2342 * @points: location to store pointer to an array of points, or %NULL
2343 * free the array of points with g_free().
2344 * @n_points: location to store the number of points in @points, or %NULL
2346 * Fetches the set of attach points for an icon. An attach point
2347 * is a location in the icon that can be used as anchor points for attaching
2348 * emblems or overlays to the icon.
2350 * Return value: %TRUE if there are any attach points for the icon.
2352 gboolean
2353 gtk_icon_info_get_attach_points (GtkIconInfo *icon_info,
2354 GdkPoint **points,
2355 gint *n_points)
2357 g_return_val_if_fail (icon_info != NULL, FALSE);
2359 if (icon_info->data && icon_info->data->n_attach_points &&
2360 icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
2362 if (points)
2364 gint i;
2366 *points = g_new (GdkPoint, icon_info->data->n_attach_points);
2367 for (i = 0; i < icon_info->data->n_attach_points; i++)
2368 icon_info_scale_point (icon_info,
2369 icon_info->data->attach_points[i].x,
2370 icon_info->data->attach_points[i].y,
2371 &(*points)[i].x,
2372 &(*points)[i].y);
2375 if (n_points)
2376 *n_points = icon_info->data->n_attach_points;
2378 return TRUE;
2380 else
2382 if (points)
2383 *points = NULL;
2384 if (n_points)
2385 *n_points = 0;
2387 return FALSE;
2392 * gtk_icon_info_get_display_name:
2393 * @icon_info: a #GtkIconInfo
2395 * Gets the display name for an icon. A display name is a
2396 * string to be used in place of the icon name in a user
2397 * visible context like a list of icons.
2399 * Return value: the display name for the icon or %NULL, if
2400 * the icon doesn't have a specified display name. This value
2401 * is owned @icon_info and must not be modified or free.
2403 G_CONST_RETURN gchar *
2404 gtk_icon_info_get_display_name (GtkIconInfo *icon_info)
2406 g_return_val_if_fail (icon_info != NULL, NULL);
2408 if (icon_info->data)
2409 return icon_info->data->display_name;
2410 else
2411 return NULL;
2415 * Builtin icons
2420 * gtk_icon_theme_add_builtin_icon:
2421 * @icon_name: the name of the icon to register
2422 * @size: the size at which to register the icon (different
2423 * images can be registered for the same icon name
2424 * at different sizes.)
2425 * @pixbuf: #GdkPixbuf that contains the image to use
2426 * for @icon_name.
2428 * Registers a built-in icon for icon theme lookups. The idea
2429 * of built-in icons is to allow an application or library
2430 * that uses themed icons to function requiring files to
2431 * be present in the file system. For instance, the default
2432 * images for all of GTK+'s stock icons are registered
2433 * as built-icons.
2435 * In general, if you use gtk_icon_theme_add_builtin_icon()
2436 * you should also install the icon in the icon theme, so
2437 * that the icon is generally available.
2439 * This function will generally be used with pixbufs loaded
2440 * via gdk_pixbuf_new_from_inline ().
2442 void
2443 gtk_icon_theme_add_builtin_icon (const gchar *icon_name,
2444 gint size,
2445 GdkPixbuf *pixbuf)
2447 BuiltinIcon *default_icon;
2448 GSList *icons;
2449 gpointer key;
2451 g_return_if_fail (icon_name != NULL);
2452 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2454 if (!icon_theme_builtin_icons)
2455 icon_theme_builtin_icons = g_hash_table_new (g_str_hash, g_str_equal);
2457 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
2458 if (!icons)
2459 key = g_strdup (icon_name);
2460 else
2461 key = (gpointer)icon_name; /* Won't get stored */
2463 default_icon = g_new (BuiltinIcon, 1);
2464 default_icon->size = size;
2465 default_icon->pixbuf = g_object_ref (pixbuf);
2466 icons = g_slist_prepend (icons, default_icon);
2468 /* Replaces value, leaves key untouched
2470 g_hash_table_insert (icon_theme_builtin_icons, key, icons);
2473 /* Look up a builtin icon; the min_difference_p and
2474 * has_larger_p out parameters allow us to combine
2475 * this lookup with searching through the actual directories
2476 * of the "hicolor" icon theme. See theme_lookup_icon()
2477 * for how they are used.
2479 static BuiltinIcon *
2480 find_builtin_icon (const gchar *icon_name,
2481 gint size,
2482 gint *min_difference_p,
2483 gboolean *has_larger_p)
2485 GSList *icons = NULL;
2486 gint min_difference = G_MAXINT;
2487 gboolean has_larger = FALSE;
2488 BuiltinIcon *min_icon = NULL;
2490 if (!icon_theme_builtin_icons)
2491 return NULL;
2493 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
2495 while (icons)
2497 BuiltinIcon *default_icon = icons->data;
2498 int min, max, difference;
2499 gboolean smaller;
2501 min = default_icon->size - 2;
2502 max = default_icon->size + 2;
2503 smaller = size < min;
2504 if (size < min)
2505 difference = min - size;
2506 if (size > max)
2507 difference = size - max;
2508 else
2509 difference = 0;
2511 if (difference == 0)
2513 min_icon = default_icon;
2514 break;
2517 if (!has_larger)
2519 if (difference < min_difference || smaller)
2521 min_difference = difference;
2522 min_icon = default_icon;
2523 has_larger = smaller;
2526 else
2528 if (difference < min_difference && smaller)
2530 min_difference = difference;
2531 min_icon = default_icon;
2535 icons = icons->next;
2538 if (min_difference_p)
2539 *min_difference_p = min_difference;
2540 if (has_larger_p)
2541 *has_larger_p = has_larger;
2543 return min_icon;