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>
28 #include <glib/gstdio.h>
32 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
34 #define WIN32_MEAN_AND_LEAN
36 #include "win32/gdkwin32.h"
37 #endif /* G_OS_WIN32 */
39 #include "gtkicontheme.h"
41 #include "gtkiconfactory.h"
42 #include "gtkiconcache.h"
43 #include "gtkbuiltincache.h"
46 #include "gtknumerableiconprivate.h"
47 #include "gtksettings.h"
48 #include "gtkprivate.h"
51 #undef GDK_DEPRECATED_FOR
52 #define GDK_DEPRECATED
53 #define GDK_DEPRECATED_FOR(f)
55 #include "deprecated/gtkstyle.h"
59 * SECTION:gtkicontheme
60 * @Short_description: Looking up icons by name
61 * @Title: GtkIconTheme
63 * #GtkIconTheme provides a facility for looking up icons by name
64 * and size. The main reason for using a name rather than simply
65 * providing a filename is to allow different icons to be used
66 * depending on what <firstterm>icon theme</firstterm> is selected
67 * by the user. The operation of icon themes on Linux and Unix
69 * url="http://www.freedesktop.org/Standards/icon-theme-spec">Icon
70 * Theme Specification</ulink>. There is a default icon theme,
71 * named <literal>hicolor</literal> where applications should install
72 * their icons, but more additional application themes can be
73 * installed as operating system vendors and users choose.
75 * Named icons are similar to the <xref linkend="gtk3-Themeable-Stock-Images"/>
76 * facility, and the distinction between the two may be a bit confusing.
77 * A few things to keep in mind:
80 * Stock images usually are used in conjunction with
81 * <xref linkend="gtk3-Stock-Items"/>, such as %GTK_STOCK_OK or
82 * %GTK_STOCK_OPEN. Named icons are easier to set up and therefore
83 * are more useful for new icons that an application wants to
84 * add, such as application icons or window icons.
87 * Stock images can only be loaded at the symbolic sizes defined
88 * by the #GtkIconSize enumeration, or by custom sizes defined
89 * by gtk_icon_size_register(), while named icons are more flexible
90 * and any pixel size can be specified.
93 * Because stock images are closely tied to stock items, and thus
94 * to actions in the user interface, stock images may come in
95 * multiple variants for different widget states or writing
99 * A good rule of thumb is that if there is a stock image for what
100 * you want to use, use it, otherwise use a named icon. It turns
101 * out that internally stock images are generally defined in
102 * terms of one or more named icons. (An example of the
103 * more than one case is icons that depend on writing direction;
104 * %GTK_STOCK_GO_FORWARD uses the two themed icons
105 * "gtk-stock-go-forward-ltr" and "gtk-stock-go-forward-rtl".)
107 * In many cases, named themes are used indirectly, via #GtkImage
108 * or stock items, rather than directly, but looking up icons
109 * directly is also simple. The #GtkIconTheme object acts
110 * as a database of all the icons in the current theme. You
111 * can create new #GtkIconTheme objects, but its much more
112 * efficient to use the standard icon theme for the #GdkScreen
113 * so that the icon information is shared with other people
114 * looking up icons. In the case where the default screen is
115 * being used, looking up an icon can be as simple as:
118 * GError *error = NULL;
119 * GtkIconTheme *icon_theme;
122 * icon_theme = gtk_icon_theme_get_default ();
123 * pixbuf = gtk_icon_theme_load_icon (icon_theme,
124 * "my-icon-name", // icon name
130 * g_warning ("Couldn't load icon: %s", error->message);
131 * g_error_free (error);
136 * g_object_unref (pixbuf);
143 #define DEFAULT_THEME_NAME "hicolor"
147 ICON_THEME_DIR_FIXED,
148 ICON_THEME_DIR_SCALABLE,
149 ICON_THEME_DIR_THRESHOLD,
150 ICON_THEME_DIR_UNTHEMED
153 /* In reverse search order: */
156 ICON_SUFFIX_NONE = 0,
157 ICON_SUFFIX_XPM = 1 << 0,
158 ICON_SUFFIX_SVG = 1 << 1,
159 ICON_SUFFIX_PNG = 1 << 2,
160 HAS_ICON_FILE = 1 << 3
163 #define INFO_CACHE_LRU_SIZE 32
165 #define DEBUG_CACHE(args) g_print args
167 #define DEBUG_CACHE(args)
170 struct _GtkIconThemePrivate
172 GHashTable *info_cache;
173 GList *info_cache_lru;
175 gchar *current_theme;
176 gchar *fallback_theme;
178 gint search_path_len;
180 guint custom_theme : 1;
181 guint is_screen_singleton : 1;
182 guint pixbuf_supports_svg : 1;
183 guint themes_valid : 1;
184 guint check_reload : 1;
185 guint loading_themes : 1;
187 /* A list of all the themes needed to look up icons.
188 * In search order, without duplicates
191 GHashTable *unthemed_icons;
193 /* Note: The keys of this hashtable are owned by the
194 * themedir and unthemed hashtables.
196 GHashTable *all_icons;
198 /* GdkScreen for the icon theme (may be NULL)
202 /* time when we last stat:ed for theme changes */
203 glong last_stat_time;
206 gulong reset_styles_idle;
212 GtkIconLookupFlags flags;
217 /* Information about the source
220 GtkIconTheme *in_cache;
224 GLoadableIcon *loadable;
225 GSList *emblem_infos;
227 /* Cache pixbuf (if there is any) */
228 GdkPixbuf *cache_pixbuf;
232 /* Information about the directory where
233 * the source was found
235 IconThemeDirType dir_type;
239 /* Parameters influencing the scaled icon
242 guint raw_coordinates : 1;
243 guint forced_size : 1;
244 guint emblems_applied : 1;
248 /* Cached information if we go ahead and try to load
252 GdkPixbuf *proxy_pixbuf;
256 GtkRequisition *symbolic_pixbuf_size;
266 /* In search order */
272 IconThemeDirType type;
287 GHashTable *icon_data;
293 char *no_svg_filename;
305 time_t mtime; /* 0 == not existing or not a dir */
310 static void gtk_icon_theme_finalize (GObject *object);
311 static void theme_dir_destroy (IconThemeDir *dir);
313 static void theme_destroy (IconTheme *theme);
314 static GtkIconInfo *theme_lookup_icon (IconTheme *theme,
315 const char *icon_name,
318 gboolean use_default_icons);
319 static void theme_list_icons (IconTheme *theme,
322 static void theme_list_contexts (IconTheme *theme,
323 GHashTable *contexts);
324 static void theme_subdir_load (GtkIconTheme *icon_theme,
326 GKeyFile *theme_file,
328 static void do_theme_change (GtkIconTheme *icon_theme);
330 static void blow_themes (GtkIconTheme *icon_themes);
331 static gboolean rescan_themes (GtkIconTheme *icon_themes);
333 static void icon_data_free (GtkIconData *icon_data);
334 static void load_icon_data (IconThemeDir *dir,
338 static IconSuffix theme_dir_get_icon_suffix (IconThemeDir *dir,
339 const gchar *icon_name,
340 gboolean *has_icon_file);
343 static GtkIconInfo *icon_info_new (void);
344 static GtkIconInfo *icon_info_new_builtin (BuiltinIcon *icon);
346 static IconSuffix suffix_from_name (const char *name);
348 static BuiltinIcon *find_builtin_icon (const gchar *icon_name,
350 gint *min_difference_p,
351 gboolean *has_larger_p);
352 static void remove_from_lru_cache (GtkIconTheme *icon_theme,
353 GtkIconInfo *icon_info);
355 static guint signal_changed = 0;
357 static GHashTable *icon_theme_builtin_icons;
359 /* also used in gtkiconfactory.c */
360 GtkIconCache *_builtin_cache = NULL;
361 static GList *builtin_dirs = NULL;
364 icon_info_key_hash (gconstpointer _key)
366 const IconInfoKey *key = _key;
369 for (i = 0; key->icon_names[i] != NULL; i++)
370 h ^= g_str_hash (key->icon_names[i]);
372 h ^= key->size * 0x10001;
373 h ^= key->flags * 0x1000010;
379 icon_info_key_equal (gconstpointer _a,
382 const IconInfoKey *a = _a;
383 const IconInfoKey *b = _b;
386 if (a->size != b->size)
389 if (a->flags != b->flags)
393 a->icon_names[i] != NULL &&
394 b->icon_names[i] != NULL; i++)
396 if (strcmp (a->icon_names[i], b->icon_names[i]) != 0)
400 return a->icon_names[i] == NULL && b->icon_names[i] == NULL;
403 G_DEFINE_TYPE (GtkIconTheme, gtk_icon_theme, G_TYPE_OBJECT)
406 * gtk_icon_theme_new:
408 * Creates a new icon theme object. Icon theme objects are used
409 * to lookup up an icon by name in a particular icon theme.
410 * Usually, you'll want to use gtk_icon_theme_get_default()
411 * or gtk_icon_theme_get_for_screen() rather than creating
412 * a new icon theme object for scratch.
414 * Return value: the newly created #GtkIconTheme object.
419 gtk_icon_theme_new (void)
421 return g_object_new (GTK_TYPE_ICON_THEME, NULL);
425 * gtk_icon_theme_get_default:
427 * Gets the icon theme for the default screen. See
428 * gtk_icon_theme_get_for_screen().
430 * Return value: (transfer none): A unique #GtkIconTheme associated with
431 * the default screen. This icon theme is associated with
432 * the screen and can be used as long as the screen
433 * is open. Do not ref or unref it.
438 gtk_icon_theme_get_default (void)
440 return gtk_icon_theme_get_for_screen (gdk_screen_get_default ());
444 * gtk_icon_theme_get_for_screen:
445 * @screen: a #GdkScreen
447 * Gets the icon theme object associated with @screen; if this
448 * function has not previously been called for the given
449 * screen, a new icon theme object will be created and
450 * associated with the screen. Icon theme objects are
451 * fairly expensive to create, so using this function
452 * is usually a better choice than calling than gtk_icon_theme_new()
453 * and setting the screen yourself; by using this function
454 * a single icon theme object will be shared between users.
456 * Return value: (transfer none): A unique #GtkIconTheme associated with
457 * the given screen. This icon theme is associated with
458 * the screen and can be used as long as the screen
459 * is open. Do not ref or unref it.
464 gtk_icon_theme_get_for_screen (GdkScreen *screen)
466 GtkIconTheme *icon_theme;
468 g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
470 icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
473 GtkIconThemePrivate *priv;
475 icon_theme = gtk_icon_theme_new ();
476 gtk_icon_theme_set_screen (icon_theme, screen);
478 priv = icon_theme->priv;
479 priv->is_screen_singleton = TRUE;
481 g_object_set_data (G_OBJECT (screen), I_("gtk-icon-theme"), icon_theme);
488 gtk_icon_theme_class_init (GtkIconThemeClass *klass)
490 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
492 gobject_class->finalize = gtk_icon_theme_finalize;
495 * GtkIconTheme::changed:
496 * @icon_theme: the icon theme
498 * Emitted when the current icon theme is switched or GTK+ detects
499 * that a change has occurred in the contents of the current
502 signal_changed = g_signal_new (I_("changed"),
503 G_TYPE_FROM_CLASS (klass),
505 G_STRUCT_OFFSET (GtkIconThemeClass, changed),
507 g_cclosure_marshal_VOID__VOID,
510 g_type_class_add_private (klass, sizeof (GtkIconThemePrivate));
514 /* Callback when the display that the icon theme is attached to
515 * is closed; unset the screen, and if it's the unique theme
516 * for the screen, drop the reference
519 display_closed (GdkDisplay *display,
521 GtkIconTheme *icon_theme)
523 GtkIconThemePrivate *priv = icon_theme->priv;
524 GdkScreen *screen = priv->screen;
525 gboolean was_screen_singleton = priv->is_screen_singleton;
527 if (was_screen_singleton)
529 g_object_set_data (G_OBJECT (screen), I_("gtk-icon-theme"), NULL);
530 priv->is_screen_singleton = FALSE;
533 gtk_icon_theme_set_screen (icon_theme, NULL);
535 if (was_screen_singleton)
537 g_object_unref (icon_theme);
542 update_current_theme (GtkIconTheme *icon_theme)
544 #define theme_changed(_old, _new) \
545 ((_old && !_new) || (!_old && _new) || \
546 (_old && _new && strcmp (_old, _new) != 0))
547 GtkIconThemePrivate *priv = icon_theme->priv;
549 if (!priv->custom_theme)
552 gchar *fallback_theme = NULL;
553 gboolean changed = FALSE;
557 GtkSettings *settings = gtk_settings_get_for_screen (priv->screen);
558 g_object_get (settings,
559 "gtk-icon-theme-name", &theme,
560 "gtk-fallback-icon-theme", &fallback_theme, NULL);
563 /* ensure that the current theme (even when just the default)
564 * is searched before any fallback theme
566 if (!theme && fallback_theme)
567 theme = g_strdup (DEFAULT_THEME_NAME);
569 if (theme_changed (priv->current_theme, theme))
571 g_free (priv->current_theme);
572 priv->current_theme = theme;
578 if (theme_changed (priv->fallback_theme, fallback_theme))
580 g_free (priv->fallback_theme);
581 priv->fallback_theme = fallback_theme;
585 g_free (fallback_theme);
588 do_theme_change (icon_theme);
593 /* Callback when the icon theme GtkSetting changes
596 theme_changed (GtkSettings *settings,
598 GtkIconTheme *icon_theme)
600 update_current_theme (icon_theme);
604 unset_screen (GtkIconTheme *icon_theme)
606 GtkIconThemePrivate *priv = icon_theme->priv;
607 GtkSettings *settings;
612 settings = gtk_settings_get_for_screen (priv->screen);
613 display = gdk_screen_get_display (priv->screen);
615 g_signal_handlers_disconnect_by_func (display,
616 (gpointer) display_closed,
618 g_signal_handlers_disconnect_by_func (settings,
619 (gpointer) theme_changed,
627 * gtk_icon_theme_set_screen:
628 * @icon_theme: a #GtkIconTheme
629 * @screen: a #GdkScreen
631 * Sets the screen for an icon theme; the screen is used
632 * to track the user's currently configured icon theme,
633 * which might be different for different screens.
638 gtk_icon_theme_set_screen (GtkIconTheme *icon_theme,
641 GtkIconThemePrivate *priv;
642 GtkSettings *settings;
645 g_return_if_fail (GTK_ICON_THEME (icon_theme));
646 g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
648 priv = icon_theme->priv;
650 unset_screen (icon_theme);
654 display = gdk_screen_get_display (screen);
655 settings = gtk_settings_get_for_screen (screen);
657 priv->screen = screen;
659 g_signal_connect (display, "closed",
660 G_CALLBACK (display_closed), icon_theme);
661 g_signal_connect (settings, "notify::gtk-icon-theme-name",
662 G_CALLBACK (theme_changed), icon_theme);
663 g_signal_connect (settings, "notify::gtk-fallback-icon-theme-name",
664 G_CALLBACK (theme_changed), icon_theme);
667 update_current_theme (icon_theme);
670 /* Checks whether a loader for SVG files has been registered
674 pixbuf_supports_svg (void)
678 static gint found_svg = -1;
683 formats = gdk_pixbuf_get_formats ();
686 for (tmp_list = formats; tmp_list && !found_svg; tmp_list = tmp_list->next)
688 gchar **mime_types = gdk_pixbuf_format_get_mime_types (tmp_list->data);
691 for (mime_type = mime_types; *mime_type && !found_svg; mime_type++)
693 if (strcmp (*mime_type, "image/svg") == 0)
697 g_strfreev (mime_types);
700 g_slist_free (formats);
705 /* The icon info was removed from the icon_info_hash hash table */
707 icon_info_uncached (GtkIconInfo *icon_info)
709 GtkIconTheme *icon_theme = icon_info->in_cache;
711 DEBUG_CACHE (("removing %p (%s %d 0x%x) from cache (icon_them: %p) (cache size %d)\n",
713 g_strjoinv (",", icon_info->key.icon_names),
714 icon_info->key.size, icon_info->key.flags,
716 icon_theme != NULL ? g_hash_table_size (icon_theme->priv->info_cache) : 0));
718 icon_info->in_cache = NULL;
720 if (icon_theme != NULL)
721 remove_from_lru_cache (icon_theme, icon_info);
725 gtk_icon_theme_init (GtkIconTheme *icon_theme)
727 GtkIconThemePrivate *priv;
728 const gchar * const *xdg_data_dirs;
731 priv = G_TYPE_INSTANCE_GET_PRIVATE (icon_theme,
733 GtkIconThemePrivate);
734 icon_theme->priv = priv;
736 priv->info_cache = g_hash_table_new_full (icon_info_key_hash, icon_info_key_equal, NULL,
737 (GDestroyNotify)icon_info_uncached);
739 priv->custom_theme = FALSE;
741 xdg_data_dirs = g_get_system_data_dirs ();
742 for (i = 0; xdg_data_dirs[i]; i++) ;
744 priv->search_path_len = 2 * i + 2;
746 priv->search_path = g_new (char *, priv->search_path_len);
749 priv->search_path[i++] = g_build_filename (g_get_user_data_dir (), "icons", NULL);
750 priv->search_path[i++] = g_build_filename (g_get_home_dir (), ".icons", NULL);
752 for (j = 0; xdg_data_dirs[j]; j++)
753 priv->search_path[i++] = g_build_filename (xdg_data_dirs[j], "icons", NULL);
755 for (j = 0; xdg_data_dirs[j]; j++)
756 priv->search_path[i++] = g_build_filename (xdg_data_dirs[j], "pixmaps", NULL);
758 priv->themes_valid = FALSE;
760 priv->unthemed_icons = NULL;
762 priv->pixbuf_supports_svg = pixbuf_supports_svg ();
766 free_dir_mtime (IconThemeDirMtime *dir_mtime)
768 if (dir_mtime->cache)
769 _gtk_icon_cache_unref (dir_mtime->cache);
771 g_free (dir_mtime->dir);
772 g_slice_free (IconThemeDirMtime, dir_mtime);
777 reset_styles_idle (gpointer user_data)
779 GtkIconTheme *icon_theme;
780 GtkIconThemePrivate *priv;
782 icon_theme = GTK_ICON_THEME (user_data);
783 priv = icon_theme->priv;
785 if (priv->screen && priv->is_screen_singleton)
786 gtk_style_context_reset_widgets (priv->screen);
788 priv->reset_styles_idle = 0;
794 do_theme_change (GtkIconTheme *icon_theme)
796 GtkIconThemePrivate *priv = icon_theme->priv;
798 g_hash_table_remove_all (priv->info_cache);
800 if (!priv->themes_valid)
804 g_print ("change to icon theme \"%s\"\n", priv->current_theme));
805 blow_themes (icon_theme);
806 g_signal_emit (icon_theme, signal_changed, 0);
808 if (!priv->reset_styles_idle)
809 priv->reset_styles_idle =
810 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2,
811 reset_styles_idle, icon_theme, NULL);
815 blow_themes (GtkIconTheme *icon_theme)
817 GtkIconThemePrivate *priv = icon_theme->priv;
819 if (priv->themes_valid)
821 g_hash_table_destroy (priv->all_icons);
822 g_list_free_full (priv->themes, (GDestroyNotify) theme_destroy);
823 g_list_free_full (priv->dir_mtimes, (GDestroyNotify) free_dir_mtime);
824 g_hash_table_destroy (priv->unthemed_icons);
827 priv->unthemed_icons = NULL;
828 priv->dir_mtimes = NULL;
829 priv->all_icons = NULL;
830 priv->themes_valid = FALSE;
834 gtk_icon_theme_finalize (GObject *object)
836 GtkIconTheme *icon_theme;
837 GtkIconThemePrivate *priv;
840 icon_theme = GTK_ICON_THEME (object);
841 priv = icon_theme->priv;
843 g_hash_table_destroy (priv->info_cache);
844 g_assert (priv->info_cache_lru == NULL);
846 if (priv->reset_styles_idle)
848 g_source_remove (priv->reset_styles_idle);
849 priv->reset_styles_idle = 0;
852 unset_screen (icon_theme);
854 g_free (priv->current_theme);
855 priv->current_theme = NULL;
857 for (i = 0; i < priv->search_path_len; i++)
858 g_free (priv->search_path[i]);
860 g_free (priv->search_path);
861 priv->search_path = NULL;
863 blow_themes (icon_theme);
865 G_OBJECT_CLASS (gtk_icon_theme_parent_class)->finalize (object);
869 * gtk_icon_theme_set_search_path:
870 * @icon_theme: a #GtkIconTheme
871 * @path: (array length=n_elements) (element-type filename): array of
872 * directories that are searched for icon themes
873 * @n_elements: number of elements in @path.
875 * Sets the search path for the icon theme object. When looking
876 * for an icon theme, GTK+ will search for a subdirectory of
877 * one or more of the directories in @path with the same name
878 * as the icon theme. (Themes from multiple of the path elements
879 * are combined to allow themes to be extended by adding icons
880 * in the user's home directory.)
882 * In addition if an icon found isn't found either in the current
883 * icon theme or the default icon theme, and an image file with
884 * the right name is found directly in one of the elements of
885 * @path, then that image will be used for the icon name.
886 * (This is legacy feature, and new icons should be put
887 * into the default icon theme, which is called DEFAULT_THEME_NAME,
888 * rather than directly on the icon path.)
893 gtk_icon_theme_set_search_path (GtkIconTheme *icon_theme,
897 GtkIconThemePrivate *priv;
900 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
902 priv = icon_theme->priv;
903 for (i = 0; i < priv->search_path_len; i++)
904 g_free (priv->search_path[i]);
906 g_free (priv->search_path);
908 priv->search_path = g_new (gchar *, n_elements);
909 priv->search_path_len = n_elements;
911 for (i = 0; i < priv->search_path_len; i++)
912 priv->search_path[i] = g_strdup (path[i]);
914 do_theme_change (icon_theme);
919 * gtk_icon_theme_get_search_path:
920 * @icon_theme: a #GtkIconTheme
921 * @path: (allow-none) (array length=n_elements) (element-type filename) (out):
922 * location to store a list of icon theme path directories or %NULL.
923 * The stored value should be freed with g_strfreev().
924 * @n_elements: location to store number of elements in @path, or %NULL
926 * Gets the current search path. See gtk_icon_theme_set_search_path().
931 gtk_icon_theme_get_search_path (GtkIconTheme *icon_theme,
935 GtkIconThemePrivate *priv;
938 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
940 priv = icon_theme->priv;
943 *n_elements = priv->search_path_len;
947 *path = g_new (gchar *, priv->search_path_len + 1);
948 for (i = 0; i < priv->search_path_len; i++)
949 (*path)[i] = g_strdup (priv->search_path[i]);
955 * gtk_icon_theme_append_search_path:
956 * @icon_theme: a #GtkIconTheme
957 * @path: (type filename): directory name to append to the icon path
959 * Appends a directory to the search path.
960 * See gtk_icon_theme_set_search_path().
965 gtk_icon_theme_append_search_path (GtkIconTheme *icon_theme,
968 GtkIconThemePrivate *priv;
970 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
971 g_return_if_fail (path != NULL);
973 priv = icon_theme->priv;
975 priv->search_path_len++;
977 priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
978 priv->search_path[priv->search_path_len-1] = g_strdup (path);
980 do_theme_change (icon_theme);
984 * gtk_icon_theme_prepend_search_path:
985 * @icon_theme: a #GtkIconTheme
986 * @path: (type filename): directory name to prepend to the icon path
988 * Prepends a directory to the search path.
989 * See gtk_icon_theme_set_search_path().
994 gtk_icon_theme_prepend_search_path (GtkIconTheme *icon_theme,
997 GtkIconThemePrivate *priv;
1000 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
1001 g_return_if_fail (path != NULL);
1003 priv = icon_theme->priv;
1005 priv->search_path_len++;
1006 priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
1008 for (i = priv->search_path_len - 1; i > 0; i--)
1009 priv->search_path[i] = priv->search_path[i - 1];
1011 priv->search_path[0] = g_strdup (path);
1013 do_theme_change (icon_theme);
1017 * gtk_icon_theme_set_custom_theme:
1018 * @icon_theme: a #GtkIconTheme
1019 * @theme_name: (allow-none): name of icon theme to use instead of
1020 * configured theme, or %NULL to unset a previously set custom theme
1022 * Sets the name of the icon theme that the #GtkIconTheme object uses
1023 * overriding system configuration. This function cannot be called
1024 * on the icon theme objects returned from gtk_icon_theme_get_default()
1025 * and gtk_icon_theme_get_for_screen().
1030 gtk_icon_theme_set_custom_theme (GtkIconTheme *icon_theme,
1031 const gchar *theme_name)
1033 GtkIconThemePrivate *priv;
1035 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
1037 priv = icon_theme->priv;
1039 g_return_if_fail (!priv->is_screen_singleton);
1041 if (theme_name != NULL)
1043 priv->custom_theme = TRUE;
1044 if (!priv->current_theme || strcmp (theme_name, priv->current_theme) != 0)
1046 g_free (priv->current_theme);
1047 priv->current_theme = g_strdup (theme_name);
1049 do_theme_change (icon_theme);
1054 if (priv->custom_theme)
1056 priv->custom_theme = FALSE;
1058 update_current_theme (icon_theme);
1064 insert_theme (GtkIconTheme *icon_theme, const char *theme_name)
1070 GtkIconThemePrivate *priv;
1071 IconTheme *theme = NULL;
1073 GKeyFile *theme_file;
1074 GError *error = NULL;
1075 IconThemeDirMtime *dir_mtime;
1078 priv = icon_theme->priv;
1080 for (l = priv->themes; l != NULL; l = l->next)
1083 if (strcmp (theme->name, theme_name) == 0)
1087 for (i = 0; i < priv->search_path_len; i++)
1089 path = g_build_filename (priv->search_path[i],
1092 dir_mtime = g_slice_new (IconThemeDirMtime);
1093 dir_mtime->cache = NULL;
1094 dir_mtime->dir = path;
1095 if (g_stat (path, &stat_buf) == 0 && S_ISDIR (stat_buf.st_mode))
1096 dir_mtime->mtime = stat_buf.st_mtime;
1098 dir_mtime->mtime = 0;
1100 priv->dir_mtimes = g_list_prepend (priv->dir_mtimes, dir_mtime);
1102 priv->dir_mtimes = g_list_reverse (priv->dir_mtimes);
1105 for (i = 0; i < priv->search_path_len && !theme_file; i++)
1107 path = g_build_filename (priv->search_path[i],
1111 if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
1113 theme_file = g_key_file_new ();
1114 g_key_file_set_list_separator (theme_file, ',');
1115 g_key_file_load_from_file (theme_file, path, 0, &error);
1118 g_key_file_free (theme_file);
1120 g_error_free (error);
1127 if (theme_file || strcmp (theme_name, DEFAULT_THEME_NAME) == 0)
1129 theme = g_new0 (IconTheme, 1);
1130 theme->name = g_strdup (theme_name);
1131 priv->themes = g_list_prepend (priv->themes, theme);
1134 if (theme_file == NULL)
1137 theme->display_name =
1138 g_key_file_get_locale_string (theme_file, "Icon Theme", "Name", NULL, NULL);
1139 if (!theme->display_name)
1140 g_warning ("Theme file for %s has no name\n", theme_name);
1142 dirs = g_key_file_get_string_list (theme_file, "Icon Theme", "Directories", NULL, NULL);
1145 g_warning ("Theme file for %s has no directories\n", theme_name);
1146 priv->themes = g_list_remove (priv->themes, theme);
1147 g_free (theme->name);
1148 g_free (theme->display_name);
1150 g_key_file_free (theme_file);
1155 g_key_file_get_locale_string (theme_file,
1156 "Icon Theme", "Comment",
1159 g_key_file_get_string (theme_file,
1160 "Icon Theme", "Example",
1164 for (i = 0; dirs[i] != NULL; i++)
1165 theme_subdir_load (icon_theme, theme, theme_file, dirs[i]);
1169 theme->dirs = g_list_reverse (theme->dirs);
1171 themes = g_key_file_get_string_list (theme_file,
1178 for (i = 0; themes[i] != NULL; i++)
1179 insert_theme (icon_theme, themes[i]);
1181 g_strfreev (themes);
1184 g_key_file_free (theme_file);
1188 free_unthemed_icon (UnthemedIcon *unthemed_icon)
1190 g_free (unthemed_icon->svg_filename);
1191 g_free (unthemed_icon->no_svg_filename);
1192 g_slice_free (UnthemedIcon, unthemed_icon);
1196 strip_suffix (const char *filename)
1200 dot = strrchr (filename, '.');
1203 return g_strdup (filename);
1205 return g_strndup (filename, dot - filename);
1209 load_themes (GtkIconTheme *icon_theme)
1211 GtkIconThemePrivate *priv;
1216 UnthemedIcon *unthemed_icon;
1217 IconSuffix old_suffix, new_suffix;
1219 IconThemeDirMtime *dir_mtime;
1222 priv = icon_theme->priv;
1224 priv->all_icons = g_hash_table_new (g_str_hash, g_str_equal);
1226 if (priv->current_theme)
1227 insert_theme (icon_theme, priv->current_theme);
1229 /* Always look in the "default" icon theme, and in a fallback theme */
1230 if (priv->fallback_theme)
1231 insert_theme (icon_theme, priv->fallback_theme);
1232 insert_theme (icon_theme, DEFAULT_THEME_NAME);
1233 priv->themes = g_list_reverse (priv->themes);
1236 priv->unthemed_icons = g_hash_table_new_full (g_str_hash, g_str_equal,
1237 g_free, (GDestroyNotify)free_unthemed_icon);
1239 for (base = 0; base < icon_theme->priv->search_path_len; base++)
1241 dir = icon_theme->priv->search_path[base];
1243 dir_mtime = g_slice_new (IconThemeDirMtime);
1244 priv->dir_mtimes = g_list_append (priv->dir_mtimes, dir_mtime);
1246 dir_mtime->dir = g_strdup (dir);
1247 dir_mtime->mtime = 0;
1248 dir_mtime->cache = NULL;
1250 if (g_stat (dir, &stat_buf) != 0 || !S_ISDIR (stat_buf.st_mode))
1252 dir_mtime->mtime = stat_buf.st_mtime;
1254 dir_mtime->cache = _gtk_icon_cache_new_for_path (dir);
1255 if (dir_mtime->cache != NULL)
1258 gdir = g_dir_open (dir, 0, NULL);
1262 while ((file = g_dir_read_name (gdir)))
1264 new_suffix = suffix_from_name (file);
1266 if (new_suffix != ICON_SUFFIX_NONE)
1271 abs_file = g_build_filename (dir, file, NULL);
1272 base_name = strip_suffix (file);
1274 if ((unthemed_icon = g_hash_table_lookup (priv->unthemed_icons,
1277 if (new_suffix == ICON_SUFFIX_SVG)
1279 if (unthemed_icon->svg_filename)
1282 unthemed_icon->svg_filename = abs_file;
1286 if (unthemed_icon->no_svg_filename)
1288 old_suffix = suffix_from_name (unthemed_icon->no_svg_filename);
1289 if (new_suffix > old_suffix)
1291 g_free (unthemed_icon->no_svg_filename);
1292 unthemed_icon->no_svg_filename = abs_file;
1298 unthemed_icon->no_svg_filename = abs_file;
1305 unthemed_icon = g_slice_new0 (UnthemedIcon);
1307 if (new_suffix == ICON_SUFFIX_SVG)
1308 unthemed_icon->svg_filename = abs_file;
1310 unthemed_icon->no_svg_filename = abs_file;
1312 /* takes ownership of base_name */
1313 g_hash_table_replace (priv->unthemed_icons,
1316 g_hash_table_insert (priv->all_icons,
1324 priv->themes_valid = TRUE;
1326 g_get_current_time(&tv);
1327 priv->last_stat_time = tv.tv_sec;
1331 _gtk_icon_theme_ensure_builtin_cache (void)
1333 static gboolean initialized = FALSE;
1335 static IconThemeDir dirs[5] =
1337 { ICON_THEME_DIR_THRESHOLD, 0, 16, 16, 16, 2, NULL, "16", -1, NULL, NULL, NULL },
1338 { ICON_THEME_DIR_THRESHOLD, 0, 20, 20, 20, 2, NULL, "20", -1, NULL, NULL, NULL },
1339 { ICON_THEME_DIR_THRESHOLD, 0, 24, 24, 24, 2, NULL, "24", -1, NULL, NULL, NULL },
1340 { ICON_THEME_DIR_THRESHOLD, 0, 32, 32, 32, 2, NULL, "32", -1, NULL, NULL, NULL },
1341 { ICON_THEME_DIR_THRESHOLD, 0, 48, 48, 48, 2, NULL, "48", -1, NULL, NULL, NULL }
1349 _builtin_cache = _gtk_icon_cache_new ((gchar *)builtin_icons);
1351 for (i = 0; i < G_N_ELEMENTS (dirs); i++)
1354 dir->cache = _gtk_icon_cache_ref (_builtin_cache);
1355 dir->subdir_index = _gtk_icon_cache_get_directory_index (dir->cache, dir->subdir);
1357 builtin_dirs = g_list_append (builtin_dirs, dir);
1363 ensure_valid_themes (GtkIconTheme *icon_theme)
1365 GtkIconThemePrivate *priv = icon_theme->priv;
1367 gboolean was_valid = priv->themes_valid;
1369 if (priv->loading_themes)
1371 priv->loading_themes = TRUE;
1373 _gtk_icon_theme_ensure_builtin_cache ();
1375 if (priv->themes_valid)
1377 g_get_current_time (&tv);
1379 if (ABS (tv.tv_sec - priv->last_stat_time) > 5 &&
1380 rescan_themes (icon_theme))
1381 blow_themes (icon_theme);
1384 if (!priv->themes_valid)
1386 load_themes (icon_theme);
1390 g_signal_emit (icon_theme, signal_changed, 0);
1394 priv->loading_themes = FALSE;
1397 /* The LRU cache is a short list of IconInfos that are kept
1398 alive even though their IconInfo would otherwise have
1399 been freed, so that we can avoid reloading these
1401 We put infos on the lru list when nothing otherwise
1402 references the info. So, when we get a cache hit
1403 we remove it from the list, and when the proxy
1404 pixmap is released we put it on the list.
1408 ensure_lru_cache_space (GtkIconTheme *icon_theme)
1410 GtkIconThemePrivate *priv = icon_theme->priv;
1413 /* Remove last item if LRU full */
1414 l = g_list_nth (priv->info_cache_lru, INFO_CACHE_LRU_SIZE - 1);
1417 GtkIconInfo *icon_info = l->data;
1419 DEBUG_CACHE (("removing (due to out of space) %p (%s %d 0x%x) from LRU cache (cache size %d)\n",
1421 g_strjoinv (",", icon_info->key.icon_names),
1422 icon_info->key.size, icon_info->key.flags,
1423 g_list_length (priv->info_cache_lru)));
1425 priv->info_cache_lru = g_list_delete_link (priv->info_cache_lru, l);
1426 gtk_icon_info_free (icon_info);
1431 add_to_lru_cache (GtkIconTheme *icon_theme,
1432 GtkIconInfo *icon_info)
1434 GtkIconThemePrivate *priv = icon_theme->priv;
1436 DEBUG_CACHE (("adding %p (%s %d 0x%x) to LRU cache (cache size %d)\n",
1438 g_strjoinv (",", icon_info->key.icon_names),
1439 icon_info->key.size, icon_info->key.flags,
1440 g_list_length (priv->info_cache_lru)));
1442 g_assert (g_list_find (priv->info_cache_lru, icon_info) == NULL);
1444 ensure_lru_cache_space (icon_theme);
1445 /* prepend new info to LRU */
1446 priv->info_cache_lru = g_list_prepend (priv->info_cache_lru,
1447 gtk_icon_info_copy (icon_info));
1451 ensure_in_lru_cache (GtkIconTheme *icon_theme,
1452 GtkIconInfo *icon_info)
1454 GtkIconThemePrivate *priv = icon_theme->priv;
1457 l = g_list_find (priv->info_cache_lru, icon_info);
1460 /* Move to front of LRU if already in it */
1461 priv->info_cache_lru = g_list_remove_link (priv->info_cache_lru, l);
1462 priv->info_cache_lru = g_list_concat (l, priv->info_cache_lru);
1465 add_to_lru_cache (icon_theme, icon_info);
1469 remove_from_lru_cache (GtkIconTheme *icon_theme,
1470 GtkIconInfo *icon_info)
1472 GtkIconThemePrivate *priv = icon_theme->priv;
1473 if (g_list_find (priv->info_cache_lru, icon_info))
1475 DEBUG_CACHE (("removing %p (%s %d 0x%x) from LRU cache (cache size %d)\n",
1477 g_strjoinv (",", icon_info->key.icon_names),
1478 icon_info->key.size, icon_info->key.flags,
1479 g_list_length (priv->info_cache_lru)));
1481 priv->info_cache_lru = g_list_remove (priv->info_cache_lru, icon_info);
1482 gtk_icon_info_free (icon_info);
1486 static GtkIconInfo *
1487 choose_icon (GtkIconTheme *icon_theme,
1488 const gchar *icon_names[],
1490 GtkIconLookupFlags flags)
1492 GtkIconThemePrivate *priv;
1494 GtkIconInfo *icon_info = NULL;
1495 UnthemedIcon *unthemed_icon = NULL;
1497 gboolean use_builtin;
1501 priv = icon_theme->priv;
1503 ensure_valid_themes (icon_theme);
1505 key.icon_names = (char **)icon_names;
1509 icon_info = g_hash_table_lookup (priv->info_cache, &key);
1510 if (icon_info != NULL)
1512 DEBUG_CACHE (("cache hit %p (%s %d 0x%x) (cache size %d)\n",
1514 g_strjoinv (",", icon_info->key.icon_names),
1515 icon_info->key.size, icon_info->key.flags,
1516 g_hash_table_size (priv->info_cache)));
1518 icon_info = gtk_icon_info_copy (icon_info);
1519 remove_from_lru_cache (icon_theme, icon_info);
1524 if (flags & GTK_ICON_LOOKUP_NO_SVG)
1526 else if (flags & GTK_ICON_LOOKUP_FORCE_SVG)
1529 allow_svg = priv->pixbuf_supports_svg;
1531 use_builtin = flags & GTK_ICON_LOOKUP_USE_BUILTIN;
1533 /* for symbolic icons, do a search in all registered themes first;
1534 * a theme that inherits them from a parent theme might provide
1535 * an alternative highcolor version, but still expect the symbolic icon
1536 * to show up instead.
1538 if (icon_names[0] &&
1539 g_str_has_suffix (icon_names[0], "-symbolic"))
1541 for (l = priv->themes; l; l = l->next)
1543 IconTheme *theme = l->data;
1544 icon_info = theme_lookup_icon (theme, icon_names[0], size, allow_svg, use_builtin);
1550 for (l = priv->themes; l; l = l->next)
1552 IconTheme *theme = l->data;
1554 for (i = 0; icon_names[i]; i++)
1556 icon_info = theme_lookup_icon (theme, icon_names[i], size, allow_svg, use_builtin);
1562 for (i = 0; icon_names[i]; i++)
1564 unthemed_icon = g_hash_table_lookup (priv->unthemed_icons, icon_names[i]);
1569 /* Still not found an icon, check if reference to a Win32 resource */
1575 resources = g_strsplit (icon_names[0], ",", 0);
1578 wchar_t *wfile = g_utf8_to_utf16 (resources[0], -1, NULL, NULL, NULL);
1579 ExtractIconExW (wfile, resources[1] ? atoi (resources[1]) : 0, &hIcon, NULL, 1);
1585 icon_info = icon_info_new ();
1586 icon_info->cache_pixbuf = gdk_win32_icon_to_pixbuf_libgtk_only (hIcon);
1587 DestroyIcon (hIcon);
1588 icon_info->dir_type = ICON_THEME_DIR_UNTHEMED;
1589 icon_info->dir_size = size;
1591 g_strfreev (resources);
1597 icon_info = icon_info_new ();
1599 /* A SVG icon, when allowed, beats out a XPM icon, but not
1603 unthemed_icon->svg_filename &&
1604 (!unthemed_icon->no_svg_filename ||
1605 suffix_from_name (unthemed_icon->no_svg_filename) != ICON_SUFFIX_PNG))
1606 icon_info->filename = g_strdup (unthemed_icon->svg_filename);
1607 else if (unthemed_icon->no_svg_filename)
1608 icon_info->filename = g_strdup (unthemed_icon->no_svg_filename);
1610 icon_info->icon_file = g_file_new_for_path (icon_info->filename);
1612 icon_info->dir_type = ICON_THEME_DIR_UNTHEMED;
1613 icon_info->dir_size = size;
1619 icon_info->desired_size = size;
1620 icon_info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
1622 icon_info->key.icon_names = g_strdupv ((char **)icon_names);
1623 icon_info->key.size = size;
1624 icon_info->key.flags = flags;
1625 icon_info->in_cache = icon_theme;
1626 DEBUG_CACHE (("adding %p (%s %d 0x%x) to cache (cache size %d)\n",
1628 g_strjoinv (",", icon_info->key.icon_names),
1629 icon_info->key.size, icon_info->key.flags,
1630 g_hash_table_size (priv->info_cache)));
1631 g_hash_table_insert (priv->info_cache, &icon_info->key, icon_info);
1635 static gboolean check_for_default_theme = TRUE;
1636 char *default_theme_path;
1637 gboolean found = FALSE;
1640 if (check_for_default_theme)
1642 check_for_default_theme = FALSE;
1644 for (i = 0; !found && i < priv->search_path_len; i++)
1646 default_theme_path = g_build_filename (priv->search_path[i],
1650 found = g_file_test (default_theme_path, G_FILE_TEST_IS_REGULAR);
1651 g_free (default_theme_path);
1656 g_warning ("Could not find the icon '%s'. The '%s' theme\n"
1657 "was not found either, perhaps you need to install it.\n"
1658 "You can get a copy from:\n"
1660 icon_names[0], DEFAULT_THEME_NAME, "http://icon-theme.freedesktop.org/releases");
1670 * gtk_icon_theme_lookup_icon:
1671 * @icon_theme: a #GtkIconTheme
1672 * @icon_name: the name of the icon to lookup
1673 * @size: desired icon size
1674 * @flags: flags modifying the behavior of the icon lookup
1676 * Looks up a named icon and returns a structure containing
1677 * information such as the filename of the icon. The icon
1678 * can then be rendered into a pixbuf using
1679 * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
1680 * combines these two steps if all you need is the pixbuf.)
1682 * Return value: a #GtkIconInfo structure containing information
1683 * about the icon, or %NULL if the icon wasn't found. Free with
1684 * gtk_icon_info_free()
1689 gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme,
1690 const gchar *icon_name,
1692 GtkIconLookupFlags flags)
1696 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1697 g_return_val_if_fail (icon_name != NULL, NULL);
1698 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1699 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1701 GTK_NOTE (ICONTHEME,
1702 g_print ("gtk_icon_theme_lookup_icon %s\n", icon_name));
1704 if (flags & GTK_ICON_LOOKUP_GENERIC_FALLBACK)
1711 for (p = (gchar *) icon_name; *p; p++)
1715 names = g_new (gchar *, dashes + 2);
1716 names[0] = g_strdup (icon_name);
1717 for (i = 1; i <= dashes; i++)
1719 names[i] = g_strdup (names[i - 1]);
1720 p = strrchr (names[i], '-');
1723 names[dashes + 1] = NULL;
1725 info = choose_icon (icon_theme, (const gchar **) names, size, flags);
1731 const gchar *names[2];
1733 names[0] = icon_name;
1736 info = choose_icon (icon_theme, names, size, flags);
1743 * gtk_icon_theme_choose_icon:
1744 * @icon_theme: a #GtkIconTheme
1745 * @icon_names: (array zero-terminated=1): %NULL-terminated array of
1746 * icon names to lookup
1747 * @size: desired icon size
1748 * @flags: flags modifying the behavior of the icon lookup
1750 * Looks up a named icon and returns a structure containing
1751 * information such as the filename of the icon. The icon
1752 * can then be rendered into a pixbuf using
1753 * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
1754 * combines these two steps if all you need is the pixbuf.)
1756 * If @icon_names contains more than one name, this function
1757 * tries them all in the given order before falling back to
1758 * inherited icon themes.
1760 * Return value: a #GtkIconInfo structure containing information
1761 * about the icon, or %NULL if the icon wasn't found. Free with
1762 * gtk_icon_info_free()
1767 gtk_icon_theme_choose_icon (GtkIconTheme *icon_theme,
1768 const gchar *icon_names[],
1770 GtkIconLookupFlags flags)
1772 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1773 g_return_val_if_fail (icon_names != NULL, NULL);
1774 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1775 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1777 return choose_icon (icon_theme, icon_names, size, flags);
1782 gtk_icon_theme_error_quark (void)
1784 return g_quark_from_static_string ("gtk-icon-theme-error-quark");
1788 * gtk_icon_theme_load_icon:
1789 * @icon_theme: a #GtkIconTheme
1790 * @icon_name: the name of the icon to lookup
1791 * @size: the desired icon size. The resulting icon may not be
1792 * exactly this size; see gtk_icon_info_load_icon().
1793 * @flags: flags modifying the behavior of the icon lookup
1794 * @error: (allow-none): Location to store error information on failure,
1797 * Looks up an icon in an icon theme, scales it to the given size
1798 * and renders it into a pixbuf. This is a convenience function;
1799 * if more details about the icon are needed, use
1800 * gtk_icon_theme_lookup_icon() followed by gtk_icon_info_load_icon().
1802 * Note that you probably want to listen for icon theme changes and
1803 * update the icon. This is usually done by connecting to the
1804 * GtkWidget::style-set signal. If for some reason you do not want to
1805 * update the icon when the icon theme changes, you should consider
1806 * using gdk_pixbuf_copy() to make a private copy of the pixbuf
1807 * returned by this function. Otherwise GTK+ may need to keep the old
1808 * icon theme loaded, which would be a waste of memory.
1810 * Return value: (transfer full): the rendered icon; this may be a
1811 * newly created icon or a new reference to an internal icon, so
1812 * you must not modify the icon. Use g_object_unref() to release
1813 * your reference to the icon. %NULL if the icon isn't found.
1818 gtk_icon_theme_load_icon (GtkIconTheme *icon_theme,
1819 const gchar *icon_name,
1821 GtkIconLookupFlags flags,
1824 GtkIconInfo *icon_info;
1825 GdkPixbuf *pixbuf = NULL;
1827 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1828 g_return_val_if_fail (icon_name != NULL, NULL);
1829 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1830 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1831 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1833 icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size,
1834 flags | GTK_ICON_LOOKUP_USE_BUILTIN);
1837 g_set_error (error, GTK_ICON_THEME_ERROR, GTK_ICON_THEME_NOT_FOUND,
1838 _("Icon '%s' not present in theme"), icon_name);
1842 pixbuf = gtk_icon_info_load_icon (icon_info, error);
1843 gtk_icon_info_free (icon_info);
1849 * gtk_icon_theme_has_icon:
1850 * @icon_theme: a #GtkIconTheme
1851 * @icon_name: the name of an icon
1853 * Checks whether an icon theme includes an icon
1854 * for a particular name.
1856 * Return value: %TRUE if @icon_theme includes an
1857 * icon for @icon_name.
1862 gtk_icon_theme_has_icon (GtkIconTheme *icon_theme,
1863 const char *icon_name)
1865 GtkIconThemePrivate *priv;
1868 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
1869 g_return_val_if_fail (icon_name != NULL, FALSE);
1871 priv = icon_theme->priv;
1873 ensure_valid_themes (icon_theme);
1875 for (l = priv->dir_mtimes; l; l = l->next)
1877 IconThemeDirMtime *dir_mtime = l->data;
1878 GtkIconCache *cache = dir_mtime->cache;
1880 if (cache && _gtk_icon_cache_has_icon (cache, icon_name))
1884 if (g_hash_table_lookup_extended (priv->all_icons,
1885 icon_name, NULL, NULL))
1888 if (_builtin_cache &&
1889 _gtk_icon_cache_has_icon (_builtin_cache, icon_name))
1892 if (icon_theme_builtin_icons &&
1893 g_hash_table_lookup_extended (icon_theme_builtin_icons,
1894 icon_name, NULL, NULL))
1901 add_size (gpointer key,
1905 gint **res_p = user_data;
1907 **res_p = GPOINTER_TO_INT (key);
1913 * gtk_icon_theme_get_icon_sizes:
1914 * @icon_theme: a #GtkIconTheme
1915 * @icon_name: the name of an icon
1917 * Returns an array of integers describing the sizes at which
1918 * the icon is available without scaling. A size of -1 means
1919 * that the icon is available in a scalable format. The array
1920 * is zero-terminated.
1922 * Return value: (array zero-terminated=1): An newly allocated array
1923 * describing the sizes at which the icon is available. The array
1924 * should be freed with g_free() when it is no longer needed.
1929 gtk_icon_theme_get_icon_sizes (GtkIconTheme *icon_theme,
1930 const char *icon_name)
1932 GList *l, *d, *icons;
1936 GtkIconThemePrivate *priv;
1938 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1940 priv = icon_theme->priv;
1942 ensure_valid_themes (icon_theme);
1944 sizes = g_hash_table_new (g_direct_hash, g_direct_equal);
1946 for (l = priv->themes; l; l = l->next)
1948 IconTheme *theme = l->data;
1949 for (d = theme->dirs; d; d = d->next)
1951 IconThemeDir *dir = d->data;
1953 if (dir->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir->size), NULL, NULL))
1956 suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
1957 if (suffix != ICON_SUFFIX_NONE)
1959 if (suffix == ICON_SUFFIX_SVG)
1960 g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL);
1962 g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL);
1967 for (d = builtin_dirs; d; d = d->next)
1969 IconThemeDir *dir = d->data;
1971 if (dir->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir->size), NULL, NULL))
1974 suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
1975 if (suffix != ICON_SUFFIX_NONE)
1977 if (suffix == ICON_SUFFIX_SVG)
1978 g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL);
1980 g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL);
1984 if (icon_theme_builtin_icons)
1986 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
1990 BuiltinIcon *icon = icons->data;
1992 g_hash_table_insert (sizes, GINT_TO_POINTER (icon->size), NULL);
1993 icons = icons->next;
1997 r = result = g_new0 (gint, g_hash_table_size (sizes) + 1);
1999 g_hash_table_foreach (sizes, add_size, &r);
2000 g_hash_table_destroy (sizes);
2006 add_key_to_hash (gpointer key,
2010 GHashTable *hash = user_data;
2012 g_hash_table_insert (hash, key, NULL);
2016 add_key_to_list (gpointer key,
2020 GList **list = user_data;
2022 *list = g_list_prepend (*list, g_strdup (key));
2026 * gtk_icon_theme_list_icons:
2027 * @icon_theme: a #GtkIconTheme
2028 * @context: (allow-none): a string identifying a particular type of
2029 * icon, or %NULL to list all icons.
2031 * Lists the icons in the current icon theme. Only a subset
2032 * of the icons can be listed by providing a context string.
2033 * The set of values for the context string is system dependent,
2034 * but will typically include such values as "Applications" and
2037 * Return value: (element-type utf8) (transfer full): a #GList list
2038 * holding the names of all the icons in the theme. You must first
2039 * free each element in the list with g_free(), then free the list
2040 * itself with g_list_free().
2045 gtk_icon_theme_list_icons (GtkIconTheme *icon_theme,
2046 const char *context)
2048 GtkIconThemePrivate *priv;
2051 GQuark context_quark;
2053 priv = icon_theme->priv;
2055 ensure_valid_themes (icon_theme);
2059 context_quark = g_quark_try_string (context);
2067 icons = g_hash_table_new (g_str_hash, g_str_equal);
2072 theme_list_icons (l->data, icons, context_quark);
2076 if (context_quark == 0)
2077 g_hash_table_foreach (priv->unthemed_icons,
2083 g_hash_table_foreach (icons,
2087 g_hash_table_destroy (icons);
2093 * gtk_icon_theme_list_contexts:
2094 * @icon_theme: a #GtkIconTheme
2096 * Gets the list of contexts available within the current
2097 * hierarchy of icon themes
2099 * Return value: (element-type utf8) (transfer full): a #GList list
2100 * holding the names of all the contexts in the theme. You must first
2101 * free each element in the list with g_free(), then free the list
2102 * itself with g_list_free().
2107 gtk_icon_theme_list_contexts (GtkIconTheme *icon_theme)
2109 GtkIconThemePrivate *priv;
2110 GHashTable *contexts;
2113 priv = icon_theme->priv;
2115 ensure_valid_themes (icon_theme);
2117 contexts = g_hash_table_new (g_str_hash, g_str_equal);
2122 theme_list_contexts (l->data, contexts);
2128 g_hash_table_foreach (contexts,
2132 g_hash_table_destroy (contexts);
2138 * gtk_icon_theme_get_example_icon_name:
2139 * @icon_theme: a #GtkIconTheme
2141 * Gets the name of an icon that is representative of the
2142 * current theme (for instance, to use when presenting
2143 * a list of themes to the user.)
2145 * Return value: the name of an example icon or %NULL.
2146 * Free with g_free().
2151 gtk_icon_theme_get_example_icon_name (GtkIconTheme *icon_theme)
2153 GtkIconThemePrivate *priv;
2157 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2159 priv = icon_theme->priv;
2161 ensure_valid_themes (icon_theme);
2168 return g_strdup (theme->example);
2178 rescan_themes (GtkIconTheme *icon_theme)
2180 GtkIconThemePrivate *priv;
2181 IconThemeDirMtime *dir_mtime;
2187 priv = icon_theme->priv;
2189 for (d = priv->dir_mtimes; d != NULL; d = d->next)
2191 dir_mtime = d->data;
2193 stat_res = g_stat (dir_mtime->dir, &stat_buf);
2195 /* dir mtime didn't change */
2196 if (stat_res == 0 &&
2197 S_ISDIR (stat_buf.st_mode) &&
2198 dir_mtime->mtime == stat_buf.st_mtime)
2200 /* didn't exist before, and still doesn't */
2201 if (dir_mtime->mtime == 0 &&
2202 (stat_res != 0 || !S_ISDIR (stat_buf.st_mode)))
2208 g_get_current_time (&tv);
2209 priv->last_stat_time = tv.tv_sec;
2215 * gtk_icon_theme_rescan_if_needed:
2216 * @icon_theme: a #GtkIconTheme
2218 * Checks to see if the icon theme has changed; if it has, any
2219 * currently cached information is discarded and will be reloaded
2220 * next time @icon_theme is accessed.
2222 * Return value: %TRUE if the icon theme has changed and needed
2228 gtk_icon_theme_rescan_if_needed (GtkIconTheme *icon_theme)
2232 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
2234 retval = rescan_themes (icon_theme);
2236 do_theme_change (icon_theme);
2242 theme_destroy (IconTheme *theme)
2244 g_free (theme->display_name);
2245 g_free (theme->comment);
2246 g_free (theme->name);
2247 g_free (theme->example);
2249 g_list_free_full (theme->dirs, (GDestroyNotify) theme_dir_destroy);
2255 theme_dir_destroy (IconThemeDir *dir)
2258 _gtk_icon_cache_unref (dir->cache);
2260 g_hash_table_destroy (dir->icons);
2263 g_hash_table_destroy (dir->icon_data);
2265 g_free (dir->subdir);
2270 theme_dir_size_difference (IconThemeDir *dir, int size, gboolean *smaller)
2275 case ICON_THEME_DIR_FIXED:
2276 *smaller = size < dir->size;
2277 return abs (size - dir->size);
2279 case ICON_THEME_DIR_SCALABLE:
2280 *smaller = size < dir->min_size;
2281 if (size < dir->min_size)
2282 return dir->min_size - size;
2283 if (size > dir->max_size)
2284 return size - dir->max_size;
2287 case ICON_THEME_DIR_THRESHOLD:
2288 min = dir->size - dir->threshold;
2289 max = dir->size + dir->threshold;
2290 *smaller = size < min;
2297 case ICON_THEME_DIR_UNTHEMED:
2298 g_assert_not_reached ();
2301 g_assert_not_reached ();
2306 string_from_suffix (IconSuffix suffix)
2310 case ICON_SUFFIX_XPM:
2312 case ICON_SUFFIX_SVG:
2314 case ICON_SUFFIX_PNG:
2317 g_assert_not_reached();
2323 suffix_from_name (const char *name)
2327 if (g_str_has_suffix (name, ".png"))
2328 retval = ICON_SUFFIX_PNG;
2329 else if (g_str_has_suffix (name, ".svg"))
2330 retval = ICON_SUFFIX_SVG;
2331 else if (g_str_has_suffix (name, ".xpm"))
2332 retval = ICON_SUFFIX_XPM;
2334 retval = ICON_SUFFIX_NONE;
2340 best_suffix (IconSuffix suffix,
2343 if ((suffix & ICON_SUFFIX_PNG) != 0)
2344 return ICON_SUFFIX_PNG;
2345 else if (allow_svg && ((suffix & ICON_SUFFIX_SVG) != 0))
2346 return ICON_SUFFIX_SVG;
2347 else if ((suffix & ICON_SUFFIX_XPM) != 0)
2348 return ICON_SUFFIX_XPM;
2350 return ICON_SUFFIX_NONE;
2355 theme_dir_get_icon_suffix (IconThemeDir *dir,
2356 const gchar *icon_name,
2357 gboolean *has_icon_file)
2363 suffix = (IconSuffix)_gtk_icon_cache_get_icon_flags (dir->cache,
2368 *has_icon_file = suffix & HAS_ICON_FILE;
2370 suffix = suffix & ~HAS_ICON_FILE;
2373 suffix = GPOINTER_TO_UINT (g_hash_table_lookup (dir->icons, icon_name));
2375 GTK_NOTE (ICONTHEME,
2376 g_print ("get_icon_suffix%s %u\n", dir->cache ? " (cached)" : "", suffix));
2381 static GtkIconInfo *
2382 theme_lookup_icon (IconTheme *theme,
2383 const char *icon_name,
2386 gboolean use_builtin)
2389 IconThemeDir *dir, *min_dir;
2391 int min_difference, difference;
2392 BuiltinIcon *closest_builtin = NULL;
2393 gboolean smaller, has_larger, match;
2396 min_difference = G_MAXINT;
2401 /* Builtin icons are logically part of the default theme and
2402 * are searched before other subdirectories of the default theme.
2404 if (use_builtin && strcmp (theme->name, DEFAULT_THEME_NAME) == 0)
2406 closest_builtin = find_builtin_icon (icon_name,
2411 if (min_difference == 0)
2412 return icon_info_new_builtin (closest_builtin);
2414 dirs = builtin_dirs;
2424 GTK_NOTE (ICONTHEME,
2425 g_print ("theme_lookup_icon dir %s\n", dir->dir));
2426 suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
2427 if (best_suffix (suffix, allow_svg) != ICON_SUFFIX_NONE)
2429 difference = theme_dir_size_difference (dir, size, &smaller);
2431 if (difference == 0)
2433 if (dir->type == ICON_THEME_DIR_SCALABLE)
2435 /* don't pick scalable if we already found
2436 * a matching non-scalable dir
2446 /* for a matching non-scalable dir keep
2447 * going and look for a closer match
2449 difference = abs (size - dir->size);
2450 if (!match || difference < min_difference)
2453 min_difference = difference;
2456 if (difference == 0)
2465 if (difference < min_difference || smaller)
2467 min_difference = difference;
2469 has_larger = smaller;
2474 if (difference < min_difference && smaller)
2476 min_difference = difference;
2485 if (l == NULL && dirs == builtin_dirs)
2494 GtkIconInfo *icon_info = icon_info_new ();
2495 gboolean has_icon_file = FALSE;
2497 suffix = theme_dir_get_icon_suffix (min_dir, icon_name, &has_icon_file);
2498 suffix = best_suffix (suffix, allow_svg);
2499 g_assert (suffix != ICON_SUFFIX_NONE);
2503 file = g_strconcat (icon_name, string_from_suffix (suffix), NULL);
2504 icon_info->filename = g_build_filename (min_dir->dir, file, NULL);
2505 icon_info->icon_file = g_file_new_for_path (icon_info->filename);
2510 icon_info->filename = NULL;
2511 icon_info->icon_file = NULL;
2514 if (min_dir->icon_data != NULL)
2515 icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
2517 if (icon_info->data == NULL && min_dir->cache != NULL)
2519 icon_info->data = _gtk_icon_cache_get_icon_data (min_dir->cache, icon_name, min_dir->subdir_index);
2520 if (icon_info->data)
2522 if (min_dir->icon_data == NULL)
2523 min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2524 g_free, (GDestroyNotify)icon_data_free);
2526 g_hash_table_replace (min_dir->icon_data, g_strdup (icon_name), icon_info->data);
2530 if (icon_info->data == NULL && has_icon_file)
2532 gchar *icon_file_name, *icon_file_path;
2534 icon_file_name = g_strconcat (icon_name, ".icon", NULL);
2535 icon_file_path = g_build_filename (min_dir->dir, icon_file_name, NULL);
2537 if (g_file_test (icon_file_path, G_FILE_TEST_IS_REGULAR))
2539 if (min_dir->icon_data == NULL)
2540 min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2541 g_free, (GDestroyNotify)icon_data_free);
2542 load_icon_data (min_dir, icon_file_path, icon_file_name);
2544 icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
2546 g_free (icon_file_name);
2547 g_free (icon_file_path);
2552 icon_info->cache_pixbuf = _gtk_icon_cache_get_icon (min_dir->cache, icon_name,
2553 min_dir->subdir_index);
2556 icon_info->dir_type = min_dir->type;
2557 icon_info->dir_size = min_dir->size;
2558 icon_info->threshold = min_dir->threshold;
2563 if (closest_builtin)
2564 return icon_info_new_builtin (closest_builtin);
2570 theme_list_icons (IconTheme *theme,
2574 GList *l = theme->dirs;
2581 if (context == dir->context ||
2586 _gtk_icon_cache_add_icons (dir->cache,
2593 g_hash_table_foreach (dir->icons,
2603 theme_list_contexts (IconTheme *theme,
2604 GHashTable *contexts)
2606 GList *l = theme->dirs;
2608 const char *context;
2614 context = g_quark_to_string (dir->context);
2615 g_hash_table_replace (contexts, (gpointer) context, NULL);
2622 load_icon_data (IconThemeDir *dir, const char *path, const char *name)
2624 GKeyFile *icon_file;
2632 GError *error = NULL;
2636 icon_file = g_key_file_new ();
2637 g_key_file_set_list_separator (icon_file, ',');
2638 g_key_file_load_from_file (icon_file, path, 0, &error);
2641 g_error_free (error);
2642 g_key_file_free (icon_file);
2647 base_name = strip_suffix (name);
2649 data = g_slice_new0 (GtkIconData);
2650 /* takes ownership of base_name */
2651 g_hash_table_replace (dir->icon_data, base_name, data);
2653 ivalues = g_key_file_get_integer_list (icon_file,
2654 "Icon Data", "EmbeddedTextRectangle",
2660 data->has_embedded_rect = TRUE;
2661 data->x0 = ivalues[0];
2662 data->y0 = ivalues[1];
2663 data->x1 = ivalues[2];
2664 data->y1 = ivalues[3];
2670 str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
2673 split = g_strsplit (str, "|", -1);
2675 data->n_attach_points = g_strv_length (split);
2676 data->attach_points = g_new (GdkPoint, data->n_attach_points);
2679 while (split[i] != NULL && i < data->n_attach_points)
2681 split_point = strchr (split[i], ',');
2686 data->attach_points[i].x = atoi (split[i]);
2687 data->attach_points[i].y = atoi (split_point);
2696 data->display_name = g_key_file_get_locale_string (icon_file,
2697 "Icon Data", "DisplayName",
2699 g_key_file_free (icon_file);
2704 scan_directory (GtkIconThemePrivate *icon_theme,
2705 IconThemeDir *dir, char *full_dir)
2710 GTK_NOTE (ICONTHEME,
2711 g_print ("scanning directory %s\n", full_dir));
2712 dir->icons = g_hash_table_new_full (g_str_hash, g_str_equal,
2715 gdir = g_dir_open (full_dir, 0, NULL);
2720 while ((name = g_dir_read_name (gdir)))
2724 IconSuffix suffix, hash_suffix;
2726 if (g_str_has_suffix (name, ".icon"))
2728 if (dir->icon_data == NULL)
2729 dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2730 g_free, (GDestroyNotify)icon_data_free);
2732 path = g_build_filename (full_dir, name, NULL);
2733 if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
2734 load_icon_data (dir, path, name);
2741 suffix = suffix_from_name (name);
2742 if (suffix == ICON_SUFFIX_NONE)
2745 base_name = strip_suffix (name);
2747 hash_suffix = GPOINTER_TO_INT (g_hash_table_lookup (dir->icons, base_name));
2748 g_hash_table_replace (icon_theme->all_icons, base_name, NULL);
2749 /* takes ownership of base_name */
2750 g_hash_table_replace (dir->icons, base_name, GUINT_TO_POINTER (hash_suffix| suffix));
2757 theme_subdir_load (GtkIconTheme *icon_theme,
2759 GKeyFile *theme_file,
2765 IconThemeDirType type;
2766 char *context_string;
2773 GError *error = NULL;
2774 IconThemeDirMtime *dir_mtime;
2776 size = g_key_file_get_integer (theme_file, subdir, "Size", &error);
2779 g_error_free (error);
2780 g_warning ("Theme directory %s of theme %s has no size field\n",
2781 subdir, theme->name);
2785 type = ICON_THEME_DIR_THRESHOLD;
2786 type_string = g_key_file_get_string (theme_file, subdir, "Type", NULL);
2789 if (strcmp (type_string, "Fixed") == 0)
2790 type = ICON_THEME_DIR_FIXED;
2791 else if (strcmp (type_string, "Scalable") == 0)
2792 type = ICON_THEME_DIR_SCALABLE;
2793 else if (strcmp (type_string, "Threshold") == 0)
2794 type = ICON_THEME_DIR_THRESHOLD;
2796 g_free (type_string);
2800 context_string = g_key_file_get_string (theme_file, subdir, "Context", NULL);
2803 context = g_quark_from_string (context_string);
2804 g_free (context_string);
2807 if (g_key_file_has_key (theme_file, subdir, "MaxSize", NULL))
2808 max_size = g_key_file_get_integer (theme_file, subdir, "MaxSize", NULL);
2812 if (g_key_file_has_key (theme_file, subdir, "MinSize", NULL))
2813 min_size = g_key_file_get_integer (theme_file, subdir, "MinSize", NULL);
2817 if (g_key_file_has_key (theme_file, subdir, "Threshold", NULL))
2818 threshold = g_key_file_get_integer (theme_file, subdir, "Threshold", NULL);
2822 for (d = icon_theme->priv->dir_mtimes; d; d = d->next)
2824 dir_mtime = (IconThemeDirMtime *)d->data;
2826 if (dir_mtime->mtime == 0)
2827 continue; /* directory doesn't exist */
2829 full_dir = g_build_filename (dir_mtime->dir, subdir, NULL);
2831 /* First, see if we have a cache for the directory */
2832 if (dir_mtime->cache != NULL || g_file_test (full_dir, G_FILE_TEST_IS_DIR))
2834 if (dir_mtime->cache == NULL)
2836 /* This will return NULL if the cache doesn't exist or is outdated */
2837 dir_mtime->cache = _gtk_icon_cache_new_for_path (dir_mtime->dir);
2840 dir = g_new (IconThemeDir, 1);
2842 dir->context = context;
2844 dir->min_size = min_size;
2845 dir->max_size = max_size;
2846 dir->threshold = threshold;
2847 dir->dir = full_dir;
2848 dir->icon_data = NULL;
2849 dir->subdir = g_strdup (subdir);
2850 if (dir_mtime->cache != NULL)
2852 dir->cache = _gtk_icon_cache_ref (dir_mtime->cache);
2853 dir->subdir_index = _gtk_icon_cache_get_directory_index (dir->cache, dir->subdir);
2858 dir->subdir_index = -1;
2859 scan_directory (icon_theme->priv, dir, full_dir);
2862 theme->dirs = g_list_prepend (theme->dirs, dir);
2870 icon_data_free (GtkIconData *icon_data)
2872 g_free (icon_data->attach_points);
2873 g_free (icon_data->display_name);
2874 g_slice_free (GtkIconData, icon_data);
2881 G_DEFINE_BOXED_TYPE (GtkIconInfo, gtk_icon_info,
2885 static GtkIconInfo *
2886 icon_info_new (void)
2888 GtkIconInfo *icon_info = g_slice_new0 (GtkIconInfo);
2890 icon_info->scale = -1.;
2891 icon_info->ref_count = 1;
2896 static GtkIconInfo *
2897 icon_info_new_builtin (BuiltinIcon *icon)
2899 GtkIconInfo *icon_info = icon_info_new ();
2901 icon_info->cache_pixbuf = g_object_ref (icon->pixbuf);
2902 icon_info->dir_type = ICON_THEME_DIR_THRESHOLD;
2903 icon_info->dir_size = icon->size;
2904 icon_info->threshold = 2;
2910 * gtk_icon_info_copy:
2911 * @icon_info: a #GtkIconInfo
2913 * Make a copy of a #GtkIconInfo.
2915 * Return value: the new GtkIconInfo
2920 gtk_icon_info_copy (GtkIconInfo *icon_info)
2923 g_return_val_if_fail (icon_info != NULL, NULL);
2925 icon_info->ref_count++;
2931 * gtk_icon_info_free:
2932 * @icon_info: a #GtkIconInfo
2934 * Free a #GtkIconInfo and associated information
2939 gtk_icon_info_free (GtkIconInfo *icon_info)
2941 g_return_if_fail (icon_info != NULL);
2943 icon_info->ref_count--;
2944 if (icon_info->ref_count > 0)
2947 if (icon_info->in_cache)
2948 g_hash_table_remove (icon_info->in_cache->priv->info_cache, &icon_info->key);
2950 g_strfreev (icon_info->key.icon_names);
2952 g_free (icon_info->filename);
2953 g_clear_object (&icon_info->icon_file);
2955 if (icon_info->loadable)
2956 g_object_unref (icon_info->loadable);
2957 g_slist_free_full (icon_info->emblem_infos, (GDestroyNotify) gtk_icon_info_free);
2958 if (icon_info->pixbuf)
2959 g_object_unref (icon_info->pixbuf);
2960 if (icon_info->cache_pixbuf)
2961 g_object_unref (icon_info->cache_pixbuf);
2962 if (icon_info->symbolic_pixbuf_size)
2963 gtk_requisition_free (icon_info->symbolic_pixbuf_size);
2965 g_slice_free (GtkIconInfo, icon_info);
2969 * gtk_icon_info_get_base_size:
2970 * @icon_info: a #GtkIconInfo
2972 * Gets the base size for the icon. The base size
2973 * is a size for the icon that was specified by
2974 * the icon theme creator. This may be different
2975 * than the actual size of image; an example of
2976 * this is small emblem icons that can be attached
2977 * to a larger icon. These icons will be given
2978 * the same base size as the larger icons to which
2979 * they are attached.
2981 * Return value: the base size, or 0, if no base
2982 * size is known for the icon.
2987 gtk_icon_info_get_base_size (GtkIconInfo *icon_info)
2989 g_return_val_if_fail (icon_info != NULL, 0);
2991 return icon_info->dir_size;
2995 * gtk_icon_info_get_filename:
2996 * @icon_info: a #GtkIconInfo
2998 * Gets the filename for the icon. If the
2999 * %GTK_ICON_LOOKUP_USE_BUILTIN flag was passed
3000 * to gtk_icon_theme_lookup_icon(), there may be
3001 * no filename if a builtin icon is returned; in this
3002 * case, you should use gtk_icon_info_get_builtin_pixbuf().
3004 * Return value: (type filename): the filename for the icon, or %NULL
3005 * if gtk_icon_info_get_builtin_pixbuf() should be used instead. The
3006 * return value is owned by GTK+ and should not be modified or freed.
3011 gtk_icon_info_get_filename (GtkIconInfo *icon_info)
3013 g_return_val_if_fail (icon_info != NULL, NULL);
3015 return icon_info->filename;
3019 * gtk_icon_info_get_builtin_pixbuf:
3020 * @icon_info: a #GtkIconInfo structure
3022 * Gets the built-in image for this icon, if any. To allow
3023 * GTK+ to use built in icon images, you must pass the
3024 * %GTK_ICON_LOOKUP_USE_BUILTIN to
3025 * gtk_icon_theme_lookup_icon().
3027 * Return value: (transfer none): the built-in image pixbuf, or %NULL. No
3028 * extra reference is added to the returned pixbuf, so if
3029 * you want to keep it around, you must use g_object_ref().
3030 * The returned image must not be modified.
3035 gtk_icon_info_get_builtin_pixbuf (GtkIconInfo *icon_info)
3037 g_return_val_if_fail (icon_info != NULL, NULL);
3039 if (icon_info->filename)
3042 return icon_info->cache_pixbuf;
3045 static gboolean icon_info_ensure_scale_and_pixbuf (GtkIconInfo*, gboolean);
3047 /* Combine the icon with all emblems, the first emblem is placed
3048 * in the southeast corner. Scale emblems to be at most 3/4 of the
3049 * size of the icon itself.
3052 apply_emblems (GtkIconInfo *info)
3054 GdkPixbuf *icon = NULL;
3058 if (info->emblem_infos == NULL)
3061 if (info->emblems_applied)
3064 w = gdk_pixbuf_get_width (info->pixbuf);
3065 h = gdk_pixbuf_get_height (info->pixbuf);
3067 for (l = info->emblem_infos, pos = 0; l; l = l->next, pos++)
3069 GtkIconInfo *emblem_info = l->data;
3071 if (icon_info_ensure_scale_and_pixbuf (emblem_info, FALSE))
3073 GdkPixbuf *emblem = emblem_info->pixbuf;
3075 gint x = 0, y = 0; /* silence compiler */
3078 ew = gdk_pixbuf_get_width (emblem);
3079 eh = gdk_pixbuf_get_height (emblem);
3111 icon = gdk_pixbuf_copy (info->pixbuf);
3116 gdk_pixbuf_composite (emblem, icon, x, y, ew, eh, x, y,
3117 scale, scale, GDK_INTERP_BILINEAR, 255);
3123 g_object_unref (info->pixbuf);
3124 info->pixbuf = icon;
3127 info->emblems_applied = TRUE;
3130 /* This function contains the complicated logic for deciding
3131 * on the size at which to load the icon and loading it at
3135 icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info,
3136 gboolean scale_only)
3138 int image_width, image_height;
3139 GdkPixbuf *source_pixbuf;
3142 /* First check if we already succeeded have the necessary
3143 * information (or failed earlier)
3145 if (scale_only && icon_info->scale >= 0)
3148 if (icon_info->pixbuf)
3150 apply_emblems (icon_info);
3154 if (icon_info->load_error)
3157 /* SVG icons are a special case - we just immediately scale them
3158 * to the desired size
3160 if (icon_info->icon_file && !icon_info->loadable)
3161 icon_info->loadable = G_LOADABLE_ICON (g_file_icon_new (icon_info->icon_file));
3164 if (G_IS_FILE_ICON (icon_info->loadable))
3167 GFileInfo *file_info;
3168 const gchar *content_type;
3170 file = g_file_icon_get_file (G_FILE_ICON (icon_info->loadable));
3171 file_info = g_file_query_info (file,
3172 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
3173 G_FILE_QUERY_INFO_NONE,
3177 content_type = g_file_info_get_content_type (file_info);
3179 if (content_type && strcmp (content_type, "image/svg+xml") == 0)
3182 g_object_unref (file_info);
3188 GInputStream *stream;
3190 icon_info->scale = icon_info->desired_size / 1000.;
3195 stream = g_loadable_icon_load (icon_info->loadable,
3196 icon_info->desired_size,
3198 &icon_info->load_error);
3201 icon_info->pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3202 icon_info->desired_size,
3203 icon_info->desired_size,
3206 &icon_info->load_error);
3207 g_object_unref (stream);
3210 if (!icon_info->pixbuf)
3213 apply_emblems (icon_info);
3218 /* In many cases, the scale can be determined without actual access
3219 * to the icon file. This is generally true when we have a size
3220 * for the directory where the icon is; the image size doesn't
3221 * matter in that case.
3223 if (icon_info->forced_size)
3224 icon_info->scale = -1;
3225 else if (icon_info->dir_type == ICON_THEME_DIR_FIXED)
3226 icon_info->scale = 1.0;
3227 else if (icon_info->dir_type == ICON_THEME_DIR_THRESHOLD)
3229 if (icon_info->desired_size >= icon_info->dir_size - icon_info->threshold &&
3230 icon_info->desired_size <= icon_info->dir_size + icon_info->threshold)
3231 icon_info->scale = 1.0;
3232 else if (icon_info->dir_size > 0)
3233 icon_info->scale =(gdouble) icon_info->desired_size / icon_info->dir_size;
3235 else if (icon_info->dir_type == ICON_THEME_DIR_SCALABLE)
3237 if (icon_info->dir_size > 0)
3238 icon_info->scale = (gdouble) icon_info->desired_size / icon_info->dir_size;
3241 if (icon_info->scale >= 0. && scale_only)
3244 /* At this point, we need to actually get the icon; either from the
3245 * builtin image or by loading the file
3247 source_pixbuf = NULL;
3248 if (icon_info->cache_pixbuf)
3249 source_pixbuf = g_object_ref (icon_info->cache_pixbuf);
3252 GInputStream *stream;
3254 stream = g_loadable_icon_load (icon_info->loadable,
3255 icon_info->desired_size,
3257 &icon_info->load_error);
3260 source_pixbuf = gdk_pixbuf_new_from_stream (stream,
3262 &icon_info->load_error);
3263 g_object_unref (stream);
3270 /* Do scale calculations that depend on the image size
3272 image_width = gdk_pixbuf_get_width (source_pixbuf);
3273 image_height = gdk_pixbuf_get_height (source_pixbuf);
3275 if (icon_info->scale < 0.0)
3277 gint image_size = MAX (image_width, image_height);
3279 icon_info->scale = (gdouble)icon_info->desired_size / (gdouble)image_size;
3281 icon_info->scale = 1.0;
3283 if (icon_info->dir_type == ICON_THEME_DIR_UNTHEMED &&
3284 !icon_info->forced_size)
3285 icon_info->scale = MIN (icon_info->scale, 1.0);
3288 /* We don't short-circuit out here for scale_only, since, now
3289 * we've loaded the icon, we might as well go ahead and finish
3290 * the job. This is a bit of a waste when we scale here
3291 * and never get the final pixbuf; at the cost of a bit of
3292 * extra complexity, we could keep the source pixbuf around
3293 * but not actually scale it until needed.
3295 if (icon_info->scale == 1.0)
3296 icon_info->pixbuf = source_pixbuf;
3299 icon_info->pixbuf = gdk_pixbuf_scale_simple (source_pixbuf,
3300 0.5 + image_width * icon_info->scale,
3301 0.5 + image_height * icon_info->scale,
3302 GDK_INTERP_BILINEAR);
3303 g_object_unref (source_pixbuf);
3306 apply_emblems (icon_info);
3312 proxy_pixbuf_destroy (guchar *pixels, gpointer data)
3314 GtkIconInfo *icon_info = data;
3315 GtkIconTheme *icon_theme = icon_info->in_cache;
3317 g_assert (icon_info->proxy_pixbuf != NULL);
3318 icon_info->proxy_pixbuf = NULL;
3320 /* Keep it alive a bit longer */
3321 if (icon_theme != NULL)
3322 ensure_in_lru_cache (icon_theme, icon_info);
3324 gtk_icon_info_free (icon_info);
3328 * gtk_icon_info_load_icon:
3329 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3330 * @error: (allow-none): location to store error information on failure,
3333 * Renders an icon previously looked up in an icon theme using
3334 * gtk_icon_theme_lookup_icon(); the size will be based on the size
3335 * passed to gtk_icon_theme_lookup_icon(). Note that the resulting
3336 * pixbuf may not be exactly this size; an icon theme may have icons
3337 * that differ slightly from their nominal sizes, and in addition GTK+
3338 * will avoid scaling icons that it considers sufficiently close to the
3339 * requested size or for which the source image would have to be scaled
3340 * up too far. (This maintains sharpness.). This behaviour can be changed
3341 * by passing the %GTK_ICON_LOOKUP_FORCE_SIZE flag when obtaining
3342 * the #GtkIconInfo. If this flag has been specified, the pixbuf
3343 * returned by this function will be scaled to the exact size.
3345 * Return value: (transfer full): the rendered icon; this may be a newly
3346 * created icon or a new reference to an internal icon, so you must
3347 * not modify the icon. Use g_object_unref() to release your reference
3353 gtk_icon_info_load_icon (GtkIconInfo *icon_info,
3356 g_return_val_if_fail (icon_info != NULL, NULL);
3357 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3359 if (!icon_info_ensure_scale_and_pixbuf (icon_info, FALSE))
3361 if (icon_info->load_error)
3362 g_propagate_error (error, icon_info->load_error);
3364 g_set_error_literal (error,
3365 GTK_ICON_THEME_ERROR,
3366 GTK_ICON_THEME_NOT_FOUND,
3367 _("Failed to load icon"));
3372 /* Instead of returning the pixbuf directly we
3373 return a proxy to it that we don't own (but that
3374 shares the data with the one we own). This way
3375 we can know when it is freed and ensure the
3376 IconInfo is alive (and thus cached) while
3377 the pixbuf is still alive. */
3379 if (icon_info->proxy_pixbuf != NULL)
3380 return g_object_ref (icon_info->proxy_pixbuf);
3382 icon_info->proxy_pixbuf =
3383 gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (icon_info->pixbuf),
3384 gdk_pixbuf_get_colorspace (icon_info->pixbuf),
3385 gdk_pixbuf_get_has_alpha (icon_info->pixbuf),
3386 gdk_pixbuf_get_bits_per_sample (icon_info->pixbuf),
3387 gdk_pixbuf_get_width (icon_info->pixbuf),
3388 gdk_pixbuf_get_height (icon_info->pixbuf),
3389 gdk_pixbuf_get_rowstride (icon_info->pixbuf),
3390 proxy_pixbuf_destroy,
3391 gtk_icon_info_copy (icon_info));
3393 return icon_info->proxy_pixbuf;
3397 gdk_color_to_css (GdkColor *color)
3399 return g_strdup_printf ("rgb(%d,%d,%d)",
3406 gdk_rgba_to_css (const GdkRGBA *color)
3408 /* drop alpha for now, since librsvg does not understand rgba() */
3409 return g_strdup_printf ("rgb(%d,%d,%d)",
3410 (gint)(color->red * 255),
3411 (gint)(color->green * 255),
3412 (gint)(color->blue * 255));
3416 _gtk_icon_info_load_symbolic_internal (GtkIconInfo *icon_info,
3417 const gchar *css_fg,
3418 const gchar *css_success,
3419 const gchar *css_warning,
3420 const gchar *css_error,
3423 GInputStream *stream;
3426 gchar *success, *warning, *err;
3427 gchar *width, *height, *uri;
3429 /* css_fg can't possibly have failed, otherwise
3430 * that would mean we have a broken style */
3431 g_return_val_if_fail (css_fg != NULL, NULL);
3433 success = warning = err = NULL;
3437 GdkColor success_default_color = { 0, 0x4e00, 0x9a00, 0x0600 };
3438 success = gdk_color_to_css (&success_default_color);
3442 GdkColor warning_default_color = { 0, 0xf500, 0x7900, 0x3e00 };
3443 warning = gdk_color_to_css (&warning_default_color);
3447 GdkColor error_default_color = { 0, 0xcc00, 0x0000, 0x0000 };
3448 err = gdk_color_to_css (&error_default_color);
3451 if (!icon_info->symbolic_pixbuf_size)
3453 stream = G_INPUT_STREAM (g_file_read (icon_info->icon_file, NULL, error));
3458 /* Fetch size from the original icon */
3459 pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
3460 g_object_unref (stream);
3465 icon_info->symbolic_pixbuf_size = gtk_requisition_new ();
3466 icon_info->symbolic_pixbuf_size->width = gdk_pixbuf_get_width (pixbuf);
3467 icon_info->symbolic_pixbuf_size->height = gdk_pixbuf_get_height (pixbuf);
3468 g_object_unref (pixbuf);
3471 width = g_strdup_printf ("%d", icon_info->symbolic_pixbuf_size->width);
3472 height = g_strdup_printf ("%d", icon_info->symbolic_pixbuf_size->height);
3473 uri = g_file_get_uri (icon_info->icon_file);
3475 data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
3476 "<svg version=\"1.1\"\n"
3477 " xmlns=\"http://www.w3.org/2000/svg\"\n"
3478 " xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n"
3479 " width=\"", width, "\"\n"
3480 " height=\"", height, "\">\n"
3481 " <style type=\"text/css\">\n"
3483 " fill: ", css_fg," !important;\n"
3486 " fill: ", css_warning ? css_warning : warning," !important;\n"
3489 " fill: ", css_error ? css_error : err," !important;\n"
3492 " fill: ", css_success ? css_success : success," !important;\n"
3495 " <xi:include href=\"", uri, "\"/>\n"
3505 stream = g_memory_input_stream_new_from_data (data, -1, g_free);
3506 pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3507 icon_info->desired_size,
3508 icon_info->desired_size,
3512 g_object_unref (stream);
3518 * gtk_icon_info_load_symbolic:
3519 * @icon_info: a #GtkIconInfo
3520 * @fg: a #GdkRGBA representing the foreground color of the icon
3521 * @success_color: (allow-none): a #GdkRGBA representing the warning color
3522 * of the icon or %NULL to use the default color
3523 * @warning_color: (allow-none): a #GdkRGBA representing the warning color
3524 * of the icon or %NULL to use the default color
3525 * @error_color: (allow-none): a #GdkRGBA representing the error color
3526 * of the icon or %NULL to use the default color (allow-none)
3527 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3528 * loaded icon was a symbolic one and whether the @fg color was
3530 * @error: (allow-none): location to store error information on failure,
3533 * Loads an icon, modifying it to match the system colours for the foreground,
3534 * success, warning and error colors provided. If the icon is not a symbolic
3535 * one, the function will return the result from gtk_icon_info_load_icon().
3537 * This allows loading symbolic icons that will match the system theme.
3539 * Unless you are implementing a widget, you will want to use
3540 * g_themed_icon_new_with_default_fallbacks() to load the icon.
3542 * As implementation details, the icon loaded needs to be of SVG type,
3543 * contain the "symbolic" term as the last component of the icon name,
3544 * and use the 'fg', 'success', 'warning' and 'error' CSS styles in the
3547 * See the <ulink url="http://www.freedesktop.org/wiki/SymbolicIcons">Symbolic Icons spec</ulink>
3548 * for more information about symbolic icons.
3550 * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
3555 gtk_icon_info_load_symbolic (GtkIconInfo *icon_info,
3557 const GdkRGBA *success_color,
3558 const GdkRGBA *warning_color,
3559 const GdkRGBA *error_color,
3560 gboolean *was_symbolic,
3569 gboolean is_symbolic;
3571 g_return_val_if_fail (icon_info != NULL, NULL);
3572 g_return_val_if_fail (fg != NULL, NULL);
3575 if (icon_info->icon_file)
3576 icon_uri = g_file_get_uri (icon_info->icon_file);
3578 is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
3582 *was_symbolic = is_symbolic;
3585 return gtk_icon_info_load_icon (icon_info, error);
3587 css_fg = gdk_rgba_to_css (fg);
3589 css_success = css_warning = css_error = NULL;
3592 css_warning = gdk_rgba_to_css (warning_color);
3595 css_error = gdk_rgba_to_css (error_color);
3598 css_success = gdk_rgba_to_css (success_color);
3600 pixbuf = _gtk_icon_info_load_symbolic_internal (icon_info,
3601 css_fg, css_success,
3602 css_warning, css_error,
3605 g_free (css_warning);
3606 g_free (css_success);
3613 * gtk_icon_info_load_symbolic_for_context:
3614 * @icon_info: a #GtkIconInfo
3615 * @context: a #GtkStyleContext
3616 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3617 * loaded icon was a symbolic one and whether the @fg color was
3619 * @error: (allow-none): location to store error information on failure,
3622 * Loads an icon, modifying it to match the system colors for the foreground,
3623 * success, warning and error colors provided. If the icon is not a symbolic
3624 * one, the function will return the result from gtk_icon_info_load_icon().
3625 * This function uses the regular foreground color and the symbolic colors
3626 * with the names "success_color", "warning_color" and "error_color" from
3629 * This allows loading symbolic icons that will match the system theme.
3631 * See gtk_icon_info_load_symbolic() for more details.
3633 * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
3638 gtk_icon_info_load_symbolic_for_context (GtkIconInfo *icon_info,
3639 GtkStyleContext *context,
3640 gboolean *was_symbolic,
3644 GdkRGBA *color = NULL;
3646 gchar *css_fg = NULL, *css_success;
3647 gchar *css_warning, *css_error;
3648 GtkStateFlags state;
3650 gboolean is_symbolic;
3652 g_return_val_if_fail (icon_info != NULL, NULL);
3653 g_return_val_if_fail (context != NULL, NULL);
3656 if (icon_info->icon_file)
3657 icon_uri = g_file_get_uri (icon_info->icon_file);
3659 is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
3663 *was_symbolic = is_symbolic;
3666 return gtk_icon_info_load_icon (icon_info, error);
3668 state = gtk_style_context_get_state (context);
3669 gtk_style_context_get (context, state, "color", &color, NULL);
3672 css_fg = gdk_rgba_to_css (color);
3673 gdk_rgba_free (color);
3676 css_success = css_warning = css_error = NULL;
3678 if (gtk_style_context_lookup_color (context, "success_color", &rgba))
3679 css_success = gdk_rgba_to_css (&rgba);
3681 if (gtk_style_context_lookup_color (context, "warning_color", &rgba))
3682 css_warning = gdk_rgba_to_css (&rgba);
3684 if (gtk_style_context_lookup_color (context, "error_color", &rgba))
3685 css_error = gdk_rgba_to_css (&rgba);
3687 pixbuf = _gtk_icon_info_load_symbolic_internal (icon_info,
3688 css_fg, css_success,
3689 css_warning, css_error,
3693 g_free (css_success);
3694 g_free (css_warning);
3701 * gtk_icon_info_load_symbolic_for_style:
3702 * @icon_info: a #GtkIconInfo
3703 * @style: a #GtkStyle to take the colors from
3704 * @state: the widget state to use for colors
3705 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3706 * loaded icon was a symbolic one and whether the @fg color was
3708 * @error: (allow-none): location to store error information on failure,
3711 * Loads an icon, modifying it to match the system colours for the foreground,
3712 * success, warning and error colors provided. If the icon is not a symbolic
3713 * one, the function will return the result from gtk_icon_info_load_icon().
3715 * This allows loading symbolic icons that will match the system theme.
3717 * See gtk_icon_info_load_symbolic() for more details.
3719 * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
3723 * Deprecated: 3.0: Use gtk_icon_info_load_symbolic_for_context() instead
3726 gtk_icon_info_load_symbolic_for_style (GtkIconInfo *icon_info,
3729 gboolean *was_symbolic,
3733 GdkColor success_color;
3734 GdkColor warning_color;
3735 GdkColor error_color;
3737 gchar *css_fg, *css_success;
3738 gchar *css_warning, *css_error;
3740 gboolean is_symbolic;
3742 g_return_val_if_fail (icon_info != NULL, NULL);
3743 g_return_val_if_fail (style != NULL, NULL);
3746 if (icon_info->icon_file)
3747 icon_uri = g_file_get_uri (icon_info->icon_file);
3749 is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
3753 *was_symbolic = is_symbolic;
3756 return gtk_icon_info_load_icon (icon_info, error);
3758 fg = &style->fg[state];
3759 css_fg = gdk_color_to_css (fg);
3761 css_success = css_warning = css_error = NULL;
3763 if (gtk_style_lookup_color (style, "success_color", &success_color))
3764 css_success = gdk_color_to_css (&success_color);
3766 if (gtk_style_lookup_color (style, "warning_color", &warning_color))
3767 css_warning = gdk_color_to_css (&warning_color);
3769 if (gtk_style_lookup_color (style, "error_color", &error_color))
3770 css_error = gdk_color_to_css (&error_color);
3772 pixbuf = _gtk_icon_info_load_symbolic_internal (icon_info,
3773 css_fg, css_success,
3774 css_warning, css_error,
3778 g_free (css_success);
3779 g_free (css_warning);
3786 * gtk_icon_info_set_raw_coordinates:
3787 * @icon_info: a #GtkIconInfo
3788 * @raw_coordinates: whether the coordinates of embedded rectangles
3789 * and attached points should be returned in their original
3792 * Sets whether the coordinates returned by gtk_icon_info_get_embedded_rect()
3793 * and gtk_icon_info_get_attach_points() should be returned in their
3794 * original form as specified in the icon theme, instead of scaled
3795 * appropriately for the pixbuf returned by gtk_icon_info_load_icon().
3797 * Raw coordinates are somewhat strange; they are specified to be with
3798 * respect to the unscaled pixmap for PNG and XPM icons, but for SVG
3799 * icons, they are in a 1000x1000 coordinate space that is scaled
3800 * to the final size of the icon. You can determine if the icon is an SVG
3801 * icon by using gtk_icon_info_get_filename(), and seeing if it is non-%NULL
3802 * and ends in '.svg'.
3804 * This function is provided primarily to allow compatibility wrappers
3805 * for older API's, and is not expected to be useful for applications.
3810 gtk_icon_info_set_raw_coordinates (GtkIconInfo *icon_info,
3811 gboolean raw_coordinates)
3813 g_return_if_fail (icon_info != NULL);
3815 icon_info->raw_coordinates = raw_coordinates != FALSE;
3818 /* Scale coordinates from the icon data prior to returning
3822 icon_info_scale_point (GtkIconInfo *icon_info,
3828 if (icon_info->raw_coordinates)
3835 if (!icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
3838 *x_out = 0.5 + x * icon_info->scale;
3839 *y_out = 0.5 + y * icon_info->scale;
3846 * gtk_icon_info_get_embedded_rect:
3847 * @icon_info: a #GtkIconInfo
3848 * @rectangle: (out): #GdkRectangle in which to store embedded
3849 * rectangle coordinates; coordinates are only stored
3850 * when this function returns %TRUE.
3852 * Gets the coordinates of a rectangle within the icon
3853 * that can be used for display of information such
3854 * as a preview of the contents of a text file.
3855 * See gtk_icon_info_set_raw_coordinates() for further
3856 * information about the coordinate system.
3858 * Return value: %TRUE if the icon has an embedded rectangle
3863 gtk_icon_info_get_embedded_rect (GtkIconInfo *icon_info,
3864 GdkRectangle *rectangle)
3866 g_return_val_if_fail (icon_info != NULL, FALSE);
3868 if (icon_info->data && icon_info->data->has_embedded_rect &&
3869 icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
3871 gint scaled_x0, scaled_y0;
3872 gint scaled_x1, scaled_y1;
3876 icon_info_scale_point (icon_info,
3877 icon_info->data->x0, icon_info->data->y0,
3878 &scaled_x0, &scaled_y0);
3879 icon_info_scale_point (icon_info,
3880 icon_info->data->x1, icon_info->data->y1,
3881 &scaled_x1, &scaled_y1);
3883 rectangle->x = scaled_x0;
3884 rectangle->y = scaled_y0;
3885 rectangle->width = scaled_x1 - rectangle->x;
3886 rectangle->height = scaled_y1 - rectangle->y;
3896 * gtk_icon_info_get_attach_points:
3897 * @icon_info: a #GtkIconInfo
3898 * @points: (allow-none) (array length=n_points) (out): location to store pointer to an array of points, or %NULL
3899 * free the array of points with g_free().
3900 * @n_points: (allow-none): location to store the number of points in @points, or %NULL
3902 * Fetches the set of attach points for an icon. An attach point
3903 * is a location in the icon that can be used as anchor points for attaching
3904 * emblems or overlays to the icon.
3906 * Return value: %TRUE if there are any attach points for the icon.
3911 gtk_icon_info_get_attach_points (GtkIconInfo *icon_info,
3915 g_return_val_if_fail (icon_info != NULL, FALSE);
3917 if (icon_info->data && icon_info->data->n_attach_points &&
3918 icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
3924 *points = g_new (GdkPoint, icon_info->data->n_attach_points);
3925 for (i = 0; i < icon_info->data->n_attach_points; i++)
3926 icon_info_scale_point (icon_info,
3927 icon_info->data->attach_points[i].x,
3928 icon_info->data->attach_points[i].y,
3934 *n_points = icon_info->data->n_attach_points;
3950 * gtk_icon_info_get_display_name:
3951 * @icon_info: a #GtkIconInfo
3953 * Gets the display name for an icon. A display name is a
3954 * string to be used in place of the icon name in a user
3955 * visible context like a list of icons.
3957 * Return value: the display name for the icon or %NULL, if
3958 * the icon doesn't have a specified display name. This value
3959 * is owned @icon_info and must not be modified or free.
3964 gtk_icon_info_get_display_name (GtkIconInfo *icon_info)
3966 g_return_val_if_fail (icon_info != NULL, NULL);
3968 if (icon_info->data)
3969 return icon_info->data->display_name;
3980 * gtk_icon_theme_add_builtin_icon:
3981 * @icon_name: the name of the icon to register
3982 * @size: the size at which to register the icon (different
3983 * images can be registered for the same icon name
3984 * at different sizes.)
3985 * @pixbuf: #GdkPixbuf that contains the image to use
3988 * Registers a built-in icon for icon theme lookups. The idea
3989 * of built-in icons is to allow an application or library
3990 * that uses themed icons to function requiring files to
3991 * be present in the file system. For instance, the default
3992 * images for all of GTK+'s stock icons are registered
3995 * In general, if you use gtk_icon_theme_add_builtin_icon()
3996 * you should also install the icon in the icon theme, so
3997 * that the icon is generally available.
3999 * This function will generally be used with pixbufs loaded
4000 * via gdk_pixbuf_new_from_inline().
4005 gtk_icon_theme_add_builtin_icon (const gchar *icon_name,
4009 BuiltinIcon *default_icon;
4013 g_return_if_fail (icon_name != NULL);
4014 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
4016 if (!icon_theme_builtin_icons)
4017 icon_theme_builtin_icons = g_hash_table_new (g_str_hash, g_str_equal);
4019 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
4021 key = g_strdup (icon_name);
4023 key = (gpointer)icon_name; /* Won't get stored */
4025 default_icon = g_new (BuiltinIcon, 1);
4026 default_icon->size = size;
4027 default_icon->pixbuf = g_object_ref (pixbuf);
4028 icons = g_slist_prepend (icons, default_icon);
4030 /* Replaces value, leaves key untouched
4032 g_hash_table_insert (icon_theme_builtin_icons, key, icons);
4035 /* Look up a builtin icon; the min_difference_p and
4036 * has_larger_p out parameters allow us to combine
4037 * this lookup with searching through the actual directories
4038 * of the "hicolor" icon theme. See theme_lookup_icon()
4039 * for how they are used.
4041 static BuiltinIcon *
4042 find_builtin_icon (const gchar *icon_name,
4044 gint *min_difference_p,
4045 gboolean *has_larger_p)
4047 GSList *icons = NULL;
4048 gint min_difference = G_MAXINT;
4049 gboolean has_larger = FALSE;
4050 BuiltinIcon *min_icon = NULL;
4052 if (!icon_theme_builtin_icons)
4055 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
4059 BuiltinIcon *default_icon = icons->data;
4060 int min, max, difference;
4063 min = default_icon->size - 2;
4064 max = default_icon->size + 2;
4065 smaller = size < min;
4067 difference = min - size;
4068 else if (size > max)
4069 difference = size - max;
4073 if (difference == 0)
4075 min_icon = default_icon;
4081 if (difference < min_difference || smaller)
4083 min_difference = difference;
4084 min_icon = default_icon;
4085 has_larger = smaller;
4090 if (difference < min_difference && smaller)
4092 min_difference = difference;
4093 min_icon = default_icon;
4097 icons = icons->next;
4100 if (min_difference_p)
4101 *min_difference_p = min_difference;
4103 *has_larger_p = has_larger;
4109 _gtk_icon_theme_check_reload (GdkDisplay *display)
4113 GtkIconTheme *icon_theme;
4115 n_screens = gdk_display_get_n_screens (display);
4117 for (i = 0; i < n_screens; i++)
4119 screen = gdk_display_get_screen (display, i);
4121 icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
4124 icon_theme->priv->check_reload = TRUE;
4125 ensure_valid_themes (icon_theme);
4126 icon_theme->priv->check_reload = FALSE;
4133 * gtk_icon_theme_lookup_by_gicon:
4134 * @icon_theme: a #GtkIconTheme
4135 * @icon: the #GIcon to look up
4136 * @size: desired icon size
4137 * @flags: flags modifying the behavior of the icon lookup
4139 * Looks up an icon and returns a structure containing
4140 * information such as the filename of the icon.
4141 * The icon can then be rendered into a pixbuf using
4142 * gtk_icon_info_load_icon().
4144 * Return value: a #GtkIconInfo structure containing
4145 * information about the icon, or %NULL if the icon
4146 * wasn't found. Free with gtk_icon_info_free()
4151 gtk_icon_theme_lookup_by_gicon (GtkIconTheme *icon_theme,
4154 GtkIconLookupFlags flags)
4158 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
4159 g_return_val_if_fail (G_IS_ICON (icon), NULL);
4161 if (G_IS_LOADABLE_ICON (icon))
4163 info = icon_info_new ();
4164 info->loadable = G_LOADABLE_ICON (g_object_ref (icon));
4166 if (G_IS_FILE_ICON (icon))
4168 GFile *file = g_file_icon_get_file (G_FILE_ICON (icon));
4171 info->icon_file = g_object_ref (file);
4172 info->filename = g_file_get_path (file);
4176 info->dir_type = ICON_THEME_DIR_UNTHEMED;
4177 info->dir_size = size;
4178 info->desired_size = size;
4179 info->threshold = 2;
4180 info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
4184 else if (G_IS_THEMED_ICON (icon))
4186 const gchar **names;
4188 names = (const gchar **)g_themed_icon_get_names (G_THEMED_ICON (icon));
4189 info = gtk_icon_theme_choose_icon (icon_theme, names, size, flags);
4193 else if (G_IS_EMBLEMED_ICON (icon))
4195 GIcon *base, *emblem;
4197 GtkIconInfo *emblem_info;
4199 if (GTK_IS_NUMERABLE_ICON (icon))
4200 _gtk_numerable_icon_set_background_icon_size (GTK_NUMERABLE_ICON (icon), size / 2);
4202 base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
4203 info = gtk_icon_theme_lookup_by_gicon (icon_theme, base, size, flags);
4206 list = g_emblemed_icon_get_emblems (G_EMBLEMED_ICON (icon));
4207 for (l = list; l; l = l->next)
4209 emblem = g_emblem_get_icon (G_EMBLEM (l->data));
4210 /* always force size for emblems */
4211 emblem_info = gtk_icon_theme_lookup_by_gicon (icon_theme, emblem, size / 2, flags | GTK_ICON_LOOKUP_FORCE_SIZE);
4213 info->emblem_infos = g_slist_prepend (info->emblem_infos, emblem_info);
4219 else if (GDK_IS_PIXBUF (icon))
4223 pixbuf = GDK_PIXBUF (icon);
4225 if ((flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0)
4227 gint width, height, max;
4231 width = gdk_pixbuf_get_width (pixbuf);
4232 height = gdk_pixbuf_get_height (pixbuf);
4233 max = MAX (width, height);
4234 scale = (gdouble) size / (gdouble) max;
4236 scaled = gdk_pixbuf_scale_simple (pixbuf,
4237 0.5 + width * scale,
4238 0.5 + height * scale,
4239 GDK_INTERP_BILINEAR);
4241 info = gtk_icon_info_new_for_pixbuf (icon_theme, scaled);
4243 g_object_unref (scaled);
4247 info = gtk_icon_info_new_for_pixbuf (icon_theme, pixbuf);
4257 * gtk_icon_info_new_for_pixbuf:
4258 * @icon_theme: a #GtkIconTheme
4259 * @pixbuf: the pixbuf to wrap in a #GtkIconInfo
4261 * Creates a #GtkIconInfo for a #GdkPixbuf.
4263 * Returns: a #GtkIconInfo
4268 gtk_icon_info_new_for_pixbuf (GtkIconTheme *icon_theme,
4273 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
4274 g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
4276 info = icon_info_new ();
4277 info->pixbuf = g_object_ref (pixbuf);
4279 info->dir_type = ICON_THEME_DIR_UNTHEMED;