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, see <http://www.gnu.org/licenses/>.
20 #include <sys/types.h>
29 #include <glib/gstdio.h>
33 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
35 #define WIN32_MEAN_AND_LEAN
37 #include "win32/gdkwin32.h"
38 #endif /* G_OS_WIN32 */
40 #include "gtkicontheme.h"
42 #include "gtkiconfactory.h"
43 #include "gtkiconcache.h"
44 #include "gtkbuiltincache.h"
47 #include "gtknumerableiconprivate.h"
48 #include "gtksettings.h"
49 #include "gtkprivate.h"
52 #undef GDK_DEPRECATED_FOR
53 #define GDK_DEPRECATED
54 #define GDK_DEPRECATED_FOR(f)
56 #include "deprecated/gtkstyle.h"
60 * SECTION:gtkicontheme
61 * @Short_description: Looking up icons by name
62 * @Title: GtkIconTheme
64 * #GtkIconTheme provides a facility for looking up icons by name
65 * and size. The main reason for using a name rather than simply
66 * providing a filename is to allow different icons to be used
67 * depending on what <firstterm>icon theme</firstterm> is selected
68 * by the user. The operation of icon themes on Linux and Unix
70 * url="http://www.freedesktop.org/Standards/icon-theme-spec">Icon
71 * Theme Specification</ulink>. There is a default icon theme,
72 * named <literal>hicolor</literal> where applications should install
73 * their icons, but more additional application themes can be
74 * installed as operating system vendors and users choose.
76 * Named icons are similar to the <xref linkend="gtk3-Themeable-Stock-Images"/>
77 * facility, and the distinction between the two may be a bit confusing.
78 * A few things to keep in mind:
81 * Stock images usually are used in conjunction with
82 * <xref linkend="gtk3-Stock-Items"/>, such as %GTK_STOCK_OK or
83 * %GTK_STOCK_OPEN. Named icons are easier to set up and therefore
84 * are more useful for new icons that an application wants to
85 * add, such as application icons or window icons.
88 * Stock images can only be loaded at the symbolic sizes defined
89 * by the #GtkIconSize enumeration, or by custom sizes defined
90 * by gtk_icon_size_register(), while named icons are more flexible
91 * and any pixel size can be specified.
94 * Because stock images are closely tied to stock items, and thus
95 * to actions in the user interface, stock images may come in
96 * multiple variants for different widget states or writing
100 * A good rule of thumb is that if there is a stock image for what
101 * you want to use, use it, otherwise use a named icon. It turns
102 * out that internally stock images are generally defined in
103 * terms of one or more named icons. (An example of the
104 * more than one case is icons that depend on writing direction;
105 * %GTK_STOCK_GO_FORWARD uses the two themed icons
106 * "gtk-stock-go-forward-ltr" and "gtk-stock-go-forward-rtl".)
108 * In many cases, named themes are used indirectly, via #GtkImage
109 * or stock items, rather than directly, but looking up icons
110 * directly is also simple. The #GtkIconTheme object acts
111 * as a database of all the icons in the current theme. You
112 * can create new #GtkIconTheme objects, but its much more
113 * efficient to use the standard icon theme for the #GdkScreen
114 * so that the icon information is shared with other people
115 * looking up icons. In the case where the default screen is
116 * being used, looking up an icon can be as simple as:
119 * GError *error = NULL;
120 * GtkIconTheme *icon_theme;
123 * icon_theme = gtk_icon_theme_get_default ();
124 * pixbuf = gtk_icon_theme_load_icon (icon_theme,
125 * "my-icon-name", // icon name
131 * g_warning ("Couldn't load icon: %s", error->message);
132 * g_error_free (error);
137 * g_object_unref (pixbuf);
144 #define DEFAULT_THEME_NAME "hicolor"
148 ICON_THEME_DIR_FIXED,
149 ICON_THEME_DIR_SCALABLE,
150 ICON_THEME_DIR_THRESHOLD,
151 ICON_THEME_DIR_UNTHEMED
154 /* In reverse search order: */
157 ICON_SUFFIX_NONE = 0,
158 ICON_SUFFIX_XPM = 1 << 0,
159 ICON_SUFFIX_SVG = 1 << 1,
160 ICON_SUFFIX_PNG = 1 << 2,
161 HAS_ICON_FILE = 1 << 3
164 #define INFO_CACHE_LRU_SIZE 32
166 #define DEBUG_CACHE(args) g_print args
168 #define DEBUG_CACHE(args)
171 struct _GtkIconThemePrivate
173 GHashTable *info_cache;
174 GList *info_cache_lru;
176 gchar *current_theme;
177 gchar *fallback_theme;
179 gint search_path_len;
181 guint custom_theme : 1;
182 guint is_screen_singleton : 1;
183 guint pixbuf_supports_svg : 1;
184 guint themes_valid : 1;
185 guint check_reload : 1;
186 guint loading_themes : 1;
188 /* A list of all the themes needed to look up icons.
189 * In search order, without duplicates
192 GHashTable *unthemed_icons;
194 /* Note: The keys of this hashtable are owned by the
195 * themedir and unthemed hashtables.
197 GHashTable *all_icons;
199 /* GdkScreen for the icon theme (may be NULL)
203 /* time when we last stat:ed for theme changes */
204 glong last_stat_time;
207 gulong reset_styles_idle;
213 GtkIconLookupFlags flags;
216 typedef struct _SymbolicPixbufCache SymbolicPixbufCache;
218 struct _SymbolicPixbufCache {
220 GdkPixbuf *proxy_pixbuf;
222 GdkRGBA success_color;
223 GdkRGBA warning_color;
225 SymbolicPixbufCache *next;
228 struct _GtkIconInfoClass
230 GObjectClass parent_class;
235 GObject parent_instance;
237 /* Information about the source
240 GtkIconTheme *in_cache;
244 GLoadableIcon *loadable;
245 GSList *emblem_infos;
247 /* Cache pixbuf (if there is any) */
248 GdkPixbuf *cache_pixbuf;
252 /* Information about the directory where
253 * the source was found
255 IconThemeDirType dir_type;
259 /* Parameters influencing the scaled icon
262 guint raw_coordinates : 1;
263 guint forced_size : 1;
264 guint emblems_applied : 1;
266 /* Cached information if we go ahead and try to load
270 GdkPixbuf *proxy_pixbuf;
274 SymbolicPixbufCache *symbolic_pixbuf_cache;
276 GtkRequisition *symbolic_pixbuf_size;
286 /* In search order */
292 IconThemeDirType type;
307 GHashTable *icon_data;
313 char *no_svg_filename;
325 time_t mtime; /* 0 == not existing or not a dir */
330 static void gtk_icon_theme_finalize (GObject *object);
331 static void theme_dir_destroy (IconThemeDir *dir);
333 static void theme_destroy (IconTheme *theme);
334 static GtkIconInfo *theme_lookup_icon (IconTheme *theme,
335 const char *icon_name,
338 gboolean use_default_icons);
339 static void theme_list_icons (IconTheme *theme,
342 static void theme_list_contexts (IconTheme *theme,
343 GHashTable *contexts);
344 static void theme_subdir_load (GtkIconTheme *icon_theme,
346 GKeyFile *theme_file,
348 static void do_theme_change (GtkIconTheme *icon_theme);
350 static void blow_themes (GtkIconTheme *icon_themes);
351 static gboolean rescan_themes (GtkIconTheme *icon_themes);
353 static GtkIconData *icon_data_dup (GtkIconData *icon_data);
354 static void icon_data_free (GtkIconData *icon_data);
355 static void load_icon_data (IconThemeDir *dir,
359 static IconSuffix theme_dir_get_icon_suffix (IconThemeDir *dir,
360 const gchar *icon_name,
361 gboolean *has_icon_file);
364 static GtkIconInfo *icon_info_new (void);
365 static GtkIconInfo *icon_info_new_builtin (BuiltinIcon *icon);
367 static IconSuffix suffix_from_name (const char *name);
369 static BuiltinIcon *find_builtin_icon (const gchar *icon_name,
371 gint *min_difference_p,
372 gboolean *has_larger_p);
373 static void remove_from_lru_cache (GtkIconTheme *icon_theme,
374 GtkIconInfo *icon_info);
376 static guint signal_changed = 0;
378 static GHashTable *icon_theme_builtin_icons;
380 /* also used in gtkiconfactory.c */
381 GtkIconCache *_builtin_cache = NULL;
382 static GList *builtin_dirs = NULL;
385 icon_info_key_hash (gconstpointer _key)
387 const IconInfoKey *key = _key;
390 for (i = 0; key->icon_names[i] != NULL; i++)
391 h ^= g_str_hash (key->icon_names[i]);
393 h ^= key->size * 0x10001;
394 h ^= key->flags * 0x1000010;
400 icon_info_key_equal (gconstpointer _a,
403 const IconInfoKey *a = _a;
404 const IconInfoKey *b = _b;
407 if (a->size != b->size)
410 if (a->flags != b->flags)
414 a->icon_names[i] != NULL &&
415 b->icon_names[i] != NULL; i++)
417 if (strcmp (a->icon_names[i], b->icon_names[i]) != 0)
421 return a->icon_names[i] == NULL && b->icon_names[i] == NULL;
424 G_DEFINE_TYPE (GtkIconTheme, gtk_icon_theme, G_TYPE_OBJECT)
427 * gtk_icon_theme_new:
429 * Creates a new icon theme object. Icon theme objects are used
430 * to lookup up an icon by name in a particular icon theme.
431 * Usually, you'll want to use gtk_icon_theme_get_default()
432 * or gtk_icon_theme_get_for_screen() rather than creating
433 * a new icon theme object for scratch.
435 * Return value: the newly created #GtkIconTheme object.
440 gtk_icon_theme_new (void)
442 return g_object_new (GTK_TYPE_ICON_THEME, NULL);
446 * gtk_icon_theme_get_default:
448 * Gets the icon theme for the default screen. See
449 * gtk_icon_theme_get_for_screen().
451 * Return value: (transfer none): A unique #GtkIconTheme associated with
452 * the default screen. This icon theme is associated with
453 * the screen and can be used as long as the screen
454 * is open. Do not ref or unref it.
459 gtk_icon_theme_get_default (void)
461 return gtk_icon_theme_get_for_screen (gdk_screen_get_default ());
465 * gtk_icon_theme_get_for_screen:
466 * @screen: a #GdkScreen
468 * Gets the icon theme object associated with @screen; if this
469 * function has not previously been called for the given
470 * screen, a new icon theme object will be created and
471 * associated with the screen. Icon theme objects are
472 * fairly expensive to create, so using this function
473 * is usually a better choice than calling than gtk_icon_theme_new()
474 * and setting the screen yourself; by using this function
475 * a single icon theme object will be shared between users.
477 * Return value: (transfer none): A unique #GtkIconTheme associated with
478 * the given screen. This icon theme is associated with
479 * the screen and can be used as long as the screen
480 * is open. Do not ref or unref it.
485 gtk_icon_theme_get_for_screen (GdkScreen *screen)
487 GtkIconTheme *icon_theme;
489 g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
491 icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
494 GtkIconThemePrivate *priv;
496 icon_theme = gtk_icon_theme_new ();
497 gtk_icon_theme_set_screen (icon_theme, screen);
499 priv = icon_theme->priv;
500 priv->is_screen_singleton = TRUE;
502 g_object_set_data (G_OBJECT (screen), I_("gtk-icon-theme"), icon_theme);
509 gtk_icon_theme_class_init (GtkIconThemeClass *klass)
511 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
513 gobject_class->finalize = gtk_icon_theme_finalize;
516 * GtkIconTheme::changed:
517 * @icon_theme: the icon theme
519 * Emitted when the current icon theme is switched or GTK+ detects
520 * that a change has occurred in the contents of the current
523 signal_changed = g_signal_new (I_("changed"),
524 G_TYPE_FROM_CLASS (klass),
526 G_STRUCT_OFFSET (GtkIconThemeClass, changed),
528 g_cclosure_marshal_VOID__VOID,
531 g_type_class_add_private (klass, sizeof (GtkIconThemePrivate));
535 /* Callback when the display that the icon theme is attached to
536 * is closed; unset the screen, and if it's the unique theme
537 * for the screen, drop the reference
540 display_closed (GdkDisplay *display,
542 GtkIconTheme *icon_theme)
544 GtkIconThemePrivate *priv = icon_theme->priv;
545 GdkScreen *screen = priv->screen;
546 gboolean was_screen_singleton = priv->is_screen_singleton;
548 if (was_screen_singleton)
550 g_object_set_data (G_OBJECT (screen), I_("gtk-icon-theme"), NULL);
551 priv->is_screen_singleton = FALSE;
554 gtk_icon_theme_set_screen (icon_theme, NULL);
556 if (was_screen_singleton)
558 g_object_unref (icon_theme);
563 update_current_theme (GtkIconTheme *icon_theme)
565 #define theme_changed(_old, _new) \
566 ((_old && !_new) || (!_old && _new) || \
567 (_old && _new && strcmp (_old, _new) != 0))
568 GtkIconThemePrivate *priv = icon_theme->priv;
570 if (!priv->custom_theme)
573 gchar *fallback_theme = NULL;
574 gboolean changed = FALSE;
578 GtkSettings *settings = gtk_settings_get_for_screen (priv->screen);
579 g_object_get (settings,
580 "gtk-icon-theme-name", &theme,
581 "gtk-fallback-icon-theme", &fallback_theme, NULL);
584 /* ensure that the current theme (even when just the default)
585 * is searched before any fallback theme
587 if (!theme && fallback_theme)
588 theme = g_strdup (DEFAULT_THEME_NAME);
590 if (theme_changed (priv->current_theme, theme))
592 g_free (priv->current_theme);
593 priv->current_theme = theme;
599 if (theme_changed (priv->fallback_theme, fallback_theme))
601 g_free (priv->fallback_theme);
602 priv->fallback_theme = fallback_theme;
606 g_free (fallback_theme);
609 do_theme_change (icon_theme);
614 /* Callback when the icon theme GtkSetting changes
617 theme_changed (GtkSettings *settings,
619 GtkIconTheme *icon_theme)
621 update_current_theme (icon_theme);
625 unset_screen (GtkIconTheme *icon_theme)
627 GtkIconThemePrivate *priv = icon_theme->priv;
628 GtkSettings *settings;
633 settings = gtk_settings_get_for_screen (priv->screen);
634 display = gdk_screen_get_display (priv->screen);
636 g_signal_handlers_disconnect_by_func (display,
637 (gpointer) display_closed,
639 g_signal_handlers_disconnect_by_func (settings,
640 (gpointer) theme_changed,
648 * gtk_icon_theme_set_screen:
649 * @icon_theme: a #GtkIconTheme
650 * @screen: a #GdkScreen
652 * Sets the screen for an icon theme; the screen is used
653 * to track the user's currently configured icon theme,
654 * which might be different for different screens.
659 gtk_icon_theme_set_screen (GtkIconTheme *icon_theme,
662 GtkIconThemePrivate *priv;
663 GtkSettings *settings;
666 g_return_if_fail (GTK_ICON_THEME (icon_theme));
667 g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
669 priv = icon_theme->priv;
671 unset_screen (icon_theme);
675 display = gdk_screen_get_display (screen);
676 settings = gtk_settings_get_for_screen (screen);
678 priv->screen = screen;
680 g_signal_connect (display, "closed",
681 G_CALLBACK (display_closed), icon_theme);
682 g_signal_connect (settings, "notify::gtk-icon-theme-name",
683 G_CALLBACK (theme_changed), icon_theme);
684 g_signal_connect (settings, "notify::gtk-fallback-icon-theme-name",
685 G_CALLBACK (theme_changed), icon_theme);
688 update_current_theme (icon_theme);
691 /* Checks whether a loader for SVG files has been registered
695 pixbuf_supports_svg (void)
699 static gint found_svg = -1;
704 formats = gdk_pixbuf_get_formats ();
707 for (tmp_list = formats; tmp_list && !found_svg; tmp_list = tmp_list->next)
709 gchar **mime_types = gdk_pixbuf_format_get_mime_types (tmp_list->data);
712 for (mime_type = mime_types; *mime_type && !found_svg; mime_type++)
714 if (strcmp (*mime_type, "image/svg") == 0)
718 g_strfreev (mime_types);
721 g_slist_free (formats);
726 /* The icon info was removed from the icon_info_hash hash table */
728 icon_info_uncached (GtkIconInfo *icon_info)
730 GtkIconTheme *icon_theme = icon_info->in_cache;
732 DEBUG_CACHE (("removing %p (%s %d 0x%x) from cache (icon_them: %p) (cache size %d)\n",
734 g_strjoinv (",", icon_info->key.icon_names),
735 icon_info->key.size, icon_info->key.flags,
737 icon_theme != NULL ? g_hash_table_size (icon_theme->priv->info_cache) : 0));
739 icon_info->in_cache = NULL;
741 if (icon_theme != NULL)
742 remove_from_lru_cache (icon_theme, icon_info);
746 gtk_icon_theme_init (GtkIconTheme *icon_theme)
748 GtkIconThemePrivate *priv;
749 const gchar * const *xdg_data_dirs;
752 priv = G_TYPE_INSTANCE_GET_PRIVATE (icon_theme,
754 GtkIconThemePrivate);
755 icon_theme->priv = priv;
757 priv->info_cache = g_hash_table_new_full (icon_info_key_hash, icon_info_key_equal, NULL,
758 (GDestroyNotify)icon_info_uncached);
760 priv->custom_theme = FALSE;
762 xdg_data_dirs = g_get_system_data_dirs ();
763 for (i = 0; xdg_data_dirs[i]; i++) ;
765 priv->search_path_len = 2 * i + 2;
767 priv->search_path = g_new (char *, priv->search_path_len);
770 priv->search_path[i++] = g_build_filename (g_get_user_data_dir (), "icons", NULL);
771 priv->search_path[i++] = g_build_filename (g_get_home_dir (), ".icons", NULL);
773 for (j = 0; xdg_data_dirs[j]; j++)
774 priv->search_path[i++] = g_build_filename (xdg_data_dirs[j], "icons", NULL);
776 for (j = 0; xdg_data_dirs[j]; j++)
777 priv->search_path[i++] = g_build_filename (xdg_data_dirs[j], "pixmaps", NULL);
779 priv->themes_valid = FALSE;
781 priv->unthemed_icons = NULL;
783 priv->pixbuf_supports_svg = pixbuf_supports_svg ();
787 free_dir_mtime (IconThemeDirMtime *dir_mtime)
789 if (dir_mtime->cache)
790 _gtk_icon_cache_unref (dir_mtime->cache);
792 g_free (dir_mtime->dir);
793 g_slice_free (IconThemeDirMtime, dir_mtime);
798 reset_styles_idle (gpointer user_data)
800 GtkIconTheme *icon_theme;
801 GtkIconThemePrivate *priv;
803 icon_theme = GTK_ICON_THEME (user_data);
804 priv = icon_theme->priv;
806 if (priv->screen && priv->is_screen_singleton)
807 gtk_style_context_reset_widgets (priv->screen);
809 priv->reset_styles_idle = 0;
815 do_theme_change (GtkIconTheme *icon_theme)
817 GtkIconThemePrivate *priv = icon_theme->priv;
819 g_hash_table_remove_all (priv->info_cache);
821 if (!priv->themes_valid)
825 g_print ("change to icon theme \"%s\"\n", priv->current_theme));
826 blow_themes (icon_theme);
827 g_signal_emit (icon_theme, signal_changed, 0);
829 if (!priv->reset_styles_idle)
830 priv->reset_styles_idle =
831 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2,
832 reset_styles_idle, icon_theme, NULL);
836 blow_themes (GtkIconTheme *icon_theme)
838 GtkIconThemePrivate *priv = icon_theme->priv;
840 if (priv->themes_valid)
842 g_hash_table_destroy (priv->all_icons);
843 g_list_free_full (priv->themes, (GDestroyNotify) theme_destroy);
844 g_list_free_full (priv->dir_mtimes, (GDestroyNotify) free_dir_mtime);
845 g_hash_table_destroy (priv->unthemed_icons);
848 priv->unthemed_icons = NULL;
849 priv->dir_mtimes = NULL;
850 priv->all_icons = NULL;
851 priv->themes_valid = FALSE;
855 gtk_icon_theme_finalize (GObject *object)
857 GtkIconTheme *icon_theme;
858 GtkIconThemePrivate *priv;
861 icon_theme = GTK_ICON_THEME (object);
862 priv = icon_theme->priv;
864 g_hash_table_destroy (priv->info_cache);
865 g_assert (priv->info_cache_lru == NULL);
867 if (priv->reset_styles_idle)
869 g_source_remove (priv->reset_styles_idle);
870 priv->reset_styles_idle = 0;
873 unset_screen (icon_theme);
875 g_free (priv->current_theme);
876 priv->current_theme = NULL;
878 for (i = 0; i < priv->search_path_len; i++)
879 g_free (priv->search_path[i]);
881 g_free (priv->search_path);
882 priv->search_path = NULL;
884 blow_themes (icon_theme);
886 G_OBJECT_CLASS (gtk_icon_theme_parent_class)->finalize (object);
890 * gtk_icon_theme_set_search_path:
891 * @icon_theme: a #GtkIconTheme
892 * @path: (array length=n_elements) (element-type filename): array of
893 * directories that are searched for icon themes
894 * @n_elements: number of elements in @path.
896 * Sets the search path for the icon theme object. When looking
897 * for an icon theme, GTK+ will search for a subdirectory of
898 * one or more of the directories in @path with the same name
899 * as the icon theme. (Themes from multiple of the path elements
900 * are combined to allow themes to be extended by adding icons
901 * in the user's home directory.)
903 * In addition if an icon found isn't found either in the current
904 * icon theme or the default icon theme, and an image file with
905 * the right name is found directly in one of the elements of
906 * @path, then that image will be used for the icon name.
907 * (This is legacy feature, and new icons should be put
908 * into the default icon theme, which is called DEFAULT_THEME_NAME,
909 * rather than directly on the icon path.)
914 gtk_icon_theme_set_search_path (GtkIconTheme *icon_theme,
918 GtkIconThemePrivate *priv;
921 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
923 priv = icon_theme->priv;
924 for (i = 0; i < priv->search_path_len; i++)
925 g_free (priv->search_path[i]);
927 g_free (priv->search_path);
929 priv->search_path = g_new (gchar *, n_elements);
930 priv->search_path_len = n_elements;
932 for (i = 0; i < priv->search_path_len; i++)
933 priv->search_path[i] = g_strdup (path[i]);
935 do_theme_change (icon_theme);
940 * gtk_icon_theme_get_search_path:
941 * @icon_theme: a #GtkIconTheme
942 * @path: (allow-none) (array length=n_elements) (element-type filename) (out):
943 * location to store a list of icon theme path directories or %NULL.
944 * The stored value should be freed with g_strfreev().
945 * @n_elements: location to store number of elements in @path, or %NULL
947 * Gets the current search path. See gtk_icon_theme_set_search_path().
952 gtk_icon_theme_get_search_path (GtkIconTheme *icon_theme,
956 GtkIconThemePrivate *priv;
959 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
961 priv = icon_theme->priv;
964 *n_elements = priv->search_path_len;
968 *path = g_new (gchar *, priv->search_path_len + 1);
969 for (i = 0; i < priv->search_path_len; i++)
970 (*path)[i] = g_strdup (priv->search_path[i]);
976 * gtk_icon_theme_append_search_path:
977 * @icon_theme: a #GtkIconTheme
978 * @path: (type filename): directory name to append to the icon path
980 * Appends a directory to the search path.
981 * See gtk_icon_theme_set_search_path().
986 gtk_icon_theme_append_search_path (GtkIconTheme *icon_theme,
989 GtkIconThemePrivate *priv;
991 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
992 g_return_if_fail (path != NULL);
994 priv = icon_theme->priv;
996 priv->search_path_len++;
998 priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
999 priv->search_path[priv->search_path_len-1] = g_strdup (path);
1001 do_theme_change (icon_theme);
1005 * gtk_icon_theme_prepend_search_path:
1006 * @icon_theme: a #GtkIconTheme
1007 * @path: (type filename): directory name to prepend to the icon path
1009 * Prepends a directory to the search path.
1010 * See gtk_icon_theme_set_search_path().
1015 gtk_icon_theme_prepend_search_path (GtkIconTheme *icon_theme,
1018 GtkIconThemePrivate *priv;
1021 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
1022 g_return_if_fail (path != NULL);
1024 priv = icon_theme->priv;
1026 priv->search_path_len++;
1027 priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
1029 for (i = priv->search_path_len - 1; i > 0; i--)
1030 priv->search_path[i] = priv->search_path[i - 1];
1032 priv->search_path[0] = g_strdup (path);
1034 do_theme_change (icon_theme);
1038 * gtk_icon_theme_set_custom_theme:
1039 * @icon_theme: a #GtkIconTheme
1040 * @theme_name: (allow-none): name of icon theme to use instead of
1041 * configured theme, or %NULL to unset a previously set custom theme
1043 * Sets the name of the icon theme that the #GtkIconTheme object uses
1044 * overriding system configuration. This function cannot be called
1045 * on the icon theme objects returned from gtk_icon_theme_get_default()
1046 * and gtk_icon_theme_get_for_screen().
1051 gtk_icon_theme_set_custom_theme (GtkIconTheme *icon_theme,
1052 const gchar *theme_name)
1054 GtkIconThemePrivate *priv;
1056 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
1058 priv = icon_theme->priv;
1060 g_return_if_fail (!priv->is_screen_singleton);
1062 if (theme_name != NULL)
1064 priv->custom_theme = TRUE;
1065 if (!priv->current_theme || strcmp (theme_name, priv->current_theme) != 0)
1067 g_free (priv->current_theme);
1068 priv->current_theme = g_strdup (theme_name);
1070 do_theme_change (icon_theme);
1075 if (priv->custom_theme)
1077 priv->custom_theme = FALSE;
1079 update_current_theme (icon_theme);
1085 insert_theme (GtkIconTheme *icon_theme, const char *theme_name)
1091 GtkIconThemePrivate *priv;
1092 IconTheme *theme = NULL;
1094 GKeyFile *theme_file;
1095 GError *error = NULL;
1096 IconThemeDirMtime *dir_mtime;
1099 priv = icon_theme->priv;
1101 for (l = priv->themes; l != NULL; l = l->next)
1104 if (strcmp (theme->name, theme_name) == 0)
1108 for (i = 0; i < priv->search_path_len; i++)
1110 path = g_build_filename (priv->search_path[i],
1113 dir_mtime = g_slice_new (IconThemeDirMtime);
1114 dir_mtime->cache = NULL;
1115 dir_mtime->dir = path;
1116 if (g_stat (path, &stat_buf) == 0 && S_ISDIR (stat_buf.st_mode))
1117 dir_mtime->mtime = stat_buf.st_mtime;
1119 dir_mtime->mtime = 0;
1121 priv->dir_mtimes = g_list_prepend (priv->dir_mtimes, dir_mtime);
1123 priv->dir_mtimes = g_list_reverse (priv->dir_mtimes);
1126 for (i = 0; i < priv->search_path_len && !theme_file; i++)
1128 path = g_build_filename (priv->search_path[i],
1132 if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
1134 theme_file = g_key_file_new ();
1135 g_key_file_set_list_separator (theme_file, ',');
1136 g_key_file_load_from_file (theme_file, path, 0, &error);
1139 g_key_file_free (theme_file);
1141 g_error_free (error);
1148 if (theme_file || strcmp (theme_name, DEFAULT_THEME_NAME) == 0)
1150 theme = g_new0 (IconTheme, 1);
1151 theme->name = g_strdup (theme_name);
1152 priv->themes = g_list_prepend (priv->themes, theme);
1155 if (theme_file == NULL)
1158 theme->display_name =
1159 g_key_file_get_locale_string (theme_file, "Icon Theme", "Name", NULL, NULL);
1160 if (!theme->display_name)
1161 g_warning ("Theme file for %s has no name\n", theme_name);
1163 dirs = g_key_file_get_string_list (theme_file, "Icon Theme", "Directories", NULL, NULL);
1166 g_warning ("Theme file for %s has no directories\n", theme_name);
1167 priv->themes = g_list_remove (priv->themes, theme);
1168 g_free (theme->name);
1169 g_free (theme->display_name);
1171 g_key_file_free (theme_file);
1176 g_key_file_get_locale_string (theme_file,
1177 "Icon Theme", "Comment",
1180 g_key_file_get_string (theme_file,
1181 "Icon Theme", "Example",
1185 for (i = 0; dirs[i] != NULL; i++)
1186 theme_subdir_load (icon_theme, theme, theme_file, dirs[i]);
1190 theme->dirs = g_list_reverse (theme->dirs);
1192 themes = g_key_file_get_string_list (theme_file,
1199 for (i = 0; themes[i] != NULL; i++)
1200 insert_theme (icon_theme, themes[i]);
1202 g_strfreev (themes);
1205 g_key_file_free (theme_file);
1209 free_unthemed_icon (UnthemedIcon *unthemed_icon)
1211 g_free (unthemed_icon->svg_filename);
1212 g_free (unthemed_icon->no_svg_filename);
1213 g_slice_free (UnthemedIcon, unthemed_icon);
1217 strip_suffix (const char *filename)
1221 dot = strrchr (filename, '.');
1224 return g_strdup (filename);
1226 return g_strndup (filename, dot - filename);
1230 load_themes (GtkIconTheme *icon_theme)
1232 GtkIconThemePrivate *priv;
1237 UnthemedIcon *unthemed_icon;
1238 IconSuffix old_suffix, new_suffix;
1240 IconThemeDirMtime *dir_mtime;
1243 priv = icon_theme->priv;
1245 priv->all_icons = g_hash_table_new (g_str_hash, g_str_equal);
1247 if (priv->current_theme)
1248 insert_theme (icon_theme, priv->current_theme);
1250 /* Always look in the "default" icon theme, and in a fallback theme */
1251 if (priv->fallback_theme)
1252 insert_theme (icon_theme, priv->fallback_theme);
1253 insert_theme (icon_theme, DEFAULT_THEME_NAME);
1254 priv->themes = g_list_reverse (priv->themes);
1257 priv->unthemed_icons = g_hash_table_new_full (g_str_hash, g_str_equal,
1258 g_free, (GDestroyNotify)free_unthemed_icon);
1260 for (base = 0; base < icon_theme->priv->search_path_len; base++)
1262 dir = icon_theme->priv->search_path[base];
1264 dir_mtime = g_slice_new (IconThemeDirMtime);
1265 priv->dir_mtimes = g_list_append (priv->dir_mtimes, dir_mtime);
1267 dir_mtime->dir = g_strdup (dir);
1268 dir_mtime->mtime = 0;
1269 dir_mtime->cache = NULL;
1271 if (g_stat (dir, &stat_buf) != 0 || !S_ISDIR (stat_buf.st_mode))
1273 dir_mtime->mtime = stat_buf.st_mtime;
1275 dir_mtime->cache = _gtk_icon_cache_new_for_path (dir);
1276 if (dir_mtime->cache != NULL)
1279 gdir = g_dir_open (dir, 0, NULL);
1283 while ((file = g_dir_read_name (gdir)))
1285 new_suffix = suffix_from_name (file);
1287 if (new_suffix != ICON_SUFFIX_NONE)
1292 abs_file = g_build_filename (dir, file, NULL);
1293 base_name = strip_suffix (file);
1295 if ((unthemed_icon = g_hash_table_lookup (priv->unthemed_icons,
1298 if (new_suffix == ICON_SUFFIX_SVG)
1300 if (unthemed_icon->svg_filename)
1303 unthemed_icon->svg_filename = abs_file;
1307 if (unthemed_icon->no_svg_filename)
1309 old_suffix = suffix_from_name (unthemed_icon->no_svg_filename);
1310 if (new_suffix > old_suffix)
1312 g_free (unthemed_icon->no_svg_filename);
1313 unthemed_icon->no_svg_filename = abs_file;
1319 unthemed_icon->no_svg_filename = abs_file;
1326 unthemed_icon = g_slice_new0 (UnthemedIcon);
1328 if (new_suffix == ICON_SUFFIX_SVG)
1329 unthemed_icon->svg_filename = abs_file;
1331 unthemed_icon->no_svg_filename = abs_file;
1333 /* takes ownership of base_name */
1334 g_hash_table_replace (priv->unthemed_icons,
1337 g_hash_table_insert (priv->all_icons,
1345 priv->themes_valid = TRUE;
1347 g_get_current_time(&tv);
1348 priv->last_stat_time = tv.tv_sec;
1352 _gtk_icon_theme_ensure_builtin_cache (void)
1354 static gboolean initialized = FALSE;
1356 static IconThemeDir dirs[5] =
1358 { ICON_THEME_DIR_THRESHOLD, 0, 16, 16, 16, 2, NULL, "16", -1, NULL, NULL, NULL },
1359 { ICON_THEME_DIR_THRESHOLD, 0, 20, 20, 20, 2, NULL, "20", -1, NULL, NULL, NULL },
1360 { ICON_THEME_DIR_THRESHOLD, 0, 24, 24, 24, 2, NULL, "24", -1, NULL, NULL, NULL },
1361 { ICON_THEME_DIR_THRESHOLD, 0, 32, 32, 32, 2, NULL, "32", -1, NULL, NULL, NULL },
1362 { ICON_THEME_DIR_THRESHOLD, 0, 48, 48, 48, 2, NULL, "48", -1, NULL, NULL, NULL }
1370 _builtin_cache = _gtk_icon_cache_new ((gchar *)builtin_icons);
1372 for (i = 0; i < G_N_ELEMENTS (dirs); i++)
1375 dir->cache = _gtk_icon_cache_ref (_builtin_cache);
1376 dir->subdir_index = _gtk_icon_cache_get_directory_index (dir->cache, dir->subdir);
1378 builtin_dirs = g_list_append (builtin_dirs, dir);
1384 ensure_valid_themes (GtkIconTheme *icon_theme)
1386 GtkIconThemePrivate *priv = icon_theme->priv;
1388 gboolean was_valid = priv->themes_valid;
1390 if (priv->loading_themes)
1392 priv->loading_themes = TRUE;
1394 _gtk_icon_theme_ensure_builtin_cache ();
1396 if (priv->themes_valid)
1398 g_get_current_time (&tv);
1400 if (ABS (tv.tv_sec - priv->last_stat_time) > 5 &&
1401 rescan_themes (icon_theme))
1402 blow_themes (icon_theme);
1405 if (!priv->themes_valid)
1407 load_themes (icon_theme);
1411 g_signal_emit (icon_theme, signal_changed, 0);
1415 priv->loading_themes = FALSE;
1418 /* The LRU cache is a short list of IconInfos that are kept
1419 alive even though their IconInfo would otherwise have
1420 been freed, so that we can avoid reloading these
1422 We put infos on the lru list when nothing otherwise
1423 references the info. So, when we get a cache hit
1424 we remove it from the list, and when the proxy
1425 pixmap is released we put it on the list.
1429 ensure_lru_cache_space (GtkIconTheme *icon_theme)
1431 GtkIconThemePrivate *priv = icon_theme->priv;
1434 /* Remove last item if LRU full */
1435 l = g_list_nth (priv->info_cache_lru, INFO_CACHE_LRU_SIZE - 1);
1438 GtkIconInfo *icon_info = l->data;
1440 DEBUG_CACHE (("removing (due to out of space) %p (%s %d 0x%x) from LRU cache (cache size %d)\n",
1442 g_strjoinv (",", icon_info->key.icon_names),
1443 icon_info->key.size, icon_info->key.flags,
1444 g_list_length (priv->info_cache_lru)));
1446 priv->info_cache_lru = g_list_delete_link (priv->info_cache_lru, l);
1447 gtk_icon_info_free (icon_info);
1452 add_to_lru_cache (GtkIconTheme *icon_theme,
1453 GtkIconInfo *icon_info)
1455 GtkIconThemePrivate *priv = icon_theme->priv;
1457 DEBUG_CACHE (("adding %p (%s %d 0x%x) to LRU cache (cache size %d)\n",
1459 g_strjoinv (",", icon_info->key.icon_names),
1460 icon_info->key.size, icon_info->key.flags,
1461 g_list_length (priv->info_cache_lru)));
1463 g_assert (g_list_find (priv->info_cache_lru, icon_info) == NULL);
1465 ensure_lru_cache_space (icon_theme);
1466 /* prepend new info to LRU */
1467 priv->info_cache_lru = g_list_prepend (priv->info_cache_lru,
1468 gtk_icon_info_copy (icon_info));
1472 ensure_in_lru_cache (GtkIconTheme *icon_theme,
1473 GtkIconInfo *icon_info)
1475 GtkIconThemePrivate *priv = icon_theme->priv;
1478 l = g_list_find (priv->info_cache_lru, icon_info);
1481 /* Move to front of LRU if already in it */
1482 priv->info_cache_lru = g_list_remove_link (priv->info_cache_lru, l);
1483 priv->info_cache_lru = g_list_concat (l, priv->info_cache_lru);
1486 add_to_lru_cache (icon_theme, icon_info);
1490 remove_from_lru_cache (GtkIconTheme *icon_theme,
1491 GtkIconInfo *icon_info)
1493 GtkIconThemePrivate *priv = icon_theme->priv;
1494 if (g_list_find (priv->info_cache_lru, icon_info))
1496 DEBUG_CACHE (("removing %p (%s %d 0x%x) from LRU cache (cache size %d)\n",
1498 g_strjoinv (",", icon_info->key.icon_names),
1499 icon_info->key.size, icon_info->key.flags,
1500 g_list_length (priv->info_cache_lru)));
1502 priv->info_cache_lru = g_list_remove (priv->info_cache_lru, icon_info);
1503 gtk_icon_info_free (icon_info);
1507 static SymbolicPixbufCache *
1508 symbolic_pixbuf_cache_new (GdkPixbuf *pixbuf,
1510 const GdkRGBA *success_color,
1511 const GdkRGBA *warning_color,
1512 const GdkRGBA *error_color,
1513 SymbolicPixbufCache *next)
1515 SymbolicPixbufCache *cache;
1517 cache = g_new0 (SymbolicPixbufCache, 1);
1518 cache->pixbuf = g_object_ref (pixbuf);
1522 cache->success_color = *success_color;
1524 cache->warning_color = *warning_color;
1526 cache->error_color = *error_color;
1532 rgba_matches (const GdkRGBA *a, const GdkRGBA *b)
1534 GdkRGBA transparent = { 0 };
1536 /* For matching we treat unset colors as transparent rather
1537 than default, which works as well, because transparent
1538 will never be used for real symbolic icon colors */
1543 fabs(a->red - b->red) < 0.0001 &&
1544 fabs(a->green - b->green) < 0.0001 &&
1545 fabs(a->blue - b->blue) < 0.0001 &&
1546 fabs(a->alpha - b->alpha) < 0.0001;
1549 static SymbolicPixbufCache *
1550 symbolic_pixbuf_cache_matches (SymbolicPixbufCache *cache,
1552 const GdkRGBA *success_color,
1553 const GdkRGBA *warning_color,
1554 const GdkRGBA *error_color)
1556 while (cache != NULL)
1558 if (rgba_matches (fg, &cache->fg) &&
1559 rgba_matches (success_color, &cache->success_color) &&
1560 rgba_matches (warning_color, &cache->warning_color) &&
1561 rgba_matches (error_color, &cache->error_color))
1564 cache = cache->next;
1571 symbolic_pixbuf_cache_free (SymbolicPixbufCache *cache)
1573 SymbolicPixbufCache *next;
1575 while (cache != NULL)
1578 g_object_unref (cache->pixbuf);
1585 static GtkIconInfo *
1586 choose_icon (GtkIconTheme *icon_theme,
1587 const gchar *icon_names[],
1589 GtkIconLookupFlags flags)
1591 GtkIconThemePrivate *priv;
1593 GtkIconInfo *icon_info = NULL;
1594 UnthemedIcon *unthemed_icon = NULL;
1596 gboolean use_builtin;
1600 priv = icon_theme->priv;
1602 ensure_valid_themes (icon_theme);
1604 key.icon_names = (char **)icon_names;
1608 icon_info = g_hash_table_lookup (priv->info_cache, &key);
1609 if (icon_info != NULL)
1611 DEBUG_CACHE (("cache hit %p (%s %d 0x%x) (cache size %d)\n",
1613 g_strjoinv (",", icon_info->key.icon_names),
1614 icon_info->key.size, icon_info->key.flags,
1615 g_hash_table_size (priv->info_cache)));
1617 icon_info = gtk_icon_info_copy (icon_info);
1618 remove_from_lru_cache (icon_theme, icon_info);
1623 if (flags & GTK_ICON_LOOKUP_NO_SVG)
1625 else if (flags & GTK_ICON_LOOKUP_FORCE_SVG)
1628 allow_svg = priv->pixbuf_supports_svg;
1630 use_builtin = flags & GTK_ICON_LOOKUP_USE_BUILTIN;
1632 /* for symbolic icons, do a search in all registered themes first;
1633 * a theme that inherits them from a parent theme might provide
1634 * an alternative highcolor version, but still expect the symbolic icon
1635 * to show up instead.
1637 if (icon_names[0] &&
1638 g_str_has_suffix (icon_names[0], "-symbolic"))
1640 for (l = priv->themes; l; l = l->next)
1642 IconTheme *theme = l->data;
1643 icon_info = theme_lookup_icon (theme, icon_names[0], size, allow_svg, use_builtin);
1649 for (l = priv->themes; l; l = l->next)
1651 IconTheme *theme = l->data;
1653 for (i = 0; icon_names[i]; i++)
1655 icon_info = theme_lookup_icon (theme, icon_names[i], size, allow_svg, use_builtin);
1661 for (i = 0; icon_names[i]; i++)
1663 unthemed_icon = g_hash_table_lookup (priv->unthemed_icons, icon_names[i]);
1668 /* Still not found an icon, check if reference to a Win32 resource */
1674 resources = g_strsplit (icon_names[0], ",", 0);
1677 wchar_t *wfile = g_utf8_to_utf16 (resources[0], -1, NULL, NULL, NULL);
1678 ExtractIconExW (wfile, resources[1] ? atoi (resources[1]) : 0, &hIcon, NULL, 1);
1684 icon_info = icon_info_new ();
1685 icon_info->cache_pixbuf = gdk_win32_icon_to_pixbuf_libgtk_only (hIcon);
1686 DestroyIcon (hIcon);
1687 icon_info->dir_type = ICON_THEME_DIR_UNTHEMED;
1688 icon_info->dir_size = size;
1690 g_strfreev (resources);
1696 icon_info = icon_info_new ();
1698 /* A SVG icon, when allowed, beats out a XPM icon, but not
1702 unthemed_icon->svg_filename &&
1703 (!unthemed_icon->no_svg_filename ||
1704 suffix_from_name (unthemed_icon->no_svg_filename) != ICON_SUFFIX_PNG))
1705 icon_info->filename = g_strdup (unthemed_icon->svg_filename);
1706 else if (unthemed_icon->no_svg_filename)
1707 icon_info->filename = g_strdup (unthemed_icon->no_svg_filename);
1709 icon_info->icon_file = g_file_new_for_path (icon_info->filename);
1711 icon_info->dir_type = ICON_THEME_DIR_UNTHEMED;
1712 icon_info->dir_size = size;
1718 icon_info->desired_size = size;
1719 icon_info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
1721 icon_info->key.icon_names = g_strdupv ((char **)icon_names);
1722 icon_info->key.size = size;
1723 icon_info->key.flags = flags;
1724 icon_info->in_cache = icon_theme;
1725 DEBUG_CACHE (("adding %p (%s %d 0x%x) to cache (cache size %d)\n",
1727 g_strjoinv (",", icon_info->key.icon_names),
1728 icon_info->key.size, icon_info->key.flags,
1729 g_hash_table_size (priv->info_cache)));
1730 g_hash_table_insert (priv->info_cache, &icon_info->key, icon_info);
1734 static gboolean check_for_default_theme = TRUE;
1735 char *default_theme_path;
1736 gboolean found = FALSE;
1739 if (check_for_default_theme)
1741 check_for_default_theme = FALSE;
1743 for (i = 0; !found && i < priv->search_path_len; i++)
1745 default_theme_path = g_build_filename (priv->search_path[i],
1749 found = g_file_test (default_theme_path, G_FILE_TEST_IS_REGULAR);
1750 g_free (default_theme_path);
1755 g_warning ("Could not find the icon '%s'. The '%s' theme\n"
1756 "was not found either, perhaps you need to install it.\n"
1757 "You can get a copy from:\n"
1759 icon_names[0], DEFAULT_THEME_NAME, "http://icon-theme.freedesktop.org/releases");
1769 * gtk_icon_theme_lookup_icon:
1770 * @icon_theme: a #GtkIconTheme
1771 * @icon_name: the name of the icon to lookup
1772 * @size: desired icon size
1773 * @flags: flags modifying the behavior of the icon lookup
1775 * Looks up a named icon and returns a structure containing
1776 * information such as the filename of the icon. The icon
1777 * can then be rendered into a pixbuf using
1778 * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
1779 * combines these two steps if all you need is the pixbuf.)
1781 * Return value: a #GtkIconInfo structure containing information
1782 * about the icon, or %NULL if the icon wasn't found. Free with
1783 * gtk_icon_info_free()
1788 gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme,
1789 const gchar *icon_name,
1791 GtkIconLookupFlags flags)
1795 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1796 g_return_val_if_fail (icon_name != NULL, NULL);
1797 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1798 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1800 GTK_NOTE (ICONTHEME,
1801 g_print ("gtk_icon_theme_lookup_icon %s\n", icon_name));
1803 if (flags & GTK_ICON_LOOKUP_GENERIC_FALLBACK)
1810 for (p = (gchar *) icon_name; *p; p++)
1814 names = g_new (gchar *, dashes + 2);
1815 names[0] = g_strdup (icon_name);
1816 for (i = 1; i <= dashes; i++)
1818 names[i] = g_strdup (names[i - 1]);
1819 p = strrchr (names[i], '-');
1822 names[dashes + 1] = NULL;
1824 info = choose_icon (icon_theme, (const gchar **) names, size, flags);
1830 const gchar *names[2];
1832 names[0] = icon_name;
1835 info = choose_icon (icon_theme, names, size, flags);
1842 * gtk_icon_theme_choose_icon:
1843 * @icon_theme: a #GtkIconTheme
1844 * @icon_names: (array zero-terminated=1): %NULL-terminated array of
1845 * icon names to lookup
1846 * @size: desired icon size
1847 * @flags: flags modifying the behavior of the icon lookup
1849 * Looks up a named icon and returns a structure containing
1850 * information such as the filename of the icon. The icon
1851 * can then be rendered into a pixbuf using
1852 * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
1853 * combines these two steps if all you need is the pixbuf.)
1855 * If @icon_names contains more than one name, this function
1856 * tries them all in the given order before falling back to
1857 * inherited icon themes.
1859 * Return value: a #GtkIconInfo structure containing information
1860 * about the icon, or %NULL if the icon wasn't found. Free with
1861 * gtk_icon_info_free()
1866 gtk_icon_theme_choose_icon (GtkIconTheme *icon_theme,
1867 const gchar *icon_names[],
1869 GtkIconLookupFlags flags)
1871 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1872 g_return_val_if_fail (icon_names != NULL, NULL);
1873 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1874 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1876 return choose_icon (icon_theme, icon_names, size, flags);
1881 gtk_icon_theme_error_quark (void)
1883 return g_quark_from_static_string ("gtk-icon-theme-error-quark");
1887 * gtk_icon_theme_load_icon:
1888 * @icon_theme: a #GtkIconTheme
1889 * @icon_name: the name of the icon to lookup
1890 * @size: the desired icon size. The resulting icon may not be
1891 * exactly this size; see gtk_icon_info_load_icon().
1892 * @flags: flags modifying the behavior of the icon lookup
1893 * @error: (allow-none): Location to store error information on failure,
1896 * Looks up an icon in an icon theme, scales it to the given size
1897 * and renders it into a pixbuf. This is a convenience function;
1898 * if more details about the icon are needed, use
1899 * gtk_icon_theme_lookup_icon() followed by gtk_icon_info_load_icon().
1901 * Note that you probably want to listen for icon theme changes and
1902 * update the icon. This is usually done by connecting to the
1903 * GtkWidget::style-set signal. If for some reason you do not want to
1904 * update the icon when the icon theme changes, you should consider
1905 * using gdk_pixbuf_copy() to make a private copy of the pixbuf
1906 * returned by this function. Otherwise GTK+ may need to keep the old
1907 * icon theme loaded, which would be a waste of memory.
1909 * Return value: (transfer full): the rendered icon; this may be a
1910 * newly created icon or a new reference to an internal icon, so
1911 * you must not modify the icon. Use g_object_unref() to release
1912 * your reference to the icon. %NULL if the icon isn't found.
1917 gtk_icon_theme_load_icon (GtkIconTheme *icon_theme,
1918 const gchar *icon_name,
1920 GtkIconLookupFlags flags,
1923 GtkIconInfo *icon_info;
1924 GdkPixbuf *pixbuf = NULL;
1926 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1927 g_return_val_if_fail (icon_name != NULL, NULL);
1928 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1929 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1930 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1932 icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size,
1933 flags | GTK_ICON_LOOKUP_USE_BUILTIN);
1936 g_set_error (error, GTK_ICON_THEME_ERROR, GTK_ICON_THEME_NOT_FOUND,
1937 _("Icon '%s' not present in theme"), icon_name);
1941 pixbuf = gtk_icon_info_load_icon (icon_info, error);
1942 gtk_icon_info_free (icon_info);
1948 * gtk_icon_theme_has_icon:
1949 * @icon_theme: a #GtkIconTheme
1950 * @icon_name: the name of an icon
1952 * Checks whether an icon theme includes an icon
1953 * for a particular name.
1955 * Return value: %TRUE if @icon_theme includes an
1956 * icon for @icon_name.
1961 gtk_icon_theme_has_icon (GtkIconTheme *icon_theme,
1962 const char *icon_name)
1964 GtkIconThemePrivate *priv;
1967 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
1968 g_return_val_if_fail (icon_name != NULL, FALSE);
1970 priv = icon_theme->priv;
1972 ensure_valid_themes (icon_theme);
1974 for (l = priv->dir_mtimes; l; l = l->next)
1976 IconThemeDirMtime *dir_mtime = l->data;
1977 GtkIconCache *cache = dir_mtime->cache;
1979 if (cache && _gtk_icon_cache_has_icon (cache, icon_name))
1983 if (g_hash_table_lookup_extended (priv->all_icons,
1984 icon_name, NULL, NULL))
1987 if (_builtin_cache &&
1988 _gtk_icon_cache_has_icon (_builtin_cache, icon_name))
1991 if (icon_theme_builtin_icons &&
1992 g_hash_table_lookup_extended (icon_theme_builtin_icons,
1993 icon_name, NULL, NULL))
2000 add_size (gpointer key,
2004 gint **res_p = user_data;
2006 **res_p = GPOINTER_TO_INT (key);
2012 * gtk_icon_theme_get_icon_sizes:
2013 * @icon_theme: a #GtkIconTheme
2014 * @icon_name: the name of an icon
2016 * Returns an array of integers describing the sizes at which
2017 * the icon is available without scaling. A size of -1 means
2018 * that the icon is available in a scalable format. The array
2019 * is zero-terminated.
2021 * Return value: (array zero-terminated=1): An newly allocated array
2022 * describing the sizes at which the icon is available. The array
2023 * should be freed with g_free() when it is no longer needed.
2028 gtk_icon_theme_get_icon_sizes (GtkIconTheme *icon_theme,
2029 const char *icon_name)
2031 GList *l, *d, *icons;
2035 GtkIconThemePrivate *priv;
2037 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2039 priv = icon_theme->priv;
2041 ensure_valid_themes (icon_theme);
2043 sizes = g_hash_table_new (g_direct_hash, g_direct_equal);
2045 for (l = priv->themes; l; l = l->next)
2047 IconTheme *theme = l->data;
2048 for (d = theme->dirs; d; d = d->next)
2050 IconThemeDir *dir = d->data;
2052 if (dir->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir->size), NULL, NULL))
2055 suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
2056 if (suffix != ICON_SUFFIX_NONE)
2058 if (suffix == ICON_SUFFIX_SVG)
2059 g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL);
2061 g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL);
2066 for (d = builtin_dirs; d; d = d->next)
2068 IconThemeDir *dir = d->data;
2070 if (dir->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir->size), NULL, NULL))
2073 suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
2074 if (suffix != ICON_SUFFIX_NONE)
2076 if (suffix == ICON_SUFFIX_SVG)
2077 g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL);
2079 g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL);
2083 if (icon_theme_builtin_icons)
2085 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
2089 BuiltinIcon *icon = icons->data;
2091 g_hash_table_insert (sizes, GINT_TO_POINTER (icon->size), NULL);
2092 icons = icons->next;
2096 r = result = g_new0 (gint, g_hash_table_size (sizes) + 1);
2098 g_hash_table_foreach (sizes, add_size, &r);
2099 g_hash_table_destroy (sizes);
2105 add_key_to_hash (gpointer key,
2109 GHashTable *hash = user_data;
2111 g_hash_table_insert (hash, key, NULL);
2115 add_key_to_list (gpointer key,
2119 GList **list = user_data;
2121 *list = g_list_prepend (*list, g_strdup (key));
2125 * gtk_icon_theme_list_icons:
2126 * @icon_theme: a #GtkIconTheme
2127 * @context: (allow-none): a string identifying a particular type of
2128 * icon, or %NULL to list all icons.
2130 * Lists the icons in the current icon theme. Only a subset
2131 * of the icons can be listed by providing a context string.
2132 * The set of values for the context string is system dependent,
2133 * but will typically include such values as "Applications" and
2136 * Return value: (element-type utf8) (transfer full): a #GList list
2137 * holding the names of all the icons in the theme. You must first
2138 * free each element in the list with g_free(), then free the list
2139 * itself with g_list_free().
2144 gtk_icon_theme_list_icons (GtkIconTheme *icon_theme,
2145 const char *context)
2147 GtkIconThemePrivate *priv;
2150 GQuark context_quark;
2152 priv = icon_theme->priv;
2154 ensure_valid_themes (icon_theme);
2158 context_quark = g_quark_try_string (context);
2166 icons = g_hash_table_new (g_str_hash, g_str_equal);
2171 theme_list_icons (l->data, icons, context_quark);
2175 if (context_quark == 0)
2176 g_hash_table_foreach (priv->unthemed_icons,
2182 g_hash_table_foreach (icons,
2186 g_hash_table_destroy (icons);
2192 * gtk_icon_theme_list_contexts:
2193 * @icon_theme: a #GtkIconTheme
2195 * Gets the list of contexts available within the current
2196 * hierarchy of icon themes
2198 * Return value: (element-type utf8) (transfer full): a #GList list
2199 * holding the names of all the contexts in the theme. You must first
2200 * free each element in the list with g_free(), then free the list
2201 * itself with g_list_free().
2206 gtk_icon_theme_list_contexts (GtkIconTheme *icon_theme)
2208 GtkIconThemePrivate *priv;
2209 GHashTable *contexts;
2212 priv = icon_theme->priv;
2214 ensure_valid_themes (icon_theme);
2216 contexts = g_hash_table_new (g_str_hash, g_str_equal);
2221 theme_list_contexts (l->data, contexts);
2227 g_hash_table_foreach (contexts,
2231 g_hash_table_destroy (contexts);
2237 * gtk_icon_theme_get_example_icon_name:
2238 * @icon_theme: a #GtkIconTheme
2240 * Gets the name of an icon that is representative of the
2241 * current theme (for instance, to use when presenting
2242 * a list of themes to the user.)
2244 * Return value: the name of an example icon or %NULL.
2245 * Free with g_free().
2250 gtk_icon_theme_get_example_icon_name (GtkIconTheme *icon_theme)
2252 GtkIconThemePrivate *priv;
2256 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2258 priv = icon_theme->priv;
2260 ensure_valid_themes (icon_theme);
2267 return g_strdup (theme->example);
2277 rescan_themes (GtkIconTheme *icon_theme)
2279 GtkIconThemePrivate *priv;
2280 IconThemeDirMtime *dir_mtime;
2286 priv = icon_theme->priv;
2288 for (d = priv->dir_mtimes; d != NULL; d = d->next)
2290 dir_mtime = d->data;
2292 stat_res = g_stat (dir_mtime->dir, &stat_buf);
2294 /* dir mtime didn't change */
2295 if (stat_res == 0 &&
2296 S_ISDIR (stat_buf.st_mode) &&
2297 dir_mtime->mtime == stat_buf.st_mtime)
2299 /* didn't exist before, and still doesn't */
2300 if (dir_mtime->mtime == 0 &&
2301 (stat_res != 0 || !S_ISDIR (stat_buf.st_mode)))
2307 g_get_current_time (&tv);
2308 priv->last_stat_time = tv.tv_sec;
2314 * gtk_icon_theme_rescan_if_needed:
2315 * @icon_theme: a #GtkIconTheme
2317 * Checks to see if the icon theme has changed; if it has, any
2318 * currently cached information is discarded and will be reloaded
2319 * next time @icon_theme is accessed.
2321 * Return value: %TRUE if the icon theme has changed and needed
2327 gtk_icon_theme_rescan_if_needed (GtkIconTheme *icon_theme)
2331 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
2333 retval = rescan_themes (icon_theme);
2335 do_theme_change (icon_theme);
2341 theme_destroy (IconTheme *theme)
2343 g_free (theme->display_name);
2344 g_free (theme->comment);
2345 g_free (theme->name);
2346 g_free (theme->example);
2348 g_list_free_full (theme->dirs, (GDestroyNotify) theme_dir_destroy);
2354 theme_dir_destroy (IconThemeDir *dir)
2357 _gtk_icon_cache_unref (dir->cache);
2359 g_hash_table_destroy (dir->icons);
2362 g_hash_table_destroy (dir->icon_data);
2364 g_free (dir->subdir);
2369 theme_dir_size_difference (IconThemeDir *dir, int size, gboolean *smaller)
2374 case ICON_THEME_DIR_FIXED:
2375 *smaller = size < dir->size;
2376 return abs (size - dir->size);
2378 case ICON_THEME_DIR_SCALABLE:
2379 *smaller = size < dir->min_size;
2380 if (size < dir->min_size)
2381 return dir->min_size - size;
2382 if (size > dir->max_size)
2383 return size - dir->max_size;
2386 case ICON_THEME_DIR_THRESHOLD:
2387 min = dir->size - dir->threshold;
2388 max = dir->size + dir->threshold;
2389 *smaller = size < min;
2396 case ICON_THEME_DIR_UNTHEMED:
2397 g_assert_not_reached ();
2400 g_assert_not_reached ();
2405 string_from_suffix (IconSuffix suffix)
2409 case ICON_SUFFIX_XPM:
2411 case ICON_SUFFIX_SVG:
2413 case ICON_SUFFIX_PNG:
2416 g_assert_not_reached();
2422 suffix_from_name (const char *name)
2426 if (g_str_has_suffix (name, ".png"))
2427 retval = ICON_SUFFIX_PNG;
2428 else if (g_str_has_suffix (name, ".svg"))
2429 retval = ICON_SUFFIX_SVG;
2430 else if (g_str_has_suffix (name, ".xpm"))
2431 retval = ICON_SUFFIX_XPM;
2433 retval = ICON_SUFFIX_NONE;
2439 best_suffix (IconSuffix suffix,
2442 if ((suffix & ICON_SUFFIX_PNG) != 0)
2443 return ICON_SUFFIX_PNG;
2444 else if (allow_svg && ((suffix & ICON_SUFFIX_SVG) != 0))
2445 return ICON_SUFFIX_SVG;
2446 else if ((suffix & ICON_SUFFIX_XPM) != 0)
2447 return ICON_SUFFIX_XPM;
2449 return ICON_SUFFIX_NONE;
2454 theme_dir_get_icon_suffix (IconThemeDir *dir,
2455 const gchar *icon_name,
2456 gboolean *has_icon_file)
2462 suffix = (IconSuffix)_gtk_icon_cache_get_icon_flags (dir->cache,
2467 *has_icon_file = suffix & HAS_ICON_FILE;
2469 suffix = suffix & ~HAS_ICON_FILE;
2472 suffix = GPOINTER_TO_UINT (g_hash_table_lookup (dir->icons, icon_name));
2474 GTK_NOTE (ICONTHEME,
2475 g_print ("get_icon_suffix%s %u\n", dir->cache ? " (cached)" : "", suffix));
2480 static GtkIconInfo *
2481 theme_lookup_icon (IconTheme *theme,
2482 const char *icon_name,
2485 gboolean use_builtin)
2488 IconThemeDir *dir, *min_dir;
2490 int min_difference, difference;
2491 BuiltinIcon *closest_builtin = NULL;
2492 gboolean smaller, has_larger, match;
2495 min_difference = G_MAXINT;
2500 /* Builtin icons are logically part of the default theme and
2501 * are searched before other subdirectories of the default theme.
2503 if (use_builtin && strcmp (theme->name, DEFAULT_THEME_NAME) == 0)
2505 closest_builtin = find_builtin_icon (icon_name,
2510 if (min_difference == 0)
2511 return icon_info_new_builtin (closest_builtin);
2513 dirs = builtin_dirs;
2523 GTK_NOTE (ICONTHEME,
2524 g_print ("theme_lookup_icon dir %s\n", dir->dir));
2525 suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
2526 if (best_suffix (suffix, allow_svg) != ICON_SUFFIX_NONE)
2528 difference = theme_dir_size_difference (dir, size, &smaller);
2530 if (difference == 0)
2532 if (dir->type == ICON_THEME_DIR_SCALABLE)
2534 /* don't pick scalable if we already found
2535 * a matching non-scalable dir
2545 /* for a matching non-scalable dir keep
2546 * going and look for a closer match
2548 difference = abs (size - dir->size);
2549 if (!match || difference < min_difference)
2552 min_difference = difference;
2555 if (difference == 0)
2564 if (difference < min_difference || smaller)
2566 min_difference = difference;
2568 has_larger = smaller;
2573 if (difference < min_difference && smaller)
2575 min_difference = difference;
2584 if (l == NULL && dirs == builtin_dirs)
2593 GtkIconInfo *icon_info = icon_info_new ();
2594 gboolean has_icon_file = FALSE;
2596 suffix = theme_dir_get_icon_suffix (min_dir, icon_name, &has_icon_file);
2597 suffix = best_suffix (suffix, allow_svg);
2598 g_assert (suffix != ICON_SUFFIX_NONE);
2602 file = g_strconcat (icon_name, string_from_suffix (suffix), NULL);
2603 icon_info->filename = g_build_filename (min_dir->dir, file, NULL);
2604 icon_info->icon_file = g_file_new_for_path (icon_info->filename);
2609 icon_info->filename = NULL;
2610 icon_info->icon_file = NULL;
2613 if (min_dir->icon_data != NULL)
2614 icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
2616 if (icon_info->data == NULL && min_dir->cache != NULL)
2618 icon_info->data = _gtk_icon_cache_get_icon_data (min_dir->cache, icon_name, min_dir->subdir_index);
2619 if (icon_info->data)
2621 if (min_dir->icon_data == NULL)
2622 min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2623 g_free, (GDestroyNotify)icon_data_free);
2625 g_hash_table_replace (min_dir->icon_data, g_strdup (icon_name), icon_info->data);
2629 if (icon_info->data == NULL && has_icon_file)
2631 gchar *icon_file_name, *icon_file_path;
2633 icon_file_name = g_strconcat (icon_name, ".icon", NULL);
2634 icon_file_path = g_build_filename (min_dir->dir, icon_file_name, NULL);
2636 if (g_file_test (icon_file_path, G_FILE_TEST_IS_REGULAR))
2638 if (min_dir->icon_data == NULL)
2639 min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2640 g_free, (GDestroyNotify)icon_data_free);
2641 load_icon_data (min_dir, icon_file_path, icon_file_name);
2643 icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
2645 g_free (icon_file_name);
2646 g_free (icon_file_path);
2651 icon_info->cache_pixbuf = _gtk_icon_cache_get_icon (min_dir->cache, icon_name,
2652 min_dir->subdir_index);
2655 icon_info->dir_type = min_dir->type;
2656 icon_info->dir_size = min_dir->size;
2657 icon_info->threshold = min_dir->threshold;
2662 if (closest_builtin)
2663 return icon_info_new_builtin (closest_builtin);
2669 theme_list_icons (IconTheme *theme,
2673 GList *l = theme->dirs;
2680 if (context == dir->context ||
2685 _gtk_icon_cache_add_icons (dir->cache,
2692 g_hash_table_foreach (dir->icons,
2702 theme_list_contexts (IconTheme *theme,
2703 GHashTable *contexts)
2705 GList *l = theme->dirs;
2707 const char *context;
2713 context = g_quark_to_string (dir->context);
2714 g_hash_table_replace (contexts, (gpointer) context, NULL);
2721 load_icon_data (IconThemeDir *dir, const char *path, const char *name)
2723 GKeyFile *icon_file;
2731 GError *error = NULL;
2735 icon_file = g_key_file_new ();
2736 g_key_file_set_list_separator (icon_file, ',');
2737 g_key_file_load_from_file (icon_file, path, 0, &error);
2740 g_error_free (error);
2741 g_key_file_free (icon_file);
2746 base_name = strip_suffix (name);
2748 data = g_slice_new0 (GtkIconData);
2749 /* takes ownership of base_name */
2750 g_hash_table_replace (dir->icon_data, base_name, data);
2752 ivalues = g_key_file_get_integer_list (icon_file,
2753 "Icon Data", "EmbeddedTextRectangle",
2759 data->has_embedded_rect = TRUE;
2760 data->x0 = ivalues[0];
2761 data->y0 = ivalues[1];
2762 data->x1 = ivalues[2];
2763 data->y1 = ivalues[3];
2769 str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
2772 split = g_strsplit (str, "|", -1);
2774 data->n_attach_points = g_strv_length (split);
2775 data->attach_points = g_new (GdkPoint, data->n_attach_points);
2778 while (split[i] != NULL && i < data->n_attach_points)
2780 split_point = strchr (split[i], ',');
2785 data->attach_points[i].x = atoi (split[i]);
2786 data->attach_points[i].y = atoi (split_point);
2795 data->display_name = g_key_file_get_locale_string (icon_file,
2796 "Icon Data", "DisplayName",
2798 g_key_file_free (icon_file);
2803 scan_directory (GtkIconThemePrivate *icon_theme,
2804 IconThemeDir *dir, char *full_dir)
2809 GTK_NOTE (ICONTHEME,
2810 g_print ("scanning directory %s\n", full_dir));
2811 dir->icons = g_hash_table_new_full (g_str_hash, g_str_equal,
2814 gdir = g_dir_open (full_dir, 0, NULL);
2819 while ((name = g_dir_read_name (gdir)))
2823 IconSuffix suffix, hash_suffix;
2825 if (g_str_has_suffix (name, ".icon"))
2827 if (dir->icon_data == NULL)
2828 dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2829 g_free, (GDestroyNotify)icon_data_free);
2831 path = g_build_filename (full_dir, name, NULL);
2832 if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
2833 load_icon_data (dir, path, name);
2840 suffix = suffix_from_name (name);
2841 if (suffix == ICON_SUFFIX_NONE)
2844 base_name = strip_suffix (name);
2846 hash_suffix = GPOINTER_TO_INT (g_hash_table_lookup (dir->icons, base_name));
2847 g_hash_table_replace (icon_theme->all_icons, base_name, NULL);
2848 /* takes ownership of base_name */
2849 g_hash_table_replace (dir->icons, base_name, GUINT_TO_POINTER (hash_suffix| suffix));
2856 theme_subdir_load (GtkIconTheme *icon_theme,
2858 GKeyFile *theme_file,
2864 IconThemeDirType type;
2865 char *context_string;
2872 GError *error = NULL;
2873 IconThemeDirMtime *dir_mtime;
2875 size = g_key_file_get_integer (theme_file, subdir, "Size", &error);
2878 g_error_free (error);
2879 g_warning ("Theme directory %s of theme %s has no size field\n",
2880 subdir, theme->name);
2884 type = ICON_THEME_DIR_THRESHOLD;
2885 type_string = g_key_file_get_string (theme_file, subdir, "Type", NULL);
2888 if (strcmp (type_string, "Fixed") == 0)
2889 type = ICON_THEME_DIR_FIXED;
2890 else if (strcmp (type_string, "Scalable") == 0)
2891 type = ICON_THEME_DIR_SCALABLE;
2892 else if (strcmp (type_string, "Threshold") == 0)
2893 type = ICON_THEME_DIR_THRESHOLD;
2895 g_free (type_string);
2899 context_string = g_key_file_get_string (theme_file, subdir, "Context", NULL);
2902 context = g_quark_from_string (context_string);
2903 g_free (context_string);
2906 if (g_key_file_has_key (theme_file, subdir, "MaxSize", NULL))
2907 max_size = g_key_file_get_integer (theme_file, subdir, "MaxSize", NULL);
2911 if (g_key_file_has_key (theme_file, subdir, "MinSize", NULL))
2912 min_size = g_key_file_get_integer (theme_file, subdir, "MinSize", NULL);
2916 if (g_key_file_has_key (theme_file, subdir, "Threshold", NULL))
2917 threshold = g_key_file_get_integer (theme_file, subdir, "Threshold", NULL);
2921 for (d = icon_theme->priv->dir_mtimes; d; d = d->next)
2923 dir_mtime = (IconThemeDirMtime *)d->data;
2925 if (dir_mtime->mtime == 0)
2926 continue; /* directory doesn't exist */
2928 full_dir = g_build_filename (dir_mtime->dir, subdir, NULL);
2930 /* First, see if we have a cache for the directory */
2931 if (dir_mtime->cache != NULL || g_file_test (full_dir, G_FILE_TEST_IS_DIR))
2933 if (dir_mtime->cache == NULL)
2935 /* This will return NULL if the cache doesn't exist or is outdated */
2936 dir_mtime->cache = _gtk_icon_cache_new_for_path (dir_mtime->dir);
2939 dir = g_new (IconThemeDir, 1);
2941 dir->context = context;
2943 dir->min_size = min_size;
2944 dir->max_size = max_size;
2945 dir->threshold = threshold;
2946 dir->dir = full_dir;
2947 dir->icon_data = NULL;
2948 dir->subdir = g_strdup (subdir);
2949 if (dir_mtime->cache != NULL)
2951 dir->cache = _gtk_icon_cache_ref (dir_mtime->cache);
2952 dir->subdir_index = _gtk_icon_cache_get_directory_index (dir->cache, dir->subdir);
2957 dir->subdir_index = -1;
2958 scan_directory (icon_theme->priv, dir, full_dir);
2961 theme->dirs = g_list_prepend (theme->dirs, dir);
2969 icon_data_free (GtkIconData *icon_data)
2971 g_free (icon_data->attach_points);
2972 g_free (icon_data->display_name);
2973 g_slice_free (GtkIconData, icon_data);
2976 static GtkIconData *
2977 icon_data_dup (GtkIconData *icon_data)
2979 GtkIconData *dup = NULL;
2982 dup = g_slice_new0 (GtkIconData);
2984 if (dup->n_attach_points > 0)
2986 dup->attach_points = g_memdup (dup->attach_points,
2987 sizeof (GdkPoint) * dup->n_attach_points);
2989 dup->display_name = g_strdup (dup->display_name);
3000 static void gtk_icon_info_class_init (GtkIconInfoClass *klass);
3002 G_DEFINE_TYPE (GtkIconInfo, gtk_icon_info, G_TYPE_OBJECT)
3005 gtk_icon_info_init (GtkIconInfo *icon_info)
3007 icon_info->scale = -1.;
3010 static GtkIconInfo *
3011 icon_info_new (void)
3013 return g_object_new (GTK_TYPE_ICON_INFO, NULL);
3016 /* This only copies whatever is needed to load the pixbuf, so that we can do
3017 * a load in a thread without affecting the original IconInfo from the thread.
3019 static GtkIconInfo *
3020 icon_info_dup (GtkIconInfo *icon_info)
3025 dup = icon_info_new ();
3027 dup->filename = g_strdup (icon_info->filename);
3028 if (icon_info->icon_file)
3029 dup->icon_file = g_object_ref (icon_info->icon_file);
3030 if (icon_info->loadable)
3031 dup->loadable = g_object_ref (icon_info->loadable);
3033 for (l = icon_info->emblem_infos; l != NULL; l = l->next)
3036 g_slist_append (dup->emblem_infos,
3037 icon_info_dup (l->data));
3040 if (icon_info->cache_pixbuf)
3041 dup->cache_pixbuf = g_object_ref (icon_info->cache_pixbuf);
3043 dup->data = icon_data_dup (icon_info->data);
3044 dup->dir_type = icon_info->dir_type;
3045 dup->dir_size = icon_info->dir_size;
3046 dup->threshold = icon_info->threshold;
3047 dup->desired_size = icon_info->desired_size;
3048 dup->raw_coordinates = icon_info->raw_coordinates;
3049 dup->forced_size = icon_info->forced_size;
3050 dup->emblems_applied = icon_info->emblems_applied;
3055 static GtkIconInfo *
3056 icon_info_new_builtin (BuiltinIcon *icon)
3058 GtkIconInfo *icon_info = icon_info_new ();
3060 icon_info->cache_pixbuf = g_object_ref (icon->pixbuf);
3061 icon_info->dir_type = ICON_THEME_DIR_THRESHOLD;
3062 icon_info->dir_size = icon->size;
3063 icon_info->threshold = 2;
3069 * gtk_icon_info_copy:
3070 * @icon_info: a #GtkIconInfo
3072 * Make a copy of a #GtkIconInfo.
3074 * Return value: the new GtkIconInfo
3079 gtk_icon_info_copy (GtkIconInfo *icon_info)
3082 g_return_val_if_fail (icon_info != NULL, NULL);
3084 return g_object_ref (icon_info);
3088 * gtk_icon_info_free:
3089 * @icon_info: a #GtkIconInfo
3091 * Free a #GtkIconInfo and associated information
3096 gtk_icon_info_free (GtkIconInfo *icon_info)
3098 g_return_if_fail (icon_info != NULL);
3100 g_object_unref (icon_info);
3104 gtk_icon_info_finalize (GObject *object)
3106 GtkIconInfo *icon_info = (GtkIconInfo *) object;
3108 if (icon_info->in_cache)
3109 g_hash_table_remove (icon_info->in_cache->priv->info_cache, &icon_info->key);
3111 g_strfreev (icon_info->key.icon_names);
3113 g_free (icon_info->filename);
3114 g_clear_object (&icon_info->icon_file);
3116 if (icon_info->loadable)
3117 g_object_unref (icon_info->loadable);
3118 g_slist_free_full (icon_info->emblem_infos, (GDestroyNotify) gtk_icon_info_free);
3119 if (icon_info->pixbuf)
3120 g_object_unref (icon_info->pixbuf);
3121 if (icon_info->cache_pixbuf)
3122 g_object_unref (icon_info->cache_pixbuf);
3123 if (icon_info->symbolic_pixbuf_size)
3124 gtk_requisition_free (icon_info->symbolic_pixbuf_size);
3126 symbolic_pixbuf_cache_free (icon_info->symbolic_pixbuf_cache);
3128 G_OBJECT_CLASS (gtk_icon_info_parent_class)->finalize (object);
3132 gtk_icon_info_class_init (GtkIconInfoClass *klass)
3134 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3136 gobject_class->finalize = gtk_icon_info_finalize;
3140 * gtk_icon_info_get_base_size:
3141 * @icon_info: a #GtkIconInfo
3143 * Gets the base size for the icon. The base size
3144 * is a size for the icon that was specified by
3145 * the icon theme creator. This may be different
3146 * than the actual size of image; an example of
3147 * this is small emblem icons that can be attached
3148 * to a larger icon. These icons will be given
3149 * the same base size as the larger icons to which
3150 * they are attached.
3152 * Return value: the base size, or 0, if no base
3153 * size is known for the icon.
3158 gtk_icon_info_get_base_size (GtkIconInfo *icon_info)
3160 g_return_val_if_fail (icon_info != NULL, 0);
3162 return icon_info->dir_size;
3166 * gtk_icon_info_get_filename:
3167 * @icon_info: a #GtkIconInfo
3169 * Gets the filename for the icon. If the
3170 * %GTK_ICON_LOOKUP_USE_BUILTIN flag was passed
3171 * to gtk_icon_theme_lookup_icon(), there may be
3172 * no filename if a builtin icon is returned; in this
3173 * case, you should use gtk_icon_info_get_builtin_pixbuf().
3175 * Return value: (type filename): the filename for the icon, or %NULL
3176 * if gtk_icon_info_get_builtin_pixbuf() should be used instead. The
3177 * return value is owned by GTK+ and should not be modified or freed.
3182 gtk_icon_info_get_filename (GtkIconInfo *icon_info)
3184 g_return_val_if_fail (icon_info != NULL, NULL);
3186 return icon_info->filename;
3190 * gtk_icon_info_get_builtin_pixbuf:
3191 * @icon_info: a #GtkIconInfo structure
3193 * Gets the built-in image for this icon, if any. To allow
3194 * GTK+ to use built in icon images, you must pass the
3195 * %GTK_ICON_LOOKUP_USE_BUILTIN to
3196 * gtk_icon_theme_lookup_icon().
3198 * Return value: (transfer none): the built-in image pixbuf, or %NULL. No
3199 * extra reference is added to the returned pixbuf, so if
3200 * you want to keep it around, you must use g_object_ref().
3201 * The returned image must not be modified.
3206 gtk_icon_info_get_builtin_pixbuf (GtkIconInfo *icon_info)
3208 g_return_val_if_fail (icon_info != NULL, NULL);
3210 if (icon_info->filename)
3213 return icon_info->cache_pixbuf;
3216 static gboolean icon_info_ensure_scale_and_pixbuf (GtkIconInfo*, gboolean);
3218 /* Combine the icon with all emblems, the first emblem is placed
3219 * in the southeast corner. Scale emblems to be at most 3/4 of the
3220 * size of the icon itself.
3223 apply_emblems (GtkIconInfo *info)
3225 GdkPixbuf *icon = NULL;
3229 if (info->emblem_infos == NULL)
3232 if (info->emblems_applied)
3235 w = gdk_pixbuf_get_width (info->pixbuf);
3236 h = gdk_pixbuf_get_height (info->pixbuf);
3238 for (l = info->emblem_infos, pos = 0; l; l = l->next, pos++)
3240 GtkIconInfo *emblem_info = l->data;
3242 if (icon_info_ensure_scale_and_pixbuf (emblem_info, FALSE))
3244 GdkPixbuf *emblem = emblem_info->pixbuf;
3246 gint x = 0, y = 0; /* silence compiler */
3249 ew = gdk_pixbuf_get_width (emblem);
3250 eh = gdk_pixbuf_get_height (emblem);
3282 icon = gdk_pixbuf_copy (info->pixbuf);
3287 gdk_pixbuf_composite (emblem, icon, x, y, ew, eh, x, y,
3288 scale, scale, GDK_INTERP_BILINEAR, 255);
3294 g_object_unref (info->pixbuf);
3295 info->pixbuf = icon;
3298 info->emblems_applied = TRUE;
3301 /* If this returns TRUE, its safe to call
3302 icon_info_ensure_scale_and_pixbuf without blocking */
3304 icon_info_get_pixbuf_ready (GtkIconInfo *icon_info)
3306 if (icon_info->pixbuf &&
3307 (icon_info->emblem_infos == NULL || icon_info->emblems_applied))
3310 if (icon_info->load_error)
3316 /* This function contains the complicated logic for deciding
3317 * on the size at which to load the icon and loading it at
3321 icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info,
3322 gboolean scale_only)
3324 int image_width, image_height;
3325 GdkPixbuf *source_pixbuf;
3328 /* First check if we already succeeded have the necessary
3329 * information (or failed earlier)
3331 if (scale_only && icon_info->scale >= 0)
3334 if (icon_info->pixbuf)
3336 apply_emblems (icon_info);
3340 if (icon_info->load_error)
3343 /* SVG icons are a special case - we just immediately scale them
3344 * to the desired size
3346 if (icon_info->icon_file && !icon_info->loadable)
3347 icon_info->loadable = G_LOADABLE_ICON (g_file_icon_new (icon_info->icon_file));
3350 if (G_IS_FILE_ICON (icon_info->loadable))
3353 GFileInfo *file_info;
3354 const gchar *content_type;
3356 file = g_file_icon_get_file (G_FILE_ICON (icon_info->loadable));
3357 file_info = g_file_query_info (file,
3358 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
3359 G_FILE_QUERY_INFO_NONE,
3363 content_type = g_file_info_get_content_type (file_info);
3365 if (content_type && strcmp (content_type, "image/svg+xml") == 0)
3368 g_object_unref (file_info);
3374 GInputStream *stream;
3376 icon_info->scale = icon_info->desired_size / 1000.;
3381 stream = g_loadable_icon_load (icon_info->loadable,
3382 icon_info->desired_size,
3384 &icon_info->load_error);
3387 icon_info->pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3388 icon_info->desired_size,
3389 icon_info->desired_size,
3392 &icon_info->load_error);
3393 g_object_unref (stream);
3396 if (!icon_info->pixbuf)
3399 apply_emblems (icon_info);
3404 /* In many cases, the scale can be determined without actual access
3405 * to the icon file. This is generally true when we have a size
3406 * for the directory where the icon is; the image size doesn't
3407 * matter in that case.
3409 if (icon_info->forced_size)
3410 icon_info->scale = -1;
3411 else if (icon_info->dir_type == ICON_THEME_DIR_FIXED)
3412 icon_info->scale = 1.0;
3413 else if (icon_info->dir_type == ICON_THEME_DIR_THRESHOLD)
3415 if (icon_info->desired_size >= icon_info->dir_size - icon_info->threshold &&
3416 icon_info->desired_size <= icon_info->dir_size + icon_info->threshold)
3417 icon_info->scale = 1.0;
3418 else if (icon_info->dir_size > 0)
3419 icon_info->scale =(gdouble) icon_info->desired_size / icon_info->dir_size;
3421 else if (icon_info->dir_type == ICON_THEME_DIR_SCALABLE)
3423 if (icon_info->dir_size > 0)
3424 icon_info->scale = (gdouble) icon_info->desired_size / icon_info->dir_size;
3427 if (icon_info->scale >= 0. && scale_only)
3430 /* At this point, we need to actually get the icon; either from the
3431 * builtin image or by loading the file
3433 source_pixbuf = NULL;
3434 if (icon_info->cache_pixbuf)
3435 source_pixbuf = g_object_ref (icon_info->cache_pixbuf);
3438 GInputStream *stream;
3440 stream = g_loadable_icon_load (icon_info->loadable,
3441 icon_info->desired_size,
3443 &icon_info->load_error);
3446 source_pixbuf = gdk_pixbuf_new_from_stream (stream,
3448 &icon_info->load_error);
3449 g_object_unref (stream);
3456 /* Do scale calculations that depend on the image size
3458 image_width = gdk_pixbuf_get_width (source_pixbuf);
3459 image_height = gdk_pixbuf_get_height (source_pixbuf);
3461 if (icon_info->scale < 0.0)
3463 gint image_size = MAX (image_width, image_height);
3465 icon_info->scale = (gdouble)icon_info->desired_size / (gdouble)image_size;
3467 icon_info->scale = 1.0;
3469 if (icon_info->dir_type == ICON_THEME_DIR_UNTHEMED &&
3470 !icon_info->forced_size)
3471 icon_info->scale = MIN (icon_info->scale, 1.0);
3474 /* We don't short-circuit out here for scale_only, since, now
3475 * we've loaded the icon, we might as well go ahead and finish
3476 * the job. This is a bit of a waste when we scale here
3477 * and never get the final pixbuf; at the cost of a bit of
3478 * extra complexity, we could keep the source pixbuf around
3479 * but not actually scale it until needed.
3481 if (icon_info->scale == 1.0)
3482 icon_info->pixbuf = source_pixbuf;
3485 icon_info->pixbuf = gdk_pixbuf_scale_simple (source_pixbuf,
3486 0.5 + image_width * icon_info->scale,
3487 0.5 + image_height * icon_info->scale,
3488 GDK_INTERP_BILINEAR);
3489 g_object_unref (source_pixbuf);
3492 apply_emblems (icon_info);
3498 proxy_pixbuf_destroy (guchar *pixels, gpointer data)
3500 GtkIconInfo *icon_info = data;
3501 GtkIconTheme *icon_theme = icon_info->in_cache;
3503 g_assert (icon_info->proxy_pixbuf != NULL);
3504 icon_info->proxy_pixbuf = NULL;
3506 /* Keep it alive a bit longer */
3507 if (icon_theme != NULL)
3508 ensure_in_lru_cache (icon_theme, icon_info);
3510 gtk_icon_info_free (icon_info);
3514 * gtk_icon_info_load_icon:
3515 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3516 * @error: (allow-none): location to store error information on failure,
3519 * Renders an icon previously looked up in an icon theme using
3520 * gtk_icon_theme_lookup_icon(); the size will be based on the size
3521 * passed to gtk_icon_theme_lookup_icon(). Note that the resulting
3522 * pixbuf may not be exactly this size; an icon theme may have icons
3523 * that differ slightly from their nominal sizes, and in addition GTK+
3524 * will avoid scaling icons that it considers sufficiently close to the
3525 * requested size or for which the source image would have to be scaled
3526 * up too far. (This maintains sharpness.). This behaviour can be changed
3527 * by passing the %GTK_ICON_LOOKUP_FORCE_SIZE flag when obtaining
3528 * the #GtkIconInfo. If this flag has been specified, the pixbuf
3529 * returned by this function will be scaled to the exact size.
3531 * Return value: (transfer full): the rendered icon; this may be a newly
3532 * created icon or a new reference to an internal icon, so you must
3533 * not modify the icon. Use g_object_unref() to release your reference
3539 gtk_icon_info_load_icon (GtkIconInfo *icon_info,
3542 g_return_val_if_fail (icon_info != NULL, NULL);
3543 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3545 if (!icon_info_ensure_scale_and_pixbuf (icon_info, FALSE))
3547 if (icon_info->load_error)
3548 g_propagate_error (error, icon_info->load_error);
3550 g_set_error_literal (error,
3551 GTK_ICON_THEME_ERROR,
3552 GTK_ICON_THEME_NOT_FOUND,
3553 _("Failed to load icon"));
3558 /* Instead of returning the pixbuf directly we
3559 return a proxy to it that we don't own (but that
3560 shares the data with the one we own). This way
3561 we can know when it is freed and ensure the
3562 IconInfo is alive (and thus cached) while
3563 the pixbuf is still alive. */
3565 if (icon_info->proxy_pixbuf != NULL)
3566 return g_object_ref (icon_info->proxy_pixbuf);
3568 icon_info->proxy_pixbuf =
3569 gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (icon_info->pixbuf),
3570 gdk_pixbuf_get_colorspace (icon_info->pixbuf),
3571 gdk_pixbuf_get_has_alpha (icon_info->pixbuf),
3572 gdk_pixbuf_get_bits_per_sample (icon_info->pixbuf),
3573 gdk_pixbuf_get_width (icon_info->pixbuf),
3574 gdk_pixbuf_get_height (icon_info->pixbuf),
3575 gdk_pixbuf_get_rowstride (icon_info->pixbuf),
3576 proxy_pixbuf_destroy,
3577 gtk_icon_info_copy (icon_info));
3579 return icon_info->proxy_pixbuf;
3583 load_icon_thread (GTask *task,
3584 gpointer source_object,
3586 GCancellable *cancellable)
3588 GtkIconInfo *dup = task_data;
3590 icon_info_ensure_scale_and_pixbuf (dup, FALSE);
3591 g_task_return_pointer (task, NULL, NULL);
3595 * gtk_icon_info_load_icon_async:
3596 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3597 * @cancellable: (allow-none): optional #GCancellable object,
3599 * @callback: (scope async): a #GAsyncReadyCallback to call when the
3600 * request is satisfied
3601 * @user_data: (closure): the data to pass to callback function
3603 * Asynchronously load, render and scale an icon previously looked up
3604 * from the icon theme using gtk_icon_theme_lookup_icon().
3606 * For more details, see gtk_icon_info_load_icon() which is the synchronous
3607 * version of this call.
3612 gtk_icon_info_load_icon_async (GtkIconInfo *icon_info,
3613 GCancellable *cancellable,
3614 GAsyncReadyCallback callback,
3620 GError *error = NULL;
3622 task = g_task_new (icon_info, cancellable, callback, user_data);
3624 if (icon_info_get_pixbuf_ready (icon_info))
3626 pixbuf = gtk_icon_info_load_icon (icon_info, &error);
3628 g_task_return_error (task, error);
3630 g_task_return_pointer (task, pixbuf, g_object_unref);
3631 g_object_unref (task);
3635 dup = icon_info_dup (icon_info);
3636 g_task_set_task_data (task, dup, g_object_unref);
3637 g_task_run_in_thread (task, load_icon_thread);
3638 g_object_unref (task);
3643 * gtk_icon_info_load_icon_finish:
3644 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3645 * @res: a #GAsyncResult
3646 * @error: (allow-none): location to store error information on failure,
3649 * Finishes an async icon load, see gtk_icon_info_load_icon_async().
3651 * Return value: (transfer full): the rendered icon; this may be a newly
3652 * created icon or a new reference to an internal icon, so you must
3653 * not modify the icon. Use g_object_unref() to release your reference
3659 gtk_icon_info_load_icon_finish (GtkIconInfo *icon_info,
3660 GAsyncResult *result,
3663 GTask *task = G_TASK (result);
3666 g_return_val_if_fail (g_task_is_valid (result, icon_info), NULL);
3668 dup = g_task_get_task_data (task);
3669 if (dup == NULL || g_task_had_error (task))
3670 return g_task_propagate_pointer (task, error);
3672 /* We ran the thread and it was not cancelled */
3674 /* Check if someone else updated the icon_info in between */
3675 if (!icon_info_get_pixbuf_ready (icon_info))
3677 /* If not, copy results from dup back to icon_info */
3679 icon_info->emblems_applied = dup->emblems_applied;
3680 icon_info->scale = dup->scale;
3681 g_clear_object (&icon_info->pixbuf);
3683 icon_info->pixbuf = g_object_ref (dup->pixbuf);
3684 g_clear_error (&icon_info->load_error);
3685 if (dup->load_error)
3686 icon_info->load_error = g_error_copy (dup->load_error);
3689 g_assert (icon_info_get_pixbuf_ready (icon_info));
3691 /* This is now guaranteed to not block */
3692 return gtk_icon_info_load_icon (icon_info, error);
3696 gdk_color_to_css (GdkColor *color)
3698 return g_strdup_printf ("rgb(%d,%d,%d)",
3705 gdk_rgba_to_css (const GdkRGBA *color)
3707 /* drop alpha for now, since librsvg does not understand rgba() */
3708 return g_strdup_printf ("rgb(%d,%d,%d)",
3709 (gint)(color->red * 255),
3710 (gint)(color->green * 255),
3711 (gint)(color->blue * 255));
3715 proxy_symbolic_pixbuf_destroy (guchar *pixels, gpointer data)
3717 GtkIconInfo *icon_info = data;
3718 GtkIconTheme *icon_theme = icon_info->in_cache;
3719 SymbolicPixbufCache *symbolic_cache;
3721 for (symbolic_cache = icon_info->symbolic_pixbuf_cache;
3722 symbolic_cache != NULL;
3723 symbolic_cache = symbolic_cache->next)
3725 if (symbolic_cache->proxy_pixbuf != NULL &&
3726 gdk_pixbuf_get_pixels (symbolic_cache->proxy_pixbuf) == pixels)
3730 g_assert (symbolic_cache != NULL);
3731 g_assert (symbolic_cache->proxy_pixbuf != NULL);
3733 symbolic_cache->proxy_pixbuf = NULL;
3735 /* Keep it alive a bit longer */
3736 if (icon_theme != NULL)
3737 ensure_in_lru_cache (icon_theme, icon_info);
3739 gtk_icon_info_free (icon_info);
3743 symbolic_cache_get_proxy (SymbolicPixbufCache *symbolic_cache,
3744 GtkIconInfo *icon_info)
3746 if (symbolic_cache->proxy_pixbuf)
3747 return g_object_ref (symbolic_cache->proxy_pixbuf);
3749 symbolic_cache->proxy_pixbuf =
3750 gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (symbolic_cache->pixbuf),
3751 gdk_pixbuf_get_colorspace (symbolic_cache->pixbuf),
3752 gdk_pixbuf_get_has_alpha (symbolic_cache->pixbuf),
3753 gdk_pixbuf_get_bits_per_sample (symbolic_cache->pixbuf),
3754 gdk_pixbuf_get_width (symbolic_cache->pixbuf),
3755 gdk_pixbuf_get_height (symbolic_cache->pixbuf),
3756 gdk_pixbuf_get_rowstride (symbolic_cache->pixbuf),
3757 proxy_symbolic_pixbuf_destroy,
3758 gtk_icon_info_copy (icon_info));
3760 return symbolic_cache->proxy_pixbuf;
3764 _gtk_icon_info_load_symbolic_internal (GtkIconInfo *icon_info,
3766 const GdkRGBA *success_color,
3767 const GdkRGBA *warning_color,
3768 const GdkRGBA *error_color,
3771 GInputStream *stream;
3778 gchar *width, *height, *uri;
3779 SymbolicPixbufCache *symbolic_cache;
3781 symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
3782 fg, success_color, warning_color, error_color);
3784 return symbolic_cache_get_proxy (symbolic_cache, icon_info);
3786 /* css_fg can't possibly have failed, otherwise
3787 * that would mean we have a broken style */
3788 g_return_val_if_fail (fg != NULL, NULL);
3790 css_fg = gdk_rgba_to_css (fg);
3792 css_success = css_warning = css_error = NULL;
3795 css_warning = gdk_rgba_to_css (warning_color);
3798 css_error = gdk_rgba_to_css (error_color);
3801 css_success = gdk_rgba_to_css (success_color);
3805 GdkColor success_default_color = { 0, 0x4e00, 0x9a00, 0x0600 };
3806 css_success = gdk_color_to_css (&success_default_color);
3810 GdkColor warning_default_color = { 0, 0xf500, 0x7900, 0x3e00 };
3811 css_warning = gdk_color_to_css (&warning_default_color);
3815 GdkColor error_default_color = { 0, 0xcc00, 0x0000, 0x0000 };
3816 css_error = gdk_color_to_css (&error_default_color);
3819 if (!icon_info->symbolic_pixbuf_size)
3821 stream = G_INPUT_STREAM (g_file_read (icon_info->icon_file, NULL, error));
3826 /* Fetch size from the original icon */
3827 pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
3828 g_object_unref (stream);
3833 icon_info->symbolic_pixbuf_size = gtk_requisition_new ();
3834 icon_info->symbolic_pixbuf_size->width = gdk_pixbuf_get_width (pixbuf);
3835 icon_info->symbolic_pixbuf_size->height = gdk_pixbuf_get_height (pixbuf);
3836 g_object_unref (pixbuf);
3839 width = g_strdup_printf ("%d", icon_info->symbolic_pixbuf_size->width);
3840 height = g_strdup_printf ("%d", icon_info->symbolic_pixbuf_size->height);
3841 uri = g_file_get_uri (icon_info->icon_file);
3843 data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
3844 "<svg version=\"1.1\"\n"
3845 " xmlns=\"http://www.w3.org/2000/svg\"\n"
3846 " xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n"
3847 " width=\"", width, "\"\n"
3848 " height=\"", height, "\">\n"
3849 " <style type=\"text/css\">\n"
3851 " fill: ", css_fg," !important;\n"
3854 " fill: ", css_warning, " !important;\n"
3857 " fill: ", css_error ," !important;\n"
3860 " fill: ", css_success, " !important;\n"
3863 " <xi:include href=\"", uri, "\"/>\n"
3867 g_free (css_warning);
3869 g_free (css_success);
3874 stream = g_memory_input_stream_new_from_data (data, -1, g_free);
3875 pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3876 icon_info->desired_size,
3877 icon_info->desired_size,
3881 g_object_unref (stream);
3885 icon_info->symbolic_pixbuf_cache =
3886 symbolic_pixbuf_cache_new (pixbuf, fg, success_color, warning_color, error_color,
3887 icon_info->symbolic_pixbuf_cache);
3889 return symbolic_cache_get_proxy (icon_info->symbolic_pixbuf_cache, icon_info);
3896 * gtk_icon_info_load_symbolic:
3897 * @icon_info: a #GtkIconInfo
3898 * @fg: a #GdkRGBA representing the foreground color of the icon
3899 * @success_color: (allow-none): a #GdkRGBA representing the warning color
3900 * of the icon or %NULL to use the default color
3901 * @warning_color: (allow-none): a #GdkRGBA representing the warning color
3902 * of the icon or %NULL to use the default color
3903 * @error_color: (allow-none): a #GdkRGBA representing the error color
3904 * of the icon or %NULL to use the default color (allow-none)
3905 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3906 * loaded icon was a symbolic one and whether the @fg color was
3908 * @error: (allow-none): location to store error information on failure,
3911 * Loads an icon, modifying it to match the system colours for the foreground,
3912 * success, warning and error colors provided. If the icon is not a symbolic
3913 * one, the function will return the result from gtk_icon_info_load_icon().
3915 * This allows loading symbolic icons that will match the system theme.
3917 * Unless you are implementing a widget, you will want to use
3918 * g_themed_icon_new_with_default_fallbacks() to load the icon.
3920 * As implementation details, the icon loaded needs to be of SVG type,
3921 * contain the "symbolic" term as the last component of the icon name,
3922 * and use the 'fg', 'success', 'warning' and 'error' CSS styles in the
3925 * See the <ulink url="http://www.freedesktop.org/wiki/SymbolicIcons">Symbolic Icons spec</ulink>
3926 * for more information about symbolic icons.
3928 * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
3933 gtk_icon_info_load_symbolic (GtkIconInfo *icon_info,
3935 const GdkRGBA *success_color,
3936 const GdkRGBA *warning_color,
3937 const GdkRGBA *error_color,
3938 gboolean *was_symbolic,
3942 gboolean is_symbolic;
3944 g_return_val_if_fail (icon_info != NULL, NULL);
3945 g_return_val_if_fail (fg != NULL, NULL);
3948 if (icon_info->icon_file)
3949 icon_uri = g_file_get_uri (icon_info->icon_file);
3951 is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
3955 *was_symbolic = is_symbolic;
3958 return gtk_icon_info_load_icon (icon_info, error);
3960 return _gtk_icon_info_load_symbolic_internal (icon_info,
3962 warning_color, error_color,
3967 * gtk_icon_info_load_symbolic_for_context:
3968 * @icon_info: a #GtkIconInfo
3969 * @context: a #GtkStyleContext
3970 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3971 * loaded icon was a symbolic one and whether the @fg color was
3973 * @error: (allow-none): location to store error information on failure,
3976 * Loads an icon, modifying it to match the system colors for the foreground,
3977 * success, warning and error colors provided. If the icon is not a symbolic
3978 * one, the function will return the result from gtk_icon_info_load_icon().
3979 * This function uses the regular foreground color and the symbolic colors
3980 * with the names "success_color", "warning_color" and "error_color" from
3983 * This allows loading symbolic icons that will match the system theme.
3985 * See gtk_icon_info_load_symbolic() for more details.
3987 * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
3992 gtk_icon_info_load_symbolic_for_context (GtkIconInfo *icon_info,
3993 GtkStyleContext *context,
3994 gboolean *was_symbolic,
3997 GdkRGBA *color = NULL;
4000 GdkRGBA success_color;
4001 GdkRGBA *success_colorp;
4002 GdkRGBA warning_color;
4003 GdkRGBA *warning_colorp;
4004 GdkRGBA error_color;
4005 GdkRGBA *error_colorp;
4006 GtkStateFlags state;
4008 gboolean is_symbolic;
4010 g_return_val_if_fail (icon_info != NULL, NULL);
4011 g_return_val_if_fail (context != NULL, NULL);
4014 if (icon_info->icon_file)
4015 icon_uri = g_file_get_uri (icon_info->icon_file);
4017 is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
4021 *was_symbolic = is_symbolic;
4024 return gtk_icon_info_load_icon (icon_info, error);
4026 fgp = success_colorp = warning_colorp = error_colorp = NULL;
4028 state = gtk_style_context_get_state (context);
4029 gtk_style_context_get (context, state, "color", &color, NULL);
4034 gdk_rgba_free (color);
4037 if (gtk_style_context_lookup_color (context, "success_color", &success_color))
4038 success_colorp = &success_color;
4040 if (gtk_style_context_lookup_color (context, "warning_color", &warning_color))
4041 warning_colorp = &warning_color;
4043 if (gtk_style_context_lookup_color (context, "error_color", &error_color))
4044 error_colorp = &error_color;
4046 return _gtk_icon_info_load_symbolic_internal (icon_info,
4047 fgp, success_colorp,
4048 warning_colorp, error_colorp,
4053 color_to_rgba (GdkColor *color, GdkRGBA *rgba)
4055 rgba->red = color->red / 65535.0;
4056 rgba->green = color->green / 65535.0;
4057 rgba->blue = color->blue / 65535.0;
4063 * gtk_icon_info_load_symbolic_for_style:
4064 * @icon_info: a #GtkIconInfo
4065 * @style: a #GtkStyle to take the colors from
4066 * @state: the widget state to use for colors
4067 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
4068 * loaded icon was a symbolic one and whether the @fg color was
4070 * @error: (allow-none): location to store error information on failure,
4073 * Loads an icon, modifying it to match the system colours for the foreground,
4074 * success, warning and error colors provided. If the icon is not a symbolic
4075 * one, the function will return the result from gtk_icon_info_load_icon().
4077 * This allows loading symbolic icons that will match the system theme.
4079 * See gtk_icon_info_load_symbolic() for more details.
4081 * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
4085 * Deprecated: 3.0: Use gtk_icon_info_load_symbolic_for_context() instead
4088 gtk_icon_info_load_symbolic_for_style (GtkIconInfo *icon_info,
4091 gboolean *was_symbolic,
4096 GdkRGBA success_color;
4097 GdkRGBA *success_colorp;
4098 GdkRGBA warning_color;
4099 GdkRGBA *warning_colorp;
4100 GdkRGBA error_color;
4101 GdkRGBA *error_colorp;
4103 gboolean is_symbolic;
4105 g_return_val_if_fail (icon_info != NULL, NULL);
4106 g_return_val_if_fail (style != NULL, NULL);
4109 if (icon_info->icon_file)
4110 icon_uri = g_file_get_uri (icon_info->icon_file);
4112 is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
4116 *was_symbolic = is_symbolic;
4119 return gtk_icon_info_load_icon (icon_info, error);
4121 color_to_rgba (&style->fg[state], &fg);
4123 success_colorp = warning_colorp = error_colorp = NULL;
4125 if (gtk_style_lookup_color (style, "success_color", &color))
4126 success_colorp = color_to_rgba (&color, &success_color);
4128 if (gtk_style_lookup_color (style, "warning_color", &color))
4129 warning_colorp = color_to_rgba (&color, &warning_color);
4131 if (gtk_style_lookup_color (style, "error_color", &color))
4132 error_colorp = color_to_rgba (&color, &error_color);
4134 return _gtk_icon_info_load_symbolic_internal (icon_info,
4135 &fg, success_colorp,
4136 warning_colorp, error_colorp,
4141 * gtk_icon_info_set_raw_coordinates:
4142 * @icon_info: a #GtkIconInfo
4143 * @raw_coordinates: whether the coordinates of embedded rectangles
4144 * and attached points should be returned in their original
4147 * Sets whether the coordinates returned by gtk_icon_info_get_embedded_rect()
4148 * and gtk_icon_info_get_attach_points() should be returned in their
4149 * original form as specified in the icon theme, instead of scaled
4150 * appropriately for the pixbuf returned by gtk_icon_info_load_icon().
4152 * Raw coordinates are somewhat strange; they are specified to be with
4153 * respect to the unscaled pixmap for PNG and XPM icons, but for SVG
4154 * icons, they are in a 1000x1000 coordinate space that is scaled
4155 * to the final size of the icon. You can determine if the icon is an SVG
4156 * icon by using gtk_icon_info_get_filename(), and seeing if it is non-%NULL
4157 * and ends in '.svg'.
4159 * This function is provided primarily to allow compatibility wrappers
4160 * for older API's, and is not expected to be useful for applications.
4165 gtk_icon_info_set_raw_coordinates (GtkIconInfo *icon_info,
4166 gboolean raw_coordinates)
4168 g_return_if_fail (icon_info != NULL);
4170 icon_info->raw_coordinates = raw_coordinates != FALSE;
4173 /* Scale coordinates from the icon data prior to returning
4177 icon_info_scale_point (GtkIconInfo *icon_info,
4183 if (icon_info->raw_coordinates)
4190 if (!icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4193 *x_out = 0.5 + x * icon_info->scale;
4194 *y_out = 0.5 + y * icon_info->scale;
4201 * gtk_icon_info_get_embedded_rect:
4202 * @icon_info: a #GtkIconInfo
4203 * @rectangle: (out): #GdkRectangle in which to store embedded
4204 * rectangle coordinates; coordinates are only stored
4205 * when this function returns %TRUE.
4207 * Gets the coordinates of a rectangle within the icon
4208 * that can be used for display of information such
4209 * as a preview of the contents of a text file.
4210 * See gtk_icon_info_set_raw_coordinates() for further
4211 * information about the coordinate system.
4213 * Return value: %TRUE if the icon has an embedded rectangle
4218 gtk_icon_info_get_embedded_rect (GtkIconInfo *icon_info,
4219 GdkRectangle *rectangle)
4221 g_return_val_if_fail (icon_info != NULL, FALSE);
4223 if (icon_info->data && icon_info->data->has_embedded_rect &&
4224 icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4226 gint scaled_x0, scaled_y0;
4227 gint scaled_x1, scaled_y1;
4231 icon_info_scale_point (icon_info,
4232 icon_info->data->x0, icon_info->data->y0,
4233 &scaled_x0, &scaled_y0);
4234 icon_info_scale_point (icon_info,
4235 icon_info->data->x1, icon_info->data->y1,
4236 &scaled_x1, &scaled_y1);
4238 rectangle->x = scaled_x0;
4239 rectangle->y = scaled_y0;
4240 rectangle->width = scaled_x1 - rectangle->x;
4241 rectangle->height = scaled_y1 - rectangle->y;
4251 * gtk_icon_info_get_attach_points:
4252 * @icon_info: a #GtkIconInfo
4253 * @points: (allow-none) (array length=n_points) (out): location to store pointer to an array of points, or %NULL
4254 * free the array of points with g_free().
4255 * @n_points: (allow-none): location to store the number of points in @points, or %NULL
4257 * Fetches the set of attach points for an icon. An attach point
4258 * is a location in the icon that can be used as anchor points for attaching
4259 * emblems or overlays to the icon.
4261 * Return value: %TRUE if there are any attach points for the icon.
4266 gtk_icon_info_get_attach_points (GtkIconInfo *icon_info,
4270 g_return_val_if_fail (icon_info != NULL, FALSE);
4272 if (icon_info->data && icon_info->data->n_attach_points &&
4273 icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4279 *points = g_new (GdkPoint, icon_info->data->n_attach_points);
4280 for (i = 0; i < icon_info->data->n_attach_points; i++)
4281 icon_info_scale_point (icon_info,
4282 icon_info->data->attach_points[i].x,
4283 icon_info->data->attach_points[i].y,
4289 *n_points = icon_info->data->n_attach_points;
4305 * gtk_icon_info_get_display_name:
4306 * @icon_info: a #GtkIconInfo
4308 * Gets the display name for an icon. A display name is a
4309 * string to be used in place of the icon name in a user
4310 * visible context like a list of icons.
4312 * Return value: the display name for the icon or %NULL, if
4313 * the icon doesn't have a specified display name. This value
4314 * is owned @icon_info and must not be modified or free.
4319 gtk_icon_info_get_display_name (GtkIconInfo *icon_info)
4321 g_return_val_if_fail (icon_info != NULL, NULL);
4323 if (icon_info->data)
4324 return icon_info->data->display_name;
4335 * gtk_icon_theme_add_builtin_icon:
4336 * @icon_name: the name of the icon to register
4337 * @size: the size at which to register the icon (different
4338 * images can be registered for the same icon name
4339 * at different sizes.)
4340 * @pixbuf: #GdkPixbuf that contains the image to use
4343 * Registers a built-in icon for icon theme lookups. The idea
4344 * of built-in icons is to allow an application or library
4345 * that uses themed icons to function requiring files to
4346 * be present in the file system. For instance, the default
4347 * images for all of GTK+'s stock icons are registered
4350 * In general, if you use gtk_icon_theme_add_builtin_icon()
4351 * you should also install the icon in the icon theme, so
4352 * that the icon is generally available.
4354 * This function will generally be used with pixbufs loaded
4355 * via gdk_pixbuf_new_from_inline().
4360 gtk_icon_theme_add_builtin_icon (const gchar *icon_name,
4364 BuiltinIcon *default_icon;
4368 g_return_if_fail (icon_name != NULL);
4369 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
4371 if (!icon_theme_builtin_icons)
4372 icon_theme_builtin_icons = g_hash_table_new (g_str_hash, g_str_equal);
4374 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
4376 key = g_strdup (icon_name);
4378 key = (gpointer)icon_name; /* Won't get stored */
4380 default_icon = g_new (BuiltinIcon, 1);
4381 default_icon->size = size;
4382 default_icon->pixbuf = g_object_ref (pixbuf);
4383 icons = g_slist_prepend (icons, default_icon);
4385 /* Replaces value, leaves key untouched
4387 g_hash_table_insert (icon_theme_builtin_icons, key, icons);
4390 /* Look up a builtin icon; the min_difference_p and
4391 * has_larger_p out parameters allow us to combine
4392 * this lookup with searching through the actual directories
4393 * of the "hicolor" icon theme. See theme_lookup_icon()
4394 * for how they are used.
4396 static BuiltinIcon *
4397 find_builtin_icon (const gchar *icon_name,
4399 gint *min_difference_p,
4400 gboolean *has_larger_p)
4402 GSList *icons = NULL;
4403 gint min_difference = G_MAXINT;
4404 gboolean has_larger = FALSE;
4405 BuiltinIcon *min_icon = NULL;
4407 if (!icon_theme_builtin_icons)
4410 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
4414 BuiltinIcon *default_icon = icons->data;
4415 int min, max, difference;
4418 min = default_icon->size - 2;
4419 max = default_icon->size + 2;
4420 smaller = size < min;
4422 difference = min - size;
4423 else if (size > max)
4424 difference = size - max;
4428 if (difference == 0)
4430 min_icon = default_icon;
4436 if (difference < min_difference || smaller)
4438 min_difference = difference;
4439 min_icon = default_icon;
4440 has_larger = smaller;
4445 if (difference < min_difference && smaller)
4447 min_difference = difference;
4448 min_icon = default_icon;
4452 icons = icons->next;
4455 if (min_difference_p)
4456 *min_difference_p = min_difference;
4458 *has_larger_p = has_larger;
4464 _gtk_icon_theme_check_reload (GdkDisplay *display)
4468 GtkIconTheme *icon_theme;
4470 n_screens = gdk_display_get_n_screens (display);
4472 for (i = 0; i < n_screens; i++)
4474 screen = gdk_display_get_screen (display, i);
4476 icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
4479 icon_theme->priv->check_reload = TRUE;
4480 ensure_valid_themes (icon_theme);
4481 icon_theme->priv->check_reload = FALSE;
4488 * gtk_icon_theme_lookup_by_gicon:
4489 * @icon_theme: a #GtkIconTheme
4490 * @icon: the #GIcon to look up
4491 * @size: desired icon size
4492 * @flags: flags modifying the behavior of the icon lookup
4494 * Looks up an icon and returns a structure containing
4495 * information such as the filename of the icon.
4496 * The icon can then be rendered into a pixbuf using
4497 * gtk_icon_info_load_icon().
4499 * Return value: a #GtkIconInfo structure containing
4500 * information about the icon, or %NULL if the icon
4501 * wasn't found. Free with gtk_icon_info_free()
4506 gtk_icon_theme_lookup_by_gicon (GtkIconTheme *icon_theme,
4509 GtkIconLookupFlags flags)
4513 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
4514 g_return_val_if_fail (G_IS_ICON (icon), NULL);
4516 if (G_IS_LOADABLE_ICON (icon))
4518 info = icon_info_new ();
4519 info->loadable = G_LOADABLE_ICON (g_object_ref (icon));
4521 if (G_IS_FILE_ICON (icon))
4523 GFile *file = g_file_icon_get_file (G_FILE_ICON (icon));
4526 info->icon_file = g_object_ref (file);
4527 info->filename = g_file_get_path (file);
4531 info->dir_type = ICON_THEME_DIR_UNTHEMED;
4532 info->dir_size = size;
4533 info->desired_size = size;
4534 info->threshold = 2;
4535 info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
4539 else if (G_IS_THEMED_ICON (icon))
4541 const gchar **names;
4543 names = (const gchar **)g_themed_icon_get_names (G_THEMED_ICON (icon));
4544 info = gtk_icon_theme_choose_icon (icon_theme, names, size, flags);
4548 else if (G_IS_EMBLEMED_ICON (icon))
4550 GIcon *base, *emblem;
4552 GtkIconInfo *emblem_info;
4554 if (GTK_IS_NUMERABLE_ICON (icon))
4555 _gtk_numerable_icon_set_background_icon_size (GTK_NUMERABLE_ICON (icon), size / 2);
4557 base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
4558 info = gtk_icon_theme_lookup_by_gicon (icon_theme, base, size, flags);
4561 list = g_emblemed_icon_get_emblems (G_EMBLEMED_ICON (icon));
4562 for (l = list; l; l = l->next)
4564 emblem = g_emblem_get_icon (G_EMBLEM (l->data));
4565 /* always force size for emblems */
4566 emblem_info = gtk_icon_theme_lookup_by_gicon (icon_theme, emblem, size / 2, flags | GTK_ICON_LOOKUP_FORCE_SIZE);
4568 info->emblem_infos = g_slist_prepend (info->emblem_infos, emblem_info);
4574 else if (GDK_IS_PIXBUF (icon))
4578 pixbuf = GDK_PIXBUF (icon);
4580 if ((flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0)
4582 gint width, height, max;
4586 width = gdk_pixbuf_get_width (pixbuf);
4587 height = gdk_pixbuf_get_height (pixbuf);
4588 max = MAX (width, height);
4589 scale = (gdouble) size / (gdouble) max;
4591 scaled = gdk_pixbuf_scale_simple (pixbuf,
4592 0.5 + width * scale,
4593 0.5 + height * scale,
4594 GDK_INTERP_BILINEAR);
4596 info = gtk_icon_info_new_for_pixbuf (icon_theme, scaled);
4598 g_object_unref (scaled);
4602 info = gtk_icon_info_new_for_pixbuf (icon_theme, pixbuf);
4612 * gtk_icon_info_new_for_pixbuf:
4613 * @icon_theme: a #GtkIconTheme
4614 * @pixbuf: the pixbuf to wrap in a #GtkIconInfo
4616 * Creates a #GtkIconInfo for a #GdkPixbuf.
4618 * Returns: a #GtkIconInfo
4623 gtk_icon_info_new_for_pixbuf (GtkIconTheme *icon_theme,
4628 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
4629 g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
4631 info = icon_info_new ();
4632 info->pixbuf = g_object_ref (pixbuf);
4634 info->dir_type = ICON_THEME_DIR_UNTHEMED;