1 /* GtkIconTheme - a loader for icon themes
2 * gtk-icon-theme.c Copyright (C) 2002, 2003 Red Hat, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 #include <sys/types.h>
29 #include <glib/gstdio.h>
33 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
35 #define WIN32_MEAN_AND_LEAN
37 #include "win32/gdkwin32.h"
38 #endif /* G_OS_WIN32 */
40 #include "gtkicontheme.h"
42 #include "gtkiconfactory.h"
43 #include "gtkiconcache.h"
44 #include "gtkbuiltincache.h"
47 #include "gtknumerableiconprivate.h"
48 #include "gtksettings.h"
49 #include "gtkprivate.h"
52 #undef GDK_DEPRECATED_FOR
53 #define GDK_DEPRECATED
54 #define GDK_DEPRECATED_FOR(f)
56 #include "deprecated/gtkstyle.h"
60 * SECTION:gtkicontheme
61 * @Short_description: Looking up icons by name
62 * @Title: GtkIconTheme
64 * #GtkIconTheme provides a facility for looking up icons by name
65 * and size. The main reason for using a name rather than simply
66 * providing a filename is to allow different icons to be used
67 * depending on what <firstterm>icon theme</firstterm> is selected
68 * by the user. The operation of icon themes on Linux and Unix
70 * url="http://www.freedesktop.org/Standards/icon-theme-spec">Icon
71 * Theme Specification</ulink>. There is a default icon theme,
72 * named <literal>hicolor</literal> where applications should install
73 * their icons, but more additional application themes can be
74 * installed as operating system vendors and users choose.
76 * Named icons are similar to the <xref linkend="gtk3-Themeable-Stock-Images"/>
77 * facility, and the distinction between the two may be a bit confusing.
78 * A few things to keep in mind:
81 * Stock images usually are used in conjunction with
82 * <xref linkend="gtk3-Stock-Items"/>, such as %GTK_STOCK_OK or
83 * %GTK_STOCK_OPEN. Named icons are easier to set up and therefore
84 * are more useful for new icons that an application wants to
85 * add, such as application icons or window icons.
88 * Stock images can only be loaded at the symbolic sizes defined
89 * by the #GtkIconSize enumeration, or by custom sizes defined
90 * by gtk_icon_size_register(), while named icons are more flexible
91 * and any pixel size can be specified.
94 * Because stock images are closely tied to stock items, and thus
95 * to actions in the user interface, stock images may come in
96 * multiple variants for different widget states or writing
100 * A good rule of thumb is that if there is a stock image for what
101 * you want to use, use it, otherwise use a named icon. It turns
102 * out that internally stock images are generally defined in
103 * terms of one or more named icons. (An example of the
104 * more than one case is icons that depend on writing direction;
105 * %GTK_STOCK_GO_FORWARD uses the two themed icons
106 * "gtk-stock-go-forward-ltr" and "gtk-stock-go-forward-rtl".)
108 * In many cases, named themes are used indirectly, via #GtkImage
109 * or stock items, rather than directly, but looking up icons
110 * directly is also simple. The #GtkIconTheme object acts
111 * as a database of all the icons in the current theme. You
112 * can create new #GtkIconTheme objects, but its much more
113 * efficient to use the standard icon theme for the #GdkScreen
114 * so that the icon information is shared with other people
115 * looking up icons. In the case where the default screen is
116 * being used, looking up an icon can be as simple as:
119 * GError *error = NULL;
120 * GtkIconTheme *icon_theme;
123 * icon_theme = gtk_icon_theme_get_default ();
124 * pixbuf = gtk_icon_theme_load_icon (icon_theme,
125 * "my-icon-name", // icon name
131 * g_warning ("Couldn't load icon: %s", error->message);
132 * g_error_free (error);
137 * g_object_unref (pixbuf);
144 #define DEFAULT_THEME_NAME "hicolor"
148 ICON_THEME_DIR_FIXED,
149 ICON_THEME_DIR_SCALABLE,
150 ICON_THEME_DIR_THRESHOLD,
151 ICON_THEME_DIR_UNTHEMED
154 /* In reverse search order: */
157 ICON_SUFFIX_NONE = 0,
158 ICON_SUFFIX_XPM = 1 << 0,
159 ICON_SUFFIX_SVG = 1 << 1,
160 ICON_SUFFIX_PNG = 1 << 2,
161 HAS_ICON_FILE = 1 << 3
164 #define INFO_CACHE_LRU_SIZE 32
166 #define DEBUG_CACHE(args) g_print args
168 #define DEBUG_CACHE(args)
171 struct _GtkIconThemePrivate
173 GHashTable *info_cache;
174 GList *info_cache_lru;
176 gchar *current_theme;
177 gchar *fallback_theme;
179 gint search_path_len;
181 guint custom_theme : 1;
182 guint is_screen_singleton : 1;
183 guint pixbuf_supports_svg : 1;
184 guint themes_valid : 1;
185 guint check_reload : 1;
186 guint loading_themes : 1;
188 /* A list of all the themes needed to look up icons.
189 * In search order, without duplicates
192 GHashTable *unthemed_icons;
194 /* Note: The keys of this hashtable are owned by the
195 * themedir and unthemed hashtables.
197 GHashTable *all_icons;
199 /* GdkScreen for the icon theme (may be NULL)
203 /* time when we last stat:ed for theme changes */
204 glong last_stat_time;
207 gulong reset_styles_idle;
213 GtkIconLookupFlags flags;
216 typedef struct _SymbolicPixbufCache SymbolicPixbufCache;
218 struct _SymbolicPixbufCache {
220 GdkPixbuf *proxy_pixbuf;
222 GdkRGBA success_color;
223 GdkRGBA warning_color;
225 SymbolicPixbufCache *next;
228 struct _GtkIconInfoClass
230 GObjectClass parent_class;
235 GObject parent_instance;
237 /* Information about the source
240 GtkIconTheme *in_cache;
244 GLoadableIcon *loadable;
245 GSList *emblem_infos;
247 /* Cache pixbuf (if there is any) */
248 GdkPixbuf *cache_pixbuf;
252 /* Information about the directory where
253 * the source was found
255 IconThemeDirType dir_type;
259 /* Parameters influencing the scaled icon
262 guint raw_coordinates : 1;
263 guint forced_size : 1;
264 guint emblems_applied : 1;
266 /* Cached information if we go ahead and try to load
270 GdkPixbuf *proxy_pixbuf;
274 SymbolicPixbufCache *symbolic_pixbuf_cache;
276 GtkRequisition *symbolic_pixbuf_size;
286 /* In search order */
292 IconThemeDirType type;
307 GHashTable *icon_data;
313 char *no_svg_filename;
325 time_t mtime; /* 0 == not existing or not a dir */
330 static void gtk_icon_theme_finalize (GObject *object);
331 static void theme_dir_destroy (IconThemeDir *dir);
333 static void theme_destroy (IconTheme *theme);
334 static GtkIconInfo *theme_lookup_icon (IconTheme *theme,
335 const char *icon_name,
338 gboolean use_default_icons);
339 static void theme_list_icons (IconTheme *theme,
342 static void theme_list_contexts (IconTheme *theme,
343 GHashTable *contexts);
344 static void theme_subdir_load (GtkIconTheme *icon_theme,
346 GKeyFile *theme_file,
348 static void do_theme_change (GtkIconTheme *icon_theme);
350 static void blow_themes (GtkIconTheme *icon_themes);
351 static gboolean rescan_themes (GtkIconTheme *icon_themes);
353 static GtkIconData *icon_data_dup (GtkIconData *icon_data);
354 static void icon_data_free (GtkIconData *icon_data);
355 static void load_icon_data (IconThemeDir *dir,
359 static IconSuffix theme_dir_get_icon_suffix (IconThemeDir *dir,
360 const gchar *icon_name,
361 gboolean *has_icon_file);
364 static GtkIconInfo *icon_info_new (void);
365 static GtkIconInfo *icon_info_new_builtin (BuiltinIcon *icon);
367 static IconSuffix suffix_from_name (const char *name);
369 static BuiltinIcon *find_builtin_icon (const gchar *icon_name,
371 gint *min_difference_p,
372 gboolean *has_larger_p);
373 static void remove_from_lru_cache (GtkIconTheme *icon_theme,
374 GtkIconInfo *icon_info);
376 static guint signal_changed = 0;
378 static GHashTable *icon_theme_builtin_icons;
380 /* also used in gtkiconfactory.c */
381 GtkIconCache *_builtin_cache = NULL;
382 static GList *builtin_dirs = NULL;
385 icon_info_key_hash (gconstpointer _key)
387 const IconInfoKey *key = _key;
390 for (i = 0; key->icon_names[i] != NULL; i++)
391 h ^= g_str_hash (key->icon_names[i]);
393 h ^= key->size * 0x10001;
394 h ^= key->flags * 0x1000010;
400 icon_info_key_equal (gconstpointer _a,
403 const IconInfoKey *a = _a;
404 const IconInfoKey *b = _b;
407 if (a->size != b->size)
410 if (a->flags != b->flags)
414 a->icon_names[i] != NULL &&
415 b->icon_names[i] != NULL; i++)
417 if (strcmp (a->icon_names[i], b->icon_names[i]) != 0)
421 return a->icon_names[i] == NULL && b->icon_names[i] == NULL;
424 G_DEFINE_TYPE (GtkIconTheme, gtk_icon_theme, G_TYPE_OBJECT)
427 * gtk_icon_theme_new:
429 * Creates a new icon theme object. Icon theme objects are used
430 * to lookup up an icon by name in a particular icon theme.
431 * Usually, you'll want to use gtk_icon_theme_get_default()
432 * or gtk_icon_theme_get_for_screen() rather than creating
433 * a new icon theme object for scratch.
435 * Return value: the newly created #GtkIconTheme object.
440 gtk_icon_theme_new (void)
442 return g_object_new (GTK_TYPE_ICON_THEME, NULL);
446 * gtk_icon_theme_get_default:
448 * Gets the icon theme for the default screen. See
449 * gtk_icon_theme_get_for_screen().
451 * Return value: (transfer none): A unique #GtkIconTheme associated with
452 * the default screen. This icon theme is associated with
453 * the screen and can be used as long as the screen
454 * is open. Do not ref or unref it.
459 gtk_icon_theme_get_default (void)
461 return gtk_icon_theme_get_for_screen (gdk_screen_get_default ());
465 * gtk_icon_theme_get_for_screen:
466 * @screen: a #GdkScreen
468 * Gets the icon theme object associated with @screen; if this
469 * function has not previously been called for the given
470 * screen, a new icon theme object will be created and
471 * associated with the screen. Icon theme objects are
472 * fairly expensive to create, so using this function
473 * is usually a better choice than calling than gtk_icon_theme_new()
474 * and setting the screen yourself; by using this function
475 * a single icon theme object will be shared between users.
477 * Return value: (transfer none): A unique #GtkIconTheme associated with
478 * the given screen. This icon theme is associated with
479 * the screen and can be used as long as the screen
480 * is open. Do not ref or unref it.
485 gtk_icon_theme_get_for_screen (GdkScreen *screen)
487 GtkIconTheme *icon_theme;
489 g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
491 icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
494 GtkIconThemePrivate *priv;
496 icon_theme = gtk_icon_theme_new ();
497 gtk_icon_theme_set_screen (icon_theme, screen);
499 priv = icon_theme->priv;
500 priv->is_screen_singleton = TRUE;
502 g_object_set_data (G_OBJECT (screen), I_("gtk-icon-theme"), icon_theme);
509 gtk_icon_theme_class_init (GtkIconThemeClass *klass)
511 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
513 gobject_class->finalize = gtk_icon_theme_finalize;
516 * GtkIconTheme::changed:
517 * @icon_theme: the icon theme
519 * Emitted when the current icon theme is switched or GTK+ detects
520 * that a change has occurred in the contents of the current
523 signal_changed = g_signal_new (I_("changed"),
524 G_TYPE_FROM_CLASS (klass),
526 G_STRUCT_OFFSET (GtkIconThemeClass, changed),
528 g_cclosure_marshal_VOID__VOID,
531 g_type_class_add_private (klass, sizeof (GtkIconThemePrivate));
535 /* Callback when the display that the icon theme is attached to
536 * is closed; unset the screen, and if it's the unique theme
537 * for the screen, drop the reference
540 display_closed (GdkDisplay *display,
542 GtkIconTheme *icon_theme)
544 GtkIconThemePrivate *priv = icon_theme->priv;
545 GdkScreen *screen = priv->screen;
546 gboolean was_screen_singleton = priv->is_screen_singleton;
548 if (was_screen_singleton)
550 g_object_set_data (G_OBJECT (screen), I_("gtk-icon-theme"), NULL);
551 priv->is_screen_singleton = FALSE;
554 gtk_icon_theme_set_screen (icon_theme, NULL);
556 if (was_screen_singleton)
558 g_object_unref (icon_theme);
563 update_current_theme (GtkIconTheme *icon_theme)
565 #define theme_changed(_old, _new) \
566 ((_old && !_new) || (!_old && _new) || \
567 (_old && _new && strcmp (_old, _new) != 0))
568 GtkIconThemePrivate *priv = icon_theme->priv;
570 if (!priv->custom_theme)
573 gchar *fallback_theme = NULL;
574 gboolean changed = FALSE;
578 GtkSettings *settings = gtk_settings_get_for_screen (priv->screen);
579 g_object_get (settings,
580 "gtk-icon-theme-name", &theme,
581 "gtk-fallback-icon-theme", &fallback_theme, NULL);
584 /* ensure that the current theme (even when just the default)
585 * is searched before any fallback theme
587 if (!theme && fallback_theme)
588 theme = g_strdup (DEFAULT_THEME_NAME);
590 if (theme_changed (priv->current_theme, theme))
592 g_free (priv->current_theme);
593 priv->current_theme = theme;
599 if (theme_changed (priv->fallback_theme, fallback_theme))
601 g_free (priv->fallback_theme);
602 priv->fallback_theme = fallback_theme;
606 g_free (fallback_theme);
609 do_theme_change (icon_theme);
614 /* Callback when the icon theme GtkSetting changes
617 theme_changed (GtkSettings *settings,
619 GtkIconTheme *icon_theme)
621 update_current_theme (icon_theme);
625 unset_screen (GtkIconTheme *icon_theme)
627 GtkIconThemePrivate *priv = icon_theme->priv;
628 GtkSettings *settings;
633 settings = gtk_settings_get_for_screen (priv->screen);
634 display = gdk_screen_get_display (priv->screen);
636 g_signal_handlers_disconnect_by_func (display,
637 (gpointer) display_closed,
639 g_signal_handlers_disconnect_by_func (settings,
640 (gpointer) theme_changed,
648 * gtk_icon_theme_set_screen:
649 * @icon_theme: a #GtkIconTheme
650 * @screen: a #GdkScreen
652 * Sets the screen for an icon theme; the screen is used
653 * to track the user's currently configured icon theme,
654 * which might be different for different screens.
659 gtk_icon_theme_set_screen (GtkIconTheme *icon_theme,
662 GtkIconThemePrivate *priv;
663 GtkSettings *settings;
666 g_return_if_fail (GTK_ICON_THEME (icon_theme));
667 g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
669 priv = icon_theme->priv;
671 unset_screen (icon_theme);
675 display = gdk_screen_get_display (screen);
676 settings = gtk_settings_get_for_screen (screen);
678 priv->screen = screen;
680 g_signal_connect (display, "closed",
681 G_CALLBACK (display_closed), icon_theme);
682 g_signal_connect (settings, "notify::gtk-icon-theme-name",
683 G_CALLBACK (theme_changed), icon_theme);
684 g_signal_connect (settings, "notify::gtk-fallback-icon-theme-name",
685 G_CALLBACK (theme_changed), icon_theme);
688 update_current_theme (icon_theme);
691 /* Checks whether a loader for SVG files has been registered
695 pixbuf_supports_svg (void)
699 static gint found_svg = -1;
704 formats = gdk_pixbuf_get_formats ();
707 for (tmp_list = formats; tmp_list && !found_svg; tmp_list = tmp_list->next)
709 gchar **mime_types = gdk_pixbuf_format_get_mime_types (tmp_list->data);
712 for (mime_type = mime_types; *mime_type && !found_svg; mime_type++)
714 if (strcmp (*mime_type, "image/svg") == 0)
718 g_strfreev (mime_types);
721 g_slist_free (formats);
726 /* The icon info was removed from the icon_info_hash hash table */
728 icon_info_uncached (GtkIconInfo *icon_info)
730 GtkIconTheme *icon_theme = icon_info->in_cache;
732 DEBUG_CACHE (("removing %p (%s %d 0x%x) from cache (icon_them: %p) (cache size %d)\n",
734 g_strjoinv (",", icon_info->key.icon_names),
735 icon_info->key.size, icon_info->key.flags,
737 icon_theme != NULL ? g_hash_table_size (icon_theme->priv->info_cache) : 0));
739 icon_info->in_cache = NULL;
741 if (icon_theme != NULL)
742 remove_from_lru_cache (icon_theme, icon_info);
746 gtk_icon_theme_init (GtkIconTheme *icon_theme)
748 GtkIconThemePrivate *priv;
749 const gchar * const *xdg_data_dirs;
752 priv = G_TYPE_INSTANCE_GET_PRIVATE (icon_theme,
754 GtkIconThemePrivate);
755 icon_theme->priv = priv;
757 priv->info_cache = g_hash_table_new_full (icon_info_key_hash, icon_info_key_equal, NULL,
758 (GDestroyNotify)icon_info_uncached);
760 priv->custom_theme = FALSE;
762 xdg_data_dirs = g_get_system_data_dirs ();
763 for (i = 0; xdg_data_dirs[i]; i++) ;
765 priv->search_path_len = 2 * i + 2;
767 priv->search_path = g_new (char *, priv->search_path_len);
770 priv->search_path[i++] = g_build_filename (g_get_user_data_dir (), "icons", NULL);
771 priv->search_path[i++] = g_build_filename (g_get_home_dir (), ".icons", NULL);
773 for (j = 0; xdg_data_dirs[j]; j++)
774 priv->search_path[i++] = g_build_filename (xdg_data_dirs[j], "icons", NULL);
776 for (j = 0; xdg_data_dirs[j]; j++)
777 priv->search_path[i++] = g_build_filename (xdg_data_dirs[j], "pixmaps", NULL);
779 priv->themes_valid = FALSE;
781 priv->unthemed_icons = NULL;
783 priv->pixbuf_supports_svg = pixbuf_supports_svg ();
787 free_dir_mtime (IconThemeDirMtime *dir_mtime)
789 if (dir_mtime->cache)
790 _gtk_icon_cache_unref (dir_mtime->cache);
792 g_free (dir_mtime->dir);
793 g_slice_free (IconThemeDirMtime, dir_mtime);
798 reset_styles_idle (gpointer user_data)
800 GtkIconTheme *icon_theme;
801 GtkIconThemePrivate *priv;
803 icon_theme = GTK_ICON_THEME (user_data);
804 priv = icon_theme->priv;
806 if (priv->screen && priv->is_screen_singleton)
807 gtk_style_context_reset_widgets (priv->screen);
809 priv->reset_styles_idle = 0;
815 do_theme_change (GtkIconTheme *icon_theme)
817 GtkIconThemePrivate *priv = icon_theme->priv;
819 g_hash_table_remove_all (priv->info_cache);
821 if (!priv->themes_valid)
825 g_print ("change to icon theme \"%s\"\n", priv->current_theme));
826 blow_themes (icon_theme);
827 g_signal_emit (icon_theme, signal_changed, 0);
829 if (!priv->reset_styles_idle)
830 priv->reset_styles_idle =
831 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2,
832 reset_styles_idle, icon_theme, NULL);
836 blow_themes (GtkIconTheme *icon_theme)
838 GtkIconThemePrivate *priv = icon_theme->priv;
840 if (priv->themes_valid)
842 g_hash_table_destroy (priv->all_icons);
843 g_list_free_full (priv->themes, (GDestroyNotify) theme_destroy);
844 g_list_free_full (priv->dir_mtimes, (GDestroyNotify) free_dir_mtime);
845 g_hash_table_destroy (priv->unthemed_icons);
848 priv->unthemed_icons = NULL;
849 priv->dir_mtimes = NULL;
850 priv->all_icons = NULL;
851 priv->themes_valid = FALSE;
855 gtk_icon_theme_finalize (GObject *object)
857 GtkIconTheme *icon_theme;
858 GtkIconThemePrivate *priv;
861 icon_theme = GTK_ICON_THEME (object);
862 priv = icon_theme->priv;
864 g_hash_table_destroy (priv->info_cache);
865 g_assert (priv->info_cache_lru == NULL);
867 if (priv->reset_styles_idle)
869 g_source_remove (priv->reset_styles_idle);
870 priv->reset_styles_idle = 0;
873 unset_screen (icon_theme);
875 g_free (priv->current_theme);
876 priv->current_theme = NULL;
878 for (i = 0; i < priv->search_path_len; i++)
879 g_free (priv->search_path[i]);
881 g_free (priv->search_path);
882 priv->search_path = NULL;
884 blow_themes (icon_theme);
886 G_OBJECT_CLASS (gtk_icon_theme_parent_class)->finalize (object);
890 * gtk_icon_theme_set_search_path:
891 * @icon_theme: a #GtkIconTheme
892 * @path: (array length=n_elements) (element-type filename): array of
893 * directories that are searched for icon themes
894 * @n_elements: number of elements in @path.
896 * Sets the search path for the icon theme object. When looking
897 * for an icon theme, GTK+ will search for a subdirectory of
898 * one or more of the directories in @path with the same name
899 * as the icon theme. (Themes from multiple of the path elements
900 * are combined to allow themes to be extended by adding icons
901 * in the user's home directory.)
903 * In addition if an icon found isn't found either in the current
904 * icon theme or the default icon theme, and an image file with
905 * the right name is found directly in one of the elements of
906 * @path, then that image will be used for the icon name.
907 * (This is legacy feature, and new icons should be put
908 * into the default icon theme, which is called DEFAULT_THEME_NAME,
909 * rather than directly on the icon path.)
914 gtk_icon_theme_set_search_path (GtkIconTheme *icon_theme,
918 GtkIconThemePrivate *priv;
921 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
923 priv = icon_theme->priv;
924 for (i = 0; i < priv->search_path_len; i++)
925 g_free (priv->search_path[i]);
927 g_free (priv->search_path);
929 priv->search_path = g_new (gchar *, n_elements);
930 priv->search_path_len = n_elements;
932 for (i = 0; i < priv->search_path_len; i++)
933 priv->search_path[i] = g_strdup (path[i]);
935 do_theme_change (icon_theme);
940 * gtk_icon_theme_get_search_path:
941 * @icon_theme: a #GtkIconTheme
942 * @path: (allow-none) (array length=n_elements) (element-type filename) (out):
943 * location to store a list of icon theme path directories or %NULL.
944 * The stored value should be freed with g_strfreev().
945 * @n_elements: location to store number of elements in @path, or %NULL
947 * Gets the current search path. See gtk_icon_theme_set_search_path().
952 gtk_icon_theme_get_search_path (GtkIconTheme *icon_theme,
956 GtkIconThemePrivate *priv;
959 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
961 priv = icon_theme->priv;
964 *n_elements = priv->search_path_len;
968 *path = g_new (gchar *, priv->search_path_len + 1);
969 for (i = 0; i < priv->search_path_len; i++)
970 (*path)[i] = g_strdup (priv->search_path[i]);
976 * gtk_icon_theme_append_search_path:
977 * @icon_theme: a #GtkIconTheme
978 * @path: (type filename): directory name to append to the icon path
980 * Appends a directory to the search path.
981 * See gtk_icon_theme_set_search_path().
986 gtk_icon_theme_append_search_path (GtkIconTheme *icon_theme,
989 GtkIconThemePrivate *priv;
991 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
992 g_return_if_fail (path != NULL);
994 priv = icon_theme->priv;
996 priv->search_path_len++;
998 priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
999 priv->search_path[priv->search_path_len-1] = g_strdup (path);
1001 do_theme_change (icon_theme);
1005 * gtk_icon_theme_prepend_search_path:
1006 * @icon_theme: a #GtkIconTheme
1007 * @path: (type filename): directory name to prepend to the icon path
1009 * Prepends a directory to the search path.
1010 * See gtk_icon_theme_set_search_path().
1015 gtk_icon_theme_prepend_search_path (GtkIconTheme *icon_theme,
1018 GtkIconThemePrivate *priv;
1021 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
1022 g_return_if_fail (path != NULL);
1024 priv = icon_theme->priv;
1026 priv->search_path_len++;
1027 priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
1029 for (i = priv->search_path_len - 1; i > 0; i--)
1030 priv->search_path[i] = priv->search_path[i - 1];
1032 priv->search_path[0] = g_strdup (path);
1034 do_theme_change (icon_theme);
1038 * gtk_icon_theme_set_custom_theme:
1039 * @icon_theme: a #GtkIconTheme
1040 * @theme_name: (allow-none): name of icon theme to use instead of
1041 * configured theme, or %NULL to unset a previously set custom theme
1043 * Sets the name of the icon theme that the #GtkIconTheme object uses
1044 * overriding system configuration. This function cannot be called
1045 * on the icon theme objects returned from gtk_icon_theme_get_default()
1046 * and gtk_icon_theme_get_for_screen().
1051 gtk_icon_theme_set_custom_theme (GtkIconTheme *icon_theme,
1052 const gchar *theme_name)
1054 GtkIconThemePrivate *priv;
1056 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
1058 priv = icon_theme->priv;
1060 g_return_if_fail (!priv->is_screen_singleton);
1062 if (theme_name != NULL)
1064 priv->custom_theme = TRUE;
1065 if (!priv->current_theme || strcmp (theme_name, priv->current_theme) != 0)
1067 g_free (priv->current_theme);
1068 priv->current_theme = g_strdup (theme_name);
1070 do_theme_change (icon_theme);
1075 if (priv->custom_theme)
1077 priv->custom_theme = FALSE;
1079 update_current_theme (icon_theme);
1085 insert_theme (GtkIconTheme *icon_theme, const char *theme_name)
1091 GtkIconThemePrivate *priv;
1092 IconTheme *theme = NULL;
1094 GKeyFile *theme_file;
1095 GError *error = NULL;
1096 IconThemeDirMtime *dir_mtime;
1099 priv = icon_theme->priv;
1101 for (l = priv->themes; l != NULL; l = l->next)
1104 if (strcmp (theme->name, theme_name) == 0)
1108 for (i = 0; i < priv->search_path_len; i++)
1110 path = g_build_filename (priv->search_path[i],
1113 dir_mtime = g_slice_new (IconThemeDirMtime);
1114 dir_mtime->cache = NULL;
1115 dir_mtime->dir = path;
1116 if (g_stat (path, &stat_buf) == 0 && S_ISDIR (stat_buf.st_mode))
1117 dir_mtime->mtime = stat_buf.st_mtime;
1119 dir_mtime->mtime = 0;
1121 priv->dir_mtimes = g_list_prepend (priv->dir_mtimes, dir_mtime);
1123 priv->dir_mtimes = g_list_reverse (priv->dir_mtimes);
1126 for (i = 0; i < priv->search_path_len && !theme_file; i++)
1128 path = g_build_filename (priv->search_path[i],
1132 if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
1134 theme_file = g_key_file_new ();
1135 g_key_file_set_list_separator (theme_file, ',');
1136 g_key_file_load_from_file (theme_file, path, 0, &error);
1139 g_key_file_free (theme_file);
1141 g_error_free (error);
1148 if (theme_file || strcmp (theme_name, DEFAULT_THEME_NAME) == 0)
1150 theme = g_new0 (IconTheme, 1);
1151 theme->name = g_strdup (theme_name);
1152 priv->themes = g_list_prepend (priv->themes, theme);
1155 if (theme_file == NULL)
1158 theme->display_name =
1159 g_key_file_get_locale_string (theme_file, "Icon Theme", "Name", NULL, NULL);
1160 if (!theme->display_name)
1161 g_warning ("Theme file for %s has no name\n", theme_name);
1163 dirs = g_key_file_get_string_list (theme_file, "Icon Theme", "Directories", NULL, NULL);
1166 g_warning ("Theme file for %s has no directories\n", theme_name);
1167 priv->themes = g_list_remove (priv->themes, theme);
1168 g_free (theme->name);
1169 g_free (theme->display_name);
1171 g_key_file_free (theme_file);
1176 g_key_file_get_locale_string (theme_file,
1177 "Icon Theme", "Comment",
1180 g_key_file_get_string (theme_file,
1181 "Icon Theme", "Example",
1185 for (i = 0; dirs[i] != NULL; i++)
1186 theme_subdir_load (icon_theme, theme, theme_file, dirs[i]);
1190 theme->dirs = g_list_reverse (theme->dirs);
1192 themes = g_key_file_get_string_list (theme_file,
1199 for (i = 0; themes[i] != NULL; i++)
1200 insert_theme (icon_theme, themes[i]);
1202 g_strfreev (themes);
1205 g_key_file_free (theme_file);
1209 free_unthemed_icon (UnthemedIcon *unthemed_icon)
1211 g_free (unthemed_icon->svg_filename);
1212 g_free (unthemed_icon->no_svg_filename);
1213 g_slice_free (UnthemedIcon, unthemed_icon);
1217 strip_suffix (const char *filename)
1221 dot = strrchr (filename, '.');
1224 return g_strdup (filename);
1226 return g_strndup (filename, dot - filename);
1230 load_themes (GtkIconTheme *icon_theme)
1232 GtkIconThemePrivate *priv;
1237 UnthemedIcon *unthemed_icon;
1238 IconSuffix old_suffix, new_suffix;
1240 IconThemeDirMtime *dir_mtime;
1243 priv = icon_theme->priv;
1245 priv->all_icons = g_hash_table_new (g_str_hash, g_str_equal);
1247 if (priv->current_theme)
1248 insert_theme (icon_theme, priv->current_theme);
1250 /* Always look in the "default" icon theme, and in a fallback theme */
1251 if (priv->fallback_theme)
1252 insert_theme (icon_theme, priv->fallback_theme);
1253 insert_theme (icon_theme, DEFAULT_THEME_NAME);
1254 priv->themes = g_list_reverse (priv->themes);
1257 priv->unthemed_icons = g_hash_table_new_full (g_str_hash, g_str_equal,
1258 g_free, (GDestroyNotify)free_unthemed_icon);
1260 for (base = 0; base < icon_theme->priv->search_path_len; base++)
1262 dir = icon_theme->priv->search_path[base];
1264 dir_mtime = g_slice_new (IconThemeDirMtime);
1265 priv->dir_mtimes = g_list_append (priv->dir_mtimes, dir_mtime);
1267 dir_mtime->dir = g_strdup (dir);
1268 dir_mtime->mtime = 0;
1269 dir_mtime->cache = NULL;
1271 if (g_stat (dir, &stat_buf) != 0 || !S_ISDIR (stat_buf.st_mode))
1273 dir_mtime->mtime = stat_buf.st_mtime;
1275 dir_mtime->cache = _gtk_icon_cache_new_for_path (dir);
1276 if (dir_mtime->cache != NULL)
1279 gdir = g_dir_open (dir, 0, NULL);
1283 while ((file = g_dir_read_name (gdir)))
1285 new_suffix = suffix_from_name (file);
1287 if (new_suffix != ICON_SUFFIX_NONE)
1292 abs_file = g_build_filename (dir, file, NULL);
1293 base_name = strip_suffix (file);
1295 if ((unthemed_icon = g_hash_table_lookup (priv->unthemed_icons,
1298 if (new_suffix == ICON_SUFFIX_SVG)
1300 if (unthemed_icon->svg_filename)
1303 unthemed_icon->svg_filename = abs_file;
1307 if (unthemed_icon->no_svg_filename)
1309 old_suffix = suffix_from_name (unthemed_icon->no_svg_filename);
1310 if (new_suffix > old_suffix)
1312 g_free (unthemed_icon->no_svg_filename);
1313 unthemed_icon->no_svg_filename = abs_file;
1319 unthemed_icon->no_svg_filename = abs_file;
1326 unthemed_icon = g_slice_new0 (UnthemedIcon);
1328 if (new_suffix == ICON_SUFFIX_SVG)
1329 unthemed_icon->svg_filename = abs_file;
1331 unthemed_icon->no_svg_filename = abs_file;
1333 /* takes ownership of base_name */
1334 g_hash_table_replace (priv->unthemed_icons,
1337 g_hash_table_insert (priv->all_icons,
1345 priv->themes_valid = TRUE;
1347 g_get_current_time(&tv);
1348 priv->last_stat_time = tv.tv_sec;
1352 _gtk_icon_theme_ensure_builtin_cache (void)
1354 static gboolean initialized = FALSE;
1356 static IconThemeDir dirs[5] =
1358 { ICON_THEME_DIR_THRESHOLD, 0, 16, 16, 16, 2, NULL, "16", -1, NULL, NULL, NULL },
1359 { ICON_THEME_DIR_THRESHOLD, 0, 20, 20, 20, 2, NULL, "20", -1, NULL, NULL, NULL },
1360 { ICON_THEME_DIR_THRESHOLD, 0, 24, 24, 24, 2, NULL, "24", -1, NULL, NULL, NULL },
1361 { ICON_THEME_DIR_THRESHOLD, 0, 32, 32, 32, 2, NULL, "32", -1, NULL, NULL, NULL },
1362 { ICON_THEME_DIR_THRESHOLD, 0, 48, 48, 48, 2, NULL, "48", -1, NULL, NULL, NULL }
1370 _builtin_cache = _gtk_icon_cache_new ((gchar *)builtin_icons);
1372 for (i = 0; i < G_N_ELEMENTS (dirs); i++)
1375 dir->cache = _gtk_icon_cache_ref (_builtin_cache);
1376 dir->subdir_index = _gtk_icon_cache_get_directory_index (dir->cache, dir->subdir);
1378 builtin_dirs = g_list_append (builtin_dirs, dir);
1384 ensure_valid_themes (GtkIconTheme *icon_theme)
1386 GtkIconThemePrivate *priv = icon_theme->priv;
1388 gboolean was_valid = priv->themes_valid;
1390 if (priv->loading_themes)
1392 priv->loading_themes = TRUE;
1394 _gtk_icon_theme_ensure_builtin_cache ();
1396 if (priv->themes_valid)
1398 g_get_current_time (&tv);
1400 if (ABS (tv.tv_sec - priv->last_stat_time) > 5 &&
1401 rescan_themes (icon_theme))
1402 blow_themes (icon_theme);
1405 if (!priv->themes_valid)
1407 load_themes (icon_theme);
1411 g_signal_emit (icon_theme, signal_changed, 0);
1415 priv->loading_themes = FALSE;
1418 /* The LRU cache is a short list of IconInfos that are kept
1419 alive even though their IconInfo would otherwise have
1420 been freed, so that we can avoid reloading these
1422 We put infos on the lru list when nothing otherwise
1423 references the info. So, when we get a cache hit
1424 we remove it from the list, and when the proxy
1425 pixmap is released we put it on the list.
1429 ensure_lru_cache_space (GtkIconTheme *icon_theme)
1431 GtkIconThemePrivate *priv = icon_theme->priv;
1434 /* Remove last item if LRU full */
1435 l = g_list_nth (priv->info_cache_lru, INFO_CACHE_LRU_SIZE - 1);
1438 GtkIconInfo *icon_info = l->data;
1440 DEBUG_CACHE (("removing (due to out of space) %p (%s %d 0x%x) from LRU cache (cache size %d)\n",
1442 g_strjoinv (",", icon_info->key.icon_names),
1443 icon_info->key.size, icon_info->key.flags,
1444 g_list_length (priv->info_cache_lru)));
1446 priv->info_cache_lru = g_list_delete_link (priv->info_cache_lru, l);
1447 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);
3031 for (l = icon_info->emblem_infos; l != NULL; l = l->next)
3034 g_slist_append (dup->emblem_infos,
3035 icon_info_dup (l->data));
3038 if (icon_info->cache_pixbuf)
3039 dup->cache_pixbuf = g_object_ref (icon_info->cache_pixbuf);
3041 dup->data = icon_data_dup (icon_info->data);
3042 dup->dir_type = icon_info->dir_type;
3043 dup->dir_size = icon_info->dir_size;
3044 dup->threshold = icon_info->threshold;
3045 dup->desired_size = icon_info->desired_size;
3046 dup->raw_coordinates = icon_info->raw_coordinates;
3047 dup->forced_size = icon_info->forced_size;
3048 dup->emblems_applied = icon_info->emblems_applied;
3053 static GtkIconInfo *
3054 icon_info_new_builtin (BuiltinIcon *icon)
3056 GtkIconInfo *icon_info = icon_info_new ();
3058 icon_info->cache_pixbuf = g_object_ref (icon->pixbuf);
3059 icon_info->dir_type = ICON_THEME_DIR_THRESHOLD;
3060 icon_info->dir_size = icon->size;
3061 icon_info->threshold = 2;
3067 * gtk_icon_info_copy: (skip)
3068 * @icon_info: a #GtkIconInfo
3070 * Make a copy of a #GtkIconInfo.
3072 * Return value: the new GtkIconInfo
3076 * Deprecated: 3.8: Use g_object_ref()
3079 gtk_icon_info_copy (GtkIconInfo *icon_info)
3082 g_return_val_if_fail (icon_info != NULL, NULL);
3084 return g_object_ref (icon_info);
3088 * gtk_icon_info_free: (skip)
3089 * @icon_info: a #GtkIconInfo
3091 * Free a #GtkIconInfo and associated information
3095 * Deprecated: 3.8: Use g_object_unref()
3098 gtk_icon_info_free (GtkIconInfo *icon_info)
3100 g_return_if_fail (icon_info != NULL);
3102 g_object_unref (icon_info);
3106 gtk_icon_info_finalize (GObject *object)
3108 GtkIconInfo *icon_info = (GtkIconInfo *) object;
3110 if (icon_info->in_cache)
3111 g_hash_table_remove (icon_info->in_cache->priv->info_cache, &icon_info->key);
3113 g_strfreev (icon_info->key.icon_names);
3115 g_free (icon_info->filename);
3116 g_clear_object (&icon_info->icon_file);
3118 if (icon_info->loadable)
3119 g_object_unref (icon_info->loadable);
3120 g_slist_free_full (icon_info->emblem_infos, (GDestroyNotify) g_object_unref);
3121 if (icon_info->pixbuf)
3122 g_object_unref (icon_info->pixbuf);
3123 if (icon_info->cache_pixbuf)
3124 g_object_unref (icon_info->cache_pixbuf);
3125 if (icon_info->symbolic_pixbuf_size)
3126 gtk_requisition_free (icon_info->symbolic_pixbuf_size);
3128 symbolic_pixbuf_cache_free (icon_info->symbolic_pixbuf_cache);
3130 G_OBJECT_CLASS (gtk_icon_info_parent_class)->finalize (object);
3134 gtk_icon_info_class_init (GtkIconInfoClass *klass)
3136 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3138 gobject_class->finalize = gtk_icon_info_finalize;
3142 * gtk_icon_info_get_base_size:
3143 * @icon_info: a #GtkIconInfo
3145 * Gets the base size for the icon. The base size
3146 * is a size for the icon that was specified by
3147 * the icon theme creator. This may be different
3148 * than the actual size of image; an example of
3149 * this is small emblem icons that can be attached
3150 * to a larger icon. These icons will be given
3151 * the same base size as the larger icons to which
3152 * they are attached.
3154 * Return value: the base size, or 0, if no base
3155 * size is known for the icon.
3160 gtk_icon_info_get_base_size (GtkIconInfo *icon_info)
3162 g_return_val_if_fail (icon_info != NULL, 0);
3164 return icon_info->dir_size;
3168 * gtk_icon_info_get_filename:
3169 * @icon_info: a #GtkIconInfo
3171 * Gets the filename for the icon. If the
3172 * %GTK_ICON_LOOKUP_USE_BUILTIN flag was passed
3173 * to gtk_icon_theme_lookup_icon(), there may be
3174 * no filename if a builtin icon is returned; in this
3175 * case, you should use gtk_icon_info_get_builtin_pixbuf().
3177 * Return value: (type filename): the filename for the icon, or %NULL
3178 * if gtk_icon_info_get_builtin_pixbuf() should be used instead. The
3179 * return value is owned by GTK+ and should not be modified or freed.
3184 gtk_icon_info_get_filename (GtkIconInfo *icon_info)
3186 g_return_val_if_fail (icon_info != NULL, NULL);
3188 return icon_info->filename;
3192 * gtk_icon_info_get_builtin_pixbuf:
3193 * @icon_info: a #GtkIconInfo structure
3195 * Gets the built-in image for this icon, if any. To allow
3196 * GTK+ to use built in icon images, you must pass the
3197 * %GTK_ICON_LOOKUP_USE_BUILTIN to
3198 * gtk_icon_theme_lookup_icon().
3200 * Return value: (transfer none): the built-in image pixbuf, or %NULL. No
3201 * extra reference is added to the returned pixbuf, so if
3202 * you want to keep it around, you must use g_object_ref().
3203 * The returned image must not be modified.
3208 gtk_icon_info_get_builtin_pixbuf (GtkIconInfo *icon_info)
3210 g_return_val_if_fail (icon_info != NULL, NULL);
3212 if (icon_info->filename)
3215 return icon_info->cache_pixbuf;
3218 static gboolean icon_info_ensure_scale_and_pixbuf (GtkIconInfo*, gboolean);
3220 /* Combine the icon with all emblems, the first emblem is placed
3221 * in the southeast corner. Scale emblems to be at most 3/4 of the
3222 * size of the icon itself.
3225 apply_emblems (GtkIconInfo *info)
3227 GdkPixbuf *icon = NULL;
3231 if (info->emblem_infos == NULL)
3234 if (info->emblems_applied)
3237 w = gdk_pixbuf_get_width (info->pixbuf);
3238 h = gdk_pixbuf_get_height (info->pixbuf);
3240 for (l = info->emblem_infos, pos = 0; l; l = l->next, pos++)
3242 GtkIconInfo *emblem_info = l->data;
3244 if (icon_info_ensure_scale_and_pixbuf (emblem_info, FALSE))
3246 GdkPixbuf *emblem = emblem_info->pixbuf;
3248 gint x = 0, y = 0; /* silence compiler */
3251 ew = gdk_pixbuf_get_width (emblem);
3252 eh = gdk_pixbuf_get_height (emblem);
3284 icon = gdk_pixbuf_copy (info->pixbuf);
3289 gdk_pixbuf_composite (emblem, icon, x, y, ew, eh, x, y,
3290 scale, scale, GDK_INTERP_BILINEAR, 255);
3296 g_object_unref (info->pixbuf);
3297 info->pixbuf = icon;
3300 info->emblems_applied = TRUE;
3303 /* If this returns TRUE, its safe to call
3304 icon_info_ensure_scale_and_pixbuf without blocking */
3306 icon_info_get_pixbuf_ready (GtkIconInfo *icon_info)
3308 if (icon_info->pixbuf &&
3309 (icon_info->emblem_infos == NULL || icon_info->emblems_applied))
3312 if (icon_info->load_error)
3318 /* This function contains the complicated logic for deciding
3319 * on the size at which to load the icon and loading it at
3323 icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info,
3324 gboolean scale_only)
3326 int image_width, image_height;
3327 GdkPixbuf *source_pixbuf;
3330 /* First check if we already succeeded have the necessary
3331 * information (or failed earlier)
3333 if (scale_only && icon_info->scale >= 0)
3336 if (icon_info->pixbuf)
3338 apply_emblems (icon_info);
3342 if (icon_info->load_error)
3345 /* SVG icons are a special case - we just immediately scale them
3346 * to the desired size
3348 if (icon_info->icon_file && !icon_info->loadable)
3349 icon_info->loadable = G_LOADABLE_ICON (g_file_icon_new (icon_info->icon_file));
3352 if (G_IS_FILE_ICON (icon_info->loadable))
3355 GFileInfo *file_info;
3356 const gchar *content_type;
3358 file = g_file_icon_get_file (G_FILE_ICON (icon_info->loadable));
3359 file_info = g_file_query_info (file,
3360 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
3361 G_FILE_QUERY_INFO_NONE,
3365 content_type = g_file_info_get_content_type (file_info);
3367 if (content_type && strcmp (content_type, "image/svg+xml") == 0)
3370 g_object_unref (file_info);
3376 GInputStream *stream;
3378 icon_info->scale = icon_info->desired_size / 1000.;
3383 stream = g_loadable_icon_load (icon_info->loadable,
3384 icon_info->desired_size,
3386 &icon_info->load_error);
3389 icon_info->pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3390 icon_info->desired_size,
3391 icon_info->desired_size,
3394 &icon_info->load_error);
3395 g_object_unref (stream);
3398 if (!icon_info->pixbuf)
3401 apply_emblems (icon_info);
3406 /* In many cases, the scale can be determined without actual access
3407 * to the icon file. This is generally true when we have a size
3408 * for the directory where the icon is; the image size doesn't
3409 * matter in that case.
3411 if (icon_info->forced_size)
3412 icon_info->scale = -1;
3413 else if (icon_info->dir_type == ICON_THEME_DIR_FIXED)
3414 icon_info->scale = 1.0;
3415 else if (icon_info->dir_type == ICON_THEME_DIR_THRESHOLD)
3417 if (icon_info->desired_size >= icon_info->dir_size - icon_info->threshold &&
3418 icon_info->desired_size <= icon_info->dir_size + icon_info->threshold)
3419 icon_info->scale = 1.0;
3420 else if (icon_info->dir_size > 0)
3421 icon_info->scale =(gdouble) icon_info->desired_size / icon_info->dir_size;
3423 else if (icon_info->dir_type == ICON_THEME_DIR_SCALABLE)
3425 if (icon_info->dir_size > 0)
3426 icon_info->scale = (gdouble) icon_info->desired_size / icon_info->dir_size;
3429 if (icon_info->scale >= 0. && scale_only)
3432 /* At this point, we need to actually get the icon; either from the
3433 * builtin image or by loading the file
3435 source_pixbuf = NULL;
3436 if (icon_info->cache_pixbuf)
3437 source_pixbuf = g_object_ref (icon_info->cache_pixbuf);
3440 GInputStream *stream;
3442 stream = g_loadable_icon_load (icon_info->loadable,
3443 icon_info->desired_size,
3445 &icon_info->load_error);
3448 source_pixbuf = gdk_pixbuf_new_from_stream (stream,
3450 &icon_info->load_error);
3451 g_object_unref (stream);
3458 /* Do scale calculations that depend on the image size
3460 image_width = gdk_pixbuf_get_width (source_pixbuf);
3461 image_height = gdk_pixbuf_get_height (source_pixbuf);
3463 if (icon_info->scale < 0.0)
3465 gint image_size = MAX (image_width, image_height);
3467 icon_info->scale = (gdouble)icon_info->desired_size / (gdouble)image_size;
3469 icon_info->scale = 1.0;
3471 if (icon_info->dir_type == ICON_THEME_DIR_UNTHEMED &&
3472 !icon_info->forced_size)
3473 icon_info->scale = MIN (icon_info->scale, 1.0);
3476 /* We don't short-circuit out here for scale_only, since, now
3477 * we've loaded the icon, we might as well go ahead and finish
3478 * the job. This is a bit of a waste when we scale here
3479 * and never get the final pixbuf; at the cost of a bit of
3480 * extra complexity, we could keep the source pixbuf around
3481 * but not actually scale it until needed.
3483 if (icon_info->scale == 1.0)
3484 icon_info->pixbuf = source_pixbuf;
3487 icon_info->pixbuf = gdk_pixbuf_scale_simple (source_pixbuf,
3488 0.5 + image_width * icon_info->scale,
3489 0.5 + image_height * icon_info->scale,
3490 GDK_INTERP_BILINEAR);
3491 g_object_unref (source_pixbuf);
3494 apply_emblems (icon_info);
3500 proxy_pixbuf_destroy (guchar *pixels, gpointer data)
3502 GtkIconInfo *icon_info = data;
3503 GtkIconTheme *icon_theme = icon_info->in_cache;
3505 g_assert (icon_info->proxy_pixbuf != NULL);
3506 icon_info->proxy_pixbuf = NULL;
3508 /* Keep it alive a bit longer */
3509 if (icon_theme != NULL)
3510 ensure_in_lru_cache (icon_theme, icon_info);
3512 g_object_unref (icon_info);
3516 * gtk_icon_info_load_icon:
3517 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3518 * @error: (allow-none): location to store error information on failure,
3521 * Renders an icon previously looked up in an icon theme using
3522 * gtk_icon_theme_lookup_icon(); the size will be based on the size
3523 * passed to gtk_icon_theme_lookup_icon(). Note that the resulting
3524 * pixbuf may not be exactly this size; an icon theme may have icons
3525 * that differ slightly from their nominal sizes, and in addition GTK+
3526 * will avoid scaling icons that it considers sufficiently close to the
3527 * requested size or for which the source image would have to be scaled
3528 * up too far. (This maintains sharpness.). This behaviour can be changed
3529 * by passing the %GTK_ICON_LOOKUP_FORCE_SIZE flag when obtaining
3530 * the #GtkIconInfo. If this flag has been specified, the pixbuf
3531 * returned by this function will be scaled to the exact size.
3533 * Return value: (transfer full): the rendered icon; this may be a newly
3534 * created icon or a new reference to an internal icon, so you must
3535 * not modify the icon. Use g_object_unref() to release your reference
3541 gtk_icon_info_load_icon (GtkIconInfo *icon_info,
3544 g_return_val_if_fail (icon_info != NULL, NULL);
3545 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3547 if (!icon_info_ensure_scale_and_pixbuf (icon_info, FALSE))
3549 if (icon_info->load_error)
3550 g_propagate_error (error, icon_info->load_error);
3552 g_set_error_literal (error,
3553 GTK_ICON_THEME_ERROR,
3554 GTK_ICON_THEME_NOT_FOUND,
3555 _("Failed to load icon"));
3560 /* Instead of returning the pixbuf directly we
3561 return a proxy to it that we don't own (but that
3562 shares the data with the one we own). This way
3563 we can know when it is freed and ensure the
3564 IconInfo is alive (and thus cached) while
3565 the pixbuf is still alive. */
3567 if (icon_info->proxy_pixbuf != NULL)
3568 return g_object_ref (icon_info->proxy_pixbuf);
3570 icon_info->proxy_pixbuf =
3571 gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (icon_info->pixbuf),
3572 gdk_pixbuf_get_colorspace (icon_info->pixbuf),
3573 gdk_pixbuf_get_has_alpha (icon_info->pixbuf),
3574 gdk_pixbuf_get_bits_per_sample (icon_info->pixbuf),
3575 gdk_pixbuf_get_width (icon_info->pixbuf),
3576 gdk_pixbuf_get_height (icon_info->pixbuf),
3577 gdk_pixbuf_get_rowstride (icon_info->pixbuf),
3578 proxy_pixbuf_destroy,
3579 g_object_ref (icon_info));
3581 return icon_info->proxy_pixbuf;
3585 load_icon_thread (GTask *task,
3586 gpointer source_object,
3588 GCancellable *cancellable)
3590 GtkIconInfo *dup = task_data;
3592 icon_info_ensure_scale_and_pixbuf (dup, FALSE);
3593 g_task_return_pointer (task, NULL, NULL);
3597 * gtk_icon_info_load_icon_async:
3598 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3599 * @cancellable: (allow-none): optional #GCancellable object,
3601 * @callback: (scope async): a #GAsyncReadyCallback to call when the
3602 * request is satisfied
3603 * @user_data: (closure): the data to pass to callback function
3605 * Asynchronously load, render and scale an icon previously looked up
3606 * from the icon theme using gtk_icon_theme_lookup_icon().
3608 * For more details, see gtk_icon_info_load_icon() which is the synchronous
3609 * version of this call.
3614 gtk_icon_info_load_icon_async (GtkIconInfo *icon_info,
3615 GCancellable *cancellable,
3616 GAsyncReadyCallback callback,
3622 GError *error = NULL;
3624 task = g_task_new (icon_info, cancellable, callback, user_data);
3626 if (icon_info_get_pixbuf_ready (icon_info))
3628 pixbuf = gtk_icon_info_load_icon (icon_info, &error);
3630 g_task_return_error (task, error);
3632 g_task_return_pointer (task, pixbuf, g_object_unref);
3633 g_object_unref (task);
3637 dup = icon_info_dup (icon_info);
3638 g_task_set_task_data (task, dup, g_object_unref);
3639 g_task_run_in_thread (task, load_icon_thread);
3640 g_object_unref (task);
3645 * gtk_icon_info_load_icon_finish:
3646 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3647 * @res: a #GAsyncResult
3648 * @error: (allow-none): location to store error information on failure,
3651 * Finishes an async icon load, see gtk_icon_info_load_icon_async().
3653 * Return value: (transfer full): the rendered icon; this may be a newly
3654 * created icon or a new reference to an internal icon, so you must
3655 * not modify the icon. Use g_object_unref() to release your reference
3661 gtk_icon_info_load_icon_finish (GtkIconInfo *icon_info,
3662 GAsyncResult *result,
3665 GTask *task = G_TASK (result);
3668 g_return_val_if_fail (g_task_is_valid (result, icon_info), NULL);
3670 dup = g_task_get_task_data (task);
3671 if (dup == NULL || g_task_had_error (task))
3672 return g_task_propagate_pointer (task, error);
3674 /* We ran the thread and it was not cancelled */
3676 /* Check if someone else updated the icon_info in between */
3677 if (!icon_info_get_pixbuf_ready (icon_info))
3679 /* If not, copy results from dup back to icon_info */
3681 icon_info->emblems_applied = dup->emblems_applied;
3682 icon_info->scale = dup->scale;
3683 g_clear_object (&icon_info->pixbuf);
3685 icon_info->pixbuf = g_object_ref (dup->pixbuf);
3686 g_clear_error (&icon_info->load_error);
3687 if (dup->load_error)
3688 icon_info->load_error = g_error_copy (dup->load_error);
3691 g_assert (icon_info_get_pixbuf_ready (icon_info));
3693 /* This is now guaranteed to not block */
3694 return gtk_icon_info_load_icon (icon_info, error);
3698 gdk_color_to_css (GdkColor *color)
3700 return g_strdup_printf ("rgb(%d,%d,%d)",
3707 gdk_rgba_to_css (const GdkRGBA *color)
3709 /* drop alpha for now, since librsvg does not understand rgba() */
3710 return g_strdup_printf ("rgb(%d,%d,%d)",
3711 (gint)(color->red * 255),
3712 (gint)(color->green * 255),
3713 (gint)(color->blue * 255));
3717 proxy_symbolic_pixbuf_destroy (guchar *pixels, gpointer data)
3719 GtkIconInfo *icon_info = data;
3720 GtkIconTheme *icon_theme = icon_info->in_cache;
3721 SymbolicPixbufCache *symbolic_cache;
3723 for (symbolic_cache = icon_info->symbolic_pixbuf_cache;
3724 symbolic_cache != NULL;
3725 symbolic_cache = symbolic_cache->next)
3727 if (symbolic_cache->proxy_pixbuf != NULL &&
3728 gdk_pixbuf_get_pixels (symbolic_cache->proxy_pixbuf) == pixels)
3732 g_assert (symbolic_cache != NULL);
3733 g_assert (symbolic_cache->proxy_pixbuf != NULL);
3735 symbolic_cache->proxy_pixbuf = NULL;
3737 /* Keep it alive a bit longer */
3738 if (icon_theme != NULL)
3739 ensure_in_lru_cache (icon_theme, icon_info);
3741 g_object_unref (icon_info);
3745 symbolic_cache_get_proxy (SymbolicPixbufCache *symbolic_cache,
3746 GtkIconInfo *icon_info)
3748 if (symbolic_cache->proxy_pixbuf)
3749 return g_object_ref (symbolic_cache->proxy_pixbuf);
3751 symbolic_cache->proxy_pixbuf =
3752 gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (symbolic_cache->pixbuf),
3753 gdk_pixbuf_get_colorspace (symbolic_cache->pixbuf),
3754 gdk_pixbuf_get_has_alpha (symbolic_cache->pixbuf),
3755 gdk_pixbuf_get_bits_per_sample (symbolic_cache->pixbuf),
3756 gdk_pixbuf_get_width (symbolic_cache->pixbuf),
3757 gdk_pixbuf_get_height (symbolic_cache->pixbuf),
3758 gdk_pixbuf_get_rowstride (symbolic_cache->pixbuf),
3759 proxy_symbolic_pixbuf_destroy,
3760 g_object_ref (icon_info));
3762 return symbolic_cache->proxy_pixbuf;
3766 _gtk_icon_info_load_symbolic_internal (GtkIconInfo *icon_info,
3768 const GdkRGBA *success_color,
3769 const GdkRGBA *warning_color,
3770 const GdkRGBA *error_color,
3774 GInputStream *stream;
3781 gchar *width, *height, *uri;
3782 SymbolicPixbufCache *symbolic_cache;
3786 symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
3787 fg, success_color, warning_color, error_color);
3789 return symbolic_cache_get_proxy (symbolic_cache, icon_info);
3792 /* css_fg can't possibly have failed, otherwise
3793 * that would mean we have a broken style */
3794 g_return_val_if_fail (fg != NULL, NULL);
3796 css_fg = gdk_rgba_to_css (fg);
3798 css_success = css_warning = css_error = NULL;
3801 css_warning = gdk_rgba_to_css (warning_color);
3804 css_error = gdk_rgba_to_css (error_color);
3807 css_success = gdk_rgba_to_css (success_color);
3811 GdkColor success_default_color = { 0, 0x4e00, 0x9a00, 0x0600 };
3812 css_success = gdk_color_to_css (&success_default_color);
3816 GdkColor warning_default_color = { 0, 0xf500, 0x7900, 0x3e00 };
3817 css_warning = gdk_color_to_css (&warning_default_color);
3821 GdkColor error_default_color = { 0, 0xcc00, 0x0000, 0x0000 };
3822 css_error = gdk_color_to_css (&error_default_color);
3825 if (!icon_info->symbolic_pixbuf_size)
3827 stream = G_INPUT_STREAM (g_file_read (icon_info->icon_file, NULL, error));
3832 /* Fetch size from the original icon */
3833 pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
3834 g_object_unref (stream);
3839 icon_info->symbolic_pixbuf_size = gtk_requisition_new ();
3840 icon_info->symbolic_pixbuf_size->width = gdk_pixbuf_get_width (pixbuf);
3841 icon_info->symbolic_pixbuf_size->height = gdk_pixbuf_get_height (pixbuf);
3842 g_object_unref (pixbuf);
3845 width = g_strdup_printf ("%d", icon_info->symbolic_pixbuf_size->width);
3846 height = g_strdup_printf ("%d", icon_info->symbolic_pixbuf_size->height);
3847 uri = g_file_get_uri (icon_info->icon_file);
3849 data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
3850 "<svg version=\"1.1\"\n"
3851 " xmlns=\"http://www.w3.org/2000/svg\"\n"
3852 " xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n"
3853 " width=\"", width, "\"\n"
3854 " height=\"", height, "\">\n"
3855 " <style type=\"text/css\">\n"
3857 " fill: ", css_fg," !important;\n"
3860 " fill: ", css_warning, " !important;\n"
3863 " fill: ", css_error ," !important;\n"
3866 " fill: ", css_success, " !important;\n"
3869 " <xi:include href=\"", uri, "\"/>\n"
3873 g_free (css_warning);
3875 g_free (css_success);
3880 stream = g_memory_input_stream_new_from_data (data, -1, g_free);
3881 pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3882 icon_info->desired_size,
3883 icon_info->desired_size,
3887 g_object_unref (stream);
3893 icon_info->symbolic_pixbuf_cache =
3894 symbolic_pixbuf_cache_new (pixbuf, fg, success_color, warning_color, error_color,
3895 icon_info->symbolic_pixbuf_cache);
3896 g_object_unref (pixbuf);
3897 return symbolic_cache_get_proxy (icon_info->symbolic_pixbuf_cache, icon_info);
3907 * gtk_icon_info_load_symbolic:
3908 * @icon_info: a #GtkIconInfo
3909 * @fg: a #GdkRGBA representing the foreground color of the icon
3910 * @success_color: (allow-none): a #GdkRGBA representing the warning color
3911 * of the icon or %NULL to use the default color
3912 * @warning_color: (allow-none): a #GdkRGBA representing the warning color
3913 * of the icon or %NULL to use the default color
3914 * @error_color: (allow-none): a #GdkRGBA representing the error color
3915 * of the icon or %NULL to use the default color (allow-none)
3916 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3917 * loaded icon was a symbolic one and whether the @fg color was
3919 * @error: (allow-none): location to store error information on failure,
3922 * Loads an icon, modifying it to match the system colours for the foreground,
3923 * success, warning and error colors provided. If the icon is not a symbolic
3924 * one, the function will return the result from gtk_icon_info_load_icon().
3926 * This allows loading symbolic icons that will match the system theme.
3928 * Unless you are implementing a widget, you will want to use
3929 * g_themed_icon_new_with_default_fallbacks() to load the icon.
3931 * As implementation details, the icon loaded needs to be of SVG type,
3932 * contain the "symbolic" term as the last component of the icon name,
3933 * and use the 'fg', 'success', 'warning' and 'error' CSS styles in the
3936 * See the <ulink url="http://www.freedesktop.org/wiki/SymbolicIcons">Symbolic Icons spec</ulink>
3937 * for more information about symbolic icons.
3939 * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
3944 gtk_icon_info_load_symbolic (GtkIconInfo *icon_info,
3946 const GdkRGBA *success_color,
3947 const GdkRGBA *warning_color,
3948 const GdkRGBA *error_color,
3949 gboolean *was_symbolic,
3953 gboolean is_symbolic;
3955 g_return_val_if_fail (icon_info != NULL, NULL);
3956 g_return_val_if_fail (fg != NULL, NULL);
3959 if (icon_info->icon_file)
3960 icon_uri = g_file_get_uri (icon_info->icon_file);
3962 is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
3966 *was_symbolic = is_symbolic;
3969 return gtk_icon_info_load_icon (icon_info, error);
3971 return _gtk_icon_info_load_symbolic_internal (icon_info,
3973 warning_color, error_color,
3979 * gtk_icon_info_load_symbolic_for_context:
3980 * @icon_info: a #GtkIconInfo
3981 * @context: a #GtkStyleContext
3982 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3983 * loaded icon was a symbolic one and whether the @fg color was
3985 * @error: (allow-none): location to store error information on failure,
3988 * Loads an icon, modifying it to match the system colors for the foreground,
3989 * success, warning and error colors provided. If the icon is not a symbolic
3990 * one, the function will return the result from gtk_icon_info_load_icon().
3991 * This function uses the regular foreground color and the symbolic colors
3992 * with the names "success_color", "warning_color" and "error_color" from
3995 * This allows loading symbolic icons that will match the system theme.
3997 * See gtk_icon_info_load_symbolic() for more details.
3999 * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
4004 gtk_icon_info_load_symbolic_for_context (GtkIconInfo *icon_info,
4005 GtkStyleContext *context,
4006 gboolean *was_symbolic,
4009 GdkRGBA *color = NULL;
4012 GdkRGBA success_color;
4013 GdkRGBA *success_colorp;
4014 GdkRGBA warning_color;
4015 GdkRGBA *warning_colorp;
4016 GdkRGBA error_color;
4017 GdkRGBA *error_colorp;
4018 GtkStateFlags state;
4020 gboolean is_symbolic;
4022 g_return_val_if_fail (icon_info != NULL, NULL);
4023 g_return_val_if_fail (context != NULL, NULL);
4026 if (icon_info->icon_file)
4027 icon_uri = g_file_get_uri (icon_info->icon_file);
4029 is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
4033 *was_symbolic = is_symbolic;
4036 return gtk_icon_info_load_icon (icon_info, error);
4038 fgp = success_colorp = warning_colorp = error_colorp = NULL;
4040 state = gtk_style_context_get_state (context);
4041 gtk_style_context_get (context, state, "color", &color, NULL);
4046 gdk_rgba_free (color);
4049 if (gtk_style_context_lookup_color (context, "success_color", &success_color))
4050 success_colorp = &success_color;
4052 if (gtk_style_context_lookup_color (context, "warning_color", &warning_color))
4053 warning_colorp = &warning_color;
4055 if (gtk_style_context_lookup_color (context, "error_color", &error_color))
4056 error_colorp = &error_color;
4058 return _gtk_icon_info_load_symbolic_internal (icon_info,
4059 fgp, success_colorp,
4060 warning_colorp, error_colorp,
4066 gboolean is_symbolic;
4070 GdkRGBA success_color;
4071 gboolean success_color_set;
4072 GdkRGBA warning_color;
4073 gboolean warning_color_set;
4074 GdkRGBA error_color;
4075 gboolean error_color_set;
4076 } AsyncSymbolicData;
4079 async_symbolic_data_free (AsyncSymbolicData *data)
4082 g_object_unref (data->dup);
4083 g_slice_free (AsyncSymbolicData, data);
4087 async_load_no_symbolic_cb (GObject *source_object,
4091 GtkIconInfo *icon_info = GTK_ICON_INFO (source_object);
4092 GTask *task = user_data;
4093 GError *error = NULL;
4096 pixbuf = gtk_icon_info_load_icon_finish (icon_info, res, &error);
4098 g_task_return_error (task, error);
4100 g_task_return_pointer (task, pixbuf, g_object_unref);
4101 g_object_unref (task);
4105 load_symbolic_icon_thread (GTask *task,
4106 gpointer source_object,
4108 GCancellable *cancellable)
4110 AsyncSymbolicData *data = task_data;
4116 _gtk_icon_info_load_symbolic_internal (data->dup,
4117 data->fg_set ? &data->fg : NULL,
4118 data->success_color_set ? &data->success_color : NULL,
4119 data->warning_color_set ? &data->warning_color : NULL,
4120 data->error_color_set ? &data->error_color : NULL,
4124 g_task_return_error (task, error);
4126 g_task_return_pointer (task, pixbuf, g_object_unref);
4130 * gtk_icon_info_load_symbolic_async:
4131 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
4132 * @fg: a #GdkRGBA representing the foreground color of the icon
4133 * @success_color: (allow-none): a #GdkRGBA representing the warning color
4134 * of the icon or %NULL to use the default color
4135 * @warning_color: (allow-none): a #GdkRGBA representing the warning color
4136 * of the icon or %NULL to use the default color
4137 * @error_color: (allow-none): a #GdkRGBA representing the error color
4138 * of the icon or %NULL to use the default color (allow-none)
4139 * @cancellable: (allow-none): optional #GCancellable object,
4141 * @callback: (scope async): a #GAsyncReadyCallback to call when the
4142 * request is satisfied
4143 * @user_data: (closure): the data to pass to callback function
4145 * Asynchronously load, render and scale a symbolic icon previously looked up
4146 * from the icon theme using gtk_icon_theme_lookup_icon().
4148 * For more details, see gtk_icon_info_load_symbolic() which is the synchronous
4149 * version of this call.
4154 gtk_icon_info_load_symbolic_async (GtkIconInfo *icon_info,
4156 const GdkRGBA *success_color,
4157 const GdkRGBA *warning_color,
4158 const GdkRGBA *error_color,
4159 GCancellable *cancellable,
4160 GAsyncReadyCallback callback,
4164 AsyncSymbolicData *data;
4166 SymbolicPixbufCache *symbolic_cache;
4169 g_return_if_fail (icon_info != NULL);
4170 g_return_if_fail (fg != NULL);
4172 task = g_task_new (icon_info, cancellable, callback, user_data);
4174 data = g_slice_new0 (AsyncSymbolicData);
4175 g_task_set_task_data (task, data, (GDestroyNotify) async_symbolic_data_free);
4178 if (icon_info->icon_file)
4179 icon_uri = g_file_get_uri (icon_info->icon_file);
4181 data->is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
4184 if (!data->is_symbolic)
4186 gtk_icon_info_load_icon_async (icon_info, cancellable, async_load_no_symbolic_cb, g_object_ref (task));
4190 symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
4191 fg, success_color, warning_color, error_color);
4194 pixbuf = symbolic_cache_get_proxy (symbolic_cache, icon_info);
4195 g_task_return_pointer (task, pixbuf, g_object_unref);
4202 data->fg_set = TRUE;
4207 data->success_color = *success_color;
4208 data->success_color_set = TRUE;
4213 data->warning_color = *warning_color;
4214 data->warning_color_set = TRUE;
4219 data->error_color = *error_color;
4220 data->error_color_set = TRUE;
4223 data->dup = icon_info_dup (icon_info);
4224 g_task_run_in_thread (task, load_symbolic_icon_thread);
4227 g_object_unref (task);
4231 * gtk_icon_info_load_symbolic_finish:
4232 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
4233 * @res: a #GAsyncResult
4234 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
4235 * loaded icon was a symbolic one and whether the @fg color was
4237 * @error: (allow-none): location to store error information on failure,
4240 * Finishes an async icon load, see gtk_icon_info_load_symbolic_async().
4242 * Return value: (transfer full): the rendered icon; this may be a newly
4243 * created icon or a new reference to an internal icon, so you must
4244 * not modify the icon. Use g_object_unref() to release your reference
4250 gtk_icon_info_load_symbolic_finish (GtkIconInfo *icon_info,
4251 GAsyncResult *result,
4252 gboolean *was_symbolic,
4255 GTask *task = G_TASK (result);
4256 AsyncSymbolicData *data = g_task_get_task_data (task);
4257 SymbolicPixbufCache *symbolic_cache;
4261 *was_symbolic = data->is_symbolic;
4263 if (data->dup && !g_task_had_error (task))
4265 pixbuf = g_task_propagate_pointer (task, NULL);
4267 g_assert (pixbuf != NULL); /* we checked for !had_error above */
4269 symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
4270 data->fg_set ? &data->fg : NULL,
4271 data->success_color_set ? &data->success_color : NULL,
4272 data->warning_color_set ? &data->warning_color : NULL,
4273 data->error_color_set ? &data->error_color : NULL);
4275 if (symbolic_cache == NULL)
4277 symbolic_cache = icon_info->symbolic_pixbuf_cache =
4278 symbolic_pixbuf_cache_new (pixbuf,
4279 data->fg_set ? &data->fg : NULL,
4280 data->success_color_set ? &data->success_color : NULL,
4281 data->warning_color_set ? &data->warning_color : NULL,
4282 data->error_color_set ? &data->error_color : NULL,
4283 icon_info->symbolic_pixbuf_cache);
4286 g_object_unref (pixbuf);
4288 return symbolic_cache_get_proxy (symbolic_cache, icon_info);
4291 return g_task_propagate_pointer (task, error);
4295 * gtk_icon_info_load_symbolic_for_context_async:
4296 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
4297 * @context: a #GtkStyleContext
4298 * @cancellable: (allow-none): optional #GCancellable object,
4300 * @callback: (scope async): a #GAsyncReadyCallback to call when the
4301 * request is satisfied
4302 * @user_data: (closure): the data to pass to callback function
4304 * Asynchronously load, render and scale a symbolic icon previously looked up
4305 * from the icon theme using gtk_icon_theme_lookup_icon().
4307 * For more details, see gtk_icon_info_load_symbolic_for_context() which is the synchronous
4308 * version of this call.
4313 gtk_icon_info_load_symbolic_for_context_async (GtkIconInfo *icon_info,
4314 GtkStyleContext *context,
4315 GCancellable *cancellable,
4316 GAsyncReadyCallback callback,
4319 GdkRGBA *color = NULL;
4322 GdkRGBA success_color;
4323 GdkRGBA *success_colorp;
4324 GdkRGBA warning_color;
4325 GdkRGBA *warning_colorp;
4326 GdkRGBA error_color;
4327 GdkRGBA *error_colorp;
4328 GtkStateFlags state;
4330 g_return_if_fail (icon_info != NULL);
4331 g_return_if_fail (context != NULL);
4333 fgp = success_colorp = warning_colorp = error_colorp = NULL;
4335 state = gtk_style_context_get_state (context);
4336 gtk_style_context_get (context, state, "color", &color, NULL);
4341 gdk_rgba_free (color);
4344 if (gtk_style_context_lookup_color (context, "success_color", &success_color))
4345 success_colorp = &success_color;
4347 if (gtk_style_context_lookup_color (context, "warning_color", &warning_color))
4348 warning_colorp = &warning_color;
4350 if (gtk_style_context_lookup_color (context, "error_color", &error_color))
4351 error_colorp = &error_color;
4353 gtk_icon_info_load_symbolic_async (icon_info,
4354 fgp, success_colorp,
4355 warning_colorp, error_colorp,
4356 cancellable, callback, user_data);
4360 * gtk_icon_info_load_symbolic_for_context_finish:
4361 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
4362 * @res: a #GAsyncResult
4363 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
4364 * loaded icon was a symbolic one and whether the @fg color was
4366 * @error: (allow-none): location to store error information on failure,
4369 * Finishes an async icon load, see gtk_icon_info_load_symbolic_for_context_async().
4371 * Return value: (transfer full): the rendered icon; this may be a newly
4372 * created icon or a new reference to an internal icon, so you must
4373 * not modify the icon. Use g_object_unref() to release your reference
4379 gtk_icon_info_load_symbolic_for_context_finish (GtkIconInfo *icon_info,
4380 GAsyncResult *result,
4381 gboolean *was_symbolic,
4384 return gtk_icon_info_load_symbolic_finish (icon_info, result, was_symbolic, error);
4388 color_to_rgba (GdkColor *color, GdkRGBA *rgba)
4390 rgba->red = color->red / 65535.0;
4391 rgba->green = color->green / 65535.0;
4392 rgba->blue = color->blue / 65535.0;
4398 * gtk_icon_info_load_symbolic_for_style:
4399 * @icon_info: a #GtkIconInfo
4400 * @style: a #GtkStyle to take the colors from
4401 * @state: the widget state to use for colors
4402 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
4403 * loaded icon was a symbolic one and whether the @fg color was
4405 * @error: (allow-none): location to store error information on failure,
4408 * Loads an icon, modifying it to match the system colours for the foreground,
4409 * success, warning and error colors provided. If the icon is not a symbolic
4410 * one, the function will return the result from gtk_icon_info_load_icon().
4412 * This allows loading symbolic icons that will match the system theme.
4414 * See gtk_icon_info_load_symbolic() for more details.
4416 * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
4420 * Deprecated: 3.0: Use gtk_icon_info_load_symbolic_for_context() instead
4423 gtk_icon_info_load_symbolic_for_style (GtkIconInfo *icon_info,
4426 gboolean *was_symbolic,
4431 GdkRGBA success_color;
4432 GdkRGBA *success_colorp;
4433 GdkRGBA warning_color;
4434 GdkRGBA *warning_colorp;
4435 GdkRGBA error_color;
4436 GdkRGBA *error_colorp;
4438 gboolean is_symbolic;
4440 g_return_val_if_fail (icon_info != NULL, NULL);
4441 g_return_val_if_fail (style != NULL, NULL);
4444 if (icon_info->icon_file)
4445 icon_uri = g_file_get_uri (icon_info->icon_file);
4447 is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
4451 *was_symbolic = is_symbolic;
4454 return gtk_icon_info_load_icon (icon_info, error);
4456 color_to_rgba (&style->fg[state], &fg);
4458 success_colorp = warning_colorp = error_colorp = NULL;
4460 if (gtk_style_lookup_color (style, "success_color", &color))
4461 success_colorp = color_to_rgba (&color, &success_color);
4463 if (gtk_style_lookup_color (style, "warning_color", &color))
4464 warning_colorp = color_to_rgba (&color, &warning_color);
4466 if (gtk_style_lookup_color (style, "error_color", &color))
4467 error_colorp = color_to_rgba (&color, &error_color);
4469 return _gtk_icon_info_load_symbolic_internal (icon_info,
4470 &fg, success_colorp,
4471 warning_colorp, error_colorp,
4477 * gtk_icon_info_set_raw_coordinates:
4478 * @icon_info: a #GtkIconInfo
4479 * @raw_coordinates: whether the coordinates of embedded rectangles
4480 * and attached points should be returned in their original
4483 * Sets whether the coordinates returned by gtk_icon_info_get_embedded_rect()
4484 * and gtk_icon_info_get_attach_points() should be returned in their
4485 * original form as specified in the icon theme, instead of scaled
4486 * appropriately for the pixbuf returned by gtk_icon_info_load_icon().
4488 * Raw coordinates are somewhat strange; they are specified to be with
4489 * respect to the unscaled pixmap for PNG and XPM icons, but for SVG
4490 * icons, they are in a 1000x1000 coordinate space that is scaled
4491 * to the final size of the icon. You can determine if the icon is an SVG
4492 * icon by using gtk_icon_info_get_filename(), and seeing if it is non-%NULL
4493 * and ends in '.svg'.
4495 * This function is provided primarily to allow compatibility wrappers
4496 * for older API's, and is not expected to be useful for applications.
4501 gtk_icon_info_set_raw_coordinates (GtkIconInfo *icon_info,
4502 gboolean raw_coordinates)
4504 g_return_if_fail (icon_info != NULL);
4506 icon_info->raw_coordinates = raw_coordinates != FALSE;
4509 /* Scale coordinates from the icon data prior to returning
4513 icon_info_scale_point (GtkIconInfo *icon_info,
4519 if (icon_info->raw_coordinates)
4526 if (!icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4529 *x_out = 0.5 + x * icon_info->scale;
4530 *y_out = 0.5 + y * icon_info->scale;
4537 * gtk_icon_info_get_embedded_rect:
4538 * @icon_info: a #GtkIconInfo
4539 * @rectangle: (out): #GdkRectangle in which to store embedded
4540 * rectangle coordinates; coordinates are only stored
4541 * when this function returns %TRUE.
4543 * Gets the coordinates of a rectangle within the icon
4544 * that can be used for display of information such
4545 * as a preview of the contents of a text file.
4546 * See gtk_icon_info_set_raw_coordinates() for further
4547 * information about the coordinate system.
4549 * Return value: %TRUE if the icon has an embedded rectangle
4554 gtk_icon_info_get_embedded_rect (GtkIconInfo *icon_info,
4555 GdkRectangle *rectangle)
4557 g_return_val_if_fail (icon_info != NULL, FALSE);
4559 if (icon_info->data && icon_info->data->has_embedded_rect &&
4560 icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4562 gint scaled_x0, scaled_y0;
4563 gint scaled_x1, scaled_y1;
4567 icon_info_scale_point (icon_info,
4568 icon_info->data->x0, icon_info->data->y0,
4569 &scaled_x0, &scaled_y0);
4570 icon_info_scale_point (icon_info,
4571 icon_info->data->x1, icon_info->data->y1,
4572 &scaled_x1, &scaled_y1);
4574 rectangle->x = scaled_x0;
4575 rectangle->y = scaled_y0;
4576 rectangle->width = scaled_x1 - rectangle->x;
4577 rectangle->height = scaled_y1 - rectangle->y;
4587 * gtk_icon_info_get_attach_points:
4588 * @icon_info: a #GtkIconInfo
4589 * @points: (allow-none) (array length=n_points) (out): location to store pointer to an array of points, or %NULL
4590 * free the array of points with g_free().
4591 * @n_points: (allow-none): location to store the number of points in @points, or %NULL
4593 * Fetches the set of attach points for an icon. An attach point
4594 * is a location in the icon that can be used as anchor points for attaching
4595 * emblems or overlays to the icon.
4597 * Return value: %TRUE if there are any attach points for the icon.
4602 gtk_icon_info_get_attach_points (GtkIconInfo *icon_info,
4606 g_return_val_if_fail (icon_info != NULL, FALSE);
4608 if (icon_info->data && icon_info->data->n_attach_points &&
4609 icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4615 *points = g_new (GdkPoint, icon_info->data->n_attach_points);
4616 for (i = 0; i < icon_info->data->n_attach_points; i++)
4617 icon_info_scale_point (icon_info,
4618 icon_info->data->attach_points[i].x,
4619 icon_info->data->attach_points[i].y,
4625 *n_points = icon_info->data->n_attach_points;
4641 * gtk_icon_info_get_display_name:
4642 * @icon_info: a #GtkIconInfo
4644 * Gets the display name for an icon. A display name is a
4645 * string to be used in place of the icon name in a user
4646 * visible context like a list of icons.
4648 * Return value: the display name for the icon or %NULL, if
4649 * the icon doesn't have a specified display name. This value
4650 * is owned @icon_info and must not be modified or free.
4655 gtk_icon_info_get_display_name (GtkIconInfo *icon_info)
4657 g_return_val_if_fail (icon_info != NULL, NULL);
4659 if (icon_info->data)
4660 return icon_info->data->display_name;
4671 * gtk_icon_theme_add_builtin_icon:
4672 * @icon_name: the name of the icon to register
4673 * @size: the size at which to register the icon (different
4674 * images can be registered for the same icon name
4675 * at different sizes.)
4676 * @pixbuf: #GdkPixbuf that contains the image to use
4679 * Registers a built-in icon for icon theme lookups. The idea
4680 * of built-in icons is to allow an application or library
4681 * that uses themed icons to function requiring files to
4682 * be present in the file system. For instance, the default
4683 * images for all of GTK+'s stock icons are registered
4686 * In general, if you use gtk_icon_theme_add_builtin_icon()
4687 * you should also install the icon in the icon theme, so
4688 * that the icon is generally available.
4690 * This function will generally be used with pixbufs loaded
4691 * via gdk_pixbuf_new_from_inline().
4696 gtk_icon_theme_add_builtin_icon (const gchar *icon_name,
4700 BuiltinIcon *default_icon;
4704 g_return_if_fail (icon_name != NULL);
4705 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
4707 if (!icon_theme_builtin_icons)
4708 icon_theme_builtin_icons = g_hash_table_new (g_str_hash, g_str_equal);
4710 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
4712 key = g_strdup (icon_name);
4714 key = (gpointer)icon_name; /* Won't get stored */
4716 default_icon = g_new (BuiltinIcon, 1);
4717 default_icon->size = size;
4718 default_icon->pixbuf = g_object_ref (pixbuf);
4719 icons = g_slist_prepend (icons, default_icon);
4721 /* Replaces value, leaves key untouched
4723 g_hash_table_insert (icon_theme_builtin_icons, key, icons);
4726 /* Look up a builtin icon; the min_difference_p and
4727 * has_larger_p out parameters allow us to combine
4728 * this lookup with searching through the actual directories
4729 * of the "hicolor" icon theme. See theme_lookup_icon()
4730 * for how they are used.
4732 static BuiltinIcon *
4733 find_builtin_icon (const gchar *icon_name,
4735 gint *min_difference_p,
4736 gboolean *has_larger_p)
4738 GSList *icons = NULL;
4739 gint min_difference = G_MAXINT;
4740 gboolean has_larger = FALSE;
4741 BuiltinIcon *min_icon = NULL;
4743 if (!icon_theme_builtin_icons)
4746 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
4750 BuiltinIcon *default_icon = icons->data;
4751 int min, max, difference;
4754 min = default_icon->size - 2;
4755 max = default_icon->size + 2;
4756 smaller = size < min;
4758 difference = min - size;
4759 else if (size > max)
4760 difference = size - max;
4764 if (difference == 0)
4766 min_icon = default_icon;
4772 if (difference < min_difference || smaller)
4774 min_difference = difference;
4775 min_icon = default_icon;
4776 has_larger = smaller;
4781 if (difference < min_difference && smaller)
4783 min_difference = difference;
4784 min_icon = default_icon;
4788 icons = icons->next;
4791 if (min_difference_p)
4792 *min_difference_p = min_difference;
4794 *has_larger_p = has_larger;
4800 _gtk_icon_theme_check_reload (GdkDisplay *display)
4804 GtkIconTheme *icon_theme;
4806 n_screens = gdk_display_get_n_screens (display);
4808 for (i = 0; i < n_screens; i++)
4810 screen = gdk_display_get_screen (display, i);
4812 icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
4815 icon_theme->priv->check_reload = TRUE;
4816 ensure_valid_themes (icon_theme);
4817 icon_theme->priv->check_reload = FALSE;
4824 * gtk_icon_theme_lookup_by_gicon:
4825 * @icon_theme: a #GtkIconTheme
4826 * @icon: the #GIcon to look up
4827 * @size: desired icon size
4828 * @flags: flags modifying the behavior of the icon lookup
4830 * Looks up an icon and returns a structure containing
4831 * information such as the filename of the icon.
4832 * The icon can then be rendered into a pixbuf using
4833 * gtk_icon_info_load_icon().
4835 * Return value: (transfer full): a #GtkIconInfo structure containing
4836 * information about the icon, or %NULL if the icon
4837 * wasn't found. Unref with g_object_unref()
4842 gtk_icon_theme_lookup_by_gicon (GtkIconTheme *icon_theme,
4845 GtkIconLookupFlags flags)
4849 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
4850 g_return_val_if_fail (G_IS_ICON (icon), NULL);
4852 if (G_IS_LOADABLE_ICON (icon))
4854 info = icon_info_new ();
4855 info->loadable = G_LOADABLE_ICON (g_object_ref (icon));
4857 if (G_IS_FILE_ICON (icon))
4859 GFile *file = g_file_icon_get_file (G_FILE_ICON (icon));
4862 info->icon_file = g_object_ref (file);
4863 info->filename = g_file_get_path (file);
4867 info->dir_type = ICON_THEME_DIR_UNTHEMED;
4868 info->dir_size = size;
4869 info->desired_size = size;
4870 info->threshold = 2;
4871 info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
4875 else if (G_IS_THEMED_ICON (icon))
4877 const gchar **names;
4879 names = (const gchar **)g_themed_icon_get_names (G_THEMED_ICON (icon));
4880 info = gtk_icon_theme_choose_icon (icon_theme, names, size, flags);
4884 else if (G_IS_EMBLEMED_ICON (icon))
4886 GIcon *base, *emblem;
4888 GtkIconInfo *emblem_info;
4890 if (GTK_IS_NUMERABLE_ICON (icon))
4891 _gtk_numerable_icon_set_background_icon_size (GTK_NUMERABLE_ICON (icon), size / 2);
4893 base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
4894 info = gtk_icon_theme_lookup_by_gicon (icon_theme, base, size, flags);
4897 list = g_emblemed_icon_get_emblems (G_EMBLEMED_ICON (icon));
4898 for (l = list; l; l = l->next)
4900 emblem = g_emblem_get_icon (G_EMBLEM (l->data));
4901 /* always force size for emblems */
4902 emblem_info = gtk_icon_theme_lookup_by_gicon (icon_theme, emblem, size / 2, flags | GTK_ICON_LOOKUP_FORCE_SIZE);
4904 info->emblem_infos = g_slist_prepend (info->emblem_infos, emblem_info);
4910 else if (GDK_IS_PIXBUF (icon))
4914 pixbuf = GDK_PIXBUF (icon);
4916 if ((flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0)
4918 gint width, height, max;
4922 width = gdk_pixbuf_get_width (pixbuf);
4923 height = gdk_pixbuf_get_height (pixbuf);
4924 max = MAX (width, height);
4925 scale = (gdouble) size / (gdouble) max;
4927 scaled = gdk_pixbuf_scale_simple (pixbuf,
4928 0.5 + width * scale,
4929 0.5 + height * scale,
4930 GDK_INTERP_BILINEAR);
4932 info = gtk_icon_info_new_for_pixbuf (icon_theme, scaled);
4934 g_object_unref (scaled);
4938 info = gtk_icon_info_new_for_pixbuf (icon_theme, pixbuf);
4948 * gtk_icon_info_new_for_pixbuf:
4949 * @icon_theme: a #GtkIconTheme
4950 * @pixbuf: the pixbuf to wrap in a #GtkIconInfo
4952 * Creates a #GtkIconInfo for a #GdkPixbuf.
4954 * Return value: (transfer full): a #GtkIconInfo
4959 gtk_icon_info_new_for_pixbuf (GtkIconTheme *icon_theme,
4964 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
4965 g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
4967 info = icon_info_new ();
4968 info->pixbuf = g_object_ref (pixbuf);
4970 info->dir_type = ICON_THEME_DIR_UNTHEMED;