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 it's 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 g_object_unref (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 g_object_ref (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 g_object_unref (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 = g_object_ref (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: (transfer full): a #GtkIconInfo object containing information
1782 * about the icon, or %NULL if the icon wasn't found.
1787 gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme,
1788 const gchar *icon_name,
1790 GtkIconLookupFlags flags)
1794 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1795 g_return_val_if_fail (icon_name != NULL, NULL);
1796 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1797 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1799 GTK_NOTE (ICONTHEME,
1800 g_print ("gtk_icon_theme_lookup_icon %s\n", icon_name));
1802 if (flags & GTK_ICON_LOOKUP_GENERIC_FALLBACK)
1809 for (p = (gchar *) icon_name; *p; p++)
1813 names = g_new (gchar *, dashes + 2);
1814 names[0] = g_strdup (icon_name);
1815 for (i = 1; i <= dashes; i++)
1817 names[i] = g_strdup (names[i - 1]);
1818 p = strrchr (names[i], '-');
1821 names[dashes + 1] = NULL;
1823 info = choose_icon (icon_theme, (const gchar **) names, size, flags);
1829 const gchar *names[2];
1831 names[0] = icon_name;
1834 info = choose_icon (icon_theme, names, size, flags);
1841 * gtk_icon_theme_choose_icon:
1842 * @icon_theme: a #GtkIconTheme
1843 * @icon_names: (array zero-terminated=1): %NULL-terminated array of
1844 * icon names to lookup
1845 * @size: desired icon size
1846 * @flags: flags modifying the behavior of the icon lookup
1848 * Looks up a named icon and returns a structure containing
1849 * information such as the filename of the icon. The icon
1850 * can then be rendered into a pixbuf using
1851 * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
1852 * combines these two steps if all you need is the pixbuf.)
1854 * If @icon_names contains more than one name, this function
1855 * tries them all in the given order before falling back to
1856 * inherited icon themes.
1858 * Return value: (transfer full): a #GtkIconInfo object containing information
1859 * about the icon, or %NULL if the icon wasn't found.
1864 gtk_icon_theme_choose_icon (GtkIconTheme *icon_theme,
1865 const gchar *icon_names[],
1867 GtkIconLookupFlags flags)
1869 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1870 g_return_val_if_fail (icon_names != NULL, NULL);
1871 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1872 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1874 return choose_icon (icon_theme, icon_names, size, flags);
1879 gtk_icon_theme_error_quark (void)
1881 return g_quark_from_static_string ("gtk-icon-theme-error-quark");
1885 * gtk_icon_theme_load_icon:
1886 * @icon_theme: a #GtkIconTheme
1887 * @icon_name: the name of the icon to lookup
1888 * @size: the desired icon size. The resulting icon may not be
1889 * exactly this size; see gtk_icon_info_load_icon().
1890 * @flags: flags modifying the behavior of the icon lookup
1891 * @error: (allow-none): Location to store error information on failure,
1894 * Looks up an icon in an icon theme, scales it to the given size
1895 * and renders it into a pixbuf. This is a convenience function;
1896 * if more details about the icon are needed, use
1897 * gtk_icon_theme_lookup_icon() followed by gtk_icon_info_load_icon().
1899 * Note that you probably want to listen for icon theme changes and
1900 * update the icon. This is usually done by connecting to the
1901 * GtkWidget::style-set signal. If for some reason you do not want to
1902 * update the icon when the icon theme changes, you should consider
1903 * using gdk_pixbuf_copy() to make a private copy of the pixbuf
1904 * returned by this function. Otherwise GTK+ may need to keep the old
1905 * icon theme loaded, which would be a waste of memory.
1907 * Return value: (transfer full): the rendered icon; this may be a
1908 * newly created icon or a new reference to an internal icon, so
1909 * you must not modify the icon. Use g_object_unref() to release
1910 * your reference to the icon. %NULL if the icon isn't found.
1915 gtk_icon_theme_load_icon (GtkIconTheme *icon_theme,
1916 const gchar *icon_name,
1918 GtkIconLookupFlags flags,
1921 GtkIconInfo *icon_info;
1922 GdkPixbuf *pixbuf = NULL;
1924 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1925 g_return_val_if_fail (icon_name != NULL, NULL);
1926 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1927 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1928 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1930 icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size,
1931 flags | GTK_ICON_LOOKUP_USE_BUILTIN);
1934 g_set_error (error, GTK_ICON_THEME_ERROR, GTK_ICON_THEME_NOT_FOUND,
1935 _("Icon '%s' not present in theme"), icon_name);
1939 pixbuf = gtk_icon_info_load_icon (icon_info, error);
1940 g_object_unref (icon_info);
1946 * gtk_icon_theme_has_icon:
1947 * @icon_theme: a #GtkIconTheme
1948 * @icon_name: the name of an icon
1950 * Checks whether an icon theme includes an icon
1951 * for a particular name.
1953 * Return value: %TRUE if @icon_theme includes an
1954 * icon for @icon_name.
1959 gtk_icon_theme_has_icon (GtkIconTheme *icon_theme,
1960 const char *icon_name)
1962 GtkIconThemePrivate *priv;
1965 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
1966 g_return_val_if_fail (icon_name != NULL, FALSE);
1968 priv = icon_theme->priv;
1970 ensure_valid_themes (icon_theme);
1972 for (l = priv->dir_mtimes; l; l = l->next)
1974 IconThemeDirMtime *dir_mtime = l->data;
1975 GtkIconCache *cache = dir_mtime->cache;
1977 if (cache && _gtk_icon_cache_has_icon (cache, icon_name))
1981 if (g_hash_table_lookup_extended (priv->all_icons,
1982 icon_name, NULL, NULL))
1985 if (_builtin_cache &&
1986 _gtk_icon_cache_has_icon (_builtin_cache, icon_name))
1989 if (icon_theme_builtin_icons &&
1990 g_hash_table_lookup_extended (icon_theme_builtin_icons,
1991 icon_name, NULL, NULL))
1998 add_size (gpointer key,
2002 gint **res_p = user_data;
2004 **res_p = GPOINTER_TO_INT (key);
2010 * gtk_icon_theme_get_icon_sizes:
2011 * @icon_theme: a #GtkIconTheme
2012 * @icon_name: the name of an icon
2014 * Returns an array of integers describing the sizes at which
2015 * the icon is available without scaling. A size of -1 means
2016 * that the icon is available in a scalable format. The array
2017 * is zero-terminated.
2019 * Return value: (array zero-terminated=1): An newly allocated array
2020 * describing the sizes at which the icon is available. The array
2021 * should be freed with g_free() when it is no longer needed.
2026 gtk_icon_theme_get_icon_sizes (GtkIconTheme *icon_theme,
2027 const char *icon_name)
2029 GList *l, *d, *icons;
2033 GtkIconThemePrivate *priv;
2035 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2037 priv = icon_theme->priv;
2039 ensure_valid_themes (icon_theme);
2041 sizes = g_hash_table_new (g_direct_hash, g_direct_equal);
2043 for (l = priv->themes; l; l = l->next)
2045 IconTheme *theme = l->data;
2046 for (d = theme->dirs; d; d = d->next)
2048 IconThemeDir *dir = d->data;
2050 if (dir->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir->size), NULL, NULL))
2053 suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
2054 if (suffix != ICON_SUFFIX_NONE)
2056 if (suffix == ICON_SUFFIX_SVG)
2057 g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL);
2059 g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL);
2064 for (d = builtin_dirs; d; d = d->next)
2066 IconThemeDir *dir = d->data;
2068 if (dir->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir->size), NULL, NULL))
2071 suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
2072 if (suffix != ICON_SUFFIX_NONE)
2074 if (suffix == ICON_SUFFIX_SVG)
2075 g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL);
2077 g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL);
2081 if (icon_theme_builtin_icons)
2083 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
2087 BuiltinIcon *icon = icons->data;
2089 g_hash_table_insert (sizes, GINT_TO_POINTER (icon->size), NULL);
2090 icons = icons->next;
2094 r = result = g_new0 (gint, g_hash_table_size (sizes) + 1);
2096 g_hash_table_foreach (sizes, add_size, &r);
2097 g_hash_table_destroy (sizes);
2103 add_key_to_hash (gpointer key,
2107 GHashTable *hash = user_data;
2109 g_hash_table_insert (hash, key, NULL);
2113 add_key_to_list (gpointer key,
2117 GList **list = user_data;
2119 *list = g_list_prepend (*list, g_strdup (key));
2123 * gtk_icon_theme_list_icons:
2124 * @icon_theme: a #GtkIconTheme
2125 * @context: (allow-none): a string identifying a particular type of
2126 * icon, or %NULL to list all icons.
2128 * Lists the icons in the current icon theme. Only a subset
2129 * of the icons can be listed by providing a context string.
2130 * The set of values for the context string is system dependent,
2131 * but will typically include such values as "Applications" and
2134 * Return value: (element-type utf8) (transfer full): a #GList list
2135 * holding the names of all the icons in the theme. You must first
2136 * free each element in the list with g_free(), then free the list
2137 * itself with g_list_free().
2142 gtk_icon_theme_list_icons (GtkIconTheme *icon_theme,
2143 const char *context)
2145 GtkIconThemePrivate *priv;
2148 GQuark context_quark;
2150 priv = icon_theme->priv;
2152 ensure_valid_themes (icon_theme);
2156 context_quark = g_quark_try_string (context);
2164 icons = g_hash_table_new (g_str_hash, g_str_equal);
2169 theme_list_icons (l->data, icons, context_quark);
2173 if (context_quark == 0)
2174 g_hash_table_foreach (priv->unthemed_icons,
2180 g_hash_table_foreach (icons,
2184 g_hash_table_destroy (icons);
2190 * gtk_icon_theme_list_contexts:
2191 * @icon_theme: a #GtkIconTheme
2193 * Gets the list of contexts available within the current
2194 * hierarchy of icon themes
2196 * Return value: (element-type utf8) (transfer full): a #GList list
2197 * holding the names of all the contexts in the theme. You must first
2198 * free each element in the list with g_free(), then free the list
2199 * itself with g_list_free().
2204 gtk_icon_theme_list_contexts (GtkIconTheme *icon_theme)
2206 GtkIconThemePrivate *priv;
2207 GHashTable *contexts;
2210 priv = icon_theme->priv;
2212 ensure_valid_themes (icon_theme);
2214 contexts = g_hash_table_new (g_str_hash, g_str_equal);
2219 theme_list_contexts (l->data, contexts);
2225 g_hash_table_foreach (contexts,
2229 g_hash_table_destroy (contexts);
2235 * gtk_icon_theme_get_example_icon_name:
2236 * @icon_theme: a #GtkIconTheme
2238 * Gets the name of an icon that is representative of the
2239 * current theme (for instance, to use when presenting
2240 * a list of themes to the user.)
2242 * Return value: the name of an example icon or %NULL.
2243 * Free with g_free().
2248 gtk_icon_theme_get_example_icon_name (GtkIconTheme *icon_theme)
2250 GtkIconThemePrivate *priv;
2254 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2256 priv = icon_theme->priv;
2258 ensure_valid_themes (icon_theme);
2265 return g_strdup (theme->example);
2275 rescan_themes (GtkIconTheme *icon_theme)
2277 GtkIconThemePrivate *priv;
2278 IconThemeDirMtime *dir_mtime;
2284 priv = icon_theme->priv;
2286 for (d = priv->dir_mtimes; d != NULL; d = d->next)
2288 dir_mtime = d->data;
2290 stat_res = g_stat (dir_mtime->dir, &stat_buf);
2292 /* dir mtime didn't change */
2293 if (stat_res == 0 &&
2294 S_ISDIR (stat_buf.st_mode) &&
2295 dir_mtime->mtime == stat_buf.st_mtime)
2297 /* didn't exist before, and still doesn't */
2298 if (dir_mtime->mtime == 0 &&
2299 (stat_res != 0 || !S_ISDIR (stat_buf.st_mode)))
2305 g_get_current_time (&tv);
2306 priv->last_stat_time = tv.tv_sec;
2312 * gtk_icon_theme_rescan_if_needed:
2313 * @icon_theme: a #GtkIconTheme
2315 * Checks to see if the icon theme has changed; if it has, any
2316 * currently cached information is discarded and will be reloaded
2317 * next time @icon_theme is accessed.
2319 * Return value: %TRUE if the icon theme has changed and needed
2325 gtk_icon_theme_rescan_if_needed (GtkIconTheme *icon_theme)
2329 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
2331 retval = rescan_themes (icon_theme);
2333 do_theme_change (icon_theme);
2339 theme_destroy (IconTheme *theme)
2341 g_free (theme->display_name);
2342 g_free (theme->comment);
2343 g_free (theme->name);
2344 g_free (theme->example);
2346 g_list_free_full (theme->dirs, (GDestroyNotify) theme_dir_destroy);
2352 theme_dir_destroy (IconThemeDir *dir)
2355 _gtk_icon_cache_unref (dir->cache);
2357 g_hash_table_destroy (dir->icons);
2360 g_hash_table_destroy (dir->icon_data);
2362 g_free (dir->subdir);
2367 theme_dir_size_difference (IconThemeDir *dir, int size, gboolean *smaller)
2372 case ICON_THEME_DIR_FIXED:
2373 *smaller = size < dir->size;
2374 return abs (size - dir->size);
2376 case ICON_THEME_DIR_SCALABLE:
2377 *smaller = size < dir->min_size;
2378 if (size < dir->min_size)
2379 return dir->min_size - size;
2380 if (size > dir->max_size)
2381 return size - dir->max_size;
2384 case ICON_THEME_DIR_THRESHOLD:
2385 min = dir->size - dir->threshold;
2386 max = dir->size + dir->threshold;
2387 *smaller = size < min;
2394 case ICON_THEME_DIR_UNTHEMED:
2395 g_assert_not_reached ();
2398 g_assert_not_reached ();
2403 string_from_suffix (IconSuffix suffix)
2407 case ICON_SUFFIX_XPM:
2409 case ICON_SUFFIX_SVG:
2411 case ICON_SUFFIX_PNG:
2414 g_assert_not_reached();
2420 suffix_from_name (const char *name)
2424 if (g_str_has_suffix (name, ".png"))
2425 retval = ICON_SUFFIX_PNG;
2426 else if (g_str_has_suffix (name, ".svg"))
2427 retval = ICON_SUFFIX_SVG;
2428 else if (g_str_has_suffix (name, ".xpm"))
2429 retval = ICON_SUFFIX_XPM;
2431 retval = ICON_SUFFIX_NONE;
2437 best_suffix (IconSuffix suffix,
2440 if ((suffix & ICON_SUFFIX_PNG) != 0)
2441 return ICON_SUFFIX_PNG;
2442 else if (allow_svg && ((suffix & ICON_SUFFIX_SVG) != 0))
2443 return ICON_SUFFIX_SVG;
2444 else if ((suffix & ICON_SUFFIX_XPM) != 0)
2445 return ICON_SUFFIX_XPM;
2447 return ICON_SUFFIX_NONE;
2452 theme_dir_get_icon_suffix (IconThemeDir *dir,
2453 const gchar *icon_name,
2454 gboolean *has_icon_file)
2460 suffix = (IconSuffix)_gtk_icon_cache_get_icon_flags (dir->cache,
2465 *has_icon_file = suffix & HAS_ICON_FILE;
2467 suffix = suffix & ~HAS_ICON_FILE;
2470 suffix = GPOINTER_TO_UINT (g_hash_table_lookup (dir->icons, icon_name));
2472 GTK_NOTE (ICONTHEME,
2473 g_print ("get_icon_suffix%s %u\n", dir->cache ? " (cached)" : "", suffix));
2478 static GtkIconInfo *
2479 theme_lookup_icon (IconTheme *theme,
2480 const char *icon_name,
2483 gboolean use_builtin)
2486 IconThemeDir *dir, *min_dir;
2488 int min_difference, difference;
2489 BuiltinIcon *closest_builtin = NULL;
2490 gboolean smaller, has_larger, match;
2493 min_difference = G_MAXINT;
2498 /* Builtin icons are logically part of the default theme and
2499 * are searched before other subdirectories of the default theme.
2501 if (use_builtin && strcmp (theme->name, DEFAULT_THEME_NAME) == 0)
2503 closest_builtin = find_builtin_icon (icon_name,
2508 if (min_difference == 0)
2509 return icon_info_new_builtin (closest_builtin);
2511 dirs = builtin_dirs;
2521 GTK_NOTE (ICONTHEME,
2522 g_print ("theme_lookup_icon dir %s\n", dir->dir));
2523 suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
2524 if (best_suffix (suffix, allow_svg) != ICON_SUFFIX_NONE)
2526 difference = theme_dir_size_difference (dir, size, &smaller);
2528 if (difference == 0)
2530 if (dir->type == ICON_THEME_DIR_SCALABLE)
2532 /* don't pick scalable if we already found
2533 * a matching non-scalable dir
2543 /* for a matching non-scalable dir keep
2544 * going and look for a closer match
2546 difference = abs (size - dir->size);
2547 if (!match || difference < min_difference)
2550 min_difference = difference;
2553 if (difference == 0)
2562 if (difference < min_difference || smaller)
2564 min_difference = difference;
2566 has_larger = smaller;
2571 if (difference < min_difference && smaller)
2573 min_difference = difference;
2582 if (l == NULL && dirs == builtin_dirs)
2591 GtkIconInfo *icon_info = icon_info_new ();
2592 gboolean has_icon_file = FALSE;
2594 suffix = theme_dir_get_icon_suffix (min_dir, icon_name, &has_icon_file);
2595 suffix = best_suffix (suffix, allow_svg);
2596 g_assert (suffix != ICON_SUFFIX_NONE);
2600 file = g_strconcat (icon_name, string_from_suffix (suffix), NULL);
2601 icon_info->filename = g_build_filename (min_dir->dir, file, NULL);
2602 icon_info->icon_file = g_file_new_for_path (icon_info->filename);
2607 icon_info->filename = NULL;
2608 icon_info->icon_file = NULL;
2611 if (min_dir->icon_data != NULL)
2612 icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
2614 if (icon_info->data == NULL && min_dir->cache != NULL)
2616 icon_info->data = _gtk_icon_cache_get_icon_data (min_dir->cache, icon_name, min_dir->subdir_index);
2617 if (icon_info->data)
2619 if (min_dir->icon_data == NULL)
2620 min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2621 g_free, (GDestroyNotify)icon_data_free);
2623 g_hash_table_replace (min_dir->icon_data, g_strdup (icon_name), icon_info->data);
2627 if (icon_info->data == NULL && has_icon_file)
2629 gchar *icon_file_name, *icon_file_path;
2631 icon_file_name = g_strconcat (icon_name, ".icon", NULL);
2632 icon_file_path = g_build_filename (min_dir->dir, icon_file_name, NULL);
2634 if (g_file_test (icon_file_path, G_FILE_TEST_IS_REGULAR))
2636 if (min_dir->icon_data == NULL)
2637 min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2638 g_free, (GDestroyNotify)icon_data_free);
2639 load_icon_data (min_dir, icon_file_path, icon_file_name);
2641 icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
2643 g_free (icon_file_name);
2644 g_free (icon_file_path);
2649 icon_info->cache_pixbuf = _gtk_icon_cache_get_icon (min_dir->cache, icon_name,
2650 min_dir->subdir_index);
2653 icon_info->dir_type = min_dir->type;
2654 icon_info->dir_size = min_dir->size;
2655 icon_info->threshold = min_dir->threshold;
2660 if (closest_builtin)
2661 return icon_info_new_builtin (closest_builtin);
2667 theme_list_icons (IconTheme *theme,
2671 GList *l = theme->dirs;
2678 if (context == dir->context ||
2683 _gtk_icon_cache_add_icons (dir->cache,
2690 g_hash_table_foreach (dir->icons,
2700 theme_list_contexts (IconTheme *theme,
2701 GHashTable *contexts)
2703 GList *l = theme->dirs;
2705 const char *context;
2711 context = g_quark_to_string (dir->context);
2712 g_hash_table_replace (contexts, (gpointer) context, NULL);
2719 load_icon_data (IconThemeDir *dir, const char *path, const char *name)
2721 GKeyFile *icon_file;
2729 GError *error = NULL;
2733 icon_file = g_key_file_new ();
2734 g_key_file_set_list_separator (icon_file, ',');
2735 g_key_file_load_from_file (icon_file, path, 0, &error);
2738 g_error_free (error);
2739 g_key_file_free (icon_file);
2744 base_name = strip_suffix (name);
2746 data = g_slice_new0 (GtkIconData);
2747 /* takes ownership of base_name */
2748 g_hash_table_replace (dir->icon_data, base_name, data);
2750 ivalues = g_key_file_get_integer_list (icon_file,
2751 "Icon Data", "EmbeddedTextRectangle",
2757 data->has_embedded_rect = TRUE;
2758 data->x0 = ivalues[0];
2759 data->y0 = ivalues[1];
2760 data->x1 = ivalues[2];
2761 data->y1 = ivalues[3];
2767 str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
2770 split = g_strsplit (str, "|", -1);
2772 data->n_attach_points = g_strv_length (split);
2773 data->attach_points = g_new (GdkPoint, data->n_attach_points);
2776 while (split[i] != NULL && i < data->n_attach_points)
2778 split_point = strchr (split[i], ',');
2783 data->attach_points[i].x = atoi (split[i]);
2784 data->attach_points[i].y = atoi (split_point);
2793 data->display_name = g_key_file_get_locale_string (icon_file,
2794 "Icon Data", "DisplayName",
2796 g_key_file_free (icon_file);
2801 scan_directory (GtkIconThemePrivate *icon_theme,
2802 IconThemeDir *dir, char *full_dir)
2807 GTK_NOTE (ICONTHEME,
2808 g_print ("scanning directory %s\n", full_dir));
2809 dir->icons = g_hash_table_new_full (g_str_hash, g_str_equal,
2812 gdir = g_dir_open (full_dir, 0, NULL);
2817 while ((name = g_dir_read_name (gdir)))
2821 IconSuffix suffix, hash_suffix;
2823 if (g_str_has_suffix (name, ".icon"))
2825 if (dir->icon_data == NULL)
2826 dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2827 g_free, (GDestroyNotify)icon_data_free);
2829 path = g_build_filename (full_dir, name, NULL);
2830 if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
2831 load_icon_data (dir, path, name);
2838 suffix = suffix_from_name (name);
2839 if (suffix == ICON_SUFFIX_NONE)
2842 base_name = strip_suffix (name);
2844 hash_suffix = GPOINTER_TO_INT (g_hash_table_lookup (dir->icons, base_name));
2845 g_hash_table_replace (icon_theme->all_icons, base_name, NULL);
2846 /* takes ownership of base_name */
2847 g_hash_table_replace (dir->icons, base_name, GUINT_TO_POINTER (hash_suffix| suffix));
2854 theme_subdir_load (GtkIconTheme *icon_theme,
2856 GKeyFile *theme_file,
2862 IconThemeDirType type;
2863 char *context_string;
2870 GError *error = NULL;
2871 IconThemeDirMtime *dir_mtime;
2873 size = g_key_file_get_integer (theme_file, subdir, "Size", &error);
2876 g_error_free (error);
2877 g_warning ("Theme directory %s of theme %s has no size field\n",
2878 subdir, theme->name);
2882 type = ICON_THEME_DIR_THRESHOLD;
2883 type_string = g_key_file_get_string (theme_file, subdir, "Type", NULL);
2886 if (strcmp (type_string, "Fixed") == 0)
2887 type = ICON_THEME_DIR_FIXED;
2888 else if (strcmp (type_string, "Scalable") == 0)
2889 type = ICON_THEME_DIR_SCALABLE;
2890 else if (strcmp (type_string, "Threshold") == 0)
2891 type = ICON_THEME_DIR_THRESHOLD;
2893 g_free (type_string);
2897 context_string = g_key_file_get_string (theme_file, subdir, "Context", NULL);
2900 context = g_quark_from_string (context_string);
2901 g_free (context_string);
2904 if (g_key_file_has_key (theme_file, subdir, "MaxSize", NULL))
2905 max_size = g_key_file_get_integer (theme_file, subdir, "MaxSize", NULL);
2909 if (g_key_file_has_key (theme_file, subdir, "MinSize", NULL))
2910 min_size = g_key_file_get_integer (theme_file, subdir, "MinSize", NULL);
2914 if (g_key_file_has_key (theme_file, subdir, "Threshold", NULL))
2915 threshold = g_key_file_get_integer (theme_file, subdir, "Threshold", NULL);
2919 for (d = icon_theme->priv->dir_mtimes; d; d = d->next)
2921 dir_mtime = (IconThemeDirMtime *)d->data;
2923 if (dir_mtime->mtime == 0)
2924 continue; /* directory doesn't exist */
2926 full_dir = g_build_filename (dir_mtime->dir, subdir, NULL);
2928 /* First, see if we have a cache for the directory */
2929 if (dir_mtime->cache != NULL || g_file_test (full_dir, G_FILE_TEST_IS_DIR))
2931 if (dir_mtime->cache == NULL)
2933 /* This will return NULL if the cache doesn't exist or is outdated */
2934 dir_mtime->cache = _gtk_icon_cache_new_for_path (dir_mtime->dir);
2937 dir = g_new (IconThemeDir, 1);
2939 dir->context = context;
2941 dir->min_size = min_size;
2942 dir->max_size = max_size;
2943 dir->threshold = threshold;
2944 dir->dir = full_dir;
2945 dir->icon_data = NULL;
2946 dir->subdir = g_strdup (subdir);
2947 if (dir_mtime->cache != NULL)
2949 dir->cache = _gtk_icon_cache_ref (dir_mtime->cache);
2950 dir->subdir_index = _gtk_icon_cache_get_directory_index (dir->cache, dir->subdir);
2955 dir->subdir_index = -1;
2956 scan_directory (icon_theme->priv, dir, full_dir);
2959 theme->dirs = g_list_prepend (theme->dirs, dir);
2967 icon_data_free (GtkIconData *icon_data)
2969 g_free (icon_data->attach_points);
2970 g_free (icon_data->display_name);
2971 g_slice_free (GtkIconData, icon_data);
2974 static GtkIconData *
2975 icon_data_dup (GtkIconData *icon_data)
2977 GtkIconData *dup = NULL;
2980 dup = g_slice_new0 (GtkIconData);
2982 if (dup->n_attach_points > 0)
2984 dup->attach_points = g_memdup (dup->attach_points,
2985 sizeof (GdkPoint) * dup->n_attach_points);
2987 dup->display_name = g_strdup (dup->display_name);
2998 static void gtk_icon_info_class_init (GtkIconInfoClass *klass);
3000 G_DEFINE_TYPE (GtkIconInfo, gtk_icon_info, G_TYPE_OBJECT)
3003 gtk_icon_info_init (GtkIconInfo *icon_info)
3005 icon_info->scale = -1.;
3008 static GtkIconInfo *
3009 icon_info_new (void)
3011 return g_object_new (GTK_TYPE_ICON_INFO, NULL);
3014 /* This only copies whatever is needed to load the pixbuf, so that we can do
3015 * a load in a thread without affecting the original IconInfo from the thread.
3017 static GtkIconInfo *
3018 icon_info_dup (GtkIconInfo *icon_info)
3023 dup = icon_info_new ();
3025 dup->filename = g_strdup (icon_info->filename);
3026 if (icon_info->icon_file)
3027 dup->icon_file = g_object_ref (icon_info->icon_file);
3028 if (icon_info->loadable)
3029 dup->loadable = g_object_ref (icon_info->loadable);
3030 if (icon_info->pixbuf)
3031 dup->pixbuf = g_object_ref (icon_info->pixbuf);
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: (skip)
3070 * @icon_info: a #GtkIconInfo
3072 * Make a copy of a #GtkIconInfo.
3074 * Return value: the new GtkIconInfo
3078 * Deprecated: 3.8: Use g_object_ref()
3081 gtk_icon_info_copy (GtkIconInfo *icon_info)
3084 g_return_val_if_fail (icon_info != NULL, NULL);
3086 return g_object_ref (icon_info);
3090 * gtk_icon_info_free: (skip)
3091 * @icon_info: a #GtkIconInfo
3093 * Free a #GtkIconInfo and associated information
3097 * Deprecated: 3.8: Use g_object_unref()
3100 gtk_icon_info_free (GtkIconInfo *icon_info)
3102 g_return_if_fail (icon_info != NULL);
3104 g_object_unref (icon_info);
3108 gtk_icon_info_finalize (GObject *object)
3110 GtkIconInfo *icon_info = (GtkIconInfo *) object;
3112 if (icon_info->in_cache)
3113 g_hash_table_remove (icon_info->in_cache->priv->info_cache, &icon_info->key);
3115 g_strfreev (icon_info->key.icon_names);
3117 g_free (icon_info->filename);
3118 g_clear_object (&icon_info->icon_file);
3120 if (icon_info->loadable)
3121 g_object_unref (icon_info->loadable);
3122 g_slist_free_full (icon_info->emblem_infos, (GDestroyNotify) g_object_unref);
3123 if (icon_info->pixbuf)
3124 g_object_unref (icon_info->pixbuf);
3125 if (icon_info->cache_pixbuf)
3126 g_object_unref (icon_info->cache_pixbuf);
3127 if (icon_info->symbolic_pixbuf_size)
3128 gtk_requisition_free (icon_info->symbolic_pixbuf_size);
3130 symbolic_pixbuf_cache_free (icon_info->symbolic_pixbuf_cache);
3132 G_OBJECT_CLASS (gtk_icon_info_parent_class)->finalize (object);
3136 gtk_icon_info_class_init (GtkIconInfoClass *klass)
3138 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3140 gobject_class->finalize = gtk_icon_info_finalize;
3144 * gtk_icon_info_get_base_size:
3145 * @icon_info: a #GtkIconInfo
3147 * Gets the base size for the icon. The base size
3148 * is a size for the icon that was specified by
3149 * the icon theme creator. This may be different
3150 * than the actual size of image; an example of
3151 * this is small emblem icons that can be attached
3152 * to a larger icon. These icons will be given
3153 * the same base size as the larger icons to which
3154 * they are attached.
3156 * Return value: the base size, or 0, if no base
3157 * size is known for the icon.
3162 gtk_icon_info_get_base_size (GtkIconInfo *icon_info)
3164 g_return_val_if_fail (icon_info != NULL, 0);
3166 return icon_info->dir_size;
3170 * gtk_icon_info_get_filename:
3171 * @icon_info: a #GtkIconInfo
3173 * Gets the filename for the icon. If the
3174 * %GTK_ICON_LOOKUP_USE_BUILTIN flag was passed
3175 * to gtk_icon_theme_lookup_icon(), there may be
3176 * no filename if a builtin icon is returned; in this
3177 * case, you should use gtk_icon_info_get_builtin_pixbuf().
3179 * Return value: (type filename): the filename for the icon, or %NULL
3180 * if gtk_icon_info_get_builtin_pixbuf() should be used instead. The
3181 * return value is owned by GTK+ and should not be modified or freed.
3186 gtk_icon_info_get_filename (GtkIconInfo *icon_info)
3188 g_return_val_if_fail (icon_info != NULL, NULL);
3190 return icon_info->filename;
3194 * gtk_icon_info_get_builtin_pixbuf:
3195 * @icon_info: a #GtkIconInfo structure
3197 * Gets the built-in image for this icon, if any. To allow
3198 * GTK+ to use built in icon images, you must pass the
3199 * %GTK_ICON_LOOKUP_USE_BUILTIN to
3200 * gtk_icon_theme_lookup_icon().
3202 * Return value: (transfer none): the built-in image pixbuf, or %NULL. No
3203 * extra reference is added to the returned pixbuf, so if
3204 * you want to keep it around, you must use g_object_ref().
3205 * The returned image must not be modified.
3210 gtk_icon_info_get_builtin_pixbuf (GtkIconInfo *icon_info)
3212 g_return_val_if_fail (icon_info != NULL, NULL);
3214 if (icon_info->filename)
3217 return icon_info->cache_pixbuf;
3220 static gboolean icon_info_ensure_scale_and_pixbuf (GtkIconInfo*, gboolean);
3222 /* Combine the icon with all emblems, the first emblem is placed
3223 * in the southeast corner. Scale emblems to be at most 3/4 of the
3224 * size of the icon itself.
3227 apply_emblems (GtkIconInfo *info)
3229 GdkPixbuf *icon = NULL;
3233 if (info->emblem_infos == NULL)
3236 if (info->emblems_applied)
3239 w = gdk_pixbuf_get_width (info->pixbuf);
3240 h = gdk_pixbuf_get_height (info->pixbuf);
3242 for (l = info->emblem_infos, pos = 0; l; l = l->next, pos++)
3244 GtkIconInfo *emblem_info = l->data;
3246 if (icon_info_ensure_scale_and_pixbuf (emblem_info, FALSE))
3248 GdkPixbuf *emblem = emblem_info->pixbuf;
3250 gint x = 0, y = 0; /* silence compiler */
3253 ew = gdk_pixbuf_get_width (emblem);
3254 eh = gdk_pixbuf_get_height (emblem);
3286 icon = gdk_pixbuf_copy (info->pixbuf);
3291 gdk_pixbuf_composite (emblem, icon, x, y, ew, eh, x, y,
3292 scale, scale, GDK_INTERP_BILINEAR, 255);
3298 g_object_unref (info->pixbuf);
3299 info->pixbuf = icon;
3302 info->emblems_applied = TRUE;
3305 /* If this returns TRUE, its safe to call
3306 icon_info_ensure_scale_and_pixbuf without blocking */
3308 icon_info_get_pixbuf_ready (GtkIconInfo *icon_info)
3310 if (icon_info->pixbuf &&
3311 (icon_info->emblem_infos == NULL || icon_info->emblems_applied))
3314 if (icon_info->load_error)
3320 /* This function contains the complicated logic for deciding
3321 * on the size at which to load the icon and loading it at
3325 icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info,
3326 gboolean scale_only)
3328 int image_width, image_height;
3329 GdkPixbuf *source_pixbuf;
3332 /* First check if we already succeeded have the necessary
3333 * information (or failed earlier)
3335 if (scale_only && icon_info->scale >= 0)
3338 if (icon_info->pixbuf)
3340 apply_emblems (icon_info);
3344 if (icon_info->load_error)
3347 /* SVG icons are a special case - we just immediately scale them
3348 * to the desired size
3350 if (icon_info->icon_file && !icon_info->loadable)
3351 icon_info->loadable = G_LOADABLE_ICON (g_file_icon_new (icon_info->icon_file));
3354 if (G_IS_FILE_ICON (icon_info->loadable))
3357 GFileInfo *file_info;
3358 const gchar *content_type;
3360 file = g_file_icon_get_file (G_FILE_ICON (icon_info->loadable));
3361 file_info = g_file_query_info (file,
3362 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
3363 G_FILE_QUERY_INFO_NONE,
3367 content_type = g_file_info_get_content_type (file_info);
3369 if (content_type && strcmp (content_type, "image/svg+xml") == 0)
3372 g_object_unref (file_info);
3378 GInputStream *stream;
3380 icon_info->scale = icon_info->desired_size / 1000.;
3385 stream = g_loadable_icon_load (icon_info->loadable,
3386 icon_info->desired_size,
3388 &icon_info->load_error);
3391 icon_info->pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3392 icon_info->desired_size,
3393 icon_info->desired_size,
3396 &icon_info->load_error);
3397 g_object_unref (stream);
3400 if (!icon_info->pixbuf)
3403 apply_emblems (icon_info);
3408 /* In many cases, the scale can be determined without actual access
3409 * to the icon file. This is generally true when we have a size
3410 * for the directory where the icon is; the image size doesn't
3411 * matter in that case.
3413 if (icon_info->forced_size)
3414 icon_info->scale = -1;
3415 else if (icon_info->dir_type == ICON_THEME_DIR_FIXED)
3416 icon_info->scale = 1.0;
3417 else if (icon_info->dir_type == ICON_THEME_DIR_THRESHOLD)
3419 if (icon_info->desired_size >= icon_info->dir_size - icon_info->threshold &&
3420 icon_info->desired_size <= icon_info->dir_size + icon_info->threshold)
3421 icon_info->scale = 1.0;
3422 else if (icon_info->dir_size > 0)
3423 icon_info->scale =(gdouble) icon_info->desired_size / icon_info->dir_size;
3425 else if (icon_info->dir_type == ICON_THEME_DIR_SCALABLE)
3427 if (icon_info->dir_size > 0)
3428 icon_info->scale = (gdouble) icon_info->desired_size / icon_info->dir_size;
3431 if (icon_info->scale >= 0. && scale_only)
3434 /* At this point, we need to actually get the icon; either from the
3435 * builtin image or by loading the file
3437 source_pixbuf = NULL;
3438 if (icon_info->cache_pixbuf)
3439 source_pixbuf = g_object_ref (icon_info->cache_pixbuf);
3442 GInputStream *stream;
3444 stream = g_loadable_icon_load (icon_info->loadable,
3445 icon_info->desired_size,
3447 &icon_info->load_error);
3450 source_pixbuf = gdk_pixbuf_new_from_stream (stream,
3452 &icon_info->load_error);
3453 g_object_unref (stream);
3460 /* Do scale calculations that depend on the image size
3462 image_width = gdk_pixbuf_get_width (source_pixbuf);
3463 image_height = gdk_pixbuf_get_height (source_pixbuf);
3465 if (icon_info->scale < 0.0)
3467 gint image_size = MAX (image_width, image_height);
3469 icon_info->scale = (gdouble)icon_info->desired_size / (gdouble)image_size;
3471 icon_info->scale = 1.0;
3473 if (icon_info->dir_type == ICON_THEME_DIR_UNTHEMED &&
3474 !icon_info->forced_size)
3475 icon_info->scale = MIN (icon_info->scale, 1.0);
3478 /* We don't short-circuit out here for scale_only, since, now
3479 * we've loaded the icon, we might as well go ahead and finish
3480 * the job. This is a bit of a waste when we scale here
3481 * and never get the final pixbuf; at the cost of a bit of
3482 * extra complexity, we could keep the source pixbuf around
3483 * but not actually scale it until needed.
3485 if (icon_info->scale == 1.0)
3486 icon_info->pixbuf = source_pixbuf;
3489 icon_info->pixbuf = gdk_pixbuf_scale_simple (source_pixbuf,
3490 0.5 + image_width * icon_info->scale,
3491 0.5 + image_height * icon_info->scale,
3492 GDK_INTERP_BILINEAR);
3493 g_object_unref (source_pixbuf);
3496 apply_emblems (icon_info);
3502 proxy_pixbuf_destroy (guchar *pixels, gpointer data)
3504 GtkIconInfo *icon_info = data;
3505 GtkIconTheme *icon_theme = icon_info->in_cache;
3507 g_assert (icon_info->proxy_pixbuf != NULL);
3508 icon_info->proxy_pixbuf = NULL;
3510 /* Keep it alive a bit longer */
3511 if (icon_theme != NULL)
3512 ensure_in_lru_cache (icon_theme, icon_info);
3514 g_object_unref (icon_info);
3518 * gtk_icon_info_load_icon:
3519 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3520 * @error: (allow-none): location to store error information on failure,
3523 * Renders an icon previously looked up in an icon theme using
3524 * gtk_icon_theme_lookup_icon(); the size will be based on the size
3525 * passed to gtk_icon_theme_lookup_icon(). Note that the resulting
3526 * pixbuf may not be exactly this size; an icon theme may have icons
3527 * that differ slightly from their nominal sizes, and in addition GTK+
3528 * will avoid scaling icons that it considers sufficiently close to the
3529 * requested size or for which the source image would have to be scaled
3530 * up too far. (This maintains sharpness.). This behaviour can be changed
3531 * by passing the %GTK_ICON_LOOKUP_FORCE_SIZE flag when obtaining
3532 * the #GtkIconInfo. If this flag has been specified, the pixbuf
3533 * returned by this function will be scaled to the exact size.
3535 * Return value: (transfer full): the rendered icon; this may be a newly
3536 * created icon or a new reference to an internal icon, so you must
3537 * not modify the icon. Use g_object_unref() to release your reference
3543 gtk_icon_info_load_icon (GtkIconInfo *icon_info,
3546 g_return_val_if_fail (icon_info != NULL, NULL);
3547 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3549 if (!icon_info_ensure_scale_and_pixbuf (icon_info, FALSE))
3551 if (icon_info->load_error)
3552 g_propagate_error (error, icon_info->load_error);
3554 g_set_error_literal (error,
3555 GTK_ICON_THEME_ERROR,
3556 GTK_ICON_THEME_NOT_FOUND,
3557 _("Failed to load icon"));
3562 /* Instead of returning the pixbuf directly we
3563 return a proxy to it that we don't own (but that
3564 shares the data with the one we own). This way
3565 we can know when it is freed and ensure the
3566 IconInfo is alive (and thus cached) while
3567 the pixbuf is still alive. */
3569 if (icon_info->proxy_pixbuf != NULL)
3570 return g_object_ref (icon_info->proxy_pixbuf);
3572 icon_info->proxy_pixbuf =
3573 gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (icon_info->pixbuf),
3574 gdk_pixbuf_get_colorspace (icon_info->pixbuf),
3575 gdk_pixbuf_get_has_alpha (icon_info->pixbuf),
3576 gdk_pixbuf_get_bits_per_sample (icon_info->pixbuf),
3577 gdk_pixbuf_get_width (icon_info->pixbuf),
3578 gdk_pixbuf_get_height (icon_info->pixbuf),
3579 gdk_pixbuf_get_rowstride (icon_info->pixbuf),
3580 proxy_pixbuf_destroy,
3581 g_object_ref (icon_info));
3583 return icon_info->proxy_pixbuf;
3587 load_icon_thread (GTask *task,
3588 gpointer source_object,
3590 GCancellable *cancellable)
3592 GtkIconInfo *dup = task_data;
3594 icon_info_ensure_scale_and_pixbuf (dup, FALSE);
3595 g_task_return_pointer (task, NULL, NULL);
3599 * gtk_icon_info_load_icon_async:
3600 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3601 * @cancellable: (allow-none): optional #GCancellable object,
3603 * @callback: (scope async): a #GAsyncReadyCallback to call when the
3604 * request is satisfied
3605 * @user_data: (closure): the data to pass to callback function
3607 * Asynchronously load, render and scale an icon previously looked up
3608 * from the icon theme using gtk_icon_theme_lookup_icon().
3610 * For more details, see gtk_icon_info_load_icon() which is the synchronous
3611 * version of this call.
3616 gtk_icon_info_load_icon_async (GtkIconInfo *icon_info,
3617 GCancellable *cancellable,
3618 GAsyncReadyCallback callback,
3624 GError *error = NULL;
3626 task = g_task_new (icon_info, cancellable, callback, user_data);
3628 if (icon_info_get_pixbuf_ready (icon_info))
3630 pixbuf = gtk_icon_info_load_icon (icon_info, &error);
3632 g_task_return_error (task, error);
3634 g_task_return_pointer (task, pixbuf, g_object_unref);
3635 g_object_unref (task);
3639 dup = icon_info_dup (icon_info);
3640 g_task_set_task_data (task, dup, g_object_unref);
3641 g_task_run_in_thread (task, load_icon_thread);
3642 g_object_unref (task);
3647 * gtk_icon_info_load_icon_finish:
3648 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3649 * @res: a #GAsyncResult
3650 * @error: (allow-none): location to store error information on failure,
3653 * Finishes an async icon load, see gtk_icon_info_load_icon_async().
3655 * Return value: (transfer full): the rendered icon; this may be a newly
3656 * created icon or a new reference to an internal icon, so you must
3657 * not modify the icon. Use g_object_unref() to release your reference
3663 gtk_icon_info_load_icon_finish (GtkIconInfo *icon_info,
3664 GAsyncResult *result,
3667 GTask *task = G_TASK (result);
3670 g_return_val_if_fail (g_task_is_valid (result, icon_info), NULL);
3672 dup = g_task_get_task_data (task);
3673 if (dup == NULL || g_task_had_error (task))
3674 return g_task_propagate_pointer (task, error);
3676 /* We ran the thread and it was not cancelled */
3678 /* Check if someone else updated the icon_info in between */
3679 if (!icon_info_get_pixbuf_ready (icon_info))
3681 /* If not, copy results from dup back to icon_info */
3683 icon_info->emblems_applied = dup->emblems_applied;
3684 icon_info->scale = dup->scale;
3685 g_clear_object (&icon_info->pixbuf);
3687 icon_info->pixbuf = g_object_ref (dup->pixbuf);
3688 g_clear_error (&icon_info->load_error);
3689 if (dup->load_error)
3690 icon_info->load_error = g_error_copy (dup->load_error);
3693 g_assert (icon_info_get_pixbuf_ready (icon_info));
3695 /* This is now guaranteed to not block */
3696 return gtk_icon_info_load_icon (icon_info, error);
3700 gdk_color_to_css (GdkColor *color)
3702 return g_strdup_printf ("rgb(%d,%d,%d)",
3709 gdk_rgba_to_css (const GdkRGBA *color)
3711 /* drop alpha for now, since librsvg does not understand rgba() */
3712 return g_strdup_printf ("rgb(%d,%d,%d)",
3713 (gint)(color->red * 255),
3714 (gint)(color->green * 255),
3715 (gint)(color->blue * 255));
3719 proxy_symbolic_pixbuf_destroy (guchar *pixels, gpointer data)
3721 GtkIconInfo *icon_info = data;
3722 GtkIconTheme *icon_theme = icon_info->in_cache;
3723 SymbolicPixbufCache *symbolic_cache;
3725 for (symbolic_cache = icon_info->symbolic_pixbuf_cache;
3726 symbolic_cache != NULL;
3727 symbolic_cache = symbolic_cache->next)
3729 if (symbolic_cache->proxy_pixbuf != NULL &&
3730 gdk_pixbuf_get_pixels (symbolic_cache->proxy_pixbuf) == pixels)
3734 g_assert (symbolic_cache != NULL);
3735 g_assert (symbolic_cache->proxy_pixbuf != NULL);
3737 symbolic_cache->proxy_pixbuf = NULL;
3739 /* Keep it alive a bit longer */
3740 if (icon_theme != NULL)
3741 ensure_in_lru_cache (icon_theme, icon_info);
3743 g_object_unref (icon_info);
3747 symbolic_cache_get_proxy (SymbolicPixbufCache *symbolic_cache,
3748 GtkIconInfo *icon_info)
3750 if (symbolic_cache->proxy_pixbuf)
3751 return g_object_ref (symbolic_cache->proxy_pixbuf);
3753 symbolic_cache->proxy_pixbuf =
3754 gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (symbolic_cache->pixbuf),
3755 gdk_pixbuf_get_colorspace (symbolic_cache->pixbuf),
3756 gdk_pixbuf_get_has_alpha (symbolic_cache->pixbuf),
3757 gdk_pixbuf_get_bits_per_sample (symbolic_cache->pixbuf),
3758 gdk_pixbuf_get_width (symbolic_cache->pixbuf),
3759 gdk_pixbuf_get_height (symbolic_cache->pixbuf),
3760 gdk_pixbuf_get_rowstride (symbolic_cache->pixbuf),
3761 proxy_symbolic_pixbuf_destroy,
3762 g_object_ref (icon_info));
3764 return symbolic_cache->proxy_pixbuf;
3768 _gtk_icon_info_load_symbolic_internal (GtkIconInfo *icon_info,
3770 const GdkRGBA *success_color,
3771 const GdkRGBA *warning_color,
3772 const GdkRGBA *error_color,
3776 GInputStream *stream;
3783 gchar *width, *height, *uri;
3784 SymbolicPixbufCache *symbolic_cache;
3788 symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
3789 fg, success_color, warning_color, error_color);
3791 return symbolic_cache_get_proxy (symbolic_cache, icon_info);
3794 /* css_fg can't possibly have failed, otherwise
3795 * that would mean we have a broken style */
3796 g_return_val_if_fail (fg != NULL, NULL);
3798 css_fg = gdk_rgba_to_css (fg);
3800 css_success = css_warning = css_error = NULL;
3803 css_warning = gdk_rgba_to_css (warning_color);
3806 css_error = gdk_rgba_to_css (error_color);
3809 css_success = gdk_rgba_to_css (success_color);
3813 GdkColor success_default_color = { 0, 0x4e00, 0x9a00, 0x0600 };
3814 css_success = gdk_color_to_css (&success_default_color);
3818 GdkColor warning_default_color = { 0, 0xf500, 0x7900, 0x3e00 };
3819 css_warning = gdk_color_to_css (&warning_default_color);
3823 GdkColor error_default_color = { 0, 0xcc00, 0x0000, 0x0000 };
3824 css_error = gdk_color_to_css (&error_default_color);
3827 if (!icon_info->symbolic_pixbuf_size)
3829 stream = G_INPUT_STREAM (g_file_read (icon_info->icon_file, NULL, error));
3834 /* Fetch size from the original icon */
3835 pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
3836 g_object_unref (stream);
3841 icon_info->symbolic_pixbuf_size = gtk_requisition_new ();
3842 icon_info->symbolic_pixbuf_size->width = gdk_pixbuf_get_width (pixbuf);
3843 icon_info->symbolic_pixbuf_size->height = gdk_pixbuf_get_height (pixbuf);
3844 g_object_unref (pixbuf);
3847 width = g_strdup_printf ("%d", icon_info->symbolic_pixbuf_size->width);
3848 height = g_strdup_printf ("%d", icon_info->symbolic_pixbuf_size->height);
3849 uri = g_file_get_uri (icon_info->icon_file);
3851 data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
3852 "<svg version=\"1.1\"\n"
3853 " xmlns=\"http://www.w3.org/2000/svg\"\n"
3854 " xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n"
3855 " width=\"", width, "\"\n"
3856 " height=\"", height, "\">\n"
3857 " <style type=\"text/css\">\n"
3859 " fill: ", css_fg," !important;\n"
3862 " fill: ", css_warning, " !important;\n"
3865 " fill: ", css_error ," !important;\n"
3868 " fill: ", css_success, " !important;\n"
3871 " <xi:include href=\"", uri, "\"/>\n"
3875 g_free (css_warning);
3877 g_free (css_success);
3882 stream = g_memory_input_stream_new_from_data (data, -1, g_free);
3883 pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3884 icon_info->desired_size,
3885 icon_info->desired_size,
3889 g_object_unref (stream);
3895 icon_info->symbolic_pixbuf_cache =
3896 symbolic_pixbuf_cache_new (pixbuf, fg, success_color, warning_color, error_color,
3897 icon_info->symbolic_pixbuf_cache);
3898 g_object_unref (pixbuf);
3899 return symbolic_cache_get_proxy (icon_info->symbolic_pixbuf_cache, icon_info);
3909 * gtk_icon_info_load_symbolic:
3910 * @icon_info: a #GtkIconInfo
3911 * @fg: a #GdkRGBA representing the foreground color of the icon
3912 * @success_color: (allow-none): a #GdkRGBA representing the warning color
3913 * of the icon or %NULL to use the default color
3914 * @warning_color: (allow-none): a #GdkRGBA representing the warning color
3915 * of the icon or %NULL to use the default color
3916 * @error_color: (allow-none): a #GdkRGBA representing the error color
3917 * of the icon or %NULL to use the default color (allow-none)
3918 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3919 * loaded icon was a symbolic one and whether the @fg color was
3921 * @error: (allow-none): location to store error information on failure,
3924 * Loads an icon, modifying it to match the system colours for the foreground,
3925 * success, warning and error colors provided. If the icon is not a symbolic
3926 * one, the function will return the result from gtk_icon_info_load_icon().
3928 * This allows loading symbolic icons that will match the system theme.
3930 * Unless you are implementing a widget, you will want to use
3931 * g_themed_icon_new_with_default_fallbacks() to load the icon.
3933 * As implementation details, the icon loaded needs to be of SVG type,
3934 * contain the "symbolic" term as the last component of the icon name,
3935 * and use the 'fg', 'success', 'warning' and 'error' CSS styles in the
3938 * See the <ulink url="http://www.freedesktop.org/wiki/SymbolicIcons">Symbolic Icons spec</ulink>
3939 * for more information about symbolic icons.
3941 * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
3946 gtk_icon_info_load_symbolic (GtkIconInfo *icon_info,
3948 const GdkRGBA *success_color,
3949 const GdkRGBA *warning_color,
3950 const GdkRGBA *error_color,
3951 gboolean *was_symbolic,
3955 gboolean is_symbolic;
3957 g_return_val_if_fail (icon_info != NULL, NULL);
3958 g_return_val_if_fail (fg != NULL, NULL);
3961 if (icon_info->icon_file)
3962 icon_uri = g_file_get_uri (icon_info->icon_file);
3964 is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
3968 *was_symbolic = is_symbolic;
3971 return gtk_icon_info_load_icon (icon_info, error);
3973 return _gtk_icon_info_load_symbolic_internal (icon_info,
3975 warning_color, error_color,
3981 * gtk_icon_info_load_symbolic_for_context:
3982 * @icon_info: a #GtkIconInfo
3983 * @context: a #GtkStyleContext
3984 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3985 * loaded icon was a symbolic one and whether the @fg color was
3987 * @error: (allow-none): location to store error information on failure,
3990 * Loads an icon, modifying it to match the system colors for the foreground,
3991 * success, warning and error colors provided. If the icon is not a symbolic
3992 * one, the function will return the result from gtk_icon_info_load_icon().
3993 * This function uses the regular foreground color and the symbolic colors
3994 * with the names "success_color", "warning_color" and "error_color" from
3997 * This allows loading symbolic icons that will match the system theme.
3999 * See gtk_icon_info_load_symbolic() for more details.
4001 * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
4006 gtk_icon_info_load_symbolic_for_context (GtkIconInfo *icon_info,
4007 GtkStyleContext *context,
4008 gboolean *was_symbolic,
4011 GdkRGBA *color = NULL;
4014 GdkRGBA success_color;
4015 GdkRGBA *success_colorp;
4016 GdkRGBA warning_color;
4017 GdkRGBA *warning_colorp;
4018 GdkRGBA error_color;
4019 GdkRGBA *error_colorp;
4020 GtkStateFlags state;
4022 gboolean is_symbolic;
4024 g_return_val_if_fail (icon_info != NULL, NULL);
4025 g_return_val_if_fail (context != NULL, NULL);
4028 if (icon_info->icon_file)
4029 icon_uri = g_file_get_uri (icon_info->icon_file);
4031 is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
4035 *was_symbolic = is_symbolic;
4038 return gtk_icon_info_load_icon (icon_info, error);
4040 fgp = success_colorp = warning_colorp = error_colorp = NULL;
4042 state = gtk_style_context_get_state (context);
4043 gtk_style_context_get (context, state, "color", &color, NULL);
4048 gdk_rgba_free (color);
4051 if (gtk_style_context_lookup_color (context, "success_color", &success_color))
4052 success_colorp = &success_color;
4054 if (gtk_style_context_lookup_color (context, "warning_color", &warning_color))
4055 warning_colorp = &warning_color;
4057 if (gtk_style_context_lookup_color (context, "error_color", &error_color))
4058 error_colorp = &error_color;
4060 return _gtk_icon_info_load_symbolic_internal (icon_info,
4061 fgp, success_colorp,
4062 warning_colorp, error_colorp,
4068 gboolean is_symbolic;
4072 GdkRGBA success_color;
4073 gboolean success_color_set;
4074 GdkRGBA warning_color;
4075 gboolean warning_color_set;
4076 GdkRGBA error_color;
4077 gboolean error_color_set;
4078 } AsyncSymbolicData;
4081 async_symbolic_data_free (AsyncSymbolicData *data)
4084 g_object_unref (data->dup);
4085 g_slice_free (AsyncSymbolicData, data);
4089 async_load_no_symbolic_cb (GObject *source_object,
4093 GtkIconInfo *icon_info = GTK_ICON_INFO (source_object);
4094 GTask *task = user_data;
4095 GError *error = NULL;
4098 pixbuf = gtk_icon_info_load_icon_finish (icon_info, res, &error);
4100 g_task_return_error (task, error);
4102 g_task_return_pointer (task, pixbuf, g_object_unref);
4103 g_object_unref (task);
4107 load_symbolic_icon_thread (GTask *task,
4108 gpointer source_object,
4110 GCancellable *cancellable)
4112 AsyncSymbolicData *data = task_data;
4118 _gtk_icon_info_load_symbolic_internal (data->dup,
4119 data->fg_set ? &data->fg : NULL,
4120 data->success_color_set ? &data->success_color : NULL,
4121 data->warning_color_set ? &data->warning_color : NULL,
4122 data->error_color_set ? &data->error_color : NULL,
4126 g_task_return_error (task, error);
4128 g_task_return_pointer (task, pixbuf, g_object_unref);
4132 * gtk_icon_info_load_symbolic_async:
4133 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
4134 * @fg: a #GdkRGBA representing the foreground color of the icon
4135 * @success_color: (allow-none): a #GdkRGBA representing the warning color
4136 * of the icon or %NULL to use the default color
4137 * @warning_color: (allow-none): a #GdkRGBA representing the warning color
4138 * of the icon or %NULL to use the default color
4139 * @error_color: (allow-none): a #GdkRGBA representing the error color
4140 * of the icon or %NULL to use the default color (allow-none)
4141 * @cancellable: (allow-none): optional #GCancellable object,
4143 * @callback: (scope async): a #GAsyncReadyCallback to call when the
4144 * request is satisfied
4145 * @user_data: (closure): the data to pass to callback function
4147 * Asynchronously load, render and scale a symbolic icon previously looked up
4148 * from the icon theme using gtk_icon_theme_lookup_icon().
4150 * For more details, see gtk_icon_info_load_symbolic() which is the synchronous
4151 * version of this call.
4156 gtk_icon_info_load_symbolic_async (GtkIconInfo *icon_info,
4158 const GdkRGBA *success_color,
4159 const GdkRGBA *warning_color,
4160 const GdkRGBA *error_color,
4161 GCancellable *cancellable,
4162 GAsyncReadyCallback callback,
4166 AsyncSymbolicData *data;
4168 SymbolicPixbufCache *symbolic_cache;
4171 g_return_if_fail (icon_info != NULL);
4172 g_return_if_fail (fg != NULL);
4174 task = g_task_new (icon_info, cancellable, callback, user_data);
4176 data = g_slice_new0 (AsyncSymbolicData);
4177 g_task_set_task_data (task, data, (GDestroyNotify) async_symbolic_data_free);
4180 if (icon_info->icon_file)
4181 icon_uri = g_file_get_uri (icon_info->icon_file);
4183 data->is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
4186 if (!data->is_symbolic)
4188 gtk_icon_info_load_icon_async (icon_info, cancellable, async_load_no_symbolic_cb, g_object_ref (task));
4192 symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
4193 fg, success_color, warning_color, error_color);
4196 pixbuf = symbolic_cache_get_proxy (symbolic_cache, icon_info);
4197 g_task_return_pointer (task, pixbuf, g_object_unref);
4204 data->fg_set = TRUE;
4209 data->success_color = *success_color;
4210 data->success_color_set = TRUE;
4215 data->warning_color = *warning_color;
4216 data->warning_color_set = TRUE;
4221 data->error_color = *error_color;
4222 data->error_color_set = TRUE;
4225 data->dup = icon_info_dup (icon_info);
4226 g_task_run_in_thread (task, load_symbolic_icon_thread);
4229 g_object_unref (task);
4233 * gtk_icon_info_load_symbolic_finish:
4234 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
4235 * @res: a #GAsyncResult
4236 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
4237 * loaded icon was a symbolic one and whether the @fg color was
4239 * @error: (allow-none): location to store error information on failure,
4242 * Finishes an async icon load, see gtk_icon_info_load_symbolic_async().
4244 * Return value: (transfer full): the rendered icon; this may be a newly
4245 * created icon or a new reference to an internal icon, so you must
4246 * not modify the icon. Use g_object_unref() to release your reference
4252 gtk_icon_info_load_symbolic_finish (GtkIconInfo *icon_info,
4253 GAsyncResult *result,
4254 gboolean *was_symbolic,
4257 GTask *task = G_TASK (result);
4258 AsyncSymbolicData *data = g_task_get_task_data (task);
4259 SymbolicPixbufCache *symbolic_cache;
4263 *was_symbolic = data->is_symbolic;
4265 if (data->dup && !g_task_had_error (task))
4267 pixbuf = g_task_propagate_pointer (task, NULL);
4269 g_assert (pixbuf != NULL); /* we checked for !had_error above */
4271 symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
4272 data->fg_set ? &data->fg : NULL,
4273 data->success_color_set ? &data->success_color : NULL,
4274 data->warning_color_set ? &data->warning_color : NULL,
4275 data->error_color_set ? &data->error_color : NULL);
4277 if (symbolic_cache == NULL)
4279 symbolic_cache = icon_info->symbolic_pixbuf_cache =
4280 symbolic_pixbuf_cache_new (pixbuf,
4281 data->fg_set ? &data->fg : NULL,
4282 data->success_color_set ? &data->success_color : NULL,
4283 data->warning_color_set ? &data->warning_color : NULL,
4284 data->error_color_set ? &data->error_color : NULL,
4285 icon_info->symbolic_pixbuf_cache);
4288 g_object_unref (pixbuf);
4290 return symbolic_cache_get_proxy (symbolic_cache, icon_info);
4293 return g_task_propagate_pointer (task, error);
4297 * gtk_icon_info_load_symbolic_for_context_async:
4298 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
4299 * @context: a #GtkStyleContext
4300 * @cancellable: (allow-none): optional #GCancellable object,
4302 * @callback: (scope async): a #GAsyncReadyCallback to call when the
4303 * request is satisfied
4304 * @user_data: (closure): the data to pass to callback function
4306 * Asynchronously load, render and scale a symbolic icon previously looked up
4307 * from the icon theme using gtk_icon_theme_lookup_icon().
4309 * For more details, see gtk_icon_info_load_symbolic_for_context() which is the synchronous
4310 * version of this call.
4315 gtk_icon_info_load_symbolic_for_context_async (GtkIconInfo *icon_info,
4316 GtkStyleContext *context,
4317 GCancellable *cancellable,
4318 GAsyncReadyCallback callback,
4321 GdkRGBA *color = NULL;
4324 GdkRGBA success_color;
4325 GdkRGBA *success_colorp;
4326 GdkRGBA warning_color;
4327 GdkRGBA *warning_colorp;
4328 GdkRGBA error_color;
4329 GdkRGBA *error_colorp;
4330 GtkStateFlags state;
4332 g_return_if_fail (icon_info != NULL);
4333 g_return_if_fail (context != NULL);
4335 fgp = success_colorp = warning_colorp = error_colorp = NULL;
4337 state = gtk_style_context_get_state (context);
4338 gtk_style_context_get (context, state, "color", &color, NULL);
4343 gdk_rgba_free (color);
4346 if (gtk_style_context_lookup_color (context, "success_color", &success_color))
4347 success_colorp = &success_color;
4349 if (gtk_style_context_lookup_color (context, "warning_color", &warning_color))
4350 warning_colorp = &warning_color;
4352 if (gtk_style_context_lookup_color (context, "error_color", &error_color))
4353 error_colorp = &error_color;
4355 gtk_icon_info_load_symbolic_async (icon_info,
4356 fgp, success_colorp,
4357 warning_colorp, error_colorp,
4358 cancellable, callback, user_data);
4362 * gtk_icon_info_load_symbolic_for_context_finish:
4363 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
4364 * @res: a #GAsyncResult
4365 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
4366 * loaded icon was a symbolic one and whether the @fg color was
4368 * @error: (allow-none): location to store error information on failure,
4371 * Finishes an async icon load, see gtk_icon_info_load_symbolic_for_context_async().
4373 * Return value: (transfer full): the rendered icon; this may be a newly
4374 * created icon or a new reference to an internal icon, so you must
4375 * not modify the icon. Use g_object_unref() to release your reference
4381 gtk_icon_info_load_symbolic_for_context_finish (GtkIconInfo *icon_info,
4382 GAsyncResult *result,
4383 gboolean *was_symbolic,
4386 return gtk_icon_info_load_symbolic_finish (icon_info, result, was_symbolic, error);
4390 color_to_rgba (GdkColor *color, GdkRGBA *rgba)
4392 rgba->red = color->red / 65535.0;
4393 rgba->green = color->green / 65535.0;
4394 rgba->blue = color->blue / 65535.0;
4400 * gtk_icon_info_load_symbolic_for_style:
4401 * @icon_info: a #GtkIconInfo
4402 * @style: a #GtkStyle to take the colors from
4403 * @state: the widget state to use for colors
4404 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
4405 * loaded icon was a symbolic one and whether the @fg color was
4407 * @error: (allow-none): location to store error information on failure,
4410 * Loads an icon, modifying it to match the system colours for the foreground,
4411 * success, warning and error colors provided. If the icon is not a symbolic
4412 * one, the function will return the result from gtk_icon_info_load_icon().
4414 * This allows loading symbolic icons that will match the system theme.
4416 * See gtk_icon_info_load_symbolic() for more details.
4418 * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
4422 * Deprecated: 3.0: Use gtk_icon_info_load_symbolic_for_context() instead
4425 gtk_icon_info_load_symbolic_for_style (GtkIconInfo *icon_info,
4428 gboolean *was_symbolic,
4433 GdkRGBA success_color;
4434 GdkRGBA *success_colorp;
4435 GdkRGBA warning_color;
4436 GdkRGBA *warning_colorp;
4437 GdkRGBA error_color;
4438 GdkRGBA *error_colorp;
4440 gboolean is_symbolic;
4442 g_return_val_if_fail (icon_info != NULL, NULL);
4443 g_return_val_if_fail (style != NULL, NULL);
4446 if (icon_info->icon_file)
4447 icon_uri = g_file_get_uri (icon_info->icon_file);
4449 is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
4453 *was_symbolic = is_symbolic;
4456 return gtk_icon_info_load_icon (icon_info, error);
4458 color_to_rgba (&style->fg[state], &fg);
4460 success_colorp = warning_colorp = error_colorp = NULL;
4462 if (gtk_style_lookup_color (style, "success_color", &color))
4463 success_colorp = color_to_rgba (&color, &success_color);
4465 if (gtk_style_lookup_color (style, "warning_color", &color))
4466 warning_colorp = color_to_rgba (&color, &warning_color);
4468 if (gtk_style_lookup_color (style, "error_color", &color))
4469 error_colorp = color_to_rgba (&color, &error_color);
4471 return _gtk_icon_info_load_symbolic_internal (icon_info,
4472 &fg, success_colorp,
4473 warning_colorp, error_colorp,
4479 * gtk_icon_info_set_raw_coordinates:
4480 * @icon_info: a #GtkIconInfo
4481 * @raw_coordinates: whether the coordinates of embedded rectangles
4482 * and attached points should be returned in their original
4485 * Sets whether the coordinates returned by gtk_icon_info_get_embedded_rect()
4486 * and gtk_icon_info_get_attach_points() should be returned in their
4487 * original form as specified in the icon theme, instead of scaled
4488 * appropriately for the pixbuf returned by gtk_icon_info_load_icon().
4490 * Raw coordinates are somewhat strange; they are specified to be with
4491 * respect to the unscaled pixmap for PNG and XPM icons, but for SVG
4492 * icons, they are in a 1000x1000 coordinate space that is scaled
4493 * to the final size of the icon. You can determine if the icon is an SVG
4494 * icon by using gtk_icon_info_get_filename(), and seeing if it is non-%NULL
4495 * and ends in '.svg'.
4497 * This function is provided primarily to allow compatibility wrappers
4498 * for older API's, and is not expected to be useful for applications.
4503 gtk_icon_info_set_raw_coordinates (GtkIconInfo *icon_info,
4504 gboolean raw_coordinates)
4506 g_return_if_fail (icon_info != NULL);
4508 icon_info->raw_coordinates = raw_coordinates != FALSE;
4511 /* Scale coordinates from the icon data prior to returning
4515 icon_info_scale_point (GtkIconInfo *icon_info,
4521 if (icon_info->raw_coordinates)
4528 if (!icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4531 *x_out = 0.5 + x * icon_info->scale;
4532 *y_out = 0.5 + y * icon_info->scale;
4539 * gtk_icon_info_get_embedded_rect:
4540 * @icon_info: a #GtkIconInfo
4541 * @rectangle: (out): #GdkRectangle in which to store embedded
4542 * rectangle coordinates; coordinates are only stored
4543 * when this function returns %TRUE.
4545 * Gets the coordinates of a rectangle within the icon
4546 * that can be used for display of information such
4547 * as a preview of the contents of a text file.
4548 * See gtk_icon_info_set_raw_coordinates() for further
4549 * information about the coordinate system.
4551 * Return value: %TRUE if the icon has an embedded rectangle
4556 gtk_icon_info_get_embedded_rect (GtkIconInfo *icon_info,
4557 GdkRectangle *rectangle)
4559 g_return_val_if_fail (icon_info != NULL, FALSE);
4561 if (icon_info->data && icon_info->data->has_embedded_rect &&
4562 icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4564 gint scaled_x0, scaled_y0;
4565 gint scaled_x1, scaled_y1;
4569 icon_info_scale_point (icon_info,
4570 icon_info->data->x0, icon_info->data->y0,
4571 &scaled_x0, &scaled_y0);
4572 icon_info_scale_point (icon_info,
4573 icon_info->data->x1, icon_info->data->y1,
4574 &scaled_x1, &scaled_y1);
4576 rectangle->x = scaled_x0;
4577 rectangle->y = scaled_y0;
4578 rectangle->width = scaled_x1 - rectangle->x;
4579 rectangle->height = scaled_y1 - rectangle->y;
4589 * gtk_icon_info_get_attach_points:
4590 * @icon_info: a #GtkIconInfo
4591 * @points: (allow-none) (array length=n_points) (out): location to store pointer to an array of points, or %NULL
4592 * free the array of points with g_free().
4593 * @n_points: (allow-none): location to store the number of points in @points, or %NULL
4595 * Fetches the set of attach points for an icon. An attach point
4596 * is a location in the icon that can be used as anchor points for attaching
4597 * emblems or overlays to the icon.
4599 * Return value: %TRUE if there are any attach points for the icon.
4604 gtk_icon_info_get_attach_points (GtkIconInfo *icon_info,
4608 g_return_val_if_fail (icon_info != NULL, FALSE);
4610 if (icon_info->data && icon_info->data->n_attach_points &&
4611 icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4617 *points = g_new (GdkPoint, icon_info->data->n_attach_points);
4618 for (i = 0; i < icon_info->data->n_attach_points; i++)
4619 icon_info_scale_point (icon_info,
4620 icon_info->data->attach_points[i].x,
4621 icon_info->data->attach_points[i].y,
4627 *n_points = icon_info->data->n_attach_points;
4643 * gtk_icon_info_get_display_name:
4644 * @icon_info: a #GtkIconInfo
4646 * Gets the display name for an icon. A display name is a
4647 * string to be used in place of the icon name in a user
4648 * visible context like a list of icons.
4650 * Return value: the display name for the icon or %NULL, if
4651 * the icon doesn't have a specified display name. This value
4652 * is owned @icon_info and must not be modified or free.
4657 gtk_icon_info_get_display_name (GtkIconInfo *icon_info)
4659 g_return_val_if_fail (icon_info != NULL, NULL);
4661 if (icon_info->data)
4662 return icon_info->data->display_name;
4673 * gtk_icon_theme_add_builtin_icon:
4674 * @icon_name: the name of the icon to register
4675 * @size: the size at which to register the icon (different
4676 * images can be registered for the same icon name
4677 * at different sizes.)
4678 * @pixbuf: #GdkPixbuf that contains the image to use
4681 * Registers a built-in icon for icon theme lookups. The idea
4682 * of built-in icons is to allow an application or library
4683 * that uses themed icons to function requiring files to
4684 * be present in the file system. For instance, the default
4685 * images for all of GTK+'s stock icons are registered
4688 * In general, if you use gtk_icon_theme_add_builtin_icon()
4689 * you should also install the icon in the icon theme, so
4690 * that the icon is generally available.
4692 * This function will generally be used with pixbufs loaded
4693 * via gdk_pixbuf_new_from_inline().
4698 gtk_icon_theme_add_builtin_icon (const gchar *icon_name,
4702 BuiltinIcon *default_icon;
4706 g_return_if_fail (icon_name != NULL);
4707 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
4709 if (!icon_theme_builtin_icons)
4710 icon_theme_builtin_icons = g_hash_table_new (g_str_hash, g_str_equal);
4712 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
4714 key = g_strdup (icon_name);
4716 key = (gpointer)icon_name; /* Won't get stored */
4718 default_icon = g_new (BuiltinIcon, 1);
4719 default_icon->size = size;
4720 default_icon->pixbuf = g_object_ref (pixbuf);
4721 icons = g_slist_prepend (icons, default_icon);
4723 /* Replaces value, leaves key untouched
4725 g_hash_table_insert (icon_theme_builtin_icons, key, icons);
4728 /* Look up a builtin icon; the min_difference_p and
4729 * has_larger_p out parameters allow us to combine
4730 * this lookup with searching through the actual directories
4731 * of the "hicolor" icon theme. See theme_lookup_icon()
4732 * for how they are used.
4734 static BuiltinIcon *
4735 find_builtin_icon (const gchar *icon_name,
4737 gint *min_difference_p,
4738 gboolean *has_larger_p)
4740 GSList *icons = NULL;
4741 gint min_difference = G_MAXINT;
4742 gboolean has_larger = FALSE;
4743 BuiltinIcon *min_icon = NULL;
4745 if (!icon_theme_builtin_icons)
4748 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
4752 BuiltinIcon *default_icon = icons->data;
4753 int min, max, difference;
4756 min = default_icon->size - 2;
4757 max = default_icon->size + 2;
4758 smaller = size < min;
4760 difference = min - size;
4761 else if (size > max)
4762 difference = size - max;
4766 if (difference == 0)
4768 min_icon = default_icon;
4774 if (difference < min_difference || smaller)
4776 min_difference = difference;
4777 min_icon = default_icon;
4778 has_larger = smaller;
4783 if (difference < min_difference && smaller)
4785 min_difference = difference;
4786 min_icon = default_icon;
4790 icons = icons->next;
4793 if (min_difference_p)
4794 *min_difference_p = min_difference;
4796 *has_larger_p = has_larger;
4802 _gtk_icon_theme_check_reload (GdkDisplay *display)
4806 GtkIconTheme *icon_theme;
4808 n_screens = gdk_display_get_n_screens (display);
4810 for (i = 0; i < n_screens; i++)
4812 screen = gdk_display_get_screen (display, i);
4814 icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
4817 icon_theme->priv->check_reload = TRUE;
4818 ensure_valid_themes (icon_theme);
4819 icon_theme->priv->check_reload = FALSE;
4826 * gtk_icon_theme_lookup_by_gicon:
4827 * @icon_theme: a #GtkIconTheme
4828 * @icon: the #GIcon to look up
4829 * @size: desired icon size
4830 * @flags: flags modifying the behavior of the icon lookup
4832 * Looks up an icon and returns a structure containing
4833 * information such as the filename of the icon.
4834 * The icon can then be rendered into a pixbuf using
4835 * gtk_icon_info_load_icon().
4837 * Return value: (transfer full): a #GtkIconInfo structure containing
4838 * information about the icon, or %NULL if the icon
4839 * wasn't found. Unref with g_object_unref()
4844 gtk_icon_theme_lookup_by_gicon (GtkIconTheme *icon_theme,
4847 GtkIconLookupFlags flags)
4851 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
4852 g_return_val_if_fail (G_IS_ICON (icon), NULL);
4854 if (G_IS_LOADABLE_ICON (icon))
4856 info = icon_info_new ();
4857 info->loadable = G_LOADABLE_ICON (g_object_ref (icon));
4859 if (G_IS_FILE_ICON (icon))
4861 GFile *file = g_file_icon_get_file (G_FILE_ICON (icon));
4864 info->icon_file = g_object_ref (file);
4865 info->filename = g_file_get_path (file);
4869 info->dir_type = ICON_THEME_DIR_UNTHEMED;
4870 info->dir_size = size;
4871 info->desired_size = size;
4872 info->threshold = 2;
4873 info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
4877 else if (G_IS_THEMED_ICON (icon))
4879 const gchar **names;
4881 names = (const gchar **)g_themed_icon_get_names (G_THEMED_ICON (icon));
4882 info = gtk_icon_theme_choose_icon (icon_theme, names, size, flags);
4886 else if (G_IS_EMBLEMED_ICON (icon))
4888 GIcon *base, *emblem;
4890 GtkIconInfo *base_info, *emblem_info;
4892 if (GTK_IS_NUMERABLE_ICON (icon))
4893 _gtk_numerable_icon_set_background_icon_size (GTK_NUMERABLE_ICON (icon), size / 2);
4895 base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
4896 base_info = gtk_icon_theme_lookup_by_gicon (icon_theme, base, size, flags);
4899 info = icon_info_dup (base_info);
4900 g_object_unref (base_info);
4902 list = g_emblemed_icon_get_emblems (G_EMBLEMED_ICON (icon));
4903 for (l = list; l; l = l->next)
4905 emblem = g_emblem_get_icon (G_EMBLEM (l->data));
4906 /* always force size for emblems */
4907 emblem_info = gtk_icon_theme_lookup_by_gicon (icon_theme, emblem, size / 2, flags | GTK_ICON_LOOKUP_FORCE_SIZE);
4909 info->emblem_infos = g_slist_prepend (info->emblem_infos, emblem_info);
4917 else if (GDK_IS_PIXBUF (icon))
4921 pixbuf = GDK_PIXBUF (icon);
4923 if ((flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0)
4925 gint width, height, max;
4929 width = gdk_pixbuf_get_width (pixbuf);
4930 height = gdk_pixbuf_get_height (pixbuf);
4931 max = MAX (width, height);
4932 scale = (gdouble) size / (gdouble) max;
4934 scaled = gdk_pixbuf_scale_simple (pixbuf,
4935 0.5 + width * scale,
4936 0.5 + height * scale,
4937 GDK_INTERP_BILINEAR);
4939 info = gtk_icon_info_new_for_pixbuf (icon_theme, scaled);
4941 g_object_unref (scaled);
4945 info = gtk_icon_info_new_for_pixbuf (icon_theme, pixbuf);
4955 * gtk_icon_info_new_for_pixbuf:
4956 * @icon_theme: a #GtkIconTheme
4957 * @pixbuf: the pixbuf to wrap in a #GtkIconInfo
4959 * Creates a #GtkIconInfo for a #GdkPixbuf.
4961 * Return value: (transfer full): a #GtkIconInfo
4966 gtk_icon_info_new_for_pixbuf (GtkIconTheme *icon_theme,
4971 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
4972 g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
4974 info = icon_info_new ();
4975 info->pixbuf = g_object_ref (pixbuf);
4977 info->dir_type = ICON_THEME_DIR_UNTHEMED;