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;
230 /* Information about the source
233 GtkIconTheme *in_cache;
237 GLoadableIcon *loadable;
238 GSList *emblem_infos;
240 /* Cache pixbuf (if there is any) */
241 GdkPixbuf *cache_pixbuf;
245 /* Information about the directory where
246 * the source was found
248 IconThemeDirType dir_type;
252 /* Parameters influencing the scaled icon
255 guint raw_coordinates : 1;
256 guint forced_size : 1;
257 guint emblems_applied : 1;
261 /* Cached information if we go ahead and try to load
265 GdkPixbuf *proxy_pixbuf;
269 SymbolicPixbufCache *symbolic_pixbuf_cache;
271 GtkRequisition *symbolic_pixbuf_size;
281 /* In search order */
287 IconThemeDirType type;
302 GHashTable *icon_data;
308 char *no_svg_filename;
320 time_t mtime; /* 0 == not existing or not a dir */
325 static void gtk_icon_theme_finalize (GObject *object);
326 static void theme_dir_destroy (IconThemeDir *dir);
328 static void theme_destroy (IconTheme *theme);
329 static GtkIconInfo *theme_lookup_icon (IconTheme *theme,
330 const char *icon_name,
333 gboolean use_default_icons);
334 static void theme_list_icons (IconTheme *theme,
337 static void theme_list_contexts (IconTheme *theme,
338 GHashTable *contexts);
339 static void theme_subdir_load (GtkIconTheme *icon_theme,
341 GKeyFile *theme_file,
343 static void do_theme_change (GtkIconTheme *icon_theme);
345 static void blow_themes (GtkIconTheme *icon_themes);
346 static gboolean rescan_themes (GtkIconTheme *icon_themes);
348 static void icon_data_free (GtkIconData *icon_data);
349 static void load_icon_data (IconThemeDir *dir,
353 static IconSuffix theme_dir_get_icon_suffix (IconThemeDir *dir,
354 const gchar *icon_name,
355 gboolean *has_icon_file);
358 static GtkIconInfo *icon_info_new (void);
359 static GtkIconInfo *icon_info_new_builtin (BuiltinIcon *icon);
361 static IconSuffix suffix_from_name (const char *name);
363 static BuiltinIcon *find_builtin_icon (const gchar *icon_name,
365 gint *min_difference_p,
366 gboolean *has_larger_p);
367 static void remove_from_lru_cache (GtkIconTheme *icon_theme,
368 GtkIconInfo *icon_info);
370 static guint signal_changed = 0;
372 static GHashTable *icon_theme_builtin_icons;
374 /* also used in gtkiconfactory.c */
375 GtkIconCache *_builtin_cache = NULL;
376 static GList *builtin_dirs = NULL;
379 icon_info_key_hash (gconstpointer _key)
381 const IconInfoKey *key = _key;
384 for (i = 0; key->icon_names[i] != NULL; i++)
385 h ^= g_str_hash (key->icon_names[i]);
387 h ^= key->size * 0x10001;
388 h ^= key->flags * 0x1000010;
394 icon_info_key_equal (gconstpointer _a,
397 const IconInfoKey *a = _a;
398 const IconInfoKey *b = _b;
401 if (a->size != b->size)
404 if (a->flags != b->flags)
408 a->icon_names[i] != NULL &&
409 b->icon_names[i] != NULL; i++)
411 if (strcmp (a->icon_names[i], b->icon_names[i]) != 0)
415 return a->icon_names[i] == NULL && b->icon_names[i] == NULL;
418 G_DEFINE_TYPE (GtkIconTheme, gtk_icon_theme, G_TYPE_OBJECT)
421 * gtk_icon_theme_new:
423 * Creates a new icon theme object. Icon theme objects are used
424 * to lookup up an icon by name in a particular icon theme.
425 * Usually, you'll want to use gtk_icon_theme_get_default()
426 * or gtk_icon_theme_get_for_screen() rather than creating
427 * a new icon theme object for scratch.
429 * Return value: the newly created #GtkIconTheme object.
434 gtk_icon_theme_new (void)
436 return g_object_new (GTK_TYPE_ICON_THEME, NULL);
440 * gtk_icon_theme_get_default:
442 * Gets the icon theme for the default screen. See
443 * gtk_icon_theme_get_for_screen().
445 * Return value: (transfer none): A unique #GtkIconTheme associated with
446 * the default screen. This icon theme is associated with
447 * the screen and can be used as long as the screen
448 * is open. Do not ref or unref it.
453 gtk_icon_theme_get_default (void)
455 return gtk_icon_theme_get_for_screen (gdk_screen_get_default ());
459 * gtk_icon_theme_get_for_screen:
460 * @screen: a #GdkScreen
462 * Gets the icon theme object associated with @screen; if this
463 * function has not previously been called for the given
464 * screen, a new icon theme object will be created and
465 * associated with the screen. Icon theme objects are
466 * fairly expensive to create, so using this function
467 * is usually a better choice than calling than gtk_icon_theme_new()
468 * and setting the screen yourself; by using this function
469 * a single icon theme object will be shared between users.
471 * Return value: (transfer none): A unique #GtkIconTheme associated with
472 * the given screen. This icon theme is associated with
473 * the screen and can be used as long as the screen
474 * is open. Do not ref or unref it.
479 gtk_icon_theme_get_for_screen (GdkScreen *screen)
481 GtkIconTheme *icon_theme;
483 g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
485 icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
488 GtkIconThemePrivate *priv;
490 icon_theme = gtk_icon_theme_new ();
491 gtk_icon_theme_set_screen (icon_theme, screen);
493 priv = icon_theme->priv;
494 priv->is_screen_singleton = TRUE;
496 g_object_set_data (G_OBJECT (screen), I_("gtk-icon-theme"), icon_theme);
503 gtk_icon_theme_class_init (GtkIconThemeClass *klass)
505 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
507 gobject_class->finalize = gtk_icon_theme_finalize;
510 * GtkIconTheme::changed:
511 * @icon_theme: the icon theme
513 * Emitted when the current icon theme is switched or GTK+ detects
514 * that a change has occurred in the contents of the current
517 signal_changed = g_signal_new (I_("changed"),
518 G_TYPE_FROM_CLASS (klass),
520 G_STRUCT_OFFSET (GtkIconThemeClass, changed),
522 g_cclosure_marshal_VOID__VOID,
525 g_type_class_add_private (klass, sizeof (GtkIconThemePrivate));
529 /* Callback when the display that the icon theme is attached to
530 * is closed; unset the screen, and if it's the unique theme
531 * for the screen, drop the reference
534 display_closed (GdkDisplay *display,
536 GtkIconTheme *icon_theme)
538 GtkIconThemePrivate *priv = icon_theme->priv;
539 GdkScreen *screen = priv->screen;
540 gboolean was_screen_singleton = priv->is_screen_singleton;
542 if (was_screen_singleton)
544 g_object_set_data (G_OBJECT (screen), I_("gtk-icon-theme"), NULL);
545 priv->is_screen_singleton = FALSE;
548 gtk_icon_theme_set_screen (icon_theme, NULL);
550 if (was_screen_singleton)
552 g_object_unref (icon_theme);
557 update_current_theme (GtkIconTheme *icon_theme)
559 #define theme_changed(_old, _new) \
560 ((_old && !_new) || (!_old && _new) || \
561 (_old && _new && strcmp (_old, _new) != 0))
562 GtkIconThemePrivate *priv = icon_theme->priv;
564 if (!priv->custom_theme)
567 gchar *fallback_theme = NULL;
568 gboolean changed = FALSE;
572 GtkSettings *settings = gtk_settings_get_for_screen (priv->screen);
573 g_object_get (settings,
574 "gtk-icon-theme-name", &theme,
575 "gtk-fallback-icon-theme", &fallback_theme, NULL);
578 /* ensure that the current theme (even when just the default)
579 * is searched before any fallback theme
581 if (!theme && fallback_theme)
582 theme = g_strdup (DEFAULT_THEME_NAME);
584 if (theme_changed (priv->current_theme, theme))
586 g_free (priv->current_theme);
587 priv->current_theme = theme;
593 if (theme_changed (priv->fallback_theme, fallback_theme))
595 g_free (priv->fallback_theme);
596 priv->fallback_theme = fallback_theme;
600 g_free (fallback_theme);
603 do_theme_change (icon_theme);
608 /* Callback when the icon theme GtkSetting changes
611 theme_changed (GtkSettings *settings,
613 GtkIconTheme *icon_theme)
615 update_current_theme (icon_theme);
619 unset_screen (GtkIconTheme *icon_theme)
621 GtkIconThemePrivate *priv = icon_theme->priv;
622 GtkSettings *settings;
627 settings = gtk_settings_get_for_screen (priv->screen);
628 display = gdk_screen_get_display (priv->screen);
630 g_signal_handlers_disconnect_by_func (display,
631 (gpointer) display_closed,
633 g_signal_handlers_disconnect_by_func (settings,
634 (gpointer) theme_changed,
642 * gtk_icon_theme_set_screen:
643 * @icon_theme: a #GtkIconTheme
644 * @screen: a #GdkScreen
646 * Sets the screen for an icon theme; the screen is used
647 * to track the user's currently configured icon theme,
648 * which might be different for different screens.
653 gtk_icon_theme_set_screen (GtkIconTheme *icon_theme,
656 GtkIconThemePrivate *priv;
657 GtkSettings *settings;
660 g_return_if_fail (GTK_ICON_THEME (icon_theme));
661 g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
663 priv = icon_theme->priv;
665 unset_screen (icon_theme);
669 display = gdk_screen_get_display (screen);
670 settings = gtk_settings_get_for_screen (screen);
672 priv->screen = screen;
674 g_signal_connect (display, "closed",
675 G_CALLBACK (display_closed), icon_theme);
676 g_signal_connect (settings, "notify::gtk-icon-theme-name",
677 G_CALLBACK (theme_changed), icon_theme);
678 g_signal_connect (settings, "notify::gtk-fallback-icon-theme-name",
679 G_CALLBACK (theme_changed), icon_theme);
682 update_current_theme (icon_theme);
685 /* Checks whether a loader for SVG files has been registered
689 pixbuf_supports_svg (void)
693 static gint found_svg = -1;
698 formats = gdk_pixbuf_get_formats ();
701 for (tmp_list = formats; tmp_list && !found_svg; tmp_list = tmp_list->next)
703 gchar **mime_types = gdk_pixbuf_format_get_mime_types (tmp_list->data);
706 for (mime_type = mime_types; *mime_type && !found_svg; mime_type++)
708 if (strcmp (*mime_type, "image/svg") == 0)
712 g_strfreev (mime_types);
715 g_slist_free (formats);
720 /* The icon info was removed from the icon_info_hash hash table */
722 icon_info_uncached (GtkIconInfo *icon_info)
724 GtkIconTheme *icon_theme = icon_info->in_cache;
726 DEBUG_CACHE (("removing %p (%s %d 0x%x) from cache (icon_them: %p) (cache size %d)\n",
728 g_strjoinv (",", icon_info->key.icon_names),
729 icon_info->key.size, icon_info->key.flags,
731 icon_theme != NULL ? g_hash_table_size (icon_theme->priv->info_cache) : 0));
733 icon_info->in_cache = NULL;
735 if (icon_theme != NULL)
736 remove_from_lru_cache (icon_theme, icon_info);
740 gtk_icon_theme_init (GtkIconTheme *icon_theme)
742 GtkIconThemePrivate *priv;
743 const gchar * const *xdg_data_dirs;
746 priv = G_TYPE_INSTANCE_GET_PRIVATE (icon_theme,
748 GtkIconThemePrivate);
749 icon_theme->priv = priv;
751 priv->info_cache = g_hash_table_new_full (icon_info_key_hash, icon_info_key_equal, NULL,
752 (GDestroyNotify)icon_info_uncached);
754 priv->custom_theme = FALSE;
756 xdg_data_dirs = g_get_system_data_dirs ();
757 for (i = 0; xdg_data_dirs[i]; i++) ;
759 priv->search_path_len = 2 * i + 2;
761 priv->search_path = g_new (char *, priv->search_path_len);
764 priv->search_path[i++] = g_build_filename (g_get_user_data_dir (), "icons", NULL);
765 priv->search_path[i++] = g_build_filename (g_get_home_dir (), ".icons", NULL);
767 for (j = 0; xdg_data_dirs[j]; j++)
768 priv->search_path[i++] = g_build_filename (xdg_data_dirs[j], "icons", NULL);
770 for (j = 0; xdg_data_dirs[j]; j++)
771 priv->search_path[i++] = g_build_filename (xdg_data_dirs[j], "pixmaps", NULL);
773 priv->themes_valid = FALSE;
775 priv->unthemed_icons = NULL;
777 priv->pixbuf_supports_svg = pixbuf_supports_svg ();
781 free_dir_mtime (IconThemeDirMtime *dir_mtime)
783 if (dir_mtime->cache)
784 _gtk_icon_cache_unref (dir_mtime->cache);
786 g_free (dir_mtime->dir);
787 g_slice_free (IconThemeDirMtime, dir_mtime);
792 reset_styles_idle (gpointer user_data)
794 GtkIconTheme *icon_theme;
795 GtkIconThemePrivate *priv;
797 icon_theme = GTK_ICON_THEME (user_data);
798 priv = icon_theme->priv;
800 if (priv->screen && priv->is_screen_singleton)
801 gtk_style_context_reset_widgets (priv->screen);
803 priv->reset_styles_idle = 0;
809 do_theme_change (GtkIconTheme *icon_theme)
811 GtkIconThemePrivate *priv = icon_theme->priv;
813 g_hash_table_remove_all (priv->info_cache);
815 if (!priv->themes_valid)
819 g_print ("change to icon theme \"%s\"\n", priv->current_theme));
820 blow_themes (icon_theme);
821 g_signal_emit (icon_theme, signal_changed, 0);
823 if (!priv->reset_styles_idle)
824 priv->reset_styles_idle =
825 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2,
826 reset_styles_idle, icon_theme, NULL);
830 blow_themes (GtkIconTheme *icon_theme)
832 GtkIconThemePrivate *priv = icon_theme->priv;
834 if (priv->themes_valid)
836 g_hash_table_destroy (priv->all_icons);
837 g_list_free_full (priv->themes, (GDestroyNotify) theme_destroy);
838 g_list_free_full (priv->dir_mtimes, (GDestroyNotify) free_dir_mtime);
839 g_hash_table_destroy (priv->unthemed_icons);
842 priv->unthemed_icons = NULL;
843 priv->dir_mtimes = NULL;
844 priv->all_icons = NULL;
845 priv->themes_valid = FALSE;
849 gtk_icon_theme_finalize (GObject *object)
851 GtkIconTheme *icon_theme;
852 GtkIconThemePrivate *priv;
855 icon_theme = GTK_ICON_THEME (object);
856 priv = icon_theme->priv;
858 g_hash_table_destroy (priv->info_cache);
859 g_assert (priv->info_cache_lru == NULL);
861 if (priv->reset_styles_idle)
863 g_source_remove (priv->reset_styles_idle);
864 priv->reset_styles_idle = 0;
867 unset_screen (icon_theme);
869 g_free (priv->current_theme);
870 priv->current_theme = NULL;
872 for (i = 0; i < priv->search_path_len; i++)
873 g_free (priv->search_path[i]);
875 g_free (priv->search_path);
876 priv->search_path = NULL;
878 blow_themes (icon_theme);
880 G_OBJECT_CLASS (gtk_icon_theme_parent_class)->finalize (object);
884 * gtk_icon_theme_set_search_path:
885 * @icon_theme: a #GtkIconTheme
886 * @path: (array length=n_elements) (element-type filename): array of
887 * directories that are searched for icon themes
888 * @n_elements: number of elements in @path.
890 * Sets the search path for the icon theme object. When looking
891 * for an icon theme, GTK+ will search for a subdirectory of
892 * one or more of the directories in @path with the same name
893 * as the icon theme. (Themes from multiple of the path elements
894 * are combined to allow themes to be extended by adding icons
895 * in the user's home directory.)
897 * In addition if an icon found isn't found either in the current
898 * icon theme or the default icon theme, and an image file with
899 * the right name is found directly in one of the elements of
900 * @path, then that image will be used for the icon name.
901 * (This is legacy feature, and new icons should be put
902 * into the default icon theme, which is called DEFAULT_THEME_NAME,
903 * rather than directly on the icon path.)
908 gtk_icon_theme_set_search_path (GtkIconTheme *icon_theme,
912 GtkIconThemePrivate *priv;
915 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
917 priv = icon_theme->priv;
918 for (i = 0; i < priv->search_path_len; i++)
919 g_free (priv->search_path[i]);
921 g_free (priv->search_path);
923 priv->search_path = g_new (gchar *, n_elements);
924 priv->search_path_len = n_elements;
926 for (i = 0; i < priv->search_path_len; i++)
927 priv->search_path[i] = g_strdup (path[i]);
929 do_theme_change (icon_theme);
934 * gtk_icon_theme_get_search_path:
935 * @icon_theme: a #GtkIconTheme
936 * @path: (allow-none) (array length=n_elements) (element-type filename) (out):
937 * location to store a list of icon theme path directories or %NULL.
938 * The stored value should be freed with g_strfreev().
939 * @n_elements: location to store number of elements in @path, or %NULL
941 * Gets the current search path. See gtk_icon_theme_set_search_path().
946 gtk_icon_theme_get_search_path (GtkIconTheme *icon_theme,
950 GtkIconThemePrivate *priv;
953 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
955 priv = icon_theme->priv;
958 *n_elements = priv->search_path_len;
962 *path = g_new (gchar *, priv->search_path_len + 1);
963 for (i = 0; i < priv->search_path_len; i++)
964 (*path)[i] = g_strdup (priv->search_path[i]);
970 * gtk_icon_theme_append_search_path:
971 * @icon_theme: a #GtkIconTheme
972 * @path: (type filename): directory name to append to the icon path
974 * Appends a directory to the search path.
975 * See gtk_icon_theme_set_search_path().
980 gtk_icon_theme_append_search_path (GtkIconTheme *icon_theme,
983 GtkIconThemePrivate *priv;
985 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
986 g_return_if_fail (path != NULL);
988 priv = icon_theme->priv;
990 priv->search_path_len++;
992 priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
993 priv->search_path[priv->search_path_len-1] = g_strdup (path);
995 do_theme_change (icon_theme);
999 * gtk_icon_theme_prepend_search_path:
1000 * @icon_theme: a #GtkIconTheme
1001 * @path: (type filename): directory name to prepend to the icon path
1003 * Prepends a directory to the search path.
1004 * See gtk_icon_theme_set_search_path().
1009 gtk_icon_theme_prepend_search_path (GtkIconTheme *icon_theme,
1012 GtkIconThemePrivate *priv;
1015 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
1016 g_return_if_fail (path != NULL);
1018 priv = icon_theme->priv;
1020 priv->search_path_len++;
1021 priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
1023 for (i = priv->search_path_len - 1; i > 0; i--)
1024 priv->search_path[i] = priv->search_path[i - 1];
1026 priv->search_path[0] = g_strdup (path);
1028 do_theme_change (icon_theme);
1032 * gtk_icon_theme_set_custom_theme:
1033 * @icon_theme: a #GtkIconTheme
1034 * @theme_name: (allow-none): name of icon theme to use instead of
1035 * configured theme, or %NULL to unset a previously set custom theme
1037 * Sets the name of the icon theme that the #GtkIconTheme object uses
1038 * overriding system configuration. This function cannot be called
1039 * on the icon theme objects returned from gtk_icon_theme_get_default()
1040 * and gtk_icon_theme_get_for_screen().
1045 gtk_icon_theme_set_custom_theme (GtkIconTheme *icon_theme,
1046 const gchar *theme_name)
1048 GtkIconThemePrivate *priv;
1050 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
1052 priv = icon_theme->priv;
1054 g_return_if_fail (!priv->is_screen_singleton);
1056 if (theme_name != NULL)
1058 priv->custom_theme = TRUE;
1059 if (!priv->current_theme || strcmp (theme_name, priv->current_theme) != 0)
1061 g_free (priv->current_theme);
1062 priv->current_theme = g_strdup (theme_name);
1064 do_theme_change (icon_theme);
1069 if (priv->custom_theme)
1071 priv->custom_theme = FALSE;
1073 update_current_theme (icon_theme);
1079 insert_theme (GtkIconTheme *icon_theme, const char *theme_name)
1085 GtkIconThemePrivate *priv;
1086 IconTheme *theme = NULL;
1088 GKeyFile *theme_file;
1089 GError *error = NULL;
1090 IconThemeDirMtime *dir_mtime;
1093 priv = icon_theme->priv;
1095 for (l = priv->themes; l != NULL; l = l->next)
1098 if (strcmp (theme->name, theme_name) == 0)
1102 for (i = 0; i < priv->search_path_len; i++)
1104 path = g_build_filename (priv->search_path[i],
1107 dir_mtime = g_slice_new (IconThemeDirMtime);
1108 dir_mtime->cache = NULL;
1109 dir_mtime->dir = path;
1110 if (g_stat (path, &stat_buf) == 0 && S_ISDIR (stat_buf.st_mode))
1111 dir_mtime->mtime = stat_buf.st_mtime;
1113 dir_mtime->mtime = 0;
1115 priv->dir_mtimes = g_list_prepend (priv->dir_mtimes, dir_mtime);
1117 priv->dir_mtimes = g_list_reverse (priv->dir_mtimes);
1120 for (i = 0; i < priv->search_path_len && !theme_file; i++)
1122 path = g_build_filename (priv->search_path[i],
1126 if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
1128 theme_file = g_key_file_new ();
1129 g_key_file_set_list_separator (theme_file, ',');
1130 g_key_file_load_from_file (theme_file, path, 0, &error);
1133 g_key_file_free (theme_file);
1135 g_error_free (error);
1142 if (theme_file || strcmp (theme_name, DEFAULT_THEME_NAME) == 0)
1144 theme = g_new0 (IconTheme, 1);
1145 theme->name = g_strdup (theme_name);
1146 priv->themes = g_list_prepend (priv->themes, theme);
1149 if (theme_file == NULL)
1152 theme->display_name =
1153 g_key_file_get_locale_string (theme_file, "Icon Theme", "Name", NULL, NULL);
1154 if (!theme->display_name)
1155 g_warning ("Theme file for %s has no name\n", theme_name);
1157 dirs = g_key_file_get_string_list (theme_file, "Icon Theme", "Directories", NULL, NULL);
1160 g_warning ("Theme file for %s has no directories\n", theme_name);
1161 priv->themes = g_list_remove (priv->themes, theme);
1162 g_free (theme->name);
1163 g_free (theme->display_name);
1165 g_key_file_free (theme_file);
1170 g_key_file_get_locale_string (theme_file,
1171 "Icon Theme", "Comment",
1174 g_key_file_get_string (theme_file,
1175 "Icon Theme", "Example",
1179 for (i = 0; dirs[i] != NULL; i++)
1180 theme_subdir_load (icon_theme, theme, theme_file, dirs[i]);
1184 theme->dirs = g_list_reverse (theme->dirs);
1186 themes = g_key_file_get_string_list (theme_file,
1193 for (i = 0; themes[i] != NULL; i++)
1194 insert_theme (icon_theme, themes[i]);
1196 g_strfreev (themes);
1199 g_key_file_free (theme_file);
1203 free_unthemed_icon (UnthemedIcon *unthemed_icon)
1205 g_free (unthemed_icon->svg_filename);
1206 g_free (unthemed_icon->no_svg_filename);
1207 g_slice_free (UnthemedIcon, unthemed_icon);
1211 strip_suffix (const char *filename)
1215 dot = strrchr (filename, '.');
1218 return g_strdup (filename);
1220 return g_strndup (filename, dot - filename);
1224 load_themes (GtkIconTheme *icon_theme)
1226 GtkIconThemePrivate *priv;
1231 UnthemedIcon *unthemed_icon;
1232 IconSuffix old_suffix, new_suffix;
1234 IconThemeDirMtime *dir_mtime;
1237 priv = icon_theme->priv;
1239 priv->all_icons = g_hash_table_new (g_str_hash, g_str_equal);
1241 if (priv->current_theme)
1242 insert_theme (icon_theme, priv->current_theme);
1244 /* Always look in the "default" icon theme, and in a fallback theme */
1245 if (priv->fallback_theme)
1246 insert_theme (icon_theme, priv->fallback_theme);
1247 insert_theme (icon_theme, DEFAULT_THEME_NAME);
1248 priv->themes = g_list_reverse (priv->themes);
1251 priv->unthemed_icons = g_hash_table_new_full (g_str_hash, g_str_equal,
1252 g_free, (GDestroyNotify)free_unthemed_icon);
1254 for (base = 0; base < icon_theme->priv->search_path_len; base++)
1256 dir = icon_theme->priv->search_path[base];
1258 dir_mtime = g_slice_new (IconThemeDirMtime);
1259 priv->dir_mtimes = g_list_append (priv->dir_mtimes, dir_mtime);
1261 dir_mtime->dir = g_strdup (dir);
1262 dir_mtime->mtime = 0;
1263 dir_mtime->cache = NULL;
1265 if (g_stat (dir, &stat_buf) != 0 || !S_ISDIR (stat_buf.st_mode))
1267 dir_mtime->mtime = stat_buf.st_mtime;
1269 dir_mtime->cache = _gtk_icon_cache_new_for_path (dir);
1270 if (dir_mtime->cache != NULL)
1273 gdir = g_dir_open (dir, 0, NULL);
1277 while ((file = g_dir_read_name (gdir)))
1279 new_suffix = suffix_from_name (file);
1281 if (new_suffix != ICON_SUFFIX_NONE)
1286 abs_file = g_build_filename (dir, file, NULL);
1287 base_name = strip_suffix (file);
1289 if ((unthemed_icon = g_hash_table_lookup (priv->unthemed_icons,
1292 if (new_suffix == ICON_SUFFIX_SVG)
1294 if (unthemed_icon->svg_filename)
1297 unthemed_icon->svg_filename = abs_file;
1301 if (unthemed_icon->no_svg_filename)
1303 old_suffix = suffix_from_name (unthemed_icon->no_svg_filename);
1304 if (new_suffix > old_suffix)
1306 g_free (unthemed_icon->no_svg_filename);
1307 unthemed_icon->no_svg_filename = abs_file;
1313 unthemed_icon->no_svg_filename = abs_file;
1320 unthemed_icon = g_slice_new0 (UnthemedIcon);
1322 if (new_suffix == ICON_SUFFIX_SVG)
1323 unthemed_icon->svg_filename = abs_file;
1325 unthemed_icon->no_svg_filename = abs_file;
1327 /* takes ownership of base_name */
1328 g_hash_table_replace (priv->unthemed_icons,
1331 g_hash_table_insert (priv->all_icons,
1339 priv->themes_valid = TRUE;
1341 g_get_current_time(&tv);
1342 priv->last_stat_time = tv.tv_sec;
1346 _gtk_icon_theme_ensure_builtin_cache (void)
1348 static gboolean initialized = FALSE;
1350 static IconThemeDir dirs[5] =
1352 { ICON_THEME_DIR_THRESHOLD, 0, 16, 16, 16, 2, NULL, "16", -1, NULL, NULL, NULL },
1353 { ICON_THEME_DIR_THRESHOLD, 0, 20, 20, 20, 2, NULL, "20", -1, NULL, NULL, NULL },
1354 { ICON_THEME_DIR_THRESHOLD, 0, 24, 24, 24, 2, NULL, "24", -1, NULL, NULL, NULL },
1355 { ICON_THEME_DIR_THRESHOLD, 0, 32, 32, 32, 2, NULL, "32", -1, NULL, NULL, NULL },
1356 { ICON_THEME_DIR_THRESHOLD, 0, 48, 48, 48, 2, NULL, "48", -1, NULL, NULL, NULL }
1364 _builtin_cache = _gtk_icon_cache_new ((gchar *)builtin_icons);
1366 for (i = 0; i < G_N_ELEMENTS (dirs); i++)
1369 dir->cache = _gtk_icon_cache_ref (_builtin_cache);
1370 dir->subdir_index = _gtk_icon_cache_get_directory_index (dir->cache, dir->subdir);
1372 builtin_dirs = g_list_append (builtin_dirs, dir);
1378 ensure_valid_themes (GtkIconTheme *icon_theme)
1380 GtkIconThemePrivate *priv = icon_theme->priv;
1382 gboolean was_valid = priv->themes_valid;
1384 if (priv->loading_themes)
1386 priv->loading_themes = TRUE;
1388 _gtk_icon_theme_ensure_builtin_cache ();
1390 if (priv->themes_valid)
1392 g_get_current_time (&tv);
1394 if (ABS (tv.tv_sec - priv->last_stat_time) > 5 &&
1395 rescan_themes (icon_theme))
1396 blow_themes (icon_theme);
1399 if (!priv->themes_valid)
1401 load_themes (icon_theme);
1405 g_signal_emit (icon_theme, signal_changed, 0);
1409 priv->loading_themes = FALSE;
1412 /* The LRU cache is a short list of IconInfos that are kept
1413 alive even though their IconInfo would otherwise have
1414 been freed, so that we can avoid reloading these
1416 We put infos on the lru list when nothing otherwise
1417 references the info. So, when we get a cache hit
1418 we remove it from the list, and when the proxy
1419 pixmap is released we put it on the list.
1423 ensure_lru_cache_space (GtkIconTheme *icon_theme)
1425 GtkIconThemePrivate *priv = icon_theme->priv;
1428 /* Remove last item if LRU full */
1429 l = g_list_nth (priv->info_cache_lru, INFO_CACHE_LRU_SIZE - 1);
1432 GtkIconInfo *icon_info = l->data;
1434 DEBUG_CACHE (("removing (due to out of space) %p (%s %d 0x%x) from LRU cache (cache size %d)\n",
1436 g_strjoinv (",", icon_info->key.icon_names),
1437 icon_info->key.size, icon_info->key.flags,
1438 g_list_length (priv->info_cache_lru)));
1440 priv->info_cache_lru = g_list_delete_link (priv->info_cache_lru, l);
1441 gtk_icon_info_free (icon_info);
1446 add_to_lru_cache (GtkIconTheme *icon_theme,
1447 GtkIconInfo *icon_info)
1449 GtkIconThemePrivate *priv = icon_theme->priv;
1451 DEBUG_CACHE (("adding %p (%s %d 0x%x) to LRU cache (cache size %d)\n",
1453 g_strjoinv (",", icon_info->key.icon_names),
1454 icon_info->key.size, icon_info->key.flags,
1455 g_list_length (priv->info_cache_lru)));
1457 g_assert (g_list_find (priv->info_cache_lru, icon_info) == NULL);
1459 ensure_lru_cache_space (icon_theme);
1460 /* prepend new info to LRU */
1461 priv->info_cache_lru = g_list_prepend (priv->info_cache_lru,
1462 gtk_icon_info_copy (icon_info));
1466 ensure_in_lru_cache (GtkIconTheme *icon_theme,
1467 GtkIconInfo *icon_info)
1469 GtkIconThemePrivate *priv = icon_theme->priv;
1472 l = g_list_find (priv->info_cache_lru, icon_info);
1475 /* Move to front of LRU if already in it */
1476 priv->info_cache_lru = g_list_remove_link (priv->info_cache_lru, l);
1477 priv->info_cache_lru = g_list_concat (l, priv->info_cache_lru);
1480 add_to_lru_cache (icon_theme, icon_info);
1484 remove_from_lru_cache (GtkIconTheme *icon_theme,
1485 GtkIconInfo *icon_info)
1487 GtkIconThemePrivate *priv = icon_theme->priv;
1488 if (g_list_find (priv->info_cache_lru, icon_info))
1490 DEBUG_CACHE (("removing %p (%s %d 0x%x) from LRU cache (cache size %d)\n",
1492 g_strjoinv (",", icon_info->key.icon_names),
1493 icon_info->key.size, icon_info->key.flags,
1494 g_list_length (priv->info_cache_lru)));
1496 priv->info_cache_lru = g_list_remove (priv->info_cache_lru, icon_info);
1497 gtk_icon_info_free (icon_info);
1501 static SymbolicPixbufCache *
1502 symbolic_pixbuf_cache_new (GdkPixbuf *pixbuf,
1504 const GdkRGBA *success_color,
1505 const GdkRGBA *warning_color,
1506 const GdkRGBA *error_color,
1507 SymbolicPixbufCache *next)
1509 SymbolicPixbufCache *cache;
1511 cache = g_new0 (SymbolicPixbufCache, 1);
1512 cache->pixbuf = g_object_ref (pixbuf);
1516 cache->success_color = *success_color;
1518 cache->warning_color = *warning_color;
1520 cache->error_color = *error_color;
1526 rgba_matches (const GdkRGBA *a, const GdkRGBA *b)
1528 GdkRGBA transparent = { 0 };
1530 /* For matching we treat unset colors as transparent rather
1531 than default, which works as well, because transparent
1532 will never be used for real symbolic icon colors */
1537 fabs(a->red - b->red) < 0.0001 &&
1538 fabs(a->green - b->green) < 0.0001 &&
1539 fabs(a->blue - b->blue) < 0.0001 &&
1540 fabs(a->alpha - b->alpha) < 0.0001;
1543 static SymbolicPixbufCache *
1544 symbolic_pixbuf_cache_matches (SymbolicPixbufCache *cache,
1546 const GdkRGBA *success_color,
1547 const GdkRGBA *warning_color,
1548 const GdkRGBA *error_color)
1550 while (cache != NULL)
1552 if (rgba_matches (fg, &cache->fg) &&
1553 rgba_matches (success_color, &cache->success_color) &&
1554 rgba_matches (warning_color, &cache->warning_color) &&
1555 rgba_matches (error_color, &cache->error_color))
1558 cache = cache->next;
1565 symbolic_pixbuf_cache_free (SymbolicPixbufCache *cache)
1567 SymbolicPixbufCache *next;
1569 while (cache != NULL)
1572 g_object_unref (cache->pixbuf);
1579 static GtkIconInfo *
1580 choose_icon (GtkIconTheme *icon_theme,
1581 const gchar *icon_names[],
1583 GtkIconLookupFlags flags)
1585 GtkIconThemePrivate *priv;
1587 GtkIconInfo *icon_info = NULL;
1588 UnthemedIcon *unthemed_icon = NULL;
1590 gboolean use_builtin;
1594 priv = icon_theme->priv;
1596 ensure_valid_themes (icon_theme);
1598 key.icon_names = (char **)icon_names;
1602 icon_info = g_hash_table_lookup (priv->info_cache, &key);
1603 if (icon_info != NULL)
1605 DEBUG_CACHE (("cache hit %p (%s %d 0x%x) (cache size %d)\n",
1607 g_strjoinv (",", icon_info->key.icon_names),
1608 icon_info->key.size, icon_info->key.flags,
1609 g_hash_table_size (priv->info_cache)));
1611 icon_info = gtk_icon_info_copy (icon_info);
1612 remove_from_lru_cache (icon_theme, icon_info);
1617 if (flags & GTK_ICON_LOOKUP_NO_SVG)
1619 else if (flags & GTK_ICON_LOOKUP_FORCE_SVG)
1622 allow_svg = priv->pixbuf_supports_svg;
1624 use_builtin = flags & GTK_ICON_LOOKUP_USE_BUILTIN;
1626 /* for symbolic icons, do a search in all registered themes first;
1627 * a theme that inherits them from a parent theme might provide
1628 * an alternative highcolor version, but still expect the symbolic icon
1629 * to show up instead.
1631 if (icon_names[0] &&
1632 g_str_has_suffix (icon_names[0], "-symbolic"))
1634 for (l = priv->themes; l; l = l->next)
1636 IconTheme *theme = l->data;
1637 icon_info = theme_lookup_icon (theme, icon_names[0], size, allow_svg, use_builtin);
1643 for (l = priv->themes; l; l = l->next)
1645 IconTheme *theme = l->data;
1647 for (i = 0; icon_names[i]; i++)
1649 icon_info = theme_lookup_icon (theme, icon_names[i], size, allow_svg, use_builtin);
1655 for (i = 0; icon_names[i]; i++)
1657 unthemed_icon = g_hash_table_lookup (priv->unthemed_icons, icon_names[i]);
1662 /* Still not found an icon, check if reference to a Win32 resource */
1668 resources = g_strsplit (icon_names[0], ",", 0);
1671 wchar_t *wfile = g_utf8_to_utf16 (resources[0], -1, NULL, NULL, NULL);
1672 ExtractIconExW (wfile, resources[1] ? atoi (resources[1]) : 0, &hIcon, NULL, 1);
1678 icon_info = icon_info_new ();
1679 icon_info->cache_pixbuf = gdk_win32_icon_to_pixbuf_libgtk_only (hIcon);
1680 DestroyIcon (hIcon);
1681 icon_info->dir_type = ICON_THEME_DIR_UNTHEMED;
1682 icon_info->dir_size = size;
1684 g_strfreev (resources);
1690 icon_info = icon_info_new ();
1692 /* A SVG icon, when allowed, beats out a XPM icon, but not
1696 unthemed_icon->svg_filename &&
1697 (!unthemed_icon->no_svg_filename ||
1698 suffix_from_name (unthemed_icon->no_svg_filename) != ICON_SUFFIX_PNG))
1699 icon_info->filename = g_strdup (unthemed_icon->svg_filename);
1700 else if (unthemed_icon->no_svg_filename)
1701 icon_info->filename = g_strdup (unthemed_icon->no_svg_filename);
1703 icon_info->icon_file = g_file_new_for_path (icon_info->filename);
1705 icon_info->dir_type = ICON_THEME_DIR_UNTHEMED;
1706 icon_info->dir_size = size;
1712 icon_info->desired_size = size;
1713 icon_info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
1715 icon_info->key.icon_names = g_strdupv ((char **)icon_names);
1716 icon_info->key.size = size;
1717 icon_info->key.flags = flags;
1718 icon_info->in_cache = icon_theme;
1719 DEBUG_CACHE (("adding %p (%s %d 0x%x) to cache (cache size %d)\n",
1721 g_strjoinv (",", icon_info->key.icon_names),
1722 icon_info->key.size, icon_info->key.flags,
1723 g_hash_table_size (priv->info_cache)));
1724 g_hash_table_insert (priv->info_cache, &icon_info->key, icon_info);
1728 static gboolean check_for_default_theme = TRUE;
1729 char *default_theme_path;
1730 gboolean found = FALSE;
1733 if (check_for_default_theme)
1735 check_for_default_theme = FALSE;
1737 for (i = 0; !found && i < priv->search_path_len; i++)
1739 default_theme_path = g_build_filename (priv->search_path[i],
1743 found = g_file_test (default_theme_path, G_FILE_TEST_IS_REGULAR);
1744 g_free (default_theme_path);
1749 g_warning ("Could not find the icon '%s'. The '%s' theme\n"
1750 "was not found either, perhaps you need to install it.\n"
1751 "You can get a copy from:\n"
1753 icon_names[0], DEFAULT_THEME_NAME, "http://icon-theme.freedesktop.org/releases");
1763 * gtk_icon_theme_lookup_icon:
1764 * @icon_theme: a #GtkIconTheme
1765 * @icon_name: the name of the icon to lookup
1766 * @size: desired icon size
1767 * @flags: flags modifying the behavior of the icon lookup
1769 * Looks up a named icon and returns a structure containing
1770 * information such as the filename of the icon. The icon
1771 * can then be rendered into a pixbuf using
1772 * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
1773 * combines these two steps if all you need is the pixbuf.)
1775 * Return value: a #GtkIconInfo structure containing information
1776 * about the icon, or %NULL if the icon wasn't found. Free with
1777 * gtk_icon_info_free()
1782 gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme,
1783 const gchar *icon_name,
1785 GtkIconLookupFlags flags)
1789 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1790 g_return_val_if_fail (icon_name != NULL, NULL);
1791 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1792 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1794 GTK_NOTE (ICONTHEME,
1795 g_print ("gtk_icon_theme_lookup_icon %s\n", icon_name));
1797 if (flags & GTK_ICON_LOOKUP_GENERIC_FALLBACK)
1804 for (p = (gchar *) icon_name; *p; p++)
1808 names = g_new (gchar *, dashes + 2);
1809 names[0] = g_strdup (icon_name);
1810 for (i = 1; i <= dashes; i++)
1812 names[i] = g_strdup (names[i - 1]);
1813 p = strrchr (names[i], '-');
1816 names[dashes + 1] = NULL;
1818 info = choose_icon (icon_theme, (const gchar **) names, size, flags);
1824 const gchar *names[2];
1826 names[0] = icon_name;
1829 info = choose_icon (icon_theme, names, size, flags);
1836 * gtk_icon_theme_choose_icon:
1837 * @icon_theme: a #GtkIconTheme
1838 * @icon_names: (array zero-terminated=1): %NULL-terminated array of
1839 * icon names to lookup
1840 * @size: desired icon size
1841 * @flags: flags modifying the behavior of the icon lookup
1843 * Looks up a named icon and returns a structure containing
1844 * information such as the filename of the icon. The icon
1845 * can then be rendered into a pixbuf using
1846 * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
1847 * combines these two steps if all you need is the pixbuf.)
1849 * If @icon_names contains more than one name, this function
1850 * tries them all in the given order before falling back to
1851 * inherited icon themes.
1853 * Return value: a #GtkIconInfo structure containing information
1854 * about the icon, or %NULL if the icon wasn't found. Free with
1855 * gtk_icon_info_free()
1860 gtk_icon_theme_choose_icon (GtkIconTheme *icon_theme,
1861 const gchar *icon_names[],
1863 GtkIconLookupFlags flags)
1865 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1866 g_return_val_if_fail (icon_names != NULL, NULL);
1867 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1868 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1870 return choose_icon (icon_theme, icon_names, size, flags);
1875 gtk_icon_theme_error_quark (void)
1877 return g_quark_from_static_string ("gtk-icon-theme-error-quark");
1881 * gtk_icon_theme_load_icon:
1882 * @icon_theme: a #GtkIconTheme
1883 * @icon_name: the name of the icon to lookup
1884 * @size: the desired icon size. The resulting icon may not be
1885 * exactly this size; see gtk_icon_info_load_icon().
1886 * @flags: flags modifying the behavior of the icon lookup
1887 * @error: (allow-none): Location to store error information on failure,
1890 * Looks up an icon in an icon theme, scales it to the given size
1891 * and renders it into a pixbuf. This is a convenience function;
1892 * if more details about the icon are needed, use
1893 * gtk_icon_theme_lookup_icon() followed by gtk_icon_info_load_icon().
1895 * Note that you probably want to listen for icon theme changes and
1896 * update the icon. This is usually done by connecting to the
1897 * GtkWidget::style-set signal. If for some reason you do not want to
1898 * update the icon when the icon theme changes, you should consider
1899 * using gdk_pixbuf_copy() to make a private copy of the pixbuf
1900 * returned by this function. Otherwise GTK+ may need to keep the old
1901 * icon theme loaded, which would be a waste of memory.
1903 * Return value: (transfer full): the rendered icon; this may be a
1904 * newly created icon or a new reference to an internal icon, so
1905 * you must not modify the icon. Use g_object_unref() to release
1906 * your reference to the icon. %NULL if the icon isn't found.
1911 gtk_icon_theme_load_icon (GtkIconTheme *icon_theme,
1912 const gchar *icon_name,
1914 GtkIconLookupFlags flags,
1917 GtkIconInfo *icon_info;
1918 GdkPixbuf *pixbuf = NULL;
1920 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1921 g_return_val_if_fail (icon_name != NULL, NULL);
1922 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1923 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1924 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1926 icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size,
1927 flags | GTK_ICON_LOOKUP_USE_BUILTIN);
1930 g_set_error (error, GTK_ICON_THEME_ERROR, GTK_ICON_THEME_NOT_FOUND,
1931 _("Icon '%s' not present in theme"), icon_name);
1935 pixbuf = gtk_icon_info_load_icon (icon_info, error);
1936 gtk_icon_info_free (icon_info);
1942 * gtk_icon_theme_has_icon:
1943 * @icon_theme: a #GtkIconTheme
1944 * @icon_name: the name of an icon
1946 * Checks whether an icon theme includes an icon
1947 * for a particular name.
1949 * Return value: %TRUE if @icon_theme includes an
1950 * icon for @icon_name.
1955 gtk_icon_theme_has_icon (GtkIconTheme *icon_theme,
1956 const char *icon_name)
1958 GtkIconThemePrivate *priv;
1961 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
1962 g_return_val_if_fail (icon_name != NULL, FALSE);
1964 priv = icon_theme->priv;
1966 ensure_valid_themes (icon_theme);
1968 for (l = priv->dir_mtimes; l; l = l->next)
1970 IconThemeDirMtime *dir_mtime = l->data;
1971 GtkIconCache *cache = dir_mtime->cache;
1973 if (cache && _gtk_icon_cache_has_icon (cache, icon_name))
1977 if (g_hash_table_lookup_extended (priv->all_icons,
1978 icon_name, NULL, NULL))
1981 if (_builtin_cache &&
1982 _gtk_icon_cache_has_icon (_builtin_cache, icon_name))
1985 if (icon_theme_builtin_icons &&
1986 g_hash_table_lookup_extended (icon_theme_builtin_icons,
1987 icon_name, NULL, NULL))
1994 add_size (gpointer key,
1998 gint **res_p = user_data;
2000 **res_p = GPOINTER_TO_INT (key);
2006 * gtk_icon_theme_get_icon_sizes:
2007 * @icon_theme: a #GtkIconTheme
2008 * @icon_name: the name of an icon
2010 * Returns an array of integers describing the sizes at which
2011 * the icon is available without scaling. A size of -1 means
2012 * that the icon is available in a scalable format. The array
2013 * is zero-terminated.
2015 * Return value: (array zero-terminated=1): An newly allocated array
2016 * describing the sizes at which the icon is available. The array
2017 * should be freed with g_free() when it is no longer needed.
2022 gtk_icon_theme_get_icon_sizes (GtkIconTheme *icon_theme,
2023 const char *icon_name)
2025 GList *l, *d, *icons;
2029 GtkIconThemePrivate *priv;
2031 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2033 priv = icon_theme->priv;
2035 ensure_valid_themes (icon_theme);
2037 sizes = g_hash_table_new (g_direct_hash, g_direct_equal);
2039 for (l = priv->themes; l; l = l->next)
2041 IconTheme *theme = l->data;
2042 for (d = theme->dirs; d; d = d->next)
2044 IconThemeDir *dir = d->data;
2046 if (dir->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir->size), NULL, NULL))
2049 suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
2050 if (suffix != ICON_SUFFIX_NONE)
2052 if (suffix == ICON_SUFFIX_SVG)
2053 g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL);
2055 g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL);
2060 for (d = builtin_dirs; d; d = d->next)
2062 IconThemeDir *dir = d->data;
2064 if (dir->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir->size), NULL, NULL))
2067 suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
2068 if (suffix != ICON_SUFFIX_NONE)
2070 if (suffix == ICON_SUFFIX_SVG)
2071 g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL);
2073 g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL);
2077 if (icon_theme_builtin_icons)
2079 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
2083 BuiltinIcon *icon = icons->data;
2085 g_hash_table_insert (sizes, GINT_TO_POINTER (icon->size), NULL);
2086 icons = icons->next;
2090 r = result = g_new0 (gint, g_hash_table_size (sizes) + 1);
2092 g_hash_table_foreach (sizes, add_size, &r);
2093 g_hash_table_destroy (sizes);
2099 add_key_to_hash (gpointer key,
2103 GHashTable *hash = user_data;
2105 g_hash_table_insert (hash, key, NULL);
2109 add_key_to_list (gpointer key,
2113 GList **list = user_data;
2115 *list = g_list_prepend (*list, g_strdup (key));
2119 * gtk_icon_theme_list_icons:
2120 * @icon_theme: a #GtkIconTheme
2121 * @context: (allow-none): a string identifying a particular type of
2122 * icon, or %NULL to list all icons.
2124 * Lists the icons in the current icon theme. Only a subset
2125 * of the icons can be listed by providing a context string.
2126 * The set of values for the context string is system dependent,
2127 * but will typically include such values as "Applications" and
2130 * Return value: (element-type utf8) (transfer full): a #GList list
2131 * holding the names of all the icons in the theme. You must first
2132 * free each element in the list with g_free(), then free the list
2133 * itself with g_list_free().
2138 gtk_icon_theme_list_icons (GtkIconTheme *icon_theme,
2139 const char *context)
2141 GtkIconThemePrivate *priv;
2144 GQuark context_quark;
2146 priv = icon_theme->priv;
2148 ensure_valid_themes (icon_theme);
2152 context_quark = g_quark_try_string (context);
2160 icons = g_hash_table_new (g_str_hash, g_str_equal);
2165 theme_list_icons (l->data, icons, context_quark);
2169 if (context_quark == 0)
2170 g_hash_table_foreach (priv->unthemed_icons,
2176 g_hash_table_foreach (icons,
2180 g_hash_table_destroy (icons);
2186 * gtk_icon_theme_list_contexts:
2187 * @icon_theme: a #GtkIconTheme
2189 * Gets the list of contexts available within the current
2190 * hierarchy of icon themes
2192 * Return value: (element-type utf8) (transfer full): a #GList list
2193 * holding the names of all the contexts in the theme. You must first
2194 * free each element in the list with g_free(), then free the list
2195 * itself with g_list_free().
2200 gtk_icon_theme_list_contexts (GtkIconTheme *icon_theme)
2202 GtkIconThemePrivate *priv;
2203 GHashTable *contexts;
2206 priv = icon_theme->priv;
2208 ensure_valid_themes (icon_theme);
2210 contexts = g_hash_table_new (g_str_hash, g_str_equal);
2215 theme_list_contexts (l->data, contexts);
2221 g_hash_table_foreach (contexts,
2225 g_hash_table_destroy (contexts);
2231 * gtk_icon_theme_get_example_icon_name:
2232 * @icon_theme: a #GtkIconTheme
2234 * Gets the name of an icon that is representative of the
2235 * current theme (for instance, to use when presenting
2236 * a list of themes to the user.)
2238 * Return value: the name of an example icon or %NULL.
2239 * Free with g_free().
2244 gtk_icon_theme_get_example_icon_name (GtkIconTheme *icon_theme)
2246 GtkIconThemePrivate *priv;
2250 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2252 priv = icon_theme->priv;
2254 ensure_valid_themes (icon_theme);
2261 return g_strdup (theme->example);
2271 rescan_themes (GtkIconTheme *icon_theme)
2273 GtkIconThemePrivate *priv;
2274 IconThemeDirMtime *dir_mtime;
2280 priv = icon_theme->priv;
2282 for (d = priv->dir_mtimes; d != NULL; d = d->next)
2284 dir_mtime = d->data;
2286 stat_res = g_stat (dir_mtime->dir, &stat_buf);
2288 /* dir mtime didn't change */
2289 if (stat_res == 0 &&
2290 S_ISDIR (stat_buf.st_mode) &&
2291 dir_mtime->mtime == stat_buf.st_mtime)
2293 /* didn't exist before, and still doesn't */
2294 if (dir_mtime->mtime == 0 &&
2295 (stat_res != 0 || !S_ISDIR (stat_buf.st_mode)))
2301 g_get_current_time (&tv);
2302 priv->last_stat_time = tv.tv_sec;
2308 * gtk_icon_theme_rescan_if_needed:
2309 * @icon_theme: a #GtkIconTheme
2311 * Checks to see if the icon theme has changed; if it has, any
2312 * currently cached information is discarded and will be reloaded
2313 * next time @icon_theme is accessed.
2315 * Return value: %TRUE if the icon theme has changed and needed
2321 gtk_icon_theme_rescan_if_needed (GtkIconTheme *icon_theme)
2325 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
2327 retval = rescan_themes (icon_theme);
2329 do_theme_change (icon_theme);
2335 theme_destroy (IconTheme *theme)
2337 g_free (theme->display_name);
2338 g_free (theme->comment);
2339 g_free (theme->name);
2340 g_free (theme->example);
2342 g_list_free_full (theme->dirs, (GDestroyNotify) theme_dir_destroy);
2348 theme_dir_destroy (IconThemeDir *dir)
2351 _gtk_icon_cache_unref (dir->cache);
2353 g_hash_table_destroy (dir->icons);
2356 g_hash_table_destroy (dir->icon_data);
2358 g_free (dir->subdir);
2363 theme_dir_size_difference (IconThemeDir *dir, int size, gboolean *smaller)
2368 case ICON_THEME_DIR_FIXED:
2369 *smaller = size < dir->size;
2370 return abs (size - dir->size);
2372 case ICON_THEME_DIR_SCALABLE:
2373 *smaller = size < dir->min_size;
2374 if (size < dir->min_size)
2375 return dir->min_size - size;
2376 if (size > dir->max_size)
2377 return size - dir->max_size;
2380 case ICON_THEME_DIR_THRESHOLD:
2381 min = dir->size - dir->threshold;
2382 max = dir->size + dir->threshold;
2383 *smaller = size < min;
2390 case ICON_THEME_DIR_UNTHEMED:
2391 g_assert_not_reached ();
2394 g_assert_not_reached ();
2399 string_from_suffix (IconSuffix suffix)
2403 case ICON_SUFFIX_XPM:
2405 case ICON_SUFFIX_SVG:
2407 case ICON_SUFFIX_PNG:
2410 g_assert_not_reached();
2416 suffix_from_name (const char *name)
2420 if (g_str_has_suffix (name, ".png"))
2421 retval = ICON_SUFFIX_PNG;
2422 else if (g_str_has_suffix (name, ".svg"))
2423 retval = ICON_SUFFIX_SVG;
2424 else if (g_str_has_suffix (name, ".xpm"))
2425 retval = ICON_SUFFIX_XPM;
2427 retval = ICON_SUFFIX_NONE;
2433 best_suffix (IconSuffix suffix,
2436 if ((suffix & ICON_SUFFIX_PNG) != 0)
2437 return ICON_SUFFIX_PNG;
2438 else if (allow_svg && ((suffix & ICON_SUFFIX_SVG) != 0))
2439 return ICON_SUFFIX_SVG;
2440 else if ((suffix & ICON_SUFFIX_XPM) != 0)
2441 return ICON_SUFFIX_XPM;
2443 return ICON_SUFFIX_NONE;
2448 theme_dir_get_icon_suffix (IconThemeDir *dir,
2449 const gchar *icon_name,
2450 gboolean *has_icon_file)
2456 suffix = (IconSuffix)_gtk_icon_cache_get_icon_flags (dir->cache,
2461 *has_icon_file = suffix & HAS_ICON_FILE;
2463 suffix = suffix & ~HAS_ICON_FILE;
2466 suffix = GPOINTER_TO_UINT (g_hash_table_lookup (dir->icons, icon_name));
2468 GTK_NOTE (ICONTHEME,
2469 g_print ("get_icon_suffix%s %u\n", dir->cache ? " (cached)" : "", suffix));
2474 static GtkIconInfo *
2475 theme_lookup_icon (IconTheme *theme,
2476 const char *icon_name,
2479 gboolean use_builtin)
2482 IconThemeDir *dir, *min_dir;
2484 int min_difference, difference;
2485 BuiltinIcon *closest_builtin = NULL;
2486 gboolean smaller, has_larger, match;
2489 min_difference = G_MAXINT;
2494 /* Builtin icons are logically part of the default theme and
2495 * are searched before other subdirectories of the default theme.
2497 if (use_builtin && strcmp (theme->name, DEFAULT_THEME_NAME) == 0)
2499 closest_builtin = find_builtin_icon (icon_name,
2504 if (min_difference == 0)
2505 return icon_info_new_builtin (closest_builtin);
2507 dirs = builtin_dirs;
2517 GTK_NOTE (ICONTHEME,
2518 g_print ("theme_lookup_icon dir %s\n", dir->dir));
2519 suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
2520 if (best_suffix (suffix, allow_svg) != ICON_SUFFIX_NONE)
2522 difference = theme_dir_size_difference (dir, size, &smaller);
2524 if (difference == 0)
2526 if (dir->type == ICON_THEME_DIR_SCALABLE)
2528 /* don't pick scalable if we already found
2529 * a matching non-scalable dir
2539 /* for a matching non-scalable dir keep
2540 * going and look for a closer match
2542 difference = abs (size - dir->size);
2543 if (!match || difference < min_difference)
2546 min_difference = difference;
2549 if (difference == 0)
2558 if (difference < min_difference || smaller)
2560 min_difference = difference;
2562 has_larger = smaller;
2567 if (difference < min_difference && smaller)
2569 min_difference = difference;
2578 if (l == NULL && dirs == builtin_dirs)
2587 GtkIconInfo *icon_info = icon_info_new ();
2588 gboolean has_icon_file = FALSE;
2590 suffix = theme_dir_get_icon_suffix (min_dir, icon_name, &has_icon_file);
2591 suffix = best_suffix (suffix, allow_svg);
2592 g_assert (suffix != ICON_SUFFIX_NONE);
2596 file = g_strconcat (icon_name, string_from_suffix (suffix), NULL);
2597 icon_info->filename = g_build_filename (min_dir->dir, file, NULL);
2598 icon_info->icon_file = g_file_new_for_path (icon_info->filename);
2603 icon_info->filename = NULL;
2604 icon_info->icon_file = NULL;
2607 if (min_dir->icon_data != NULL)
2608 icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
2610 if (icon_info->data == NULL && min_dir->cache != NULL)
2612 icon_info->data = _gtk_icon_cache_get_icon_data (min_dir->cache, icon_name, min_dir->subdir_index);
2613 if (icon_info->data)
2615 if (min_dir->icon_data == NULL)
2616 min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2617 g_free, (GDestroyNotify)icon_data_free);
2619 g_hash_table_replace (min_dir->icon_data, g_strdup (icon_name), icon_info->data);
2623 if (icon_info->data == NULL && has_icon_file)
2625 gchar *icon_file_name, *icon_file_path;
2627 icon_file_name = g_strconcat (icon_name, ".icon", NULL);
2628 icon_file_path = g_build_filename (min_dir->dir, icon_file_name, NULL);
2630 if (g_file_test (icon_file_path, G_FILE_TEST_IS_REGULAR))
2632 if (min_dir->icon_data == NULL)
2633 min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2634 g_free, (GDestroyNotify)icon_data_free);
2635 load_icon_data (min_dir, icon_file_path, icon_file_name);
2637 icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
2639 g_free (icon_file_name);
2640 g_free (icon_file_path);
2645 icon_info->cache_pixbuf = _gtk_icon_cache_get_icon (min_dir->cache, icon_name,
2646 min_dir->subdir_index);
2649 icon_info->dir_type = min_dir->type;
2650 icon_info->dir_size = min_dir->size;
2651 icon_info->threshold = min_dir->threshold;
2656 if (closest_builtin)
2657 return icon_info_new_builtin (closest_builtin);
2663 theme_list_icons (IconTheme *theme,
2667 GList *l = theme->dirs;
2674 if (context == dir->context ||
2679 _gtk_icon_cache_add_icons (dir->cache,
2686 g_hash_table_foreach (dir->icons,
2696 theme_list_contexts (IconTheme *theme,
2697 GHashTable *contexts)
2699 GList *l = theme->dirs;
2701 const char *context;
2707 context = g_quark_to_string (dir->context);
2708 g_hash_table_replace (contexts, (gpointer) context, NULL);
2715 load_icon_data (IconThemeDir *dir, const char *path, const char *name)
2717 GKeyFile *icon_file;
2725 GError *error = NULL;
2729 icon_file = g_key_file_new ();
2730 g_key_file_set_list_separator (icon_file, ',');
2731 g_key_file_load_from_file (icon_file, path, 0, &error);
2734 g_error_free (error);
2735 g_key_file_free (icon_file);
2740 base_name = strip_suffix (name);
2742 data = g_slice_new0 (GtkIconData);
2743 /* takes ownership of base_name */
2744 g_hash_table_replace (dir->icon_data, base_name, data);
2746 ivalues = g_key_file_get_integer_list (icon_file,
2747 "Icon Data", "EmbeddedTextRectangle",
2753 data->has_embedded_rect = TRUE;
2754 data->x0 = ivalues[0];
2755 data->y0 = ivalues[1];
2756 data->x1 = ivalues[2];
2757 data->y1 = ivalues[3];
2763 str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
2766 split = g_strsplit (str, "|", -1);
2768 data->n_attach_points = g_strv_length (split);
2769 data->attach_points = g_new (GdkPoint, data->n_attach_points);
2772 while (split[i] != NULL && i < data->n_attach_points)
2774 split_point = strchr (split[i], ',');
2779 data->attach_points[i].x = atoi (split[i]);
2780 data->attach_points[i].y = atoi (split_point);
2789 data->display_name = g_key_file_get_locale_string (icon_file,
2790 "Icon Data", "DisplayName",
2792 g_key_file_free (icon_file);
2797 scan_directory (GtkIconThemePrivate *icon_theme,
2798 IconThemeDir *dir, char *full_dir)
2803 GTK_NOTE (ICONTHEME,
2804 g_print ("scanning directory %s\n", full_dir));
2805 dir->icons = g_hash_table_new_full (g_str_hash, g_str_equal,
2808 gdir = g_dir_open (full_dir, 0, NULL);
2813 while ((name = g_dir_read_name (gdir)))
2817 IconSuffix suffix, hash_suffix;
2819 if (g_str_has_suffix (name, ".icon"))
2821 if (dir->icon_data == NULL)
2822 dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2823 g_free, (GDestroyNotify)icon_data_free);
2825 path = g_build_filename (full_dir, name, NULL);
2826 if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
2827 load_icon_data (dir, path, name);
2834 suffix = suffix_from_name (name);
2835 if (suffix == ICON_SUFFIX_NONE)
2838 base_name = strip_suffix (name);
2840 hash_suffix = GPOINTER_TO_INT (g_hash_table_lookup (dir->icons, base_name));
2841 g_hash_table_replace (icon_theme->all_icons, base_name, NULL);
2842 /* takes ownership of base_name */
2843 g_hash_table_replace (dir->icons, base_name, GUINT_TO_POINTER (hash_suffix| suffix));
2850 theme_subdir_load (GtkIconTheme *icon_theme,
2852 GKeyFile *theme_file,
2858 IconThemeDirType type;
2859 char *context_string;
2866 GError *error = NULL;
2867 IconThemeDirMtime *dir_mtime;
2869 size = g_key_file_get_integer (theme_file, subdir, "Size", &error);
2872 g_error_free (error);
2873 g_warning ("Theme directory %s of theme %s has no size field\n",
2874 subdir, theme->name);
2878 type = ICON_THEME_DIR_THRESHOLD;
2879 type_string = g_key_file_get_string (theme_file, subdir, "Type", NULL);
2882 if (strcmp (type_string, "Fixed") == 0)
2883 type = ICON_THEME_DIR_FIXED;
2884 else if (strcmp (type_string, "Scalable") == 0)
2885 type = ICON_THEME_DIR_SCALABLE;
2886 else if (strcmp (type_string, "Threshold") == 0)
2887 type = ICON_THEME_DIR_THRESHOLD;
2889 g_free (type_string);
2893 context_string = g_key_file_get_string (theme_file, subdir, "Context", NULL);
2896 context = g_quark_from_string (context_string);
2897 g_free (context_string);
2900 if (g_key_file_has_key (theme_file, subdir, "MaxSize", NULL))
2901 max_size = g_key_file_get_integer (theme_file, subdir, "MaxSize", NULL);
2905 if (g_key_file_has_key (theme_file, subdir, "MinSize", NULL))
2906 min_size = g_key_file_get_integer (theme_file, subdir, "MinSize", NULL);
2910 if (g_key_file_has_key (theme_file, subdir, "Threshold", NULL))
2911 threshold = g_key_file_get_integer (theme_file, subdir, "Threshold", NULL);
2915 for (d = icon_theme->priv->dir_mtimes; d; d = d->next)
2917 dir_mtime = (IconThemeDirMtime *)d->data;
2919 if (dir_mtime->mtime == 0)
2920 continue; /* directory doesn't exist */
2922 full_dir = g_build_filename (dir_mtime->dir, subdir, NULL);
2924 /* First, see if we have a cache for the directory */
2925 if (dir_mtime->cache != NULL || g_file_test (full_dir, G_FILE_TEST_IS_DIR))
2927 if (dir_mtime->cache == NULL)
2929 /* This will return NULL if the cache doesn't exist or is outdated */
2930 dir_mtime->cache = _gtk_icon_cache_new_for_path (dir_mtime->dir);
2933 dir = g_new (IconThemeDir, 1);
2935 dir->context = context;
2937 dir->min_size = min_size;
2938 dir->max_size = max_size;
2939 dir->threshold = threshold;
2940 dir->dir = full_dir;
2941 dir->icon_data = NULL;
2942 dir->subdir = g_strdup (subdir);
2943 if (dir_mtime->cache != NULL)
2945 dir->cache = _gtk_icon_cache_ref (dir_mtime->cache);
2946 dir->subdir_index = _gtk_icon_cache_get_directory_index (dir->cache, dir->subdir);
2951 dir->subdir_index = -1;
2952 scan_directory (icon_theme->priv, dir, full_dir);
2955 theme->dirs = g_list_prepend (theme->dirs, dir);
2963 icon_data_free (GtkIconData *icon_data)
2965 g_free (icon_data->attach_points);
2966 g_free (icon_data->display_name);
2967 g_slice_free (GtkIconData, icon_data);
2974 G_DEFINE_BOXED_TYPE (GtkIconInfo, gtk_icon_info,
2978 static GtkIconInfo *
2979 icon_info_new (void)
2981 GtkIconInfo *icon_info = g_slice_new0 (GtkIconInfo);
2983 icon_info->scale = -1.;
2984 icon_info->ref_count = 1;
2989 static GtkIconInfo *
2990 icon_info_new_builtin (BuiltinIcon *icon)
2992 GtkIconInfo *icon_info = icon_info_new ();
2994 icon_info->cache_pixbuf = g_object_ref (icon->pixbuf);
2995 icon_info->dir_type = ICON_THEME_DIR_THRESHOLD;
2996 icon_info->dir_size = icon->size;
2997 icon_info->threshold = 2;
3003 * gtk_icon_info_copy:
3004 * @icon_info: a #GtkIconInfo
3006 * Make a copy of a #GtkIconInfo.
3008 * Return value: the new GtkIconInfo
3013 gtk_icon_info_copy (GtkIconInfo *icon_info)
3016 g_return_val_if_fail (icon_info != NULL, NULL);
3018 icon_info->ref_count++;
3024 * gtk_icon_info_free:
3025 * @icon_info: a #GtkIconInfo
3027 * Free a #GtkIconInfo and associated information
3032 gtk_icon_info_free (GtkIconInfo *icon_info)
3034 g_return_if_fail (icon_info != NULL);
3036 icon_info->ref_count--;
3037 if (icon_info->ref_count > 0)
3040 if (icon_info->in_cache)
3041 g_hash_table_remove (icon_info->in_cache->priv->info_cache, &icon_info->key);
3043 g_strfreev (icon_info->key.icon_names);
3045 g_free (icon_info->filename);
3046 g_clear_object (&icon_info->icon_file);
3048 if (icon_info->loadable)
3049 g_object_unref (icon_info->loadable);
3050 g_slist_free_full (icon_info->emblem_infos, (GDestroyNotify) gtk_icon_info_free);
3051 if (icon_info->pixbuf)
3052 g_object_unref (icon_info->pixbuf);
3053 if (icon_info->cache_pixbuf)
3054 g_object_unref (icon_info->cache_pixbuf);
3055 if (icon_info->symbolic_pixbuf_size)
3056 gtk_requisition_free (icon_info->symbolic_pixbuf_size);
3058 symbolic_pixbuf_cache_free (icon_info->symbolic_pixbuf_cache);
3060 g_slice_free (GtkIconInfo, icon_info);
3064 * gtk_icon_info_get_base_size:
3065 * @icon_info: a #GtkIconInfo
3067 * Gets the base size for the icon. The base size
3068 * is a size for the icon that was specified by
3069 * the icon theme creator. This may be different
3070 * than the actual size of image; an example of
3071 * this is small emblem icons that can be attached
3072 * to a larger icon. These icons will be given
3073 * the same base size as the larger icons to which
3074 * they are attached.
3076 * Return value: the base size, or 0, if no base
3077 * size is known for the icon.
3082 gtk_icon_info_get_base_size (GtkIconInfo *icon_info)
3084 g_return_val_if_fail (icon_info != NULL, 0);
3086 return icon_info->dir_size;
3090 * gtk_icon_info_get_filename:
3091 * @icon_info: a #GtkIconInfo
3093 * Gets the filename for the icon. If the
3094 * %GTK_ICON_LOOKUP_USE_BUILTIN flag was passed
3095 * to gtk_icon_theme_lookup_icon(), there may be
3096 * no filename if a builtin icon is returned; in this
3097 * case, you should use gtk_icon_info_get_builtin_pixbuf().
3099 * Return value: (type filename): the filename for the icon, or %NULL
3100 * if gtk_icon_info_get_builtin_pixbuf() should be used instead. The
3101 * return value is owned by GTK+ and should not be modified or freed.
3106 gtk_icon_info_get_filename (GtkIconInfo *icon_info)
3108 g_return_val_if_fail (icon_info != NULL, NULL);
3110 return icon_info->filename;
3114 * gtk_icon_info_get_builtin_pixbuf:
3115 * @icon_info: a #GtkIconInfo structure
3117 * Gets the built-in image for this icon, if any. To allow
3118 * GTK+ to use built in icon images, you must pass the
3119 * %GTK_ICON_LOOKUP_USE_BUILTIN to
3120 * gtk_icon_theme_lookup_icon().
3122 * Return value: (transfer none): the built-in image pixbuf, or %NULL. No
3123 * extra reference is added to the returned pixbuf, so if
3124 * you want to keep it around, you must use g_object_ref().
3125 * The returned image must not be modified.
3130 gtk_icon_info_get_builtin_pixbuf (GtkIconInfo *icon_info)
3132 g_return_val_if_fail (icon_info != NULL, NULL);
3134 if (icon_info->filename)
3137 return icon_info->cache_pixbuf;
3140 static gboolean icon_info_ensure_scale_and_pixbuf (GtkIconInfo*, gboolean);
3142 /* Combine the icon with all emblems, the first emblem is placed
3143 * in the southeast corner. Scale emblems to be at most 3/4 of the
3144 * size of the icon itself.
3147 apply_emblems (GtkIconInfo *info)
3149 GdkPixbuf *icon = NULL;
3153 if (info->emblem_infos == NULL)
3156 if (info->emblems_applied)
3159 w = gdk_pixbuf_get_width (info->pixbuf);
3160 h = gdk_pixbuf_get_height (info->pixbuf);
3162 for (l = info->emblem_infos, pos = 0; l; l = l->next, pos++)
3164 GtkIconInfo *emblem_info = l->data;
3166 if (icon_info_ensure_scale_and_pixbuf (emblem_info, FALSE))
3168 GdkPixbuf *emblem = emblem_info->pixbuf;
3170 gint x = 0, y = 0; /* silence compiler */
3173 ew = gdk_pixbuf_get_width (emblem);
3174 eh = gdk_pixbuf_get_height (emblem);
3206 icon = gdk_pixbuf_copy (info->pixbuf);
3211 gdk_pixbuf_composite (emblem, icon, x, y, ew, eh, x, y,
3212 scale, scale, GDK_INTERP_BILINEAR, 255);
3218 g_object_unref (info->pixbuf);
3219 info->pixbuf = icon;
3222 info->emblems_applied = TRUE;
3225 /* This function contains the complicated logic for deciding
3226 * on the size at which to load the icon and loading it at
3230 icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info,
3231 gboolean scale_only)
3233 int image_width, image_height;
3234 GdkPixbuf *source_pixbuf;
3237 /* First check if we already succeeded have the necessary
3238 * information (or failed earlier)
3240 if (scale_only && icon_info->scale >= 0)
3243 if (icon_info->pixbuf)
3245 apply_emblems (icon_info);
3249 if (icon_info->load_error)
3252 /* SVG icons are a special case - we just immediately scale them
3253 * to the desired size
3255 if (icon_info->icon_file && !icon_info->loadable)
3256 icon_info->loadable = G_LOADABLE_ICON (g_file_icon_new (icon_info->icon_file));
3259 if (G_IS_FILE_ICON (icon_info->loadable))
3262 GFileInfo *file_info;
3263 const gchar *content_type;
3265 file = g_file_icon_get_file (G_FILE_ICON (icon_info->loadable));
3266 file_info = g_file_query_info (file,
3267 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
3268 G_FILE_QUERY_INFO_NONE,
3272 content_type = g_file_info_get_content_type (file_info);
3274 if (content_type && strcmp (content_type, "image/svg+xml") == 0)
3277 g_object_unref (file_info);
3283 GInputStream *stream;
3285 icon_info->scale = icon_info->desired_size / 1000.;
3290 stream = g_loadable_icon_load (icon_info->loadable,
3291 icon_info->desired_size,
3293 &icon_info->load_error);
3296 icon_info->pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3297 icon_info->desired_size,
3298 icon_info->desired_size,
3301 &icon_info->load_error);
3302 g_object_unref (stream);
3305 if (!icon_info->pixbuf)
3308 apply_emblems (icon_info);
3313 /* In many cases, the scale can be determined without actual access
3314 * to the icon file. This is generally true when we have a size
3315 * for the directory where the icon is; the image size doesn't
3316 * matter in that case.
3318 if (icon_info->forced_size)
3319 icon_info->scale = -1;
3320 else if (icon_info->dir_type == ICON_THEME_DIR_FIXED)
3321 icon_info->scale = 1.0;
3322 else if (icon_info->dir_type == ICON_THEME_DIR_THRESHOLD)
3324 if (icon_info->desired_size >= icon_info->dir_size - icon_info->threshold &&
3325 icon_info->desired_size <= icon_info->dir_size + icon_info->threshold)
3326 icon_info->scale = 1.0;
3327 else if (icon_info->dir_size > 0)
3328 icon_info->scale =(gdouble) icon_info->desired_size / icon_info->dir_size;
3330 else if (icon_info->dir_type == ICON_THEME_DIR_SCALABLE)
3332 if (icon_info->dir_size > 0)
3333 icon_info->scale = (gdouble) icon_info->desired_size / icon_info->dir_size;
3336 if (icon_info->scale >= 0. && scale_only)
3339 /* At this point, we need to actually get the icon; either from the
3340 * builtin image or by loading the file
3342 source_pixbuf = NULL;
3343 if (icon_info->cache_pixbuf)
3344 source_pixbuf = g_object_ref (icon_info->cache_pixbuf);
3347 GInputStream *stream;
3349 stream = g_loadable_icon_load (icon_info->loadable,
3350 icon_info->desired_size,
3352 &icon_info->load_error);
3355 source_pixbuf = gdk_pixbuf_new_from_stream (stream,
3357 &icon_info->load_error);
3358 g_object_unref (stream);
3365 /* Do scale calculations that depend on the image size
3367 image_width = gdk_pixbuf_get_width (source_pixbuf);
3368 image_height = gdk_pixbuf_get_height (source_pixbuf);
3370 if (icon_info->scale < 0.0)
3372 gint image_size = MAX (image_width, image_height);
3374 icon_info->scale = (gdouble)icon_info->desired_size / (gdouble)image_size;
3376 icon_info->scale = 1.0;
3378 if (icon_info->dir_type == ICON_THEME_DIR_UNTHEMED &&
3379 !icon_info->forced_size)
3380 icon_info->scale = MIN (icon_info->scale, 1.0);
3383 /* We don't short-circuit out here for scale_only, since, now
3384 * we've loaded the icon, we might as well go ahead and finish
3385 * the job. This is a bit of a waste when we scale here
3386 * and never get the final pixbuf; at the cost of a bit of
3387 * extra complexity, we could keep the source pixbuf around
3388 * but not actually scale it until needed.
3390 if (icon_info->scale == 1.0)
3391 icon_info->pixbuf = source_pixbuf;
3394 icon_info->pixbuf = gdk_pixbuf_scale_simple (source_pixbuf,
3395 0.5 + image_width * icon_info->scale,
3396 0.5 + image_height * icon_info->scale,
3397 GDK_INTERP_BILINEAR);
3398 g_object_unref (source_pixbuf);
3401 apply_emblems (icon_info);
3407 proxy_pixbuf_destroy (guchar *pixels, gpointer data)
3409 GtkIconInfo *icon_info = data;
3410 GtkIconTheme *icon_theme = icon_info->in_cache;
3412 g_assert (icon_info->proxy_pixbuf != NULL);
3413 icon_info->proxy_pixbuf = NULL;
3415 /* Keep it alive a bit longer */
3416 if (icon_theme != NULL)
3417 ensure_in_lru_cache (icon_theme, icon_info);
3419 gtk_icon_info_free (icon_info);
3423 * gtk_icon_info_load_icon:
3424 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3425 * @error: (allow-none): location to store error information on failure,
3428 * Renders an icon previously looked up in an icon theme using
3429 * gtk_icon_theme_lookup_icon(); the size will be based on the size
3430 * passed to gtk_icon_theme_lookup_icon(). Note that the resulting
3431 * pixbuf may not be exactly this size; an icon theme may have icons
3432 * that differ slightly from their nominal sizes, and in addition GTK+
3433 * will avoid scaling icons that it considers sufficiently close to the
3434 * requested size or for which the source image would have to be scaled
3435 * up too far. (This maintains sharpness.). This behaviour can be changed
3436 * by passing the %GTK_ICON_LOOKUP_FORCE_SIZE flag when obtaining
3437 * the #GtkIconInfo. If this flag has been specified, the pixbuf
3438 * returned by this function will be scaled to the exact size.
3440 * Return value: (transfer full): the rendered icon; this may be a newly
3441 * created icon or a new reference to an internal icon, so you must
3442 * not modify the icon. Use g_object_unref() to release your reference
3448 gtk_icon_info_load_icon (GtkIconInfo *icon_info,
3451 g_return_val_if_fail (icon_info != NULL, NULL);
3452 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3454 if (!icon_info_ensure_scale_and_pixbuf (icon_info, FALSE))
3456 if (icon_info->load_error)
3457 g_propagate_error (error, icon_info->load_error);
3459 g_set_error_literal (error,
3460 GTK_ICON_THEME_ERROR,
3461 GTK_ICON_THEME_NOT_FOUND,
3462 _("Failed to load icon"));
3467 /* Instead of returning the pixbuf directly we
3468 return a proxy to it that we don't own (but that
3469 shares the data with the one we own). This way
3470 we can know when it is freed and ensure the
3471 IconInfo is alive (and thus cached) while
3472 the pixbuf is still alive. */
3474 if (icon_info->proxy_pixbuf != NULL)
3475 return g_object_ref (icon_info->proxy_pixbuf);
3477 icon_info->proxy_pixbuf =
3478 gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (icon_info->pixbuf),
3479 gdk_pixbuf_get_colorspace (icon_info->pixbuf),
3480 gdk_pixbuf_get_has_alpha (icon_info->pixbuf),
3481 gdk_pixbuf_get_bits_per_sample (icon_info->pixbuf),
3482 gdk_pixbuf_get_width (icon_info->pixbuf),
3483 gdk_pixbuf_get_height (icon_info->pixbuf),
3484 gdk_pixbuf_get_rowstride (icon_info->pixbuf),
3485 proxy_pixbuf_destroy,
3486 gtk_icon_info_copy (icon_info));
3488 return icon_info->proxy_pixbuf;
3492 gdk_color_to_css (GdkColor *color)
3494 return g_strdup_printf ("rgb(%d,%d,%d)",
3501 gdk_rgba_to_css (const GdkRGBA *color)
3503 /* drop alpha for now, since librsvg does not understand rgba() */
3504 return g_strdup_printf ("rgb(%d,%d,%d)",
3505 (gint)(color->red * 255),
3506 (gint)(color->green * 255),
3507 (gint)(color->blue * 255));
3511 proxy_symbolic_pixbuf_destroy (guchar *pixels, gpointer data)
3513 GtkIconInfo *icon_info = data;
3514 GtkIconTheme *icon_theme = icon_info->in_cache;
3515 SymbolicPixbufCache *symbolic_cache;
3517 for (symbolic_cache = icon_info->symbolic_pixbuf_cache;
3518 symbolic_cache != NULL;
3519 symbolic_cache = symbolic_cache->next)
3521 if (symbolic_cache->proxy_pixbuf != NULL &&
3522 gdk_pixbuf_get_pixels (symbolic_cache->proxy_pixbuf) == pixels)
3526 g_assert (symbolic_cache != NULL);
3527 g_assert (symbolic_cache->proxy_pixbuf != NULL);
3529 symbolic_cache->proxy_pixbuf = NULL;
3531 /* Keep it alive a bit longer */
3532 if (icon_theme != NULL)
3533 ensure_in_lru_cache (icon_theme, icon_info);
3535 gtk_icon_info_free (icon_info);
3539 symbolic_cache_get_proxy (SymbolicPixbufCache *symbolic_cache,
3540 GtkIconInfo *icon_info)
3542 if (symbolic_cache->proxy_pixbuf)
3543 return g_object_ref (symbolic_cache->proxy_pixbuf);
3545 symbolic_cache->proxy_pixbuf =
3546 gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (symbolic_cache->pixbuf),
3547 gdk_pixbuf_get_colorspace (symbolic_cache->pixbuf),
3548 gdk_pixbuf_get_has_alpha (symbolic_cache->pixbuf),
3549 gdk_pixbuf_get_bits_per_sample (symbolic_cache->pixbuf),
3550 gdk_pixbuf_get_width (symbolic_cache->pixbuf),
3551 gdk_pixbuf_get_height (symbolic_cache->pixbuf),
3552 gdk_pixbuf_get_rowstride (symbolic_cache->pixbuf),
3553 proxy_symbolic_pixbuf_destroy,
3554 gtk_icon_info_copy (icon_info));
3556 return symbolic_cache->proxy_pixbuf;
3560 _gtk_icon_info_load_symbolic_internal (GtkIconInfo *icon_info,
3562 const GdkRGBA *success_color,
3563 const GdkRGBA *warning_color,
3564 const GdkRGBA *error_color,
3567 GInputStream *stream;
3574 gchar *width, *height, *uri;
3575 SymbolicPixbufCache *symbolic_cache;
3577 symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
3578 fg, success_color, warning_color, error_color);
3580 return symbolic_cache_get_proxy (symbolic_cache, icon_info);
3582 /* css_fg can't possibly have failed, otherwise
3583 * that would mean we have a broken style */
3584 g_return_val_if_fail (fg != NULL, NULL);
3586 css_fg = gdk_rgba_to_css (fg);
3588 css_success = css_warning = css_error = NULL;
3591 css_warning = gdk_rgba_to_css (warning_color);
3594 css_error = gdk_rgba_to_css (error_color);
3597 css_success = gdk_rgba_to_css (success_color);
3601 GdkColor success_default_color = { 0, 0x4e00, 0x9a00, 0x0600 };
3602 css_success = gdk_color_to_css (&success_default_color);
3606 GdkColor warning_default_color = { 0, 0xf500, 0x7900, 0x3e00 };
3607 css_warning = gdk_color_to_css (&warning_default_color);
3611 GdkColor error_default_color = { 0, 0xcc00, 0x0000, 0x0000 };
3612 css_error = gdk_color_to_css (&error_default_color);
3615 if (!icon_info->symbolic_pixbuf_size)
3617 stream = G_INPUT_STREAM (g_file_read (icon_info->icon_file, NULL, error));
3622 /* Fetch size from the original icon */
3623 pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
3624 g_object_unref (stream);
3629 icon_info->symbolic_pixbuf_size = gtk_requisition_new ();
3630 icon_info->symbolic_pixbuf_size->width = gdk_pixbuf_get_width (pixbuf);
3631 icon_info->symbolic_pixbuf_size->height = gdk_pixbuf_get_height (pixbuf);
3632 g_object_unref (pixbuf);
3635 width = g_strdup_printf ("%d", icon_info->symbolic_pixbuf_size->width);
3636 height = g_strdup_printf ("%d", icon_info->symbolic_pixbuf_size->height);
3637 uri = g_file_get_uri (icon_info->icon_file);
3639 data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
3640 "<svg version=\"1.1\"\n"
3641 " xmlns=\"http://www.w3.org/2000/svg\"\n"
3642 " xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n"
3643 " width=\"", width, "\"\n"
3644 " height=\"", height, "\">\n"
3645 " <style type=\"text/css\">\n"
3647 " fill: ", css_fg," !important;\n"
3650 " fill: ", css_warning, " !important;\n"
3653 " fill: ", css_error ," !important;\n"
3656 " fill: ", css_success, " !important;\n"
3659 " <xi:include href=\"", uri, "\"/>\n"
3663 g_free (css_warning);
3665 g_free (css_success);
3670 stream = g_memory_input_stream_new_from_data (data, -1, g_free);
3671 pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3672 icon_info->desired_size,
3673 icon_info->desired_size,
3677 g_object_unref (stream);
3681 icon_info->symbolic_pixbuf_cache =
3682 symbolic_pixbuf_cache_new (pixbuf, fg, success_color, warning_color, error_color,
3683 icon_info->symbolic_pixbuf_cache);
3685 return symbolic_cache_get_proxy (icon_info->symbolic_pixbuf_cache, icon_info);
3692 * gtk_icon_info_load_symbolic:
3693 * @icon_info: a #GtkIconInfo
3694 * @fg: a #GdkRGBA representing the foreground color of the icon
3695 * @success_color: (allow-none): a #GdkRGBA representing the warning color
3696 * of the icon or %NULL to use the default color
3697 * @warning_color: (allow-none): a #GdkRGBA representing the warning color
3698 * of the icon or %NULL to use the default color
3699 * @error_color: (allow-none): a #GdkRGBA representing the error color
3700 * of the icon or %NULL to use the default color (allow-none)
3701 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3702 * loaded icon was a symbolic one and whether the @fg color was
3704 * @error: (allow-none): location to store error information on failure,
3707 * Loads an icon, modifying it to match the system colours for the foreground,
3708 * success, warning and error colors provided. If the icon is not a symbolic
3709 * one, the function will return the result from gtk_icon_info_load_icon().
3711 * This allows loading symbolic icons that will match the system theme.
3713 * Unless you are implementing a widget, you will want to use
3714 * g_themed_icon_new_with_default_fallbacks() to load the icon.
3716 * As implementation details, the icon loaded needs to be of SVG type,
3717 * contain the "symbolic" term as the last component of the icon name,
3718 * and use the 'fg', 'success', 'warning' and 'error' CSS styles in the
3721 * See the <ulink url="http://www.freedesktop.org/wiki/SymbolicIcons">Symbolic Icons spec</ulink>
3722 * for more information about symbolic icons.
3724 * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
3729 gtk_icon_info_load_symbolic (GtkIconInfo *icon_info,
3731 const GdkRGBA *success_color,
3732 const GdkRGBA *warning_color,
3733 const GdkRGBA *error_color,
3734 gboolean *was_symbolic,
3738 gboolean is_symbolic;
3740 g_return_val_if_fail (icon_info != NULL, NULL);
3741 g_return_val_if_fail (fg != NULL, NULL);
3744 if (icon_info->icon_file)
3745 icon_uri = g_file_get_uri (icon_info->icon_file);
3747 is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
3751 *was_symbolic = is_symbolic;
3754 return gtk_icon_info_load_icon (icon_info, error);
3756 return _gtk_icon_info_load_symbolic_internal (icon_info,
3758 warning_color, error_color,
3763 * gtk_icon_info_load_symbolic_for_context:
3764 * @icon_info: a #GtkIconInfo
3765 * @context: a #GtkStyleContext
3766 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3767 * loaded icon was a symbolic one and whether the @fg color was
3769 * @error: (allow-none): location to store error information on failure,
3772 * Loads an icon, modifying it to match the system colors for the foreground,
3773 * success, warning and error colors provided. If the icon is not a symbolic
3774 * one, the function will return the result from gtk_icon_info_load_icon().
3775 * This function uses the regular foreground color and the symbolic colors
3776 * with the names "success_color", "warning_color" and "error_color" from
3779 * This allows loading symbolic icons that will match the system theme.
3781 * See gtk_icon_info_load_symbolic() for more details.
3783 * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
3788 gtk_icon_info_load_symbolic_for_context (GtkIconInfo *icon_info,
3789 GtkStyleContext *context,
3790 gboolean *was_symbolic,
3793 GdkRGBA *color = NULL;
3796 GdkRGBA success_color;
3797 GdkRGBA *success_colorp;
3798 GdkRGBA warning_color;
3799 GdkRGBA *warning_colorp;
3800 GdkRGBA error_color;
3801 GdkRGBA *error_colorp;
3802 GtkStateFlags state;
3804 gboolean is_symbolic;
3806 g_return_val_if_fail (icon_info != NULL, NULL);
3807 g_return_val_if_fail (context != NULL, NULL);
3810 if (icon_info->icon_file)
3811 icon_uri = g_file_get_uri (icon_info->icon_file);
3813 is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
3817 *was_symbolic = is_symbolic;
3820 return gtk_icon_info_load_icon (icon_info, error);
3822 fgp = success_colorp = warning_colorp = error_colorp = NULL;
3824 state = gtk_style_context_get_state (context);
3825 gtk_style_context_get (context, state, "color", &color, NULL);
3830 gdk_rgba_free (color);
3833 if (gtk_style_context_lookup_color (context, "success_color", &success_color))
3834 success_colorp = &success_color;
3836 if (gtk_style_context_lookup_color (context, "warning_color", &warning_color))
3837 warning_colorp = &warning_color;
3839 if (gtk_style_context_lookup_color (context, "error_color", &error_color))
3840 error_colorp = &error_color;
3842 return _gtk_icon_info_load_symbolic_internal (icon_info,
3843 fgp, success_colorp,
3844 warning_colorp, error_colorp,
3849 color_to_rgba (GdkColor *color, GdkRGBA *rgba)
3851 rgba->red = color->red / 65535.0;
3852 rgba->green = color->green / 65535.0;
3853 rgba->blue = color->blue / 65535.0;
3859 * gtk_icon_info_load_symbolic_for_style:
3860 * @icon_info: a #GtkIconInfo
3861 * @style: a #GtkStyle to take the colors from
3862 * @state: the widget state to use for colors
3863 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3864 * loaded icon was a symbolic one and whether the @fg color was
3866 * @error: (allow-none): location to store error information on failure,
3869 * Loads an icon, modifying it to match the system colours for the foreground,
3870 * success, warning and error colors provided. If the icon is not a symbolic
3871 * one, the function will return the result from gtk_icon_info_load_icon().
3873 * This allows loading symbolic icons that will match the system theme.
3875 * See gtk_icon_info_load_symbolic() for more details.
3877 * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
3881 * Deprecated: 3.0: Use gtk_icon_info_load_symbolic_for_context() instead
3884 gtk_icon_info_load_symbolic_for_style (GtkIconInfo *icon_info,
3887 gboolean *was_symbolic,
3892 GdkRGBA success_color;
3893 GdkRGBA *success_colorp;
3894 GdkRGBA warning_color;
3895 GdkRGBA *warning_colorp;
3896 GdkRGBA error_color;
3897 GdkRGBA *error_colorp;
3899 gboolean is_symbolic;
3901 g_return_val_if_fail (icon_info != NULL, NULL);
3902 g_return_val_if_fail (style != NULL, NULL);
3905 if (icon_info->icon_file)
3906 icon_uri = g_file_get_uri (icon_info->icon_file);
3908 is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
3912 *was_symbolic = is_symbolic;
3915 return gtk_icon_info_load_icon (icon_info, error);
3917 color_to_rgba (&style->fg[state], &fg);
3919 success_colorp = warning_colorp = error_colorp = NULL;
3921 if (gtk_style_lookup_color (style, "success_color", &color))
3922 success_colorp = color_to_rgba (&color, &success_color);
3924 if (gtk_style_lookup_color (style, "warning_color", &color))
3925 warning_colorp = color_to_rgba (&color, &warning_color);
3927 if (gtk_style_lookup_color (style, "error_color", &color))
3928 error_colorp = color_to_rgba (&color, &error_color);
3930 return _gtk_icon_info_load_symbolic_internal (icon_info,
3931 &fg, success_colorp,
3932 warning_colorp, error_colorp,
3937 * gtk_icon_info_set_raw_coordinates:
3938 * @icon_info: a #GtkIconInfo
3939 * @raw_coordinates: whether the coordinates of embedded rectangles
3940 * and attached points should be returned in their original
3943 * Sets whether the coordinates returned by gtk_icon_info_get_embedded_rect()
3944 * and gtk_icon_info_get_attach_points() should be returned in their
3945 * original form as specified in the icon theme, instead of scaled
3946 * appropriately for the pixbuf returned by gtk_icon_info_load_icon().
3948 * Raw coordinates are somewhat strange; they are specified to be with
3949 * respect to the unscaled pixmap for PNG and XPM icons, but for SVG
3950 * icons, they are in a 1000x1000 coordinate space that is scaled
3951 * to the final size of the icon. You can determine if the icon is an SVG
3952 * icon by using gtk_icon_info_get_filename(), and seeing if it is non-%NULL
3953 * and ends in '.svg'.
3955 * This function is provided primarily to allow compatibility wrappers
3956 * for older API's, and is not expected to be useful for applications.
3961 gtk_icon_info_set_raw_coordinates (GtkIconInfo *icon_info,
3962 gboolean raw_coordinates)
3964 g_return_if_fail (icon_info != NULL);
3966 icon_info->raw_coordinates = raw_coordinates != FALSE;
3969 /* Scale coordinates from the icon data prior to returning
3973 icon_info_scale_point (GtkIconInfo *icon_info,
3979 if (icon_info->raw_coordinates)
3986 if (!icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
3989 *x_out = 0.5 + x * icon_info->scale;
3990 *y_out = 0.5 + y * icon_info->scale;
3997 * gtk_icon_info_get_embedded_rect:
3998 * @icon_info: a #GtkIconInfo
3999 * @rectangle: (out): #GdkRectangle in which to store embedded
4000 * rectangle coordinates; coordinates are only stored
4001 * when this function returns %TRUE.
4003 * Gets the coordinates of a rectangle within the icon
4004 * that can be used for display of information such
4005 * as a preview of the contents of a text file.
4006 * See gtk_icon_info_set_raw_coordinates() for further
4007 * information about the coordinate system.
4009 * Return value: %TRUE if the icon has an embedded rectangle
4014 gtk_icon_info_get_embedded_rect (GtkIconInfo *icon_info,
4015 GdkRectangle *rectangle)
4017 g_return_val_if_fail (icon_info != NULL, FALSE);
4019 if (icon_info->data && icon_info->data->has_embedded_rect &&
4020 icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4022 gint scaled_x0, scaled_y0;
4023 gint scaled_x1, scaled_y1;
4027 icon_info_scale_point (icon_info,
4028 icon_info->data->x0, icon_info->data->y0,
4029 &scaled_x0, &scaled_y0);
4030 icon_info_scale_point (icon_info,
4031 icon_info->data->x1, icon_info->data->y1,
4032 &scaled_x1, &scaled_y1);
4034 rectangle->x = scaled_x0;
4035 rectangle->y = scaled_y0;
4036 rectangle->width = scaled_x1 - rectangle->x;
4037 rectangle->height = scaled_y1 - rectangle->y;
4047 * gtk_icon_info_get_attach_points:
4048 * @icon_info: a #GtkIconInfo
4049 * @points: (allow-none) (array length=n_points) (out): location to store pointer to an array of points, or %NULL
4050 * free the array of points with g_free().
4051 * @n_points: (allow-none): location to store the number of points in @points, or %NULL
4053 * Fetches the set of attach points for an icon. An attach point
4054 * is a location in the icon that can be used as anchor points for attaching
4055 * emblems or overlays to the icon.
4057 * Return value: %TRUE if there are any attach points for the icon.
4062 gtk_icon_info_get_attach_points (GtkIconInfo *icon_info,
4066 g_return_val_if_fail (icon_info != NULL, FALSE);
4068 if (icon_info->data && icon_info->data->n_attach_points &&
4069 icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4075 *points = g_new (GdkPoint, icon_info->data->n_attach_points);
4076 for (i = 0; i < icon_info->data->n_attach_points; i++)
4077 icon_info_scale_point (icon_info,
4078 icon_info->data->attach_points[i].x,
4079 icon_info->data->attach_points[i].y,
4085 *n_points = icon_info->data->n_attach_points;
4101 * gtk_icon_info_get_display_name:
4102 * @icon_info: a #GtkIconInfo
4104 * Gets the display name for an icon. A display name is a
4105 * string to be used in place of the icon name in a user
4106 * visible context like a list of icons.
4108 * Return value: the display name for the icon or %NULL, if
4109 * the icon doesn't have a specified display name. This value
4110 * is owned @icon_info and must not be modified or free.
4115 gtk_icon_info_get_display_name (GtkIconInfo *icon_info)
4117 g_return_val_if_fail (icon_info != NULL, NULL);
4119 if (icon_info->data)
4120 return icon_info->data->display_name;
4131 * gtk_icon_theme_add_builtin_icon:
4132 * @icon_name: the name of the icon to register
4133 * @size: the size at which to register the icon (different
4134 * images can be registered for the same icon name
4135 * at different sizes.)
4136 * @pixbuf: #GdkPixbuf that contains the image to use
4139 * Registers a built-in icon for icon theme lookups. The idea
4140 * of built-in icons is to allow an application or library
4141 * that uses themed icons to function requiring files to
4142 * be present in the file system. For instance, the default
4143 * images for all of GTK+'s stock icons are registered
4146 * In general, if you use gtk_icon_theme_add_builtin_icon()
4147 * you should also install the icon in the icon theme, so
4148 * that the icon is generally available.
4150 * This function will generally be used with pixbufs loaded
4151 * via gdk_pixbuf_new_from_inline().
4156 gtk_icon_theme_add_builtin_icon (const gchar *icon_name,
4160 BuiltinIcon *default_icon;
4164 g_return_if_fail (icon_name != NULL);
4165 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
4167 if (!icon_theme_builtin_icons)
4168 icon_theme_builtin_icons = g_hash_table_new (g_str_hash, g_str_equal);
4170 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
4172 key = g_strdup (icon_name);
4174 key = (gpointer)icon_name; /* Won't get stored */
4176 default_icon = g_new (BuiltinIcon, 1);
4177 default_icon->size = size;
4178 default_icon->pixbuf = g_object_ref (pixbuf);
4179 icons = g_slist_prepend (icons, default_icon);
4181 /* Replaces value, leaves key untouched
4183 g_hash_table_insert (icon_theme_builtin_icons, key, icons);
4186 /* Look up a builtin icon; the min_difference_p and
4187 * has_larger_p out parameters allow us to combine
4188 * this lookup with searching through the actual directories
4189 * of the "hicolor" icon theme. See theme_lookup_icon()
4190 * for how they are used.
4192 static BuiltinIcon *
4193 find_builtin_icon (const gchar *icon_name,
4195 gint *min_difference_p,
4196 gboolean *has_larger_p)
4198 GSList *icons = NULL;
4199 gint min_difference = G_MAXINT;
4200 gboolean has_larger = FALSE;
4201 BuiltinIcon *min_icon = NULL;
4203 if (!icon_theme_builtin_icons)
4206 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
4210 BuiltinIcon *default_icon = icons->data;
4211 int min, max, difference;
4214 min = default_icon->size - 2;
4215 max = default_icon->size + 2;
4216 smaller = size < min;
4218 difference = min - size;
4219 else if (size > max)
4220 difference = size - max;
4224 if (difference == 0)
4226 min_icon = default_icon;
4232 if (difference < min_difference || smaller)
4234 min_difference = difference;
4235 min_icon = default_icon;
4236 has_larger = smaller;
4241 if (difference < min_difference && smaller)
4243 min_difference = difference;
4244 min_icon = default_icon;
4248 icons = icons->next;
4251 if (min_difference_p)
4252 *min_difference_p = min_difference;
4254 *has_larger_p = has_larger;
4260 _gtk_icon_theme_check_reload (GdkDisplay *display)
4264 GtkIconTheme *icon_theme;
4266 n_screens = gdk_display_get_n_screens (display);
4268 for (i = 0; i < n_screens; i++)
4270 screen = gdk_display_get_screen (display, i);
4272 icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
4275 icon_theme->priv->check_reload = TRUE;
4276 ensure_valid_themes (icon_theme);
4277 icon_theme->priv->check_reload = FALSE;
4284 * gtk_icon_theme_lookup_by_gicon:
4285 * @icon_theme: a #GtkIconTheme
4286 * @icon: the #GIcon to look up
4287 * @size: desired icon size
4288 * @flags: flags modifying the behavior of the icon lookup
4290 * Looks up an icon and returns a structure containing
4291 * information such as the filename of the icon.
4292 * The icon can then be rendered into a pixbuf using
4293 * gtk_icon_info_load_icon().
4295 * Return value: a #GtkIconInfo structure containing
4296 * information about the icon, or %NULL if the icon
4297 * wasn't found. Free with gtk_icon_info_free()
4302 gtk_icon_theme_lookup_by_gicon (GtkIconTheme *icon_theme,
4305 GtkIconLookupFlags flags)
4309 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
4310 g_return_val_if_fail (G_IS_ICON (icon), NULL);
4312 if (G_IS_LOADABLE_ICON (icon))
4314 info = icon_info_new ();
4315 info->loadable = G_LOADABLE_ICON (g_object_ref (icon));
4317 if (G_IS_FILE_ICON (icon))
4319 GFile *file = g_file_icon_get_file (G_FILE_ICON (icon));
4322 info->icon_file = g_object_ref (file);
4323 info->filename = g_file_get_path (file);
4327 info->dir_type = ICON_THEME_DIR_UNTHEMED;
4328 info->dir_size = size;
4329 info->desired_size = size;
4330 info->threshold = 2;
4331 info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
4335 else if (G_IS_THEMED_ICON (icon))
4337 const gchar **names;
4339 names = (const gchar **)g_themed_icon_get_names (G_THEMED_ICON (icon));
4340 info = gtk_icon_theme_choose_icon (icon_theme, names, size, flags);
4344 else if (G_IS_EMBLEMED_ICON (icon))
4346 GIcon *base, *emblem;
4348 GtkIconInfo *emblem_info;
4350 if (GTK_IS_NUMERABLE_ICON (icon))
4351 _gtk_numerable_icon_set_background_icon_size (GTK_NUMERABLE_ICON (icon), size / 2);
4353 base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
4354 info = gtk_icon_theme_lookup_by_gicon (icon_theme, base, size, flags);
4357 list = g_emblemed_icon_get_emblems (G_EMBLEMED_ICON (icon));
4358 for (l = list; l; l = l->next)
4360 emblem = g_emblem_get_icon (G_EMBLEM (l->data));
4361 /* always force size for emblems */
4362 emblem_info = gtk_icon_theme_lookup_by_gicon (icon_theme, emblem, size / 2, flags | GTK_ICON_LOOKUP_FORCE_SIZE);
4364 info->emblem_infos = g_slist_prepend (info->emblem_infos, emblem_info);
4370 else if (GDK_IS_PIXBUF (icon))
4374 pixbuf = GDK_PIXBUF (icon);
4376 if ((flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0)
4378 gint width, height, max;
4382 width = gdk_pixbuf_get_width (pixbuf);
4383 height = gdk_pixbuf_get_height (pixbuf);
4384 max = MAX (width, height);
4385 scale = (gdouble) size / (gdouble) max;
4387 scaled = gdk_pixbuf_scale_simple (pixbuf,
4388 0.5 + width * scale,
4389 0.5 + height * scale,
4390 GDK_INTERP_BILINEAR);
4392 info = gtk_icon_info_new_for_pixbuf (icon_theme, scaled);
4394 g_object_unref (scaled);
4398 info = gtk_icon_info_new_for_pixbuf (icon_theme, pixbuf);
4408 * gtk_icon_info_new_for_pixbuf:
4409 * @icon_theme: a #GtkIconTheme
4410 * @pixbuf: the pixbuf to wrap in a #GtkIconInfo
4412 * Creates a #GtkIconInfo for a #GdkPixbuf.
4414 * Returns: a #GtkIconInfo
4419 gtk_icon_info_new_for_pixbuf (GtkIconTheme *icon_theme,
4424 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
4425 g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
4427 info = icon_info_new ();
4428 info->pixbuf = g_object_ref (pixbuf);
4430 info->dir_type = ICON_THEME_DIR_UNTHEMED;