]> Pileus Git - ~andy/gtk/blob - gtk/gtkicontheme.c
treeview: properly calculate the treeview expander size
[~andy/gtk] / gtk / gtkicontheme.c
1 /* GtkIconTheme - a loader for icon themes
2  * gtk-icon-theme.c Copyright (C) 2002, 2003 Red Hat, Inc.
3  *
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.
8  *
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.
13  *
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/>.
16  */
17
18 #include "config.h"
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #ifdef HAVE_UNISTD_H
23 #include <unistd.h>
24 #endif
25 #include <string.h>
26 #include <stdlib.h>
27 #include <math.h>
28 #include <glib.h>
29 #include <glib/gstdio.h>
30
31 #ifdef G_OS_WIN32
32 #ifndef S_ISDIR
33 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
34 #endif
35 #define WIN32_MEAN_AND_LEAN
36 #include <windows.h>
37 #include "win32/gdkwin32.h"
38 #endif /* G_OS_WIN32 */
39
40 #include "gtkicontheme.h"
41 #include "gtkdebug.h"
42 #include "gtkiconfactory.h"
43 #include "gtkiconcache.h"
44 #include "gtkbuiltincache.h"
45 #include "gtkintl.h"
46 #include "gtkmain.h"
47 #include "gtknumerableiconprivate.h"
48 #include "gtksettings.h"
49 #include "gtkprivate.h"
50
51 #undef GDK_DEPRECATED
52 #undef GDK_DEPRECATED_FOR
53 #define GDK_DEPRECATED
54 #define GDK_DEPRECATED_FOR(f)
55
56 #include "deprecated/gtkstyle.h"
57
58
59 /**
60  * SECTION:gtkicontheme
61  * @Short_description: Looking up icons by name
62  * @Title: GtkIconTheme
63  *
64  * #GtkIconTheme provides a facility for looking up icons by name
65  * and size. The main reason for using a name rather than simply
66  * providing a filename is to allow different icons to be used
67  * depending on what <firstterm>icon theme</firstterm> is selected
68  * by the user. The operation of icon themes on Linux and Unix
69  * follows the <ulink
70  * url="http://www.freedesktop.org/Standards/icon-theme-spec">Icon
71  * Theme Specification</ulink>. There is a default icon theme,
72  * named <literal>hicolor</literal> where applications should install
73  * their icons, but more additional application themes can be
74  * installed as operating system vendors and users choose.
75  *
76  * Named icons are similar to the <xref linkend="gtk3-Themeable-Stock-Images"/>
77  * facility, and the distinction between the two may be a bit confusing.
78  * A few things to keep in mind:
79  * <itemizedlist>
80  * <listitem>
81  * Stock images usually are used in conjunction with
82  * <xref linkend="gtk3-Stock-Items"/>, such as %GTK_STOCK_OK or
83  * %GTK_STOCK_OPEN. Named icons are easier to set up and therefore
84  * are more useful for new icons that an application wants to
85  * add, such as application icons or window icons.
86  * </listitem>
87  * <listitem>
88  * Stock images can only be loaded at the symbolic sizes defined
89  * by the #GtkIconSize enumeration, or by custom sizes defined
90  * by gtk_icon_size_register(), while named icons are more flexible
91  * and any pixel size can be specified.
92  * </listitem>
93  * <listitem>
94  * Because stock images are closely tied to stock items, and thus
95  * to actions in the user interface, stock images may come in
96  * multiple variants for different widget states or writing
97  * directions.
98  * </listitem>
99  * </itemizedlist>
100  * A good rule of thumb is that if there is a stock image for what
101  * you want to use, use it, otherwise use a named icon. It turns
102  * out that internally stock images are generally defined in
103  * terms of one or more named icons. (An example of the
104  * more than one case is icons that depend on writing direction;
105  * %GTK_STOCK_GO_FORWARD uses the two themed icons
106  * "gtk-stock-go-forward-ltr" and "gtk-stock-go-forward-rtl".)
107  *
108  * In many cases, named themes are used indirectly, via #GtkImage
109  * or stock items, rather than directly, but looking up icons
110  * directly is also simple. The #GtkIconTheme object acts
111  * as a database of all the icons in the current theme. You
112  * can create new #GtkIconTheme objects, but its much more
113  * efficient to use the standard icon theme for the #GdkScreen
114  * so that the icon information is shared with other people
115  * looking up icons. In the case where the default screen is
116  * being used, looking up an icon can be as simple as:
117  * <informalexample>
118  * <programlisting>
119  * GError *error = NULL;
120  * GtkIconTheme *icon_theme;
121  * GdkPixbuf *pixbuf;
122  *
123  * icon_theme = gtk_icon_theme_get_default ();
124  * pixbuf = gtk_icon_theme_load_icon (icon_theme,
125  *                                    "my-icon-name", // icon name
126  *                                    48, // size
127  *                                    0,  // flags
128  *                                    &error);
129  * if (!pixbuf)
130  *   {
131  *     g_warning ("Couldn't load icon: &percnt;s", error->message);
132  *     g_error_free (error);
133  *   }
134  * else
135  *   {
136  *     // Use the pixbuf
137  *     g_object_unref (pixbuf);
138  *   }
139  * </programlisting>
140  * </informalexample>
141  */
142
143
144 #define DEFAULT_THEME_NAME "hicolor"
145
146 typedef enum
147 {
148   ICON_THEME_DIR_FIXED,  
149   ICON_THEME_DIR_SCALABLE,  
150   ICON_THEME_DIR_THRESHOLD,
151   ICON_THEME_DIR_UNTHEMED
152 } IconThemeDirType;
153
154 /* In reverse search order: */
155 typedef enum
156 {
157   ICON_SUFFIX_NONE = 0,
158   ICON_SUFFIX_XPM = 1 << 0,
159   ICON_SUFFIX_SVG = 1 << 1,
160   ICON_SUFFIX_PNG = 1 << 2,
161   HAS_ICON_FILE = 1 << 3
162 } IconSuffix;
163
164 #define INFO_CACHE_LRU_SIZE 32
165 #if 0
166 #define DEBUG_CACHE(args) g_print args
167 #else
168 #define DEBUG_CACHE(args)
169 #endif
170
171 struct _GtkIconThemePrivate
172 {
173   GHashTable *info_cache;
174   GList *info_cache_lru;
175
176   gchar *current_theme;
177   gchar *fallback_theme;
178   gchar **search_path;
179   gint search_path_len;
180
181   guint custom_theme        : 1;
182   guint is_screen_singleton : 1;
183   guint pixbuf_supports_svg : 1;
184   guint themes_valid        : 1;
185   guint check_reload        : 1;
186   guint loading_themes      : 1;
187
188   /* A list of all the themes needed to look up icons.
189    * In search order, without duplicates
190    */
191   GList *themes;
192   GHashTable *unthemed_icons;
193
194   /* Note: The keys of this hashtable are owned by the
195    * themedir and unthemed hashtables.
196    */
197   GHashTable *all_icons;
198
199   /* GdkScreen for the icon theme (may be NULL)
200    */
201   GdkScreen *screen;
202
203   /* time when we last stat:ed for theme changes */
204   glong last_stat_time;
205   GList *dir_mtimes;
206
207   gulong reset_styles_idle;
208 };
209
210 typedef struct {
211   gchar **icon_names;
212   gint size;
213   GtkIconLookupFlags flags;
214 } IconInfoKey;
215
216 typedef struct _SymbolicPixbufCache SymbolicPixbufCache;
217
218 struct _SymbolicPixbufCache {
219   GdkPixbuf *pixbuf;
220   GdkPixbuf *proxy_pixbuf;
221   GdkRGBA  fg;
222   GdkRGBA  success_color;
223   GdkRGBA  warning_color;
224   GdkRGBA  error_color;
225   SymbolicPixbufCache *next;
226 };
227
228 struct _GtkIconInfoClass
229 {
230   GObjectClass parent_class;
231 };
232
233 struct _GtkIconInfo
234 {
235   GObject parent_instance;
236
237   /* Information about the source
238    */
239   IconInfoKey key;
240   GtkIconTheme *in_cache;
241
242   gchar *filename;
243   GFile *icon_file;
244   GLoadableIcon *loadable;
245   GSList *emblem_infos;
246
247   /* Cache pixbuf (if there is any) */
248   GdkPixbuf *cache_pixbuf;
249
250   GtkIconData *data;
251
252   /* Information about the directory where
253    * the source was found
254    */
255   IconThemeDirType dir_type;
256   gint dir_size;
257   gint threshold;
258
259   /* Parameters influencing the scaled icon
260    */
261   gint desired_size;
262   guint raw_coordinates : 1;
263   guint forced_size     : 1;
264   guint emblems_applied : 1;
265
266   /* Cached information if we go ahead and try to load
267    * the icon.
268    */
269   GdkPixbuf *pixbuf;
270   GdkPixbuf *proxy_pixbuf;
271   GError *load_error;
272   gdouble scale;
273
274   SymbolicPixbufCache *symbolic_pixbuf_cache;
275
276   GtkRequisition *symbolic_pixbuf_size;
277 };
278
279 typedef struct
280 {
281   char *name;
282   char *display_name;
283   char *comment;
284   char *example;
285
286   /* In search order */
287   GList *dirs;
288 } IconTheme;
289
290 typedef struct
291 {
292   IconThemeDirType type;
293   GQuark context;
294
295   int size;
296   int min_size;
297   int max_size;
298   int threshold;
299
300   char *dir;
301   char *subdir;
302   int subdir_index;
303   
304   GtkIconCache *cache;
305   
306   GHashTable *icons;
307   GHashTable *icon_data;
308 } IconThemeDir;
309
310 typedef struct
311 {
312   char *svg_filename;
313   char *no_svg_filename;
314 } UnthemedIcon;
315
316 typedef struct
317 {
318   gint size;
319   GdkPixbuf *pixbuf;
320 } BuiltinIcon;
321
322 typedef struct 
323 {
324   char *dir;
325   time_t mtime; /* 0 == not existing or not a dir */
326
327   GtkIconCache *cache;
328 } IconThemeDirMtime;
329
330 static void  gtk_icon_theme_finalize   (GObject              *object);
331 static void  theme_dir_destroy         (IconThemeDir         *dir);
332
333 static void         theme_destroy     (IconTheme        *theme);
334 static GtkIconInfo *theme_lookup_icon (IconTheme        *theme,
335                                        const char       *icon_name,
336                                        int               size,
337                                        gboolean          allow_svg,
338                                        gboolean          use_default_icons);
339 static void         theme_list_icons  (IconTheme        *theme,
340                                        GHashTable       *icons,
341                                        GQuark            context);
342 static void         theme_list_contexts  (IconTheme        *theme,
343                                           GHashTable       *contexts);
344 static void         theme_subdir_load (GtkIconTheme     *icon_theme,
345                                        IconTheme        *theme,
346                                        GKeyFile         *theme_file,
347                                        char             *subdir);
348 static void         do_theme_change   (GtkIconTheme     *icon_theme);
349
350 static void     blow_themes               (GtkIconTheme    *icon_themes);
351 static gboolean rescan_themes             (GtkIconTheme    *icon_themes);
352
353 static GtkIconData *icon_data_dup      (GtkIconData     *icon_data);
354 static void  icon_data_free            (GtkIconData     *icon_data);
355 static void load_icon_data             (IconThemeDir    *dir,
356                                         const char      *path,
357                                         const char      *name);
358
359 static IconSuffix theme_dir_get_icon_suffix (IconThemeDir *dir,
360                                              const gchar  *icon_name,
361                                              gboolean     *has_icon_file);
362
363
364 static GtkIconInfo *icon_info_new             (void);
365 static GtkIconInfo *icon_info_new_builtin     (BuiltinIcon *icon);
366
367 static IconSuffix suffix_from_name (const char *name);
368
369 static BuiltinIcon *find_builtin_icon (const gchar *icon_name,
370                                        gint        size,
371                                        gint        *min_difference_p,
372                                        gboolean    *has_larger_p);
373 static void remove_from_lru_cache (GtkIconTheme *icon_theme,
374                                    GtkIconInfo *icon_info);
375
376 static guint signal_changed = 0;
377
378 static GHashTable *icon_theme_builtin_icons;
379
380 /* also used in gtkiconfactory.c */
381 GtkIconCache *_builtin_cache = NULL;
382 static GList *builtin_dirs = NULL;
383
384 static guint
385 icon_info_key_hash (gconstpointer _key)
386 {
387   const IconInfoKey *key = _key;
388   guint h = 0;
389   int i;
390   for (i = 0; key->icon_names[i] != NULL; i++)
391     h ^= g_str_hash (key->icon_names[i]);
392
393   h ^= key->size * 0x10001;
394   h ^= key->flags * 0x1000010;
395
396   return h;
397 }
398
399 static gboolean
400 icon_info_key_equal (gconstpointer  _a,
401                      gconstpointer  _b)
402 {
403   const IconInfoKey *a = _a;
404   const IconInfoKey *b = _b;
405   int i;
406
407   if (a->size != b->size)
408     return FALSE;
409
410   if (a->flags != b->flags)
411     return FALSE;
412
413   for (i = 0;
414        a->icon_names[i] != NULL &&
415        b->icon_names[i] != NULL; i++)
416     {
417       if (strcmp (a->icon_names[i], b->icon_names[i]) != 0)
418         return FALSE;
419     }
420
421   return a->icon_names[i] == NULL && b->icon_names[i] == NULL;
422 }
423
424 G_DEFINE_TYPE (GtkIconTheme, gtk_icon_theme, G_TYPE_OBJECT)
425
426 /**
427  * gtk_icon_theme_new:
428  * 
429  * Creates a new icon theme object. Icon theme objects are used
430  * to lookup up an icon by name in a particular icon theme.
431  * Usually, you'll want to use gtk_icon_theme_get_default()
432  * or gtk_icon_theme_get_for_screen() rather than creating
433  * a new icon theme object for scratch.
434  * 
435  * Return value: the newly created #GtkIconTheme object.
436  *
437  * Since: 2.4
438  **/
439 GtkIconTheme *
440 gtk_icon_theme_new (void)
441 {
442   return g_object_new (GTK_TYPE_ICON_THEME, NULL);
443 }
444
445 /**
446  * gtk_icon_theme_get_default:
447  * 
448  * Gets the icon theme for the default screen. See
449  * gtk_icon_theme_get_for_screen().
450  *
451  * Return value: (transfer none): A unique #GtkIconTheme associated with
452  *  the default screen. This icon theme is associated with
453  *  the screen and can be used as long as the screen
454  *  is open. Do not ref or unref it.
455  *
456  * Since: 2.4
457  **/
458 GtkIconTheme *
459 gtk_icon_theme_get_default (void)
460 {
461   return gtk_icon_theme_get_for_screen (gdk_screen_get_default ());
462 }
463
464 /**
465  * gtk_icon_theme_get_for_screen:
466  * @screen: a #GdkScreen
467  * 
468  * Gets the icon theme object associated with @screen; if this
469  * function has not previously been called for the given
470  * screen, a new icon theme object will be created and
471  * associated with the screen. Icon theme objects are
472  * fairly expensive to create, so using this function
473  * is usually a better choice than calling than gtk_icon_theme_new()
474  * and setting the screen yourself; by using this function
475  * a single icon theme object will be shared between users.
476  *
477  * Return value: (transfer none): A unique #GtkIconTheme associated with
478  *  the given screen. This icon theme is associated with
479  *  the screen and can be used as long as the screen
480  *  is open. Do not ref or unref it.
481  *
482  * Since: 2.4
483  **/
484 GtkIconTheme *
485 gtk_icon_theme_get_for_screen (GdkScreen *screen)
486 {
487   GtkIconTheme *icon_theme;
488
489   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
490
491   icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
492   if (!icon_theme)
493     {
494       GtkIconThemePrivate *priv;
495
496       icon_theme = gtk_icon_theme_new ();
497       gtk_icon_theme_set_screen (icon_theme, screen);
498
499       priv = icon_theme->priv;
500       priv->is_screen_singleton = TRUE;
501
502       g_object_set_data (G_OBJECT (screen), I_("gtk-icon-theme"), icon_theme);
503     }
504
505   return icon_theme;
506 }
507
508 static void
509 gtk_icon_theme_class_init (GtkIconThemeClass *klass)
510 {
511   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
512
513   gobject_class->finalize = gtk_icon_theme_finalize;
514
515 /**
516  * GtkIconTheme::changed:
517  * @icon_theme: the icon theme
518  * 
519  * Emitted when the current icon theme is switched or GTK+ detects
520  * that a change has occurred in the contents of the current
521  * icon theme.
522  **/
523   signal_changed = g_signal_new (I_("changed"),
524                                  G_TYPE_FROM_CLASS (klass),
525                                  G_SIGNAL_RUN_LAST,
526                                  G_STRUCT_OFFSET (GtkIconThemeClass, changed),
527                                  NULL, NULL,
528                                  g_cclosure_marshal_VOID__VOID,
529                                  G_TYPE_NONE, 0);
530
531   g_type_class_add_private (klass, sizeof (GtkIconThemePrivate));
532 }
533
534
535 /* Callback when the display that the icon theme is attached to
536  * is closed; unset the screen, and if it's the unique theme
537  * for the screen, drop the reference
538  */
539 static void
540 display_closed (GdkDisplay   *display,
541                 gboolean      is_error,
542                 GtkIconTheme *icon_theme)
543 {
544   GtkIconThemePrivate *priv = icon_theme->priv;
545   GdkScreen *screen = priv->screen;
546   gboolean was_screen_singleton = priv->is_screen_singleton;
547
548   if (was_screen_singleton)
549     {
550       g_object_set_data (G_OBJECT (screen), I_("gtk-icon-theme"), NULL);
551       priv->is_screen_singleton = FALSE;
552     }
553
554   gtk_icon_theme_set_screen (icon_theme, NULL);
555
556   if (was_screen_singleton)
557     {
558       g_object_unref (icon_theme);
559     }
560 }
561
562 static void
563 update_current_theme (GtkIconTheme *icon_theme)
564 {
565 #define theme_changed(_old, _new) \
566   ((_old && !_new) || (!_old && _new) || \
567    (_old && _new && strcmp (_old, _new) != 0))
568   GtkIconThemePrivate *priv = icon_theme->priv;
569
570   if (!priv->custom_theme)
571     {
572       gchar *theme = NULL;
573       gchar *fallback_theme = NULL;
574       gboolean changed = FALSE;
575
576       if (priv->screen)
577         {
578           GtkSettings *settings = gtk_settings_get_for_screen (priv->screen);
579           g_object_get (settings, 
580                         "gtk-icon-theme-name", &theme, 
581                         "gtk-fallback-icon-theme", &fallback_theme, NULL);
582         }
583
584       /* ensure that the current theme (even when just the default)
585        * is searched before any fallback theme
586        */
587       if (!theme && fallback_theme)
588         theme = g_strdup (DEFAULT_THEME_NAME);
589
590       if (theme_changed (priv->current_theme, theme))
591         {
592           g_free (priv->current_theme);
593           priv->current_theme = theme;
594           changed = TRUE;
595         }
596       else
597         g_free (theme);
598
599       if (theme_changed (priv->fallback_theme, fallback_theme))
600         {
601           g_free (priv->fallback_theme);
602           priv->fallback_theme = fallback_theme;
603           changed = TRUE;
604         }
605       else
606         g_free (fallback_theme);
607
608       if (changed)
609         do_theme_change (icon_theme);
610     }
611 #undef theme_changed
612 }
613
614 /* Callback when the icon theme GtkSetting changes
615  */
616 static void
617 theme_changed (GtkSettings  *settings,
618                GParamSpec   *pspec,
619                GtkIconTheme *icon_theme)
620 {
621   update_current_theme (icon_theme);
622 }
623
624 static void
625 unset_screen (GtkIconTheme *icon_theme)
626 {
627   GtkIconThemePrivate *priv = icon_theme->priv;
628   GtkSettings *settings;
629   GdkDisplay *display;
630   
631   if (priv->screen)
632     {
633       settings = gtk_settings_get_for_screen (priv->screen);
634       display = gdk_screen_get_display (priv->screen);
635       
636       g_signal_handlers_disconnect_by_func (display,
637                                             (gpointer) display_closed,
638                                             icon_theme);
639       g_signal_handlers_disconnect_by_func (settings,
640                                             (gpointer) theme_changed,
641                                             icon_theme);
642
643       priv->screen = NULL;
644     }
645 }
646
647 /**
648  * gtk_icon_theme_set_screen:
649  * @icon_theme: a #GtkIconTheme
650  * @screen: a #GdkScreen
651  * 
652  * Sets the screen for an icon theme; the screen is used
653  * to track the user's currently configured icon theme,
654  * which might be different for different screens.
655  *
656  * Since: 2.4
657  **/
658 void
659 gtk_icon_theme_set_screen (GtkIconTheme *icon_theme,
660                            GdkScreen    *screen)
661 {
662   GtkIconThemePrivate *priv;
663   GtkSettings *settings;
664   GdkDisplay *display;
665
666   g_return_if_fail (GTK_ICON_THEME (icon_theme));
667   g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
668
669   priv = icon_theme->priv;
670
671   unset_screen (icon_theme);
672   
673   if (screen)
674     {
675       display = gdk_screen_get_display (screen);
676       settings = gtk_settings_get_for_screen (screen);
677       
678       priv->screen = screen;
679       
680       g_signal_connect (display, "closed",
681                         G_CALLBACK (display_closed), icon_theme);
682       g_signal_connect (settings, "notify::gtk-icon-theme-name",
683                         G_CALLBACK (theme_changed), icon_theme);
684       g_signal_connect (settings, "notify::gtk-fallback-icon-theme-name",
685                         G_CALLBACK (theme_changed), icon_theme);
686     }
687
688   update_current_theme (icon_theme);
689 }
690
691 /* Checks whether a loader for SVG files has been registered
692  * with GdkPixbuf.
693  */
694 static gboolean
695 pixbuf_supports_svg (void)
696 {
697   GSList *formats;
698   GSList *tmp_list;
699   static gint found_svg = -1;
700
701   if (found_svg != -1)
702     return found_svg;
703
704   formats = gdk_pixbuf_get_formats ();
705
706   found_svg = FALSE; 
707   for (tmp_list = formats; tmp_list && !found_svg; tmp_list = tmp_list->next)
708     {
709       gchar **mime_types = gdk_pixbuf_format_get_mime_types (tmp_list->data);
710       gchar **mime_type;
711       
712       for (mime_type = mime_types; *mime_type && !found_svg; mime_type++)
713         {
714           if (strcmp (*mime_type, "image/svg") == 0)
715             found_svg = TRUE;
716         }
717
718       g_strfreev (mime_types);
719     }
720
721   g_slist_free (formats);
722   
723   return found_svg;
724 }
725
726 /* The icon info was removed from the icon_info_hash hash table */
727 static void
728 icon_info_uncached (GtkIconInfo *icon_info)
729 {
730   GtkIconTheme *icon_theme = icon_info->in_cache;
731
732   DEBUG_CACHE (("removing %p (%s %d 0x%x) from cache (icon_them: %p)  (cache size %d)\n",
733                 icon_info,
734                 g_strjoinv (",", icon_info->key.icon_names),
735                 icon_info->key.size, icon_info->key.flags,
736                 icon_theme,
737                 icon_theme != NULL ? g_hash_table_size (icon_theme->priv->info_cache) : 0));
738
739   icon_info->in_cache = NULL;
740
741   if (icon_theme != NULL)
742     remove_from_lru_cache (icon_theme, icon_info);
743 }
744
745 static void
746 gtk_icon_theme_init (GtkIconTheme *icon_theme)
747 {
748   GtkIconThemePrivate *priv;
749   const gchar * const *xdg_data_dirs;
750   int i, j;
751
752   priv = G_TYPE_INSTANCE_GET_PRIVATE (icon_theme,
753                                       GTK_TYPE_ICON_THEME,
754                                       GtkIconThemePrivate);
755   icon_theme->priv = priv;
756
757   priv->info_cache = g_hash_table_new_full (icon_info_key_hash, icon_info_key_equal, NULL,
758                                             (GDestroyNotify)icon_info_uncached);
759
760   priv->custom_theme = FALSE;
761
762   xdg_data_dirs = g_get_system_data_dirs ();
763   for (i = 0; xdg_data_dirs[i]; i++) ;
764
765   priv->search_path_len = 2 * i + 2;
766   
767   priv->search_path = g_new (char *, priv->search_path_len);
768   
769   i = 0;
770   priv->search_path[i++] = g_build_filename (g_get_user_data_dir (), "icons", NULL);
771   priv->search_path[i++] = g_build_filename (g_get_home_dir (), ".icons", NULL);
772   
773   for (j = 0; xdg_data_dirs[j]; j++) 
774     priv->search_path[i++] = g_build_filename (xdg_data_dirs[j], "icons", NULL);
775
776   for (j = 0; xdg_data_dirs[j]; j++) 
777     priv->search_path[i++] = g_build_filename (xdg_data_dirs[j], "pixmaps", NULL);
778
779   priv->themes_valid = FALSE;
780   priv->themes = NULL;
781   priv->unthemed_icons = NULL;
782   
783   priv->pixbuf_supports_svg = pixbuf_supports_svg ();
784 }
785
786 static void
787 free_dir_mtime (IconThemeDirMtime *dir_mtime)
788 {
789   if (dir_mtime->cache)
790     _gtk_icon_cache_unref (dir_mtime->cache);
791
792   g_free (dir_mtime->dir);
793   g_slice_free (IconThemeDirMtime, dir_mtime);
794
795 }
796
797 static gboolean
798 reset_styles_idle (gpointer user_data)
799 {
800   GtkIconTheme *icon_theme;
801   GtkIconThemePrivate *priv;
802
803   icon_theme = GTK_ICON_THEME (user_data);
804   priv = icon_theme->priv;
805
806   if (priv->screen && priv->is_screen_singleton)
807     gtk_style_context_reset_widgets (priv->screen);
808
809   priv->reset_styles_idle = 0;
810
811   return FALSE;
812 }
813
814 static void
815 do_theme_change (GtkIconTheme *icon_theme)
816 {
817   GtkIconThemePrivate *priv = icon_theme->priv;
818
819   g_hash_table_remove_all (priv->info_cache);
820
821   if (!priv->themes_valid)
822     return;
823   
824   GTK_NOTE (ICONTHEME, 
825             g_print ("change to icon theme \"%s\"\n", priv->current_theme));
826   blow_themes (icon_theme);
827   g_signal_emit (icon_theme, signal_changed, 0);
828
829   if (!priv->reset_styles_idle)
830     priv->reset_styles_idle = 
831       gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, 
832                        reset_styles_idle, icon_theme, NULL);
833 }
834
835 static void
836 blow_themes (GtkIconTheme *icon_theme)
837 {
838   GtkIconThemePrivate *priv = icon_theme->priv;
839   
840   if (priv->themes_valid)
841     {
842       g_hash_table_destroy (priv->all_icons);
843       g_list_free_full (priv->themes, (GDestroyNotify) theme_destroy);
844       g_list_free_full (priv->dir_mtimes, (GDestroyNotify) free_dir_mtime);
845       g_hash_table_destroy (priv->unthemed_icons);
846     }
847   priv->themes = NULL;
848   priv->unthemed_icons = NULL;
849   priv->dir_mtimes = NULL;
850   priv->all_icons = NULL;
851   priv->themes_valid = FALSE;
852 }
853
854 static void
855 gtk_icon_theme_finalize (GObject *object)
856 {
857   GtkIconTheme *icon_theme;
858   GtkIconThemePrivate *priv;
859   int i;
860
861   icon_theme = GTK_ICON_THEME (object);
862   priv = icon_theme->priv;
863
864   g_hash_table_destroy (priv->info_cache);
865   g_assert (priv->info_cache_lru == NULL);
866
867   if (priv->reset_styles_idle)
868     {
869       g_source_remove (priv->reset_styles_idle);
870       priv->reset_styles_idle = 0;
871     }
872
873   unset_screen (icon_theme);
874
875   g_free (priv->current_theme);
876   priv->current_theme = NULL;
877
878   for (i = 0; i < priv->search_path_len; i++)
879     g_free (priv->search_path[i]);
880
881   g_free (priv->search_path);
882   priv->search_path = NULL;
883
884   blow_themes (icon_theme);
885
886   G_OBJECT_CLASS (gtk_icon_theme_parent_class)->finalize (object);  
887 }
888
889 /**
890  * gtk_icon_theme_set_search_path:
891  * @icon_theme: a #GtkIconTheme
892  * @path: (array length=n_elements) (element-type filename): array of
893  *     directories that are searched for icon themes
894  * @n_elements: number of elements in @path.
895  * 
896  * Sets the search path for the icon theme object. When looking
897  * for an icon theme, GTK+ will search for a subdirectory of
898  * one or more of the directories in @path with the same name
899  * as the icon theme. (Themes from multiple of the path elements
900  * are combined to allow themes to be extended by adding icons
901  * in the user's home directory.)
902  *
903  * In addition if an icon found isn't found either in the current
904  * icon theme or the default icon theme, and an image file with
905  * the right name is found directly in one of the elements of
906  * @path, then that image will be used for the icon name.
907  * (This is legacy feature, and new icons should be put
908  * into the default icon theme, which is called DEFAULT_THEME_NAME,
909  * rather than directly on the icon path.)
910  *
911  * Since: 2.4
912  **/
913 void
914 gtk_icon_theme_set_search_path (GtkIconTheme *icon_theme,
915                                 const gchar  *path[],
916                                 gint          n_elements)
917 {
918   GtkIconThemePrivate *priv;
919   gint i;
920
921   g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
922
923   priv = icon_theme->priv;
924   for (i = 0; i < priv->search_path_len; i++)
925     g_free (priv->search_path[i]);
926
927   g_free (priv->search_path);
928
929   priv->search_path = g_new (gchar *, n_elements);
930   priv->search_path_len = n_elements;
931
932   for (i = 0; i < priv->search_path_len; i++)
933     priv->search_path[i] = g_strdup (path[i]);
934
935   do_theme_change (icon_theme);
936 }
937
938
939 /**
940  * gtk_icon_theme_get_search_path:
941  * @icon_theme: a #GtkIconTheme
942  * @path: (allow-none) (array length=n_elements) (element-type filename) (out):
943  *     location to store a list of icon theme path directories or %NULL.
944  *     The stored value should be freed with g_strfreev().
945  * @n_elements: location to store number of elements in @path, or %NULL
946  *
947  * Gets the current search path. See gtk_icon_theme_set_search_path().
948  *
949  * Since: 2.4
950  */
951 void
952 gtk_icon_theme_get_search_path (GtkIconTheme  *icon_theme,
953                                 gchar        **path[],
954                                 gint          *n_elements)
955 {
956   GtkIconThemePrivate *priv;
957   int i;
958
959   g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
960
961   priv = icon_theme->priv;
962
963   if (n_elements)
964     *n_elements = priv->search_path_len;
965   
966   if (path)
967     {
968       *path = g_new (gchar *, priv->search_path_len + 1);
969       for (i = 0; i < priv->search_path_len; i++)
970         (*path)[i] = g_strdup (priv->search_path[i]);
971       (*path)[i] = NULL;
972     }
973 }
974
975 /**
976  * gtk_icon_theme_append_search_path:
977  * @icon_theme: a #GtkIconTheme
978  * @path: (type filename): directory name to append to the icon path
979  * 
980  * Appends a directory to the search path. 
981  * See gtk_icon_theme_set_search_path(). 
982  *
983  * Since: 2.4
984  **/
985 void
986 gtk_icon_theme_append_search_path (GtkIconTheme *icon_theme,
987                                    const gchar  *path)
988 {
989   GtkIconThemePrivate *priv;
990
991   g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
992   g_return_if_fail (path != NULL);
993
994   priv = icon_theme->priv;
995   
996   priv->search_path_len++;
997
998   priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
999   priv->search_path[priv->search_path_len-1] = g_strdup (path);
1000
1001   do_theme_change (icon_theme);
1002 }
1003
1004 /**
1005  * gtk_icon_theme_prepend_search_path:
1006  * @icon_theme: a #GtkIconTheme
1007  * @path: (type filename): directory name to prepend to the icon path
1008  * 
1009  * Prepends a directory to the search path. 
1010  * See gtk_icon_theme_set_search_path().
1011  *
1012  * Since: 2.4
1013  **/
1014 void
1015 gtk_icon_theme_prepend_search_path (GtkIconTheme *icon_theme,
1016                                     const gchar  *path)
1017 {
1018   GtkIconThemePrivate *priv;
1019   int i;
1020
1021   g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
1022   g_return_if_fail (path != NULL);
1023
1024   priv = icon_theme->priv;
1025   
1026   priv->search_path_len++;
1027   priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
1028
1029   for (i = priv->search_path_len - 1; i > 0; i--)
1030     priv->search_path[i] = priv->search_path[i - 1];
1031   
1032   priv->search_path[0] = g_strdup (path);
1033
1034   do_theme_change (icon_theme);
1035 }
1036
1037 /**
1038  * gtk_icon_theme_set_custom_theme:
1039  * @icon_theme: a #GtkIconTheme
1040  * @theme_name: (allow-none): name of icon theme to use instead of
1041  *   configured theme, or %NULL to unset a previously set custom theme
1042  * 
1043  * Sets the name of the icon theme that the #GtkIconTheme object uses
1044  * overriding system configuration. This function cannot be called
1045  * on the icon theme objects returned from gtk_icon_theme_get_default()
1046  * and gtk_icon_theme_get_for_screen().
1047  *
1048  * Since: 2.4
1049  **/
1050 void
1051 gtk_icon_theme_set_custom_theme (GtkIconTheme *icon_theme,
1052                                  const gchar  *theme_name)
1053 {
1054   GtkIconThemePrivate *priv;
1055
1056   g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
1057
1058   priv = icon_theme->priv;
1059
1060   g_return_if_fail (!priv->is_screen_singleton);
1061   
1062   if (theme_name != NULL)
1063     {
1064       priv->custom_theme = TRUE;
1065       if (!priv->current_theme || strcmp (theme_name, priv->current_theme) != 0)
1066         {
1067           g_free (priv->current_theme);
1068           priv->current_theme = g_strdup (theme_name);
1069
1070           do_theme_change (icon_theme);
1071         }
1072     }
1073   else
1074     {
1075       if (priv->custom_theme)
1076         {
1077           priv->custom_theme = FALSE;
1078
1079           update_current_theme (icon_theme);
1080         }
1081     }
1082 }
1083
1084 static void
1085 insert_theme (GtkIconTheme *icon_theme, const char *theme_name)
1086 {
1087   int i;
1088   GList *l;
1089   char **dirs;
1090   char **themes;
1091   GtkIconThemePrivate *priv;
1092   IconTheme *theme = NULL;
1093   char *path;
1094   GKeyFile *theme_file;
1095   GError *error = NULL;
1096   IconThemeDirMtime *dir_mtime;
1097   GStatBuf stat_buf;
1098   
1099   priv = icon_theme->priv;
1100
1101   for (l = priv->themes; l != NULL; l = l->next)
1102     {
1103       theme = l->data;
1104       if (strcmp (theme->name, theme_name) == 0)
1105         return;
1106     }
1107   
1108   for (i = 0; i < priv->search_path_len; i++)
1109     {
1110       path = g_build_filename (priv->search_path[i],
1111                                theme_name,
1112                                NULL);
1113       dir_mtime = g_slice_new (IconThemeDirMtime);
1114       dir_mtime->cache = NULL;
1115       dir_mtime->dir = path;
1116       if (g_stat (path, &stat_buf) == 0 && S_ISDIR (stat_buf.st_mode))
1117         dir_mtime->mtime = stat_buf.st_mtime;
1118       else
1119         dir_mtime->mtime = 0;
1120
1121       priv->dir_mtimes = g_list_prepend (priv->dir_mtimes, dir_mtime);
1122     }
1123   priv->dir_mtimes = g_list_reverse (priv->dir_mtimes);
1124
1125   theme_file = NULL;
1126   for (i = 0; i < priv->search_path_len && !theme_file; i++)
1127     {
1128       path = g_build_filename (priv->search_path[i],
1129                                theme_name,
1130                                "index.theme",
1131                                NULL);
1132       if (g_file_test (path, G_FILE_TEST_IS_REGULAR)) 
1133         {
1134           theme_file = g_key_file_new ();
1135           g_key_file_set_list_separator (theme_file, ',');
1136           g_key_file_load_from_file (theme_file, path, 0, &error);
1137           if (error)
1138             {
1139               g_key_file_free (theme_file);
1140               theme_file = NULL;
1141               g_error_free (error);
1142               error = NULL;
1143             }
1144         }
1145       g_free (path);
1146     }
1147
1148   if (theme_file || strcmp (theme_name, DEFAULT_THEME_NAME) == 0)
1149     {
1150       theme = g_new0 (IconTheme, 1);
1151       theme->name = g_strdup (theme_name);
1152       priv->themes = g_list_prepend (priv->themes, theme);
1153     }
1154
1155   if (theme_file == NULL)
1156     return;
1157
1158   theme->display_name = 
1159     g_key_file_get_locale_string (theme_file, "Icon Theme", "Name", NULL, NULL);
1160   if (!theme->display_name)
1161     g_warning ("Theme file for %s has no name\n", theme_name);
1162
1163   dirs = g_key_file_get_string_list (theme_file, "Icon Theme", "Directories", NULL, NULL);
1164   if (!dirs)
1165     {
1166       g_warning ("Theme file for %s has no directories\n", theme_name);
1167       priv->themes = g_list_remove (priv->themes, theme);
1168       g_free (theme->name);
1169       g_free (theme->display_name);
1170       g_free (theme);
1171       g_key_file_free (theme_file);
1172       return;
1173     }
1174   
1175   theme->comment = 
1176     g_key_file_get_locale_string (theme_file, 
1177                                   "Icon Theme", "Comment",
1178                                   NULL, NULL);
1179   theme->example = 
1180     g_key_file_get_string (theme_file, 
1181                            "Icon Theme", "Example",
1182                            NULL);
1183
1184   theme->dirs = NULL;
1185   for (i = 0; dirs[i] != NULL; i++)
1186     theme_subdir_load (icon_theme, theme, theme_file, dirs[i]);
1187
1188   g_strfreev (dirs);
1189
1190   theme->dirs = g_list_reverse (theme->dirs);
1191
1192   themes = g_key_file_get_string_list (theme_file,
1193                                        "Icon Theme",
1194                                        "Inherits",
1195                                        NULL,
1196                                        NULL);
1197   if (themes)
1198     {
1199       for (i = 0; themes[i] != NULL; i++)
1200         insert_theme (icon_theme, themes[i]);
1201       
1202       g_strfreev (themes);
1203     }
1204
1205   g_key_file_free (theme_file);
1206 }
1207
1208 static void
1209 free_unthemed_icon (UnthemedIcon *unthemed_icon)
1210 {
1211   g_free (unthemed_icon->svg_filename);
1212   g_free (unthemed_icon->no_svg_filename);
1213   g_slice_free (UnthemedIcon, unthemed_icon);
1214 }
1215
1216 static char *
1217 strip_suffix (const char *filename)
1218 {
1219   const char *dot;
1220
1221   dot = strrchr (filename, '.');
1222
1223   if (dot == NULL)
1224     return g_strdup (filename);
1225
1226   return g_strndup (filename, dot - filename);
1227 }
1228
1229 static void
1230 load_themes (GtkIconTheme *icon_theme)
1231 {
1232   GtkIconThemePrivate *priv;
1233   GDir *gdir;
1234   int base;
1235   char *dir;
1236   const char *file;
1237   UnthemedIcon *unthemed_icon;
1238   IconSuffix old_suffix, new_suffix;
1239   GTimeVal tv;
1240   IconThemeDirMtime *dir_mtime;
1241   GStatBuf stat_buf;
1242   
1243   priv = icon_theme->priv;
1244
1245   priv->all_icons = g_hash_table_new (g_str_hash, g_str_equal);
1246   
1247   if (priv->current_theme)
1248     insert_theme (icon_theme, priv->current_theme);
1249
1250   /* Always look in the "default" icon theme, and in a fallback theme */
1251   if (priv->fallback_theme)
1252     insert_theme (icon_theme, priv->fallback_theme);
1253   insert_theme (icon_theme, DEFAULT_THEME_NAME);
1254   priv->themes = g_list_reverse (priv->themes);
1255
1256
1257   priv->unthemed_icons = g_hash_table_new_full (g_str_hash, g_str_equal,
1258                                                 g_free, (GDestroyNotify)free_unthemed_icon);
1259
1260   for (base = 0; base < icon_theme->priv->search_path_len; base++)
1261     {
1262       dir = icon_theme->priv->search_path[base];
1263
1264       dir_mtime = g_slice_new (IconThemeDirMtime);
1265       priv->dir_mtimes = g_list_append (priv->dir_mtimes, dir_mtime);
1266       
1267       dir_mtime->dir = g_strdup (dir);
1268       dir_mtime->mtime = 0;
1269       dir_mtime->cache = NULL;
1270
1271       if (g_stat (dir, &stat_buf) != 0 || !S_ISDIR (stat_buf.st_mode))
1272         continue;
1273       dir_mtime->mtime = stat_buf.st_mtime;
1274
1275       dir_mtime->cache = _gtk_icon_cache_new_for_path (dir);
1276       if (dir_mtime->cache != NULL)
1277         continue;
1278
1279       gdir = g_dir_open (dir, 0, NULL);
1280       if (gdir == NULL)
1281         continue;
1282
1283       while ((file = g_dir_read_name (gdir)))
1284         {
1285           new_suffix = suffix_from_name (file);
1286           
1287           if (new_suffix != ICON_SUFFIX_NONE)
1288             {
1289               char *abs_file;
1290               char *base_name;
1291
1292               abs_file = g_build_filename (dir, file, NULL);
1293               base_name = strip_suffix (file);
1294
1295               if ((unthemed_icon = g_hash_table_lookup (priv->unthemed_icons,
1296                                                         base_name)))
1297                 {
1298                   if (new_suffix == ICON_SUFFIX_SVG)
1299                     {
1300                       if (unthemed_icon->svg_filename)
1301                         g_free (abs_file);
1302                       else
1303                         unthemed_icon->svg_filename = abs_file;
1304                     }
1305                   else
1306                     {
1307                       if (unthemed_icon->no_svg_filename)
1308                         {
1309                           old_suffix = suffix_from_name (unthemed_icon->no_svg_filename);
1310                           if (new_suffix > old_suffix)
1311                             {
1312                               g_free (unthemed_icon->no_svg_filename);
1313                               unthemed_icon->no_svg_filename = abs_file;                              
1314                             }
1315                           else
1316                             g_free (abs_file);
1317                         }
1318                       else
1319                         unthemed_icon->no_svg_filename = abs_file;                            
1320                     }
1321
1322                   g_free (base_name);
1323                 }
1324               else
1325                 {
1326                   unthemed_icon = g_slice_new0 (UnthemedIcon);
1327                   
1328                   if (new_suffix == ICON_SUFFIX_SVG)
1329                     unthemed_icon->svg_filename = abs_file;
1330                   else
1331                     unthemed_icon->no_svg_filename = abs_file;
1332
1333                   /* takes ownership of base_name */
1334                   g_hash_table_replace (priv->unthemed_icons,
1335                                         base_name,
1336                                         unthemed_icon);
1337                   g_hash_table_insert (priv->all_icons,
1338                                        base_name, NULL);
1339                 }
1340             }
1341         }
1342       g_dir_close (gdir);
1343     }
1344
1345   priv->themes_valid = TRUE;
1346   
1347   g_get_current_time(&tv);
1348   priv->last_stat_time = tv.tv_sec;
1349 }
1350
1351 void
1352 _gtk_icon_theme_ensure_builtin_cache (void)
1353 {
1354   static gboolean initialized = FALSE;
1355   IconThemeDir *dir;
1356   static IconThemeDir dirs[5] = 
1357     {
1358       { ICON_THEME_DIR_THRESHOLD, 0, 16, 16, 16, 2, NULL, "16", -1, NULL, NULL, NULL },
1359       { ICON_THEME_DIR_THRESHOLD, 0, 20, 20, 20, 2, NULL, "20", -1,  NULL, NULL, NULL },
1360       { ICON_THEME_DIR_THRESHOLD, 0, 24, 24, 24, 2, NULL, "24", -1, NULL, NULL, NULL },
1361       { ICON_THEME_DIR_THRESHOLD, 0, 32, 32, 32, 2, NULL, "32", -1, NULL, NULL, NULL },
1362       { ICON_THEME_DIR_THRESHOLD, 0, 48, 48, 48, 2, NULL, "48", -1, NULL, NULL, NULL }
1363     };
1364   gint i;
1365
1366   if (!initialized)
1367     {
1368       initialized = TRUE;
1369
1370       _builtin_cache = _gtk_icon_cache_new ((gchar *)builtin_icons);
1371
1372       for (i = 0; i < G_N_ELEMENTS (dirs); i++)
1373         {
1374           dir = &(dirs[i]);
1375           dir->cache = _gtk_icon_cache_ref (_builtin_cache);
1376           dir->subdir_index = _gtk_icon_cache_get_directory_index (dir->cache, dir->subdir);
1377
1378           builtin_dirs = g_list_append (builtin_dirs, dir);
1379         }
1380     }
1381 }
1382
1383 static void
1384 ensure_valid_themes (GtkIconTheme *icon_theme)
1385 {
1386   GtkIconThemePrivate *priv = icon_theme->priv;
1387   GTimeVal tv;
1388   gboolean was_valid = priv->themes_valid;
1389
1390   if (priv->loading_themes)
1391     return;
1392   priv->loading_themes = TRUE;
1393
1394   _gtk_icon_theme_ensure_builtin_cache ();
1395
1396   if (priv->themes_valid)
1397     {
1398       g_get_current_time (&tv);
1399
1400       if (ABS (tv.tv_sec - priv->last_stat_time) > 5 &&
1401           rescan_themes (icon_theme))
1402         blow_themes (icon_theme);
1403     }
1404   
1405   if (!priv->themes_valid)
1406     {
1407       load_themes (icon_theme);
1408
1409       if (was_valid)
1410         {
1411           g_signal_emit (icon_theme, signal_changed, 0);
1412         }
1413     }
1414
1415   priv->loading_themes = FALSE;
1416 }
1417
1418 /* The LRU cache is a short list of IconInfos that are kept
1419    alive even though their IconInfo would otherwise have
1420    been freed, so that we can avoid reloading these
1421    constantly.
1422    We put infos on the lru list when nothing otherwise
1423    references the info. So, when we get a cache hit
1424    we remove it from the list, and when the proxy
1425    pixmap is released we put it on the list.
1426 */
1427
1428 static void
1429 ensure_lru_cache_space (GtkIconTheme *icon_theme)
1430 {
1431   GtkIconThemePrivate *priv = icon_theme->priv;
1432   GList *l;
1433
1434   /* Remove last item if LRU full */
1435   l = g_list_nth (priv->info_cache_lru, INFO_CACHE_LRU_SIZE - 1);
1436   if (l)
1437     {
1438       GtkIconInfo *icon_info = l->data;
1439
1440       DEBUG_CACHE (("removing (due to out of space) %p (%s %d 0x%x) from LRU cache (cache size %d)\n",
1441                     icon_info,
1442                     g_strjoinv (",", icon_info->key.icon_names),
1443                     icon_info->key.size, icon_info->key.flags,
1444                     g_list_length (priv->info_cache_lru)));
1445
1446       priv->info_cache_lru = g_list_delete_link (priv->info_cache_lru, l);
1447       g_object_unref (icon_info);
1448     }
1449 }
1450
1451 static void
1452 add_to_lru_cache (GtkIconTheme *icon_theme,
1453                   GtkIconInfo *icon_info)
1454 {
1455   GtkIconThemePrivate *priv = icon_theme->priv;
1456
1457   DEBUG_CACHE (("adding  %p (%s %d 0x%x) to LRU cache (cache size %d)\n",
1458                 icon_info,
1459                 g_strjoinv (",", icon_info->key.icon_names),
1460                 icon_info->key.size, icon_info->key.flags,
1461                 g_list_length (priv->info_cache_lru)));
1462
1463   g_assert (g_list_find (priv->info_cache_lru, icon_info) == NULL);
1464
1465   ensure_lru_cache_space (icon_theme);
1466   /* prepend new info to LRU */
1467   priv->info_cache_lru = g_list_prepend (priv->info_cache_lru,
1468                                          g_object_ref (icon_info));
1469 }
1470
1471 static void
1472 ensure_in_lru_cache (GtkIconTheme *icon_theme,
1473                      GtkIconInfo *icon_info)
1474 {
1475   GtkIconThemePrivate *priv = icon_theme->priv;
1476   GList *l;
1477
1478   l = g_list_find (priv->info_cache_lru, icon_info);
1479   if (l)
1480     {
1481       /* Move to front of LRU if already in it */
1482       priv->info_cache_lru = g_list_remove_link (priv->info_cache_lru, l);
1483       priv->info_cache_lru = g_list_concat (l, priv->info_cache_lru);
1484     }
1485   else
1486     add_to_lru_cache (icon_theme, icon_info);
1487 }
1488
1489 static void
1490 remove_from_lru_cache (GtkIconTheme *icon_theme,
1491                        GtkIconInfo *icon_info)
1492 {
1493   GtkIconThemePrivate *priv = icon_theme->priv;
1494   if (g_list_find (priv->info_cache_lru, icon_info))
1495     {
1496       DEBUG_CACHE (("removing %p (%s %d 0x%x) from LRU cache (cache size %d)\n",
1497                     icon_info,
1498                     g_strjoinv (",", icon_info->key.icon_names),
1499                     icon_info->key.size, icon_info->key.flags,
1500                     g_list_length (priv->info_cache_lru)));
1501
1502       priv->info_cache_lru = g_list_remove (priv->info_cache_lru, icon_info);
1503       g_object_unref (icon_info);
1504     }
1505 }
1506
1507 static SymbolicPixbufCache *
1508 symbolic_pixbuf_cache_new (GdkPixbuf *pixbuf,
1509                            const GdkRGBA  *fg,
1510                            const GdkRGBA  *success_color,
1511                            const GdkRGBA  *warning_color,
1512                            const GdkRGBA  *error_color,
1513                            SymbolicPixbufCache *next)
1514 {
1515   SymbolicPixbufCache *cache;
1516
1517   cache = g_new0 (SymbolicPixbufCache, 1);
1518   cache->pixbuf = g_object_ref (pixbuf);
1519   if (fg)
1520     cache->fg = *fg;
1521   if (success_color)
1522     cache->success_color = *success_color;
1523   if (warning_color)
1524     cache->warning_color = *warning_color;
1525   if (error_color)
1526     cache->error_color = *error_color;
1527   cache->next = next;
1528   return cache;
1529 }
1530
1531 static gboolean
1532 rgba_matches (const GdkRGBA  *a, const GdkRGBA  *b)
1533 {
1534   GdkRGBA transparent = { 0 };
1535
1536   /* For matching we treat unset colors as transparent rather
1537      than default, which works as well, because transparent
1538      will never be used for real symbolic icon colors */
1539   if (a == NULL)
1540     a = &transparent;
1541
1542   return
1543     fabs(a->red - b->red) < 0.0001 &&
1544     fabs(a->green - b->green) < 0.0001 &&
1545     fabs(a->blue - b->blue) < 0.0001 &&
1546     fabs(a->alpha - b->alpha) < 0.0001;
1547 }
1548
1549 static SymbolicPixbufCache *
1550 symbolic_pixbuf_cache_matches (SymbolicPixbufCache *cache,
1551                                const GdkRGBA  *fg,
1552                                const GdkRGBA  *success_color,
1553                                const GdkRGBA  *warning_color,
1554                                const GdkRGBA  *error_color)
1555 {
1556   while (cache != NULL)
1557     {
1558       if (rgba_matches (fg, &cache->fg) &&
1559           rgba_matches (success_color, &cache->success_color) &&
1560           rgba_matches (warning_color, &cache->warning_color) &&
1561           rgba_matches (error_color, &cache->error_color))
1562         return cache;
1563
1564       cache = cache->next;
1565     }
1566
1567   return NULL;
1568 }
1569
1570 static void
1571 symbolic_pixbuf_cache_free (SymbolicPixbufCache *cache)
1572 {
1573   SymbolicPixbufCache *next;
1574
1575   while (cache != NULL)
1576     {
1577       next = cache->next;
1578       g_object_unref (cache->pixbuf);
1579       g_free (cache);
1580
1581       cache = next;
1582     }
1583 }
1584
1585 static GtkIconInfo *
1586 choose_icon (GtkIconTheme       *icon_theme,
1587              const gchar        *icon_names[],
1588              gint                size,
1589              GtkIconLookupFlags  flags)
1590 {
1591   GtkIconThemePrivate *priv;
1592   GList *l;
1593   GtkIconInfo *icon_info = NULL;
1594   UnthemedIcon *unthemed_icon = NULL;
1595   gboolean allow_svg;
1596   gboolean use_builtin;
1597   gint i;
1598   IconInfoKey key;
1599
1600   priv = icon_theme->priv;
1601
1602   ensure_valid_themes (icon_theme);
1603
1604   key.icon_names = (char **)icon_names;
1605   key.size = size;
1606   key.flags = flags;
1607
1608   icon_info = g_hash_table_lookup (priv->info_cache, &key);
1609   if (icon_info != NULL)
1610     {
1611       DEBUG_CACHE (("cache hit %p (%s %d 0x%x) (cache size %d)\n",
1612                     icon_info,
1613                     g_strjoinv (",", icon_info->key.icon_names),
1614                     icon_info->key.size, icon_info->key.flags,
1615                     g_hash_table_size (priv->info_cache)));
1616
1617       icon_info = g_object_ref (icon_info);
1618       remove_from_lru_cache (icon_theme, icon_info);
1619
1620       return icon_info;
1621     }
1622
1623   if (flags & GTK_ICON_LOOKUP_NO_SVG)
1624     allow_svg = FALSE;
1625   else if (flags & GTK_ICON_LOOKUP_FORCE_SVG)
1626     allow_svg = TRUE;
1627   else
1628     allow_svg = priv->pixbuf_supports_svg;
1629
1630   use_builtin = flags & GTK_ICON_LOOKUP_USE_BUILTIN;
1631
1632   /* for symbolic icons, do a search in all registered themes first;
1633    * a theme that inherits them from a parent theme might provide
1634    * an alternative highcolor version, but still expect the symbolic icon
1635    * to show up instead.
1636    */
1637   if (icon_names[0] &&
1638       g_str_has_suffix (icon_names[0], "-symbolic"))
1639     {
1640       for (l = priv->themes; l; l = l->next)
1641         {
1642           IconTheme *theme = l->data;
1643           icon_info = theme_lookup_icon (theme, icon_names[0], size, allow_svg, use_builtin);
1644           if (icon_info)
1645             goto out;
1646         }
1647     }
1648
1649   for (l = priv->themes; l; l = l->next)
1650     {
1651       IconTheme *theme = l->data;
1652       
1653       for (i = 0; icon_names[i]; i++)
1654         {
1655           icon_info = theme_lookup_icon (theme, icon_names[i], size, allow_svg, use_builtin);
1656           if (icon_info)
1657             goto out;
1658         }
1659     }
1660
1661   for (i = 0; icon_names[i]; i++)
1662     {
1663       unthemed_icon = g_hash_table_lookup (priv->unthemed_icons, icon_names[i]);
1664       if (unthemed_icon)
1665         break;
1666     }
1667 #ifdef G_OS_WIN32
1668   /* Still not found an icon, check if reference to a Win32 resource */
1669   if (!unthemed_icon)
1670     {
1671       gchar **resources;
1672       HICON hIcon = NULL;
1673       
1674       resources = g_strsplit (icon_names[0], ",", 0);
1675       if (resources[0])
1676         {
1677           wchar_t *wfile = g_utf8_to_utf16 (resources[0], -1, NULL, NULL, NULL);
1678           ExtractIconExW (wfile, resources[1] ? atoi (resources[1]) : 0, &hIcon, NULL, 1);
1679           g_free (wfile);
1680         }
1681       
1682       if (hIcon)
1683         {
1684           icon_info = icon_info_new ();
1685           icon_info->cache_pixbuf = gdk_win32_icon_to_pixbuf_libgtk_only (hIcon);
1686           DestroyIcon (hIcon);
1687           icon_info->dir_type = ICON_THEME_DIR_UNTHEMED;
1688           icon_info->dir_size = size;
1689         }
1690       g_strfreev (resources);
1691     }
1692 #endif
1693
1694   if (unthemed_icon)
1695     {
1696       icon_info = icon_info_new ();
1697
1698       /* A SVG icon, when allowed, beats out a XPM icon, but not
1699        * a PNG icon
1700        */
1701       if (allow_svg &&
1702           unthemed_icon->svg_filename &&
1703           (!unthemed_icon->no_svg_filename ||
1704            suffix_from_name (unthemed_icon->no_svg_filename) != ICON_SUFFIX_PNG))
1705         icon_info->filename = g_strdup (unthemed_icon->svg_filename);
1706       else if (unthemed_icon->no_svg_filename)
1707         icon_info->filename = g_strdup (unthemed_icon->no_svg_filename);
1708
1709       icon_info->icon_file = g_file_new_for_path (icon_info->filename);
1710
1711       icon_info->dir_type = ICON_THEME_DIR_UNTHEMED;
1712       icon_info->dir_size = size;
1713     }
1714
1715  out:
1716   if (icon_info)
1717     {
1718       icon_info->desired_size = size;
1719       icon_info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
1720
1721       icon_info->key.icon_names = g_strdupv ((char **)icon_names);
1722       icon_info->key.size = size;
1723       icon_info->key.flags = flags;
1724       icon_info->in_cache = icon_theme;
1725       DEBUG_CACHE (("adding %p (%s %d 0x%x) to cache (cache size %d)\n",
1726                     icon_info,
1727                     g_strjoinv (",", icon_info->key.icon_names),
1728                     icon_info->key.size, icon_info->key.flags,
1729                     g_hash_table_size (priv->info_cache)));
1730      g_hash_table_insert (priv->info_cache, &icon_info->key, icon_info);
1731     }
1732   else
1733     {
1734       static gboolean check_for_default_theme = TRUE;
1735       char *default_theme_path;
1736       gboolean found = FALSE;
1737       unsigned i;
1738
1739       if (check_for_default_theme)
1740         {
1741           check_for_default_theme = FALSE;
1742
1743           for (i = 0; !found && i < priv->search_path_len; i++)
1744             {
1745               default_theme_path = g_build_filename (priv->search_path[i],
1746                                                      DEFAULT_THEME_NAME,
1747                                                      "index.theme",
1748                                                      NULL);
1749               found = g_file_test (default_theme_path, G_FILE_TEST_IS_REGULAR);
1750               g_free (default_theme_path);
1751             }
1752
1753           if (!found)
1754             {
1755               g_warning ("Could not find the icon '%s'. The '%s' theme\n"
1756                          "was not found either, perhaps you need to install it.\n"
1757                          "You can get a copy from:\n"
1758                          "\t%s",
1759                          icon_names[0], DEFAULT_THEME_NAME, "http://icon-theme.freedesktop.org/releases");
1760             }
1761         }
1762     }
1763
1764   return icon_info;
1765 }
1766
1767
1768 /**
1769  * gtk_icon_theme_lookup_icon:
1770  * @icon_theme: a #GtkIconTheme
1771  * @icon_name: the name of the icon to lookup
1772  * @size: desired icon size
1773  * @flags: flags modifying the behavior of the icon lookup
1774  * 
1775  * Looks up a named icon and returns a structure containing
1776  * information such as the filename of the icon. The icon
1777  * can then be rendered into a pixbuf using
1778  * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
1779  * combines these two steps if all you need is the pixbuf.)
1780  * 
1781  * Return value: (transfer full): a #GtkIconInfo object containing information
1782  * about the icon, or %NULL if the icon wasn't found.
1783  *
1784  * Since: 2.4
1785  */
1786 GtkIconInfo *
1787 gtk_icon_theme_lookup_icon (GtkIconTheme       *icon_theme,
1788                             const gchar        *icon_name,
1789                             gint                size,
1790                             GtkIconLookupFlags  flags)
1791 {
1792   GtkIconInfo *info;
1793
1794   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1795   g_return_val_if_fail (icon_name != NULL, NULL);
1796   g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1797                         (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1798
1799   GTK_NOTE (ICONTHEME, 
1800             g_print ("gtk_icon_theme_lookup_icon %s\n", icon_name));
1801
1802   if (flags & GTK_ICON_LOOKUP_GENERIC_FALLBACK)
1803     {
1804       gchar **names;
1805       gint dashes, i;
1806       gchar *p;
1807  
1808       dashes = 0;
1809       for (p = (gchar *) icon_name; *p; p++)
1810         if (*p == '-')
1811           dashes++;
1812
1813       names = g_new (gchar *, dashes + 2);
1814       names[0] = g_strdup (icon_name);
1815       for (i = 1; i <= dashes; i++)
1816         {
1817           names[i] = g_strdup (names[i - 1]);
1818           p = strrchr (names[i], '-');
1819           *p = '\0';
1820         }
1821       names[dashes + 1] = NULL;
1822    
1823       info = choose_icon (icon_theme, (const gchar **) names, size, flags);
1824       
1825       g_strfreev (names);
1826     }
1827   else 
1828     {
1829       const gchar *names[2];
1830       
1831       names[0] = icon_name;
1832       names[1] = NULL;
1833
1834       info = choose_icon (icon_theme, names, size, flags);
1835     }
1836
1837   return info;
1838 }
1839
1840 /**
1841  * gtk_icon_theme_choose_icon:
1842  * @icon_theme: a #GtkIconTheme
1843  * @icon_names: (array zero-terminated=1): %NULL-terminated array of
1844  *     icon names to lookup
1845  * @size: desired icon size
1846  * @flags: flags modifying the behavior of the icon lookup
1847  * 
1848  * Looks up a named icon and returns a structure containing
1849  * information such as the filename of the icon. The icon
1850  * can then be rendered into a pixbuf using
1851  * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
1852  * combines these two steps if all you need is the pixbuf.)
1853  *
1854  * If @icon_names contains more than one name, this function 
1855  * tries them all in the given order before falling back to 
1856  * inherited icon themes.
1857  * 
1858  * Return value: (transfer full): a #GtkIconInfo object containing information
1859  * about the icon, or %NULL if the icon wasn't found.
1860  *
1861  * Since: 2.12
1862  */
1863 GtkIconInfo *
1864 gtk_icon_theme_choose_icon (GtkIconTheme       *icon_theme,
1865                             const gchar        *icon_names[],
1866                             gint                size,
1867                             GtkIconLookupFlags  flags)
1868 {
1869   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1870   g_return_val_if_fail (icon_names != NULL, NULL);
1871   g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1872                         (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1873
1874   return choose_icon (icon_theme, icon_names, size, flags);
1875 }
1876
1877 /* Error quark */
1878 GQuark
1879 gtk_icon_theme_error_quark (void)
1880 {
1881   return g_quark_from_static_string ("gtk-icon-theme-error-quark");
1882 }
1883
1884 /**
1885  * gtk_icon_theme_load_icon:
1886  * @icon_theme: a #GtkIconTheme
1887  * @icon_name: the name of the icon to lookup
1888  * @size: the desired icon size. The resulting icon may not be
1889  *     exactly this size; see gtk_icon_info_load_icon().
1890  * @flags: flags modifying the behavior of the icon lookup
1891  * @error: (allow-none): Location to store error information on failure,
1892  *     or %NULL.
1893  *
1894  * Looks up an icon in an icon theme, scales it to the given size
1895  * and renders it into a pixbuf. This is a convenience function;
1896  * if more details about the icon are needed, use
1897  * gtk_icon_theme_lookup_icon() followed by gtk_icon_info_load_icon().
1898  *
1899  * Note that you probably want to listen for icon theme changes and
1900  * update the icon. This is usually done by connecting to the
1901  * GtkWidget::style-set signal. If for some reason you do not want to
1902  * update the icon when the icon theme changes, you should consider
1903  * using gdk_pixbuf_copy() to make a private copy of the pixbuf
1904  * returned by this function. Otherwise GTK+ may need to keep the old
1905  * icon theme loaded, which would be a waste of memory.
1906  *
1907  * Return value: (transfer full): the rendered icon; this may be a
1908  *     newly created icon or a new reference to an internal icon, so
1909  *     you must not modify the icon. Use g_object_unref() to release
1910  *     your reference to the icon. %NULL if the icon isn't found.
1911  *
1912  * Since: 2.4
1913  **/
1914 GdkPixbuf *
1915 gtk_icon_theme_load_icon (GtkIconTheme         *icon_theme,
1916                           const gchar          *icon_name,
1917                           gint                  size,
1918                           GtkIconLookupFlags    flags,
1919                           GError              **error)
1920 {
1921   GtkIconInfo *icon_info;
1922   GdkPixbuf *pixbuf = NULL;
1923   
1924   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1925   g_return_val_if_fail (icon_name != NULL, NULL);
1926   g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1927                         (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1928   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1929   
1930   icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size,
1931                                           flags | GTK_ICON_LOOKUP_USE_BUILTIN);
1932   if (!icon_info)
1933     {
1934       g_set_error (error, GTK_ICON_THEME_ERROR,  GTK_ICON_THEME_NOT_FOUND,
1935                    _("Icon '%s' not present in theme"), icon_name);
1936       return NULL;
1937     }
1938
1939   pixbuf = gtk_icon_info_load_icon (icon_info, error);
1940   g_object_unref (icon_info);
1941
1942   return pixbuf;
1943 }
1944
1945 /**
1946  * gtk_icon_theme_has_icon:
1947  * @icon_theme: a #GtkIconTheme
1948  * @icon_name: the name of an icon
1949  * 
1950  * Checks whether an icon theme includes an icon
1951  * for a particular name.
1952  * 
1953  * Return value: %TRUE if @icon_theme includes an
1954  *  icon for @icon_name.
1955  *
1956  * Since: 2.4
1957  **/
1958 gboolean 
1959 gtk_icon_theme_has_icon (GtkIconTheme *icon_theme,
1960                          const char   *icon_name)
1961 {
1962   GtkIconThemePrivate *priv;
1963   GList *l;
1964
1965   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
1966   g_return_val_if_fail (icon_name != NULL, FALSE);
1967
1968   priv = icon_theme->priv;
1969   
1970   ensure_valid_themes (icon_theme);
1971
1972   for (l = priv->dir_mtimes; l; l = l->next)
1973     {
1974       IconThemeDirMtime *dir_mtime = l->data;
1975       GtkIconCache *cache = dir_mtime->cache;
1976       
1977       if (cache && _gtk_icon_cache_has_icon (cache, icon_name))
1978         return TRUE;
1979     }
1980
1981   if (g_hash_table_lookup_extended (priv->all_icons,
1982                                     icon_name, NULL, NULL))
1983     return TRUE;
1984
1985   if (_builtin_cache &&
1986       _gtk_icon_cache_has_icon (_builtin_cache, icon_name))
1987     return TRUE;
1988
1989   if (icon_theme_builtin_icons &&
1990       g_hash_table_lookup_extended (icon_theme_builtin_icons,
1991                                     icon_name, NULL, NULL))
1992     return TRUE;
1993
1994   return FALSE;
1995 }
1996
1997 static void
1998 add_size (gpointer  key,
1999           gpointer  value,
2000           gpointer  user_data)
2001 {
2002   gint **res_p = user_data;
2003
2004   **res_p = GPOINTER_TO_INT (key);
2005
2006   (*res_p)++;
2007 }
2008
2009 /**
2010  * gtk_icon_theme_get_icon_sizes:
2011  * @icon_theme: a #GtkIconTheme
2012  * @icon_name: the name of an icon
2013  * 
2014  * Returns an array of integers describing the sizes at which
2015  * the icon is available without scaling. A size of -1 means 
2016  * that the icon is available in a scalable format. The array 
2017  * is zero-terminated.
2018  * 
2019  * Return value: (array zero-terminated=1): An newly allocated array
2020  * describing the sizes at which the icon is available. The array
2021  * should be freed with g_free() when it is no longer needed.
2022  *
2023  * Since: 2.6
2024  **/
2025 gint *
2026 gtk_icon_theme_get_icon_sizes (GtkIconTheme *icon_theme,
2027                                const char   *icon_name)
2028 {
2029   GList *l, *d, *icons;
2030   GHashTable *sizes;
2031   gint *result, *r;
2032   guint suffix;  
2033   GtkIconThemePrivate *priv;
2034
2035   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2036   
2037   priv = icon_theme->priv;
2038
2039   ensure_valid_themes (icon_theme);
2040
2041   sizes = g_hash_table_new (g_direct_hash, g_direct_equal);
2042
2043   for (l = priv->themes; l; l = l->next)
2044     {
2045       IconTheme *theme = l->data;
2046       for (d = theme->dirs; d; d = d->next)
2047         {
2048           IconThemeDir *dir = d->data;
2049
2050           if (dir->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir->size), NULL, NULL))
2051             continue;
2052
2053           suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);      
2054           if (suffix != ICON_SUFFIX_NONE)
2055             {
2056               if (suffix == ICON_SUFFIX_SVG)
2057                 g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL);
2058               else
2059                 g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL);
2060             }
2061         }
2062     }
2063
2064   for (d = builtin_dirs; d; d = d->next)
2065     {
2066       IconThemeDir *dir = d->data;
2067       
2068       if (dir->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir->size), NULL, NULL))
2069         continue;
2070
2071       suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);          
2072       if (suffix != ICON_SUFFIX_NONE)
2073         {
2074           if (suffix == ICON_SUFFIX_SVG)
2075             g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL);
2076           else
2077             g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL);
2078         }
2079     }
2080
2081   if (icon_theme_builtin_icons)
2082     {
2083       icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
2084       
2085       while (icons)
2086         {
2087           BuiltinIcon *icon = icons->data;
2088         
2089           g_hash_table_insert (sizes, GINT_TO_POINTER (icon->size), NULL);
2090           icons = icons->next;
2091         }      
2092     }
2093
2094   r = result = g_new0 (gint, g_hash_table_size (sizes) + 1);
2095
2096   g_hash_table_foreach (sizes, add_size, &r);
2097   g_hash_table_destroy (sizes);
2098   
2099   return result;
2100 }
2101
2102 static void
2103 add_key_to_hash (gpointer  key,
2104                  gpointer  value,
2105                  gpointer  user_data)
2106 {
2107   GHashTable *hash = user_data;
2108
2109   g_hash_table_insert (hash, key, NULL);
2110 }
2111
2112 static void
2113 add_key_to_list (gpointer  key,
2114                  gpointer  value,
2115                  gpointer  user_data)
2116 {
2117   GList **list = user_data;
2118
2119   *list = g_list_prepend (*list, g_strdup (key));
2120 }
2121
2122 /**
2123  * gtk_icon_theme_list_icons:
2124  * @icon_theme: a #GtkIconTheme
2125  * @context: (allow-none): a string identifying a particular type of
2126  *           icon, or %NULL to list all icons.
2127  * 
2128  * Lists the icons in the current icon theme. Only a subset
2129  * of the icons can be listed by providing a context string.
2130  * The set of values for the context string is system dependent,
2131  * but will typically include such values as "Applications" and
2132  * "MimeTypes".
2133  *
2134  * Return value: (element-type utf8) (transfer full): a #GList list
2135  *  holding the names of all the icons in the theme. You must first
2136  *  free each element in the list with g_free(), then free the list
2137  *  itself with g_list_free().
2138  *
2139  * Since: 2.4
2140  **/
2141 GList *
2142 gtk_icon_theme_list_icons (GtkIconTheme *icon_theme,
2143                            const char   *context)
2144 {
2145   GtkIconThemePrivate *priv;
2146   GHashTable *icons;
2147   GList *list, *l;
2148   GQuark context_quark;
2149   
2150   priv = icon_theme->priv;
2151   
2152   ensure_valid_themes (icon_theme);
2153
2154   if (context)
2155     {
2156       context_quark = g_quark_try_string (context);
2157
2158       if (!context_quark)
2159         return NULL;
2160     }
2161   else
2162     context_quark = 0;
2163
2164   icons = g_hash_table_new (g_str_hash, g_str_equal);
2165   
2166   l = priv->themes;
2167   while (l != NULL)
2168     {
2169       theme_list_icons (l->data, icons, context_quark);
2170       l = l->next;
2171     }
2172
2173   if (context_quark == 0)
2174     g_hash_table_foreach (priv->unthemed_icons,
2175                           add_key_to_hash,
2176                           icons);
2177
2178   list = NULL;
2179   
2180   g_hash_table_foreach (icons,
2181                         add_key_to_list,
2182                         &list);
2183
2184   g_hash_table_destroy (icons);
2185   
2186   return list;
2187 }
2188
2189 /**
2190  * gtk_icon_theme_list_contexts:
2191  * @icon_theme: a #GtkIconTheme
2192  *
2193  * Gets the list of contexts available within the current
2194  * hierarchy of icon themes
2195  *
2196  * Return value: (element-type utf8) (transfer full): a #GList list
2197  *  holding the names of all the contexts in the theme. You must first
2198  *  free each element in the list with g_free(), then free the list
2199  *  itself with g_list_free().
2200  *
2201  * Since: 2.12
2202  **/
2203 GList *
2204 gtk_icon_theme_list_contexts (GtkIconTheme *icon_theme)
2205 {
2206   GtkIconThemePrivate *priv;
2207   GHashTable *contexts;
2208   GList *list, *l;
2209
2210   priv = icon_theme->priv;
2211   
2212   ensure_valid_themes (icon_theme);
2213
2214   contexts = g_hash_table_new (g_str_hash, g_str_equal);
2215
2216   l = priv->themes;
2217   while (l != NULL)
2218     {
2219       theme_list_contexts (l->data, contexts);
2220       l = l->next;
2221     }
2222
2223   list = NULL;
2224
2225   g_hash_table_foreach (contexts,
2226                         add_key_to_list,
2227                         &list);
2228
2229   g_hash_table_destroy (contexts);
2230
2231   return list;
2232 }
2233
2234 /**
2235  * gtk_icon_theme_get_example_icon_name:
2236  * @icon_theme: a #GtkIconTheme
2237  * 
2238  * Gets the name of an icon that is representative of the
2239  * current theme (for instance, to use when presenting
2240  * a list of themes to the user.)
2241  * 
2242  * Return value: the name of an example icon or %NULL.
2243  *  Free with g_free().
2244  *
2245  * Since: 2.4
2246  **/
2247 char *
2248 gtk_icon_theme_get_example_icon_name (GtkIconTheme *icon_theme)
2249 {
2250   GtkIconThemePrivate *priv;
2251   GList *l;
2252   IconTheme *theme;
2253
2254   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2255   
2256   priv = icon_theme->priv;
2257   
2258   ensure_valid_themes (icon_theme);
2259
2260   l = priv->themes;
2261   while (l != NULL)
2262     {
2263       theme = l->data;
2264       if (theme->example)
2265         return g_strdup (theme->example);
2266       
2267       l = l->next;
2268     }
2269   
2270   return NULL;
2271 }
2272
2273
2274 static gboolean
2275 rescan_themes (GtkIconTheme *icon_theme)
2276 {
2277   GtkIconThemePrivate *priv;
2278   IconThemeDirMtime *dir_mtime;
2279   GList *d;
2280   int stat_res;
2281   GStatBuf stat_buf;
2282   GTimeVal tv;
2283
2284   priv = icon_theme->priv;
2285
2286   for (d = priv->dir_mtimes; d != NULL; d = d->next)
2287     {
2288       dir_mtime = d->data;
2289
2290       stat_res = g_stat (dir_mtime->dir, &stat_buf);
2291
2292       /* dir mtime didn't change */
2293       if (stat_res == 0 &&
2294           S_ISDIR (stat_buf.st_mode) &&
2295           dir_mtime->mtime == stat_buf.st_mtime)
2296         continue;
2297       /* didn't exist before, and still doesn't */
2298       if (dir_mtime->mtime == 0 &&
2299           (stat_res != 0 || !S_ISDIR (stat_buf.st_mode)))
2300         continue;
2301
2302       return TRUE;
2303     }
2304
2305   g_get_current_time (&tv);
2306   priv->last_stat_time = tv.tv_sec;
2307
2308   return FALSE;
2309 }
2310
2311 /**
2312  * gtk_icon_theme_rescan_if_needed:
2313  * @icon_theme: a #GtkIconTheme
2314  * 
2315  * Checks to see if the icon theme has changed; if it has, any
2316  * currently cached information is discarded and will be reloaded
2317  * next time @icon_theme is accessed.
2318  * 
2319  * Return value: %TRUE if the icon theme has changed and needed
2320  *   to be reloaded.
2321  *
2322  * Since: 2.4
2323  **/
2324 gboolean
2325 gtk_icon_theme_rescan_if_needed (GtkIconTheme *icon_theme)
2326 {
2327   gboolean retval;
2328
2329   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
2330
2331   retval = rescan_themes (icon_theme);
2332   if (retval)
2333       do_theme_change (icon_theme);
2334
2335   return retval;
2336 }
2337
2338 static void
2339 theme_destroy (IconTheme *theme)
2340 {
2341   g_free (theme->display_name);
2342   g_free (theme->comment);
2343   g_free (theme->name);
2344   g_free (theme->example);
2345
2346   g_list_free_full (theme->dirs, (GDestroyNotify) theme_dir_destroy);
2347   
2348   g_free (theme);
2349 }
2350
2351 static void
2352 theme_dir_destroy (IconThemeDir *dir)
2353 {
2354   if (dir->cache)
2355       _gtk_icon_cache_unref (dir->cache);
2356   else
2357     g_hash_table_destroy (dir->icons);
2358   
2359   if (dir->icon_data)
2360     g_hash_table_destroy (dir->icon_data);
2361   g_free (dir->dir);
2362   g_free (dir->subdir);
2363   g_free (dir);
2364 }
2365
2366 static int
2367 theme_dir_size_difference (IconThemeDir *dir, int size, gboolean *smaller)
2368 {
2369   int min, max;
2370   switch (dir->type)
2371     {
2372     case ICON_THEME_DIR_FIXED:
2373       *smaller = size < dir->size;
2374       return abs (size - dir->size);
2375       break;
2376     case ICON_THEME_DIR_SCALABLE:
2377       *smaller = size < dir->min_size;
2378       if (size < dir->min_size)
2379         return dir->min_size - size;
2380       if (size > dir->max_size)
2381         return size - dir->max_size;
2382       return 0;
2383       break;
2384     case ICON_THEME_DIR_THRESHOLD:
2385       min = dir->size - dir->threshold;
2386       max = dir->size + dir->threshold;
2387       *smaller = size < min;
2388       if (size < min)
2389         return min - size;
2390       if (size > max)
2391         return size - max;
2392       return 0;
2393       break;
2394     case ICON_THEME_DIR_UNTHEMED:
2395       g_assert_not_reached ();
2396       break;
2397     }
2398   g_assert_not_reached ();
2399   return 1000;
2400 }
2401
2402 static const char *
2403 string_from_suffix (IconSuffix suffix)
2404 {
2405   switch (suffix)
2406     {
2407     case ICON_SUFFIX_XPM:
2408       return ".xpm";
2409     case ICON_SUFFIX_SVG:
2410       return ".svg";
2411     case ICON_SUFFIX_PNG:
2412       return ".png";
2413     default:
2414       g_assert_not_reached();
2415     }
2416   return NULL;
2417 }
2418
2419 static IconSuffix
2420 suffix_from_name (const char *name)
2421 {
2422   IconSuffix retval;
2423
2424   if (g_str_has_suffix (name, ".png"))
2425     retval = ICON_SUFFIX_PNG;
2426   else if (g_str_has_suffix (name, ".svg"))
2427     retval = ICON_SUFFIX_SVG;
2428   else if (g_str_has_suffix (name, ".xpm"))
2429     retval = ICON_SUFFIX_XPM;
2430   else
2431     retval = ICON_SUFFIX_NONE;
2432
2433   return retval;
2434 }
2435
2436 static IconSuffix
2437 best_suffix (IconSuffix suffix,
2438              gboolean   allow_svg)
2439 {
2440   if ((suffix & ICON_SUFFIX_PNG) != 0)
2441     return ICON_SUFFIX_PNG;
2442   else if (allow_svg && ((suffix & ICON_SUFFIX_SVG) != 0))
2443     return ICON_SUFFIX_SVG;
2444   else if ((suffix & ICON_SUFFIX_XPM) != 0)
2445     return ICON_SUFFIX_XPM;
2446   else
2447     return ICON_SUFFIX_NONE;
2448 }
2449
2450
2451 static IconSuffix
2452 theme_dir_get_icon_suffix (IconThemeDir *dir,
2453                            const gchar  *icon_name,
2454                            gboolean     *has_icon_file)
2455 {
2456   IconSuffix suffix;
2457
2458   if (dir->cache)
2459     {
2460       suffix = (IconSuffix)_gtk_icon_cache_get_icon_flags (dir->cache,
2461                                                            icon_name,
2462                                                            dir->subdir_index);
2463
2464       if (has_icon_file)
2465         *has_icon_file = suffix & HAS_ICON_FILE;
2466
2467       suffix = suffix & ~HAS_ICON_FILE;
2468     }
2469   else
2470     suffix = GPOINTER_TO_UINT (g_hash_table_lookup (dir->icons, icon_name));
2471
2472   GTK_NOTE (ICONTHEME, 
2473             g_print ("get_icon_suffix%s %u\n", dir->cache ? " (cached)" : "", suffix));
2474
2475   return suffix;
2476 }
2477
2478 static GtkIconInfo *
2479 theme_lookup_icon (IconTheme          *theme,
2480                    const char         *icon_name,
2481                    int                 size,
2482                    gboolean            allow_svg,
2483                    gboolean            use_builtin)
2484 {
2485   GList *dirs, *l;
2486   IconThemeDir *dir, *min_dir;
2487   char *file;
2488   int min_difference, difference;
2489   BuiltinIcon *closest_builtin = NULL;
2490   gboolean smaller, has_larger, match;
2491   IconSuffix suffix;
2492
2493   min_difference = G_MAXINT;
2494   min_dir = NULL;
2495   has_larger = FALSE;
2496   match = FALSE;
2497
2498   /* Builtin icons are logically part of the default theme and
2499    * are searched before other subdirectories of the default theme.
2500    */
2501   if (use_builtin && strcmp (theme->name, DEFAULT_THEME_NAME) == 0)
2502     {
2503       closest_builtin = find_builtin_icon (icon_name, 
2504                                            size,
2505                                            &min_difference,
2506                                            &has_larger);
2507
2508       if (min_difference == 0)
2509         return icon_info_new_builtin (closest_builtin);
2510
2511       dirs = builtin_dirs;
2512     }
2513   else
2514     dirs = theme->dirs;
2515
2516   l = dirs;
2517   while (l != NULL)
2518     {
2519       dir = l->data;
2520
2521       GTK_NOTE (ICONTHEME,
2522                 g_print ("theme_lookup_icon dir %s\n", dir->dir));
2523       suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
2524       if (best_suffix (suffix, allow_svg) != ICON_SUFFIX_NONE)
2525         {
2526           difference = theme_dir_size_difference (dir, size, &smaller);
2527
2528           if (difference == 0)
2529             {
2530               if (dir->type == ICON_THEME_DIR_SCALABLE)
2531                 {
2532                   /* don't pick scalable if we already found
2533                    * a matching non-scalable dir
2534                    */
2535                   if (!match)
2536                     {
2537                       min_dir = dir;
2538                       break;
2539                     }
2540                 }
2541               else
2542                 {
2543                   /* for a matching non-scalable dir keep
2544                    * going and look for a closer match
2545                    */             
2546                   difference = abs (size - dir->size);
2547                   if (!match || difference < min_difference)
2548                     {
2549                       match = TRUE;
2550                       min_difference = difference;
2551                       min_dir = dir;
2552                     }
2553                   if (difference == 0)
2554                     break;
2555                 }
2556             } 
2557   
2558           if (!match)
2559             {
2560               if (!has_larger)
2561                 {
2562                   if (difference < min_difference || smaller)
2563                     {
2564                       min_difference = difference;
2565                       min_dir = dir;
2566                       has_larger = smaller;
2567                     }
2568                 }
2569               else
2570                 {
2571                   if (difference < min_difference && smaller)
2572                     {
2573                       min_difference = difference;
2574                       min_dir = dir;
2575                     }
2576                 }
2577             }
2578         }
2579
2580       l = l->next;
2581
2582       if (l == NULL && dirs == builtin_dirs)
2583         {
2584           dirs = theme->dirs;
2585           l = dirs;
2586         }
2587     }
2588
2589   if (min_dir)
2590     {
2591       GtkIconInfo *icon_info = icon_info_new ();
2592       gboolean has_icon_file = FALSE;
2593       
2594       suffix = theme_dir_get_icon_suffix (min_dir, icon_name, &has_icon_file);
2595       suffix = best_suffix (suffix, allow_svg);
2596       g_assert (suffix != ICON_SUFFIX_NONE);
2597       
2598       if (min_dir->dir)
2599         {
2600           file = g_strconcat (icon_name, string_from_suffix (suffix), NULL);
2601           icon_info->filename = g_build_filename (min_dir->dir, file, NULL);
2602           icon_info->icon_file = g_file_new_for_path (icon_info->filename);
2603           g_free (file);
2604         }
2605       else
2606         {
2607           icon_info->filename = NULL;
2608           icon_info->icon_file = NULL;
2609         }
2610       
2611       if (min_dir->icon_data != NULL)
2612         icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
2613
2614       if (icon_info->data == NULL && min_dir->cache != NULL)
2615         {
2616           icon_info->data = _gtk_icon_cache_get_icon_data (min_dir->cache, icon_name, min_dir->subdir_index);
2617           if (icon_info->data)
2618             {
2619               if (min_dir->icon_data == NULL)
2620                 min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2621                                                             g_free, (GDestroyNotify)icon_data_free);
2622
2623               g_hash_table_replace (min_dir->icon_data, g_strdup (icon_name), icon_info->data);
2624             }
2625         }
2626
2627       if (icon_info->data == NULL && has_icon_file)
2628         {
2629           gchar *icon_file_name, *icon_file_path;
2630
2631           icon_file_name = g_strconcat (icon_name, ".icon", NULL);
2632           icon_file_path = g_build_filename (min_dir->dir, icon_file_name, NULL);
2633
2634           if (g_file_test (icon_file_path, G_FILE_TEST_IS_REGULAR))
2635             {
2636               if (min_dir->icon_data == NULL)   
2637                 min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2638                                                             g_free, (GDestroyNotify)icon_data_free);
2639               load_icon_data (min_dir, icon_file_path, icon_file_name);
2640               
2641               icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
2642             }
2643           g_free (icon_file_name);
2644           g_free (icon_file_path);
2645         }
2646
2647       if (min_dir->cache)
2648         {
2649           icon_info->cache_pixbuf = _gtk_icon_cache_get_icon (min_dir->cache, icon_name,
2650                                                               min_dir->subdir_index);
2651         }
2652
2653       icon_info->dir_type = min_dir->type;
2654       icon_info->dir_size = min_dir->size;
2655       icon_info->threshold = min_dir->threshold;
2656       
2657       return icon_info;
2658     }
2659
2660   if (closest_builtin)
2661     return icon_info_new_builtin (closest_builtin);
2662   
2663   return NULL;
2664 }
2665
2666 static void
2667 theme_list_icons (IconTheme  *theme, 
2668                   GHashTable *icons,
2669                   GQuark      context)
2670 {
2671   GList *l = theme->dirs;
2672   IconThemeDir *dir;
2673   
2674   while (l != NULL)
2675     {
2676       dir = l->data;
2677
2678       if (context == dir->context ||
2679           context == 0)
2680         {
2681           if (dir->cache)
2682             {
2683               _gtk_icon_cache_add_icons (dir->cache,
2684                                          dir->subdir,
2685                                          icons);
2686                                          
2687             }
2688           else
2689             {
2690               g_hash_table_foreach (dir->icons,
2691                                     add_key_to_hash,
2692                                     icons);
2693             }
2694         }
2695       l = l->next;
2696     }
2697 }
2698
2699 static void
2700 theme_list_contexts (IconTheme  *theme, 
2701                      GHashTable *contexts)
2702 {
2703   GList *l = theme->dirs;
2704   IconThemeDir *dir;
2705   const char *context;
2706
2707   while (l != NULL)
2708     {
2709       dir = l->data;
2710
2711       context = g_quark_to_string (dir->context);
2712       g_hash_table_replace (contexts, (gpointer) context, NULL);
2713
2714       l = l->next;
2715     }
2716 }
2717
2718 static void
2719 load_icon_data (IconThemeDir *dir, const char *path, const char *name)
2720 {
2721   GKeyFile *icon_file;
2722   char *base_name;
2723   char **split;
2724   gsize length;
2725   char *str;
2726   char *split_point;
2727   int i;
2728   gint *ivalues;
2729   GError *error = NULL;
2730   
2731   GtkIconData *data;
2732
2733   icon_file = g_key_file_new ();
2734   g_key_file_set_list_separator (icon_file, ',');
2735   g_key_file_load_from_file (icon_file, path, 0, &error);
2736   if (error)
2737     {
2738       g_error_free (error);
2739       g_key_file_free (icon_file);      
2740       return;
2741     }
2742   else
2743     {
2744       base_name = strip_suffix (name);
2745       
2746       data = g_slice_new0 (GtkIconData);
2747       /* takes ownership of base_name */
2748       g_hash_table_replace (dir->icon_data, base_name, data);
2749       
2750       ivalues = g_key_file_get_integer_list (icon_file, 
2751                                              "Icon Data", "EmbeddedTextRectangle",
2752                                               &length, NULL);
2753       if (ivalues)
2754         {
2755           if (length == 4)
2756             {
2757               data->has_embedded_rect = TRUE;
2758               data->x0 = ivalues[0];
2759               data->y0 = ivalues[1];
2760               data->x1 = ivalues[2];
2761               data->y1 = ivalues[3];
2762             }
2763           
2764           g_free (ivalues);
2765         }
2766       
2767       str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
2768       if (str)
2769         {
2770           split = g_strsplit (str, "|", -1);
2771           
2772           data->n_attach_points = g_strv_length (split);
2773           data->attach_points = g_new (GdkPoint, data->n_attach_points);
2774
2775           i = 0;
2776           while (split[i] != NULL && i < data->n_attach_points)
2777             {
2778               split_point = strchr (split[i], ',');
2779               if (split_point)
2780                 {
2781                   *split_point = 0;
2782                   split_point++;
2783                   data->attach_points[i].x = atoi (split[i]);
2784                   data->attach_points[i].y = atoi (split_point);
2785                 }
2786               i++;
2787             }
2788           
2789           g_strfreev (split);
2790           g_free (str);
2791         }
2792       
2793       data->display_name = g_key_file_get_locale_string (icon_file, 
2794                                                          "Icon Data", "DisplayName",
2795                                                          NULL, NULL);
2796       g_key_file_free (icon_file);
2797     }
2798 }
2799
2800 static void
2801 scan_directory (GtkIconThemePrivate *icon_theme,
2802                 IconThemeDir *dir, char *full_dir)
2803 {
2804   GDir *gdir;
2805   const char *name;
2806
2807   GTK_NOTE (ICONTHEME, 
2808             g_print ("scanning directory %s\n", full_dir));
2809   dir->icons = g_hash_table_new_full (g_str_hash, g_str_equal,
2810                                       g_free, NULL);
2811   
2812   gdir = g_dir_open (full_dir, 0, NULL);
2813
2814   if (gdir == NULL)
2815     return;
2816
2817   while ((name = g_dir_read_name (gdir)))
2818     {
2819       char *path;
2820       char *base_name;
2821       IconSuffix suffix, hash_suffix;
2822
2823       if (g_str_has_suffix (name, ".icon"))
2824         {
2825           if (dir->icon_data == NULL)
2826             dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2827                                                     g_free, (GDestroyNotify)icon_data_free);
2828           
2829           path = g_build_filename (full_dir, name, NULL);
2830           if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
2831             load_icon_data (dir, path, name);
2832           
2833           g_free (path);
2834           
2835           continue;
2836         }
2837
2838       suffix = suffix_from_name (name);
2839       if (suffix == ICON_SUFFIX_NONE)
2840         continue;
2841
2842       base_name = strip_suffix (name);
2843
2844       hash_suffix = GPOINTER_TO_INT (g_hash_table_lookup (dir->icons, base_name));
2845       g_hash_table_replace (icon_theme->all_icons, base_name, NULL);
2846       /* takes ownership of base_name */
2847       g_hash_table_replace (dir->icons, base_name, GUINT_TO_POINTER (hash_suffix| suffix));
2848     }
2849   
2850   g_dir_close (gdir);
2851 }
2852
2853 static void
2854 theme_subdir_load (GtkIconTheme *icon_theme,
2855                    IconTheme    *theme,
2856                    GKeyFile     *theme_file,
2857                    char         *subdir)
2858 {
2859   GList *d;
2860   char *type_string;
2861   IconThemeDir *dir;
2862   IconThemeDirType type;
2863   char *context_string;
2864   GQuark context;
2865   int size;
2866   int min_size;
2867   int max_size;
2868   int threshold;
2869   char *full_dir;
2870   GError *error = NULL;
2871   IconThemeDirMtime *dir_mtime;
2872
2873   size = g_key_file_get_integer (theme_file, subdir, "Size", &error);
2874   if (error)
2875     {
2876       g_error_free (error);
2877       g_warning ("Theme directory %s of theme %s has no size field\n", 
2878                  subdir, theme->name);
2879       return;
2880     }
2881   
2882   type = ICON_THEME_DIR_THRESHOLD;
2883   type_string = g_key_file_get_string (theme_file, subdir, "Type", NULL);
2884   if (type_string)
2885     {
2886       if (strcmp (type_string, "Fixed") == 0)
2887         type = ICON_THEME_DIR_FIXED;
2888       else if (strcmp (type_string, "Scalable") == 0)
2889         type = ICON_THEME_DIR_SCALABLE;
2890       else if (strcmp (type_string, "Threshold") == 0)
2891         type = ICON_THEME_DIR_THRESHOLD;
2892
2893       g_free (type_string);
2894     }
2895   
2896   context = 0;
2897   context_string = g_key_file_get_string (theme_file, subdir, "Context", NULL);
2898   if (context_string)
2899     {
2900       context = g_quark_from_string (context_string);
2901       g_free (context_string);
2902     }
2903
2904   if (g_key_file_has_key (theme_file, subdir, "MaxSize", NULL))
2905     max_size = g_key_file_get_integer (theme_file, subdir, "MaxSize", NULL);
2906   else
2907     max_size = size;
2908
2909   if (g_key_file_has_key (theme_file, subdir, "MinSize", NULL))
2910     min_size = g_key_file_get_integer (theme_file, subdir, "MinSize", NULL);
2911   else
2912     min_size = size;
2913
2914   if (g_key_file_has_key (theme_file, subdir, "Threshold", NULL))
2915     threshold = g_key_file_get_integer (theme_file, subdir, "Threshold", NULL);
2916   else
2917     threshold = 2;
2918
2919   for (d = icon_theme->priv->dir_mtimes; d; d = d->next)
2920     {
2921       dir_mtime = (IconThemeDirMtime *)d->data;
2922
2923       if (dir_mtime->mtime == 0)
2924         continue; /* directory doesn't exist */
2925
2926        full_dir = g_build_filename (dir_mtime->dir, subdir, NULL);
2927
2928       /* First, see if we have a cache for the directory */
2929       if (dir_mtime->cache != NULL || g_file_test (full_dir, G_FILE_TEST_IS_DIR))
2930         {
2931           if (dir_mtime->cache == NULL)
2932             {
2933               /* This will return NULL if the cache doesn't exist or is outdated */
2934               dir_mtime->cache = _gtk_icon_cache_new_for_path (dir_mtime->dir);
2935             }
2936           
2937           dir = g_new (IconThemeDir, 1);
2938           dir->type = type;
2939           dir->context = context;
2940           dir->size = size;
2941           dir->min_size = min_size;
2942           dir->max_size = max_size;
2943           dir->threshold = threshold;
2944           dir->dir = full_dir;
2945           dir->icon_data = NULL;
2946           dir->subdir = g_strdup (subdir);
2947           if (dir_mtime->cache != NULL)
2948             {
2949               dir->cache = _gtk_icon_cache_ref (dir_mtime->cache);
2950               dir->subdir_index = _gtk_icon_cache_get_directory_index (dir->cache, dir->subdir);
2951             }
2952           else
2953             {
2954               dir->cache = NULL;
2955               dir->subdir_index = -1;
2956               scan_directory (icon_theme->priv, dir, full_dir);
2957             }
2958
2959           theme->dirs = g_list_prepend (theme->dirs, dir);
2960         }
2961       else
2962         g_free (full_dir);
2963     }
2964 }
2965
2966 static void
2967 icon_data_free (GtkIconData *icon_data)
2968 {
2969   g_free (icon_data->attach_points);
2970   g_free (icon_data->display_name);
2971   g_slice_free (GtkIconData, icon_data);
2972 }
2973
2974 static GtkIconData *
2975 icon_data_dup (GtkIconData *icon_data)
2976 {
2977   GtkIconData *dup = NULL;
2978   if (icon_data)
2979     {
2980       dup = g_slice_new0 (GtkIconData);
2981       *dup = *icon_data;
2982       if (dup->n_attach_points > 0)
2983         {
2984           dup->attach_points = g_memdup (dup->attach_points,
2985                                          sizeof (GdkPoint) * dup->n_attach_points);
2986         }
2987       dup->display_name = g_strdup (dup->display_name);
2988     }
2989
2990   return dup;
2991 }
2992
2993
2994 /*
2995  * GtkIconInfo
2996  */
2997
2998 static void gtk_icon_info_class_init (GtkIconInfoClass *klass);
2999
3000 G_DEFINE_TYPE (GtkIconInfo, gtk_icon_info, G_TYPE_OBJECT)
3001
3002 static void
3003 gtk_icon_info_init (GtkIconInfo *icon_info)
3004 {
3005   icon_info->scale = -1.;
3006 }
3007
3008 static GtkIconInfo *
3009 icon_info_new (void)
3010 {
3011   return g_object_new (GTK_TYPE_ICON_INFO, NULL);
3012 }
3013
3014 /* This only copies whatever is needed to load the pixbuf, so that we can do
3015  * a load in a thread without affecting the original IconInfo from the thread.
3016  */
3017 static GtkIconInfo *
3018 icon_info_dup (GtkIconInfo *icon_info)
3019 {
3020   GtkIconInfo *dup;
3021   GSList *l;
3022
3023   dup = icon_info_new ();
3024
3025   dup->filename = g_strdup (icon_info->filename);
3026   if (icon_info->icon_file)
3027     dup->icon_file = g_object_ref (icon_info->icon_file);
3028   if (icon_info->loadable)
3029     dup->loadable = g_object_ref (icon_info->loadable);
3030
3031   for (l = icon_info->emblem_infos; l != NULL; l = l->next)
3032     {
3033       dup->emblem_infos =
3034         g_slist_append (dup->emblem_infos,
3035                         icon_info_dup (l->data));
3036     }
3037
3038   if (icon_info->cache_pixbuf)
3039     dup->cache_pixbuf = g_object_ref (icon_info->cache_pixbuf);
3040
3041   dup->data = icon_data_dup (icon_info->data);
3042   dup->dir_type = icon_info->dir_type;
3043   dup->dir_size = icon_info->dir_size;
3044   dup->threshold = icon_info->threshold;
3045   dup->desired_size = icon_info->desired_size;
3046   dup->raw_coordinates = icon_info->raw_coordinates;
3047   dup->forced_size = icon_info->forced_size;
3048   dup->emblems_applied = icon_info->emblems_applied;
3049
3050   return dup;
3051 }
3052
3053 static GtkIconInfo *
3054 icon_info_new_builtin (BuiltinIcon *icon)
3055 {
3056   GtkIconInfo *icon_info = icon_info_new ();
3057
3058   icon_info->cache_pixbuf = g_object_ref (icon->pixbuf);
3059   icon_info->dir_type = ICON_THEME_DIR_THRESHOLD;
3060   icon_info->dir_size = icon->size;
3061   icon_info->threshold = 2;
3062
3063   return icon_info;
3064 }
3065
3066 /**
3067  * gtk_icon_info_copy: (skip)
3068  * @icon_info: a #GtkIconInfo
3069  * 
3070  * Make a copy of a #GtkIconInfo.
3071  * 
3072  * Return value: the new GtkIconInfo
3073  *
3074  * Since: 2.4
3075  *
3076  * Deprecated: 3.8: Use g_object_ref()
3077  **/
3078 GtkIconInfo *
3079 gtk_icon_info_copy (GtkIconInfo *icon_info)
3080 {
3081   
3082   g_return_val_if_fail (icon_info != NULL, NULL);
3083
3084   return g_object_ref (icon_info);
3085 }
3086
3087 /**
3088  * gtk_icon_info_free: (skip)
3089  * @icon_info: a #GtkIconInfo
3090  * 
3091  * Free a #GtkIconInfo and associated information
3092  *
3093  * Since: 2.4
3094  *
3095  * Deprecated: 3.8: Use g_object_unref()
3096  **/
3097 void
3098 gtk_icon_info_free (GtkIconInfo *icon_info)
3099 {
3100   g_return_if_fail (icon_info != NULL);
3101
3102   g_object_unref (icon_info);
3103 }
3104
3105 static void
3106 gtk_icon_info_finalize (GObject *object)
3107 {
3108   GtkIconInfo *icon_info = (GtkIconInfo *) object;
3109
3110   if (icon_info->in_cache)
3111     g_hash_table_remove (icon_info->in_cache->priv->info_cache, &icon_info->key);
3112
3113   g_strfreev (icon_info->key.icon_names);
3114
3115   g_free (icon_info->filename);
3116   g_clear_object (&icon_info->icon_file);
3117
3118   if (icon_info->loadable)
3119     g_object_unref (icon_info->loadable);
3120   g_slist_free_full (icon_info->emblem_infos, (GDestroyNotify) g_object_unref);
3121   if (icon_info->pixbuf)
3122     g_object_unref (icon_info->pixbuf);
3123   if (icon_info->cache_pixbuf)
3124     g_object_unref (icon_info->cache_pixbuf);
3125   if (icon_info->symbolic_pixbuf_size)
3126     gtk_requisition_free (icon_info->symbolic_pixbuf_size);
3127
3128   symbolic_pixbuf_cache_free (icon_info->symbolic_pixbuf_cache);
3129
3130   G_OBJECT_CLASS (gtk_icon_info_parent_class)->finalize (object);
3131 }
3132
3133 static void
3134 gtk_icon_info_class_init (GtkIconInfoClass *klass)
3135 {
3136   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3137
3138   gobject_class->finalize = gtk_icon_info_finalize;
3139 }
3140
3141 /**
3142  * gtk_icon_info_get_base_size:
3143  * @icon_info: a #GtkIconInfo
3144  * 
3145  * Gets the base size for the icon. The base size
3146  * is a size for the icon that was specified by
3147  * the icon theme creator. This may be different
3148  * than the actual size of image; an example of
3149  * this is small emblem icons that can be attached
3150  * to a larger icon. These icons will be given
3151  * the same base size as the larger icons to which
3152  * they are attached.
3153  * 
3154  * Return value: the base size, or 0, if no base
3155  *  size is known for the icon.
3156  *
3157  * Since: 2.4
3158  **/
3159 gint
3160 gtk_icon_info_get_base_size (GtkIconInfo *icon_info)
3161 {
3162   g_return_val_if_fail (icon_info != NULL, 0);
3163
3164   return icon_info->dir_size;
3165 }
3166
3167 /**
3168  * gtk_icon_info_get_filename:
3169  * @icon_info: a #GtkIconInfo
3170  * 
3171  * Gets the filename for the icon. If the
3172  * %GTK_ICON_LOOKUP_USE_BUILTIN flag was passed
3173  * to gtk_icon_theme_lookup_icon(), there may be
3174  * no filename if a builtin icon is returned; in this
3175  * case, you should use gtk_icon_info_get_builtin_pixbuf().
3176  * 
3177  * Return value: (type filename): the filename for the icon, or %NULL
3178  *  if gtk_icon_info_get_builtin_pixbuf() should be used instead. The
3179  *  return value is owned by GTK+ and should not be modified or freed.
3180  *
3181  * Since: 2.4
3182  **/
3183 const gchar *
3184 gtk_icon_info_get_filename (GtkIconInfo *icon_info)
3185 {
3186   g_return_val_if_fail (icon_info != NULL, NULL);
3187
3188   return icon_info->filename;
3189 }
3190
3191 /**
3192  * gtk_icon_info_get_builtin_pixbuf:
3193  * @icon_info: a #GtkIconInfo structure
3194  * 
3195  * Gets the built-in image for this icon, if any. To allow
3196  * GTK+ to use built in icon images, you must pass the
3197  * %GTK_ICON_LOOKUP_USE_BUILTIN to
3198  * gtk_icon_theme_lookup_icon().
3199  *
3200  * Return value: (transfer none): the built-in image pixbuf, or %NULL. No
3201  *  extra reference is added to the returned pixbuf, so if
3202  *  you want to keep it around, you must use g_object_ref().
3203  *  The returned image must not be modified.
3204  *
3205  * Since: 2.4
3206  **/
3207 GdkPixbuf *
3208 gtk_icon_info_get_builtin_pixbuf (GtkIconInfo *icon_info)
3209 {
3210   g_return_val_if_fail (icon_info != NULL, NULL);
3211
3212   if (icon_info->filename)
3213     return NULL;
3214   
3215   return icon_info->cache_pixbuf;
3216 }
3217
3218 static gboolean icon_info_ensure_scale_and_pixbuf (GtkIconInfo*, gboolean);
3219
3220 /* Combine the icon with all emblems, the first emblem is placed 
3221  * in the southeast corner. Scale emblems to be at most 3/4 of the
3222  * size of the icon itself.
3223  */
3224 static void 
3225 apply_emblems (GtkIconInfo *info)
3226 {
3227   GdkPixbuf *icon = NULL;
3228   gint w, h, pos;
3229   GSList *l;
3230
3231   if (info->emblem_infos == NULL)
3232     return;
3233
3234   if (info->emblems_applied)
3235     return;
3236
3237   w = gdk_pixbuf_get_width (info->pixbuf);
3238   h = gdk_pixbuf_get_height (info->pixbuf);
3239
3240   for (l = info->emblem_infos, pos = 0; l; l = l->next, pos++)
3241     {
3242       GtkIconInfo *emblem_info = l->data;
3243
3244       if (icon_info_ensure_scale_and_pixbuf (emblem_info, FALSE))
3245         {
3246           GdkPixbuf *emblem = emblem_info->pixbuf;
3247           gint ew, eh;
3248           gint x = 0, y = 0; /* silence compiler */
3249           gdouble scale;
3250
3251           ew = gdk_pixbuf_get_width (emblem);
3252           eh = gdk_pixbuf_get_height (emblem);
3253           if (ew >= w)
3254             {
3255               scale = 0.75;
3256               ew = ew * 0.75;
3257               eh = eh * 0.75;
3258             }
3259           else
3260             scale = 1.0;
3261
3262           switch (pos % 4)
3263             {
3264             case 0:
3265               x = w - ew;
3266               y = h - eh;
3267               break;
3268             case 1:
3269               x = w - ew;
3270               y = 0;
3271               break;
3272             case 2:
3273               x = 0;
3274               y = h - eh;
3275               break;
3276             case 3:
3277               x = 0;
3278               y = 0;
3279               break;
3280             }
3281
3282           if (icon == NULL)
3283             {
3284               icon = gdk_pixbuf_copy (info->pixbuf);
3285               if (icon == NULL)
3286                 break;
3287             }
3288
3289           gdk_pixbuf_composite (emblem, icon, x, y, ew, eh, x, y,
3290                                 scale, scale, GDK_INTERP_BILINEAR, 255);
3291        }
3292    }
3293
3294   if (icon)
3295     {
3296       g_object_unref (info->pixbuf);
3297       info->pixbuf = icon;
3298     }
3299
3300   info->emblems_applied = TRUE;
3301 }
3302
3303 /* If this returns TRUE, its safe to call
3304    icon_info_ensure_scale_and_pixbuf without blocking */
3305 static gboolean
3306 icon_info_get_pixbuf_ready (GtkIconInfo *icon_info)
3307 {
3308   if (icon_info->pixbuf &&
3309       (icon_info->emblem_infos == NULL || icon_info->emblems_applied))
3310     return TRUE;
3311
3312   if (icon_info->load_error)
3313     return TRUE;
3314
3315   return FALSE;
3316 }
3317
3318 /* This function contains the complicated logic for deciding
3319  * on the size at which to load the icon and loading it at
3320  * that size.
3321  */
3322 static gboolean
3323 icon_info_ensure_scale_and_pixbuf (GtkIconInfo  *icon_info,
3324                                    gboolean      scale_only)
3325 {
3326   int image_width, image_height;
3327   GdkPixbuf *source_pixbuf;
3328   gboolean is_svg;
3329
3330   /* First check if we already succeeded have the necessary
3331    * information (or failed earlier)
3332    */
3333   if (scale_only && icon_info->scale >= 0)
3334     return TRUE;
3335
3336   if (icon_info->pixbuf)
3337     {
3338       apply_emblems (icon_info);
3339       return TRUE;
3340     }
3341
3342   if (icon_info->load_error)
3343     return FALSE;
3344
3345   /* SVG icons are a special case - we just immediately scale them
3346    * to the desired size
3347    */
3348   if (icon_info->icon_file && !icon_info->loadable)
3349     icon_info->loadable = G_LOADABLE_ICON (g_file_icon_new (icon_info->icon_file));
3350
3351   is_svg = FALSE;
3352   if (G_IS_FILE_ICON (icon_info->loadable))
3353     {
3354       GFile *file;
3355       GFileInfo *file_info;
3356       const gchar *content_type;
3357
3358       file = g_file_icon_get_file (G_FILE_ICON (icon_info->loadable));
3359       file_info = g_file_query_info (file, 
3360                                      G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
3361                                      G_FILE_QUERY_INFO_NONE,
3362                                      NULL, NULL);
3363       if (file_info) 
3364         {
3365           content_type = g_file_info_get_content_type (file_info);
3366
3367           if (content_type && strcmp (content_type, "image/svg+xml") == 0)
3368             is_svg = TRUE;
3369
3370           g_object_unref (file_info);
3371        }
3372     }
3373
3374   if (is_svg)
3375     {
3376       GInputStream *stream;
3377
3378       icon_info->scale = icon_info->desired_size / 1000.;
3379
3380       if (scale_only)
3381         return TRUE;
3382       
3383       stream = g_loadable_icon_load (icon_info->loadable,
3384                                      icon_info->desired_size,
3385                                      NULL, NULL,
3386                                      &icon_info->load_error);
3387       if (stream)
3388         {
3389           icon_info->pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3390                                                                    icon_info->desired_size,
3391                                                                    icon_info->desired_size,
3392                                                                    TRUE,
3393                                                                    NULL,
3394                                                                    &icon_info->load_error);
3395           g_object_unref (stream);
3396         }
3397
3398       if (!icon_info->pixbuf)
3399         return FALSE;
3400
3401       apply_emblems (icon_info);
3402         
3403       return TRUE;
3404     }
3405
3406   /* In many cases, the scale can be determined without actual access
3407    * to the icon file. This is generally true when we have a size
3408    * for the directory where the icon is; the image size doesn't
3409    * matter in that case.
3410    */
3411   if (icon_info->forced_size)
3412     icon_info->scale = -1;
3413   else if (icon_info->dir_type == ICON_THEME_DIR_FIXED)
3414     icon_info->scale = 1.0;
3415   else if (icon_info->dir_type == ICON_THEME_DIR_THRESHOLD)
3416     {
3417       if (icon_info->desired_size >= icon_info->dir_size - icon_info->threshold &&
3418           icon_info->desired_size <= icon_info->dir_size + icon_info->threshold)
3419         icon_info->scale = 1.0;
3420       else if (icon_info->dir_size > 0)
3421         icon_info->scale =(gdouble) icon_info->desired_size / icon_info->dir_size;
3422     }
3423   else if (icon_info->dir_type == ICON_THEME_DIR_SCALABLE)
3424     {
3425       if (icon_info->dir_size > 0)
3426         icon_info->scale = (gdouble) icon_info->desired_size / icon_info->dir_size;
3427     }
3428
3429   if (icon_info->scale >= 0. && scale_only)
3430     return TRUE;
3431
3432   /* At this point, we need to actually get the icon; either from the
3433    * builtin image or by loading the file
3434    */
3435   source_pixbuf = NULL;
3436   if (icon_info->cache_pixbuf)
3437     source_pixbuf = g_object_ref (icon_info->cache_pixbuf);
3438   else
3439     {
3440       GInputStream *stream;
3441
3442       stream = g_loadable_icon_load (icon_info->loadable,
3443                                      icon_info->desired_size,
3444                                      NULL, NULL,
3445                                      &icon_info->load_error);
3446       if (stream)
3447         {
3448           source_pixbuf = gdk_pixbuf_new_from_stream (stream,
3449                                                       NULL,
3450                                                       &icon_info->load_error);
3451           g_object_unref (stream);
3452         }
3453     }
3454
3455   if (!source_pixbuf)
3456     return FALSE;
3457
3458   /* Do scale calculations that depend on the image size
3459    */
3460   image_width = gdk_pixbuf_get_width (source_pixbuf);
3461   image_height = gdk_pixbuf_get_height (source_pixbuf);
3462
3463   if (icon_info->scale < 0.0)
3464     {
3465       gint image_size = MAX (image_width, image_height);
3466       if (image_size > 0)
3467         icon_info->scale = (gdouble)icon_info->desired_size / (gdouble)image_size;
3468       else
3469         icon_info->scale = 1.0;
3470       
3471       if (icon_info->dir_type == ICON_THEME_DIR_UNTHEMED && 
3472           !icon_info->forced_size)
3473         icon_info->scale = MIN (icon_info->scale, 1.0);
3474     }
3475
3476   /* We don't short-circuit out here for scale_only, since, now
3477    * we've loaded the icon, we might as well go ahead and finish
3478    * the job. This is a bit of a waste when we scale here
3479    * and never get the final pixbuf; at the cost of a bit of
3480    * extra complexity, we could keep the source pixbuf around
3481    * but not actually scale it until needed.
3482    */
3483   if (icon_info->scale == 1.0)
3484     icon_info->pixbuf = source_pixbuf;
3485   else
3486     {
3487       icon_info->pixbuf = gdk_pixbuf_scale_simple (source_pixbuf,
3488                                                    0.5 + image_width * icon_info->scale,
3489                                                    0.5 + image_height * icon_info->scale,
3490                                                    GDK_INTERP_BILINEAR);
3491       g_object_unref (source_pixbuf);
3492     }
3493
3494   apply_emblems (icon_info);
3495
3496   return TRUE;
3497 }
3498
3499 static void
3500 proxy_pixbuf_destroy (guchar *pixels, gpointer data)
3501 {
3502   GtkIconInfo *icon_info = data;
3503   GtkIconTheme *icon_theme = icon_info->in_cache;
3504
3505   g_assert (icon_info->proxy_pixbuf != NULL);
3506   icon_info->proxy_pixbuf = NULL;
3507
3508   /* Keep it alive a bit longer */
3509   if (icon_theme != NULL)
3510     ensure_in_lru_cache (icon_theme, icon_info);
3511
3512   g_object_unref (icon_info);
3513 }
3514
3515 /**
3516  * gtk_icon_info_load_icon:
3517  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3518  * @error: (allow-none): location to store error information on failure,
3519  *     or %NULL.
3520  *
3521  * Renders an icon previously looked up in an icon theme using
3522  * gtk_icon_theme_lookup_icon(); the size will be based on the size
3523  * passed to gtk_icon_theme_lookup_icon(). Note that the resulting
3524  * pixbuf may not be exactly this size; an icon theme may have icons
3525  * that differ slightly from their nominal sizes, and in addition GTK+
3526  * will avoid scaling icons that it considers sufficiently close to the
3527  * requested size or for which the source image would have to be scaled
3528  * up too far. (This maintains sharpness.). This behaviour can be changed
3529  * by passing the %GTK_ICON_LOOKUP_FORCE_SIZE flag when obtaining
3530  * the #GtkIconInfo. If this flag has been specified, the pixbuf
3531  * returned by this function will be scaled to the exact size.
3532  *
3533  * Return value: (transfer full): the rendered icon; this may be a newly
3534  *     created icon or a new reference to an internal icon, so you must
3535  *     not modify the icon. Use g_object_unref() to release your reference
3536  *     to the icon.
3537  *
3538  * Since: 2.4
3539  **/
3540 GdkPixbuf *
3541 gtk_icon_info_load_icon (GtkIconInfo *icon_info,
3542                          GError     **error)
3543 {
3544   g_return_val_if_fail (icon_info != NULL, NULL);
3545   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3546
3547   if (!icon_info_ensure_scale_and_pixbuf (icon_info, FALSE))
3548     {
3549       if (icon_info->load_error)
3550         g_propagate_error (error, icon_info->load_error);
3551       else
3552         g_set_error_literal (error,  
3553                              GTK_ICON_THEME_ERROR,  
3554                              GTK_ICON_THEME_NOT_FOUND,
3555                              _("Failed to load icon"));
3556  
3557       return NULL;
3558     }
3559
3560   /* Instead of returning the pixbuf directly we
3561      return a proxy to it that we don't own (but that
3562      shares the data with the one we own). This way
3563      we can know when it is freed and ensure the
3564      IconInfo is alive (and thus cached) while
3565      the pixbuf is still alive. */
3566
3567   if (icon_info->proxy_pixbuf != NULL)
3568     return g_object_ref (icon_info->proxy_pixbuf);
3569
3570   icon_info->proxy_pixbuf =
3571     gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (icon_info->pixbuf),
3572                               gdk_pixbuf_get_colorspace (icon_info->pixbuf),
3573                               gdk_pixbuf_get_has_alpha (icon_info->pixbuf),
3574                               gdk_pixbuf_get_bits_per_sample (icon_info->pixbuf),
3575                               gdk_pixbuf_get_width (icon_info->pixbuf),
3576                               gdk_pixbuf_get_height (icon_info->pixbuf),
3577                               gdk_pixbuf_get_rowstride (icon_info->pixbuf),
3578                               proxy_pixbuf_destroy,
3579                               g_object_ref (icon_info));
3580
3581   return icon_info->proxy_pixbuf;
3582 }
3583
3584 static void
3585 load_icon_thread  (GTask           *task,
3586                    gpointer         source_object,
3587                    gpointer         task_data,
3588                    GCancellable    *cancellable)
3589 {
3590   GtkIconInfo *dup = task_data;
3591
3592   icon_info_ensure_scale_and_pixbuf (dup, FALSE);
3593   g_task_return_pointer (task, NULL, NULL);
3594 }
3595
3596 /**
3597  * gtk_icon_info_load_icon_async:
3598  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3599  * @cancellable: (allow-none): optional #GCancellable object,
3600  *     %NULL to ignore
3601  * @callback: (scope async): a #GAsyncReadyCallback to call when the
3602  *     request is satisfied
3603  * @user_data: (closure): the data to pass to callback function
3604  *
3605  * Asynchronously load, render and scale an icon previously looked up
3606  * from the icon theme using gtk_icon_theme_lookup_icon().
3607  *
3608  * For more details, see gtk_icon_info_load_icon() which is the synchronous
3609  * version of this call.
3610  *
3611  * Since: 3.8
3612  **/
3613 void
3614 gtk_icon_info_load_icon_async (GtkIconInfo          *icon_info,
3615                                GCancellable         *cancellable,
3616                                GAsyncReadyCallback   callback,
3617                                gpointer              user_data)
3618 {
3619   GTask *task;
3620   GdkPixbuf *pixbuf;
3621   GtkIconInfo *dup;
3622   GError *error = NULL;
3623
3624   task = g_task_new (icon_info, cancellable, callback, user_data);
3625
3626   if (icon_info_get_pixbuf_ready (icon_info))
3627     {
3628       pixbuf = gtk_icon_info_load_icon (icon_info, &error);
3629       if (pixbuf == NULL)
3630         g_task_return_error (task, error);
3631       else
3632         g_task_return_pointer (task, pixbuf, g_object_unref);
3633       g_object_unref (task);
3634     }
3635   else
3636     {
3637       dup = icon_info_dup (icon_info);
3638       g_task_set_task_data (task, dup, g_object_unref);
3639       g_task_run_in_thread (task, load_icon_thread);
3640       g_object_unref (task);
3641     }
3642 }
3643
3644 /**
3645  * gtk_icon_info_load_icon_finish:
3646  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3647  * @res: a #GAsyncResult
3648  * @error: (allow-none): location to store error information on failure,
3649  *     or %NULL.
3650  *
3651  * Finishes an async icon load, see gtk_icon_info_load_icon_async().
3652  *
3653  * Return value: (transfer full): the rendered icon; this may be a newly
3654  *     created icon or a new reference to an internal icon, so you must
3655  *     not modify the icon. Use g_object_unref() to release your reference
3656  *     to the icon.
3657  *
3658  * Since: 3.8
3659  **/
3660 GdkPixbuf *
3661 gtk_icon_info_load_icon_finish (GtkIconInfo          *icon_info,
3662                                 GAsyncResult         *result,
3663                                 GError              **error)
3664 {
3665   GTask *task = G_TASK (result);
3666   GtkIconInfo *dup;
3667
3668   g_return_val_if_fail (g_task_is_valid (result, icon_info), NULL);
3669
3670   dup = g_task_get_task_data (task);
3671   if (dup == NULL || g_task_had_error (task))
3672     return g_task_propagate_pointer (task, error);
3673
3674   /* We ran the thread and it was not cancelled */
3675
3676   /* Check if someone else updated the icon_info in between */
3677   if (!icon_info_get_pixbuf_ready (icon_info))
3678     {
3679       /* If not, copy results from dup back to icon_info */
3680
3681       icon_info->emblems_applied = dup->emblems_applied;
3682       icon_info->scale = dup->scale;
3683       g_clear_object (&icon_info->pixbuf);
3684       if (dup->pixbuf)
3685         icon_info->pixbuf = g_object_ref (dup->pixbuf);
3686       g_clear_error (&icon_info->load_error);
3687       if (dup->load_error)
3688         icon_info->load_error = g_error_copy (dup->load_error);
3689     }
3690
3691   g_assert (icon_info_get_pixbuf_ready (icon_info));
3692
3693   /* This is now guaranteed to not block */
3694   return gtk_icon_info_load_icon (icon_info, error);
3695 }
3696
3697 static gchar *
3698 gdk_color_to_css (GdkColor *color)
3699 {
3700   return g_strdup_printf ("rgb(%d,%d,%d)",
3701                           color->red >> 8,
3702                           color->green >> 8,
3703                           color->blue >> 8);
3704 }
3705
3706 static gchar *
3707 gdk_rgba_to_css (const GdkRGBA *color)
3708 {
3709   /* drop alpha for now, since librsvg does not understand rgba() */
3710   return g_strdup_printf ("rgb(%d,%d,%d)",
3711                           (gint)(color->red * 255),
3712                           (gint)(color->green * 255),
3713                           (gint)(color->blue * 255));
3714 }
3715
3716 static void
3717 proxy_symbolic_pixbuf_destroy (guchar *pixels, gpointer data)
3718 {
3719   GtkIconInfo *icon_info = data;
3720   GtkIconTheme *icon_theme = icon_info->in_cache;
3721   SymbolicPixbufCache *symbolic_cache;
3722
3723   for (symbolic_cache = icon_info->symbolic_pixbuf_cache;
3724        symbolic_cache != NULL;
3725        symbolic_cache = symbolic_cache->next)
3726     {
3727       if (symbolic_cache->proxy_pixbuf != NULL &&
3728           gdk_pixbuf_get_pixels (symbolic_cache->proxy_pixbuf) == pixels)
3729         break;
3730     }
3731
3732   g_assert (symbolic_cache != NULL);
3733   g_assert (symbolic_cache->proxy_pixbuf != NULL);
3734
3735   symbolic_cache->proxy_pixbuf = NULL;
3736
3737   /* Keep it alive a bit longer */
3738   if (icon_theme != NULL)
3739     ensure_in_lru_cache (icon_theme, icon_info);
3740
3741   g_object_unref (icon_info);
3742 }
3743
3744 static GdkPixbuf *
3745 symbolic_cache_get_proxy (SymbolicPixbufCache *symbolic_cache,
3746                           GtkIconInfo  *icon_info)
3747 {
3748   if (symbolic_cache->proxy_pixbuf)
3749     return g_object_ref (symbolic_cache->proxy_pixbuf);
3750
3751   symbolic_cache->proxy_pixbuf =
3752     gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (symbolic_cache->pixbuf),
3753                               gdk_pixbuf_get_colorspace (symbolic_cache->pixbuf),
3754                               gdk_pixbuf_get_has_alpha (symbolic_cache->pixbuf),
3755                               gdk_pixbuf_get_bits_per_sample (symbolic_cache->pixbuf),
3756                               gdk_pixbuf_get_width (symbolic_cache->pixbuf),
3757                               gdk_pixbuf_get_height (symbolic_cache->pixbuf),
3758                               gdk_pixbuf_get_rowstride (symbolic_cache->pixbuf),
3759                               proxy_symbolic_pixbuf_destroy,
3760                               g_object_ref (icon_info));
3761
3762   return symbolic_cache->proxy_pixbuf;
3763 }
3764
3765 static GdkPixbuf *
3766 _gtk_icon_info_load_symbolic_internal (GtkIconInfo  *icon_info,
3767                                        const GdkRGBA  *fg,
3768                                        const GdkRGBA  *success_color,
3769                                        const GdkRGBA  *warning_color,
3770                                        const GdkRGBA  *error_color,
3771                                        gboolean        use_cache,
3772                                        GError        **error)
3773 {
3774   GInputStream *stream;
3775   GdkPixbuf *pixbuf;
3776   gchar *css_fg;
3777   gchar *css_success;
3778   gchar *css_warning;
3779   gchar *css_error;
3780   gchar *data;
3781   gchar *width, *height, *uri;
3782   SymbolicPixbufCache *symbolic_cache;
3783
3784   if (use_cache)
3785     {
3786       symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
3787                                                       fg, success_color, warning_color, error_color);
3788       if (symbolic_cache)
3789         return symbolic_cache_get_proxy (symbolic_cache, icon_info);
3790     }
3791
3792   /* css_fg can't possibly have failed, otherwise
3793    * that would mean we have a broken style */
3794   g_return_val_if_fail (fg != NULL, NULL);
3795
3796   css_fg = gdk_rgba_to_css (fg);
3797
3798   css_success = css_warning = css_error = NULL;
3799
3800   if (warning_color)
3801     css_warning = gdk_rgba_to_css (warning_color);
3802
3803   if (error_color)
3804     css_error = gdk_rgba_to_css (error_color);
3805
3806   if (success_color)
3807     css_success = gdk_rgba_to_css (success_color);
3808
3809   if (!css_success)
3810     {
3811       GdkColor success_default_color = { 0, 0x4e00, 0x9a00, 0x0600 };
3812       css_success = gdk_color_to_css (&success_default_color);
3813     }
3814   if (!css_warning)
3815     {
3816       GdkColor warning_default_color = { 0, 0xf500, 0x7900, 0x3e00 };
3817       css_warning = gdk_color_to_css (&warning_default_color);
3818     }
3819   if (!css_error)
3820     {
3821       GdkColor error_default_color = { 0, 0xcc00, 0x0000, 0x0000 };
3822       css_error = gdk_color_to_css (&error_default_color);
3823     }
3824
3825   if (!icon_info->symbolic_pixbuf_size)
3826     {
3827       stream = G_INPUT_STREAM (g_file_read (icon_info->icon_file, NULL, error));
3828
3829       if (!stream)
3830         return NULL;
3831
3832       /* Fetch size from the original icon */
3833       pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
3834       g_object_unref (stream);
3835
3836       if (!pixbuf)
3837         return NULL;
3838
3839       icon_info->symbolic_pixbuf_size = gtk_requisition_new ();
3840       icon_info->symbolic_pixbuf_size->width = gdk_pixbuf_get_width (pixbuf);
3841       icon_info->symbolic_pixbuf_size->height = gdk_pixbuf_get_height (pixbuf);
3842       g_object_unref (pixbuf);
3843     }
3844
3845   width = g_strdup_printf ("%d", icon_info->symbolic_pixbuf_size->width);
3846   height = g_strdup_printf ("%d", icon_info->symbolic_pixbuf_size->height);
3847   uri = g_file_get_uri (icon_info->icon_file);
3848
3849   data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
3850                       "<svg version=\"1.1\"\n"
3851                       "     xmlns=\"http://www.w3.org/2000/svg\"\n"
3852                       "     xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n"
3853                       "     width=\"", width, "\"\n"
3854                       "     height=\"", height, "\">\n"
3855                       "  <style type=\"text/css\">\n"
3856                       "    rect,path {\n"
3857                       "      fill: ", css_fg," !important;\n"
3858                       "    }\n"
3859                       "    .warning {\n"
3860                       "      fill: ", css_warning, " !important;\n"
3861                       "    }\n"
3862                       "    .error {\n"
3863                       "      fill: ", css_error ," !important;\n"
3864                       "    }\n"
3865                       "    .success {\n"
3866                       "      fill: ", css_success, " !important;\n"
3867                       "    }\n"
3868                       "  </style>\n"
3869                       "  <xi:include href=\"", uri, "\"/>\n"
3870                       "</svg>",
3871                       NULL);
3872   g_free (css_fg);
3873   g_free (css_warning);
3874   g_free (css_error);
3875   g_free (css_success);
3876   g_free (width);
3877   g_free (height);
3878   g_free (uri);
3879
3880   stream = g_memory_input_stream_new_from_data (data, -1, g_free);
3881   pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3882                                                 icon_info->desired_size,
3883                                                 icon_info->desired_size,
3884                                                 TRUE,
3885                                                 NULL,
3886                                                 error);
3887   g_object_unref (stream);
3888
3889   if (pixbuf != NULL)
3890     {
3891       if (use_cache)
3892         {
3893           icon_info->symbolic_pixbuf_cache =
3894             symbolic_pixbuf_cache_new (pixbuf, fg, success_color, warning_color, error_color,
3895                                        icon_info->symbolic_pixbuf_cache);
3896           g_object_unref (pixbuf);
3897           return symbolic_cache_get_proxy (icon_info->symbolic_pixbuf_cache, icon_info);
3898         }
3899       else
3900         return pixbuf;
3901     }
3902
3903   return NULL;
3904 }
3905
3906 /**
3907  * gtk_icon_info_load_symbolic:
3908  * @icon_info: a #GtkIconInfo
3909  * @fg: a #GdkRGBA representing the foreground color of the icon
3910  * @success_color: (allow-none): a #GdkRGBA representing the warning color
3911  *     of the icon or %NULL to use the default color
3912  * @warning_color: (allow-none): a #GdkRGBA representing the warning color
3913  *     of the icon or %NULL to use the default color
3914  * @error_color: (allow-none): a #GdkRGBA representing the error color
3915  *     of the icon or %NULL to use the default color (allow-none)
3916  * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3917  *     loaded icon was a symbolic one and whether the @fg color was
3918  *     applied to it.
3919  * @error: (allow-none): location to store error information on failure,
3920  *     or %NULL.
3921  *
3922  * Loads an icon, modifying it to match the system colours for the foreground,
3923  * success, warning and error colors provided. If the icon is not a symbolic
3924  * one, the function will return the result from gtk_icon_info_load_icon().
3925  *
3926  * This allows loading symbolic icons that will match the system theme.
3927  *
3928  * Unless you are implementing a widget, you will want to use
3929  * g_themed_icon_new_with_default_fallbacks() to load the icon.
3930  *
3931  * As implementation details, the icon loaded needs to be of SVG type,
3932  * contain the "symbolic" term as the last component of the icon name,
3933  * and use the 'fg', 'success', 'warning' and 'error' CSS styles in the
3934  * SVG file itself.
3935  *
3936  * See the <ulink url="http://www.freedesktop.org/wiki/SymbolicIcons">Symbolic Icons spec</ulink>
3937  * for more information about symbolic icons.
3938  *
3939  * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
3940  *
3941  * Since: 3.0
3942  **/
3943 GdkPixbuf *
3944 gtk_icon_info_load_symbolic (GtkIconInfo    *icon_info,
3945                              const GdkRGBA  *fg,
3946                              const GdkRGBA  *success_color,
3947                              const GdkRGBA  *warning_color,
3948                              const GdkRGBA  *error_color,
3949                              gboolean       *was_symbolic,
3950                              GError        **error)
3951 {
3952   gchar *icon_uri;
3953   gboolean is_symbolic;
3954
3955   g_return_val_if_fail (icon_info != NULL, NULL);
3956   g_return_val_if_fail (fg != NULL, NULL);
3957
3958   icon_uri = NULL;
3959   if (icon_info->icon_file)
3960     icon_uri = g_file_get_uri (icon_info->icon_file);
3961
3962   is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
3963   g_free (icon_uri);
3964
3965   if (was_symbolic)
3966     *was_symbolic = is_symbolic;
3967
3968   if (!is_symbolic)
3969     return gtk_icon_info_load_icon (icon_info, error);
3970
3971   return _gtk_icon_info_load_symbolic_internal (icon_info,
3972                                                 fg, success_color,
3973                                                 warning_color, error_color,
3974                                                 TRUE,
3975                                                 error);
3976 }
3977
3978 /**
3979  * gtk_icon_info_load_symbolic_for_context:
3980  * @icon_info: a #GtkIconInfo
3981  * @context: a #GtkStyleContext
3982  * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3983  *     loaded icon was a symbolic one and whether the @fg color was
3984  *     applied to it.
3985  * @error: (allow-none): location to store error information on failure,
3986  *     or %NULL.
3987  *
3988  * Loads an icon, modifying it to match the system colors for the foreground,
3989  * success, warning and error colors provided. If the icon is not a symbolic
3990  * one, the function will return the result from gtk_icon_info_load_icon().
3991  * This function uses the regular foreground color and the symbolic colors
3992  * with the names "success_color", "warning_color" and "error_color" from
3993  * the context.
3994  *
3995  * This allows loading symbolic icons that will match the system theme.
3996  *
3997  * See gtk_icon_info_load_symbolic() for more details.
3998  *
3999  * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
4000  *
4001  * Since: 3.0
4002  **/
4003 GdkPixbuf *
4004 gtk_icon_info_load_symbolic_for_context (GtkIconInfo      *icon_info,
4005                                          GtkStyleContext  *context,
4006                                          gboolean         *was_symbolic,
4007                                          GError          **error)
4008 {
4009   GdkRGBA *color = NULL;
4010   GdkRGBA fg;
4011   GdkRGBA *fgp;
4012   GdkRGBA success_color;
4013   GdkRGBA *success_colorp;
4014   GdkRGBA warning_color;
4015   GdkRGBA *warning_colorp;
4016   GdkRGBA error_color;
4017   GdkRGBA *error_colorp;
4018   GtkStateFlags state;
4019   gchar *icon_uri;
4020   gboolean is_symbolic;
4021
4022   g_return_val_if_fail (icon_info != NULL, NULL);
4023   g_return_val_if_fail (context != NULL, NULL);
4024
4025   icon_uri = NULL;
4026   if (icon_info->icon_file)
4027     icon_uri = g_file_get_uri (icon_info->icon_file);
4028
4029   is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
4030   g_free (icon_uri);
4031
4032   if (was_symbolic)
4033     *was_symbolic = is_symbolic;
4034
4035   if (!is_symbolic)
4036     return gtk_icon_info_load_icon (icon_info, error);
4037
4038   fgp = success_colorp = warning_colorp = error_colorp = NULL;
4039
4040   state = gtk_style_context_get_state (context);
4041   gtk_style_context_get (context, state, "color", &color, NULL);
4042   if (color)
4043     {
4044       fg = *color;
4045       fgp = &fg;
4046       gdk_rgba_free (color);
4047     }
4048
4049   if (gtk_style_context_lookup_color (context, "success_color", &success_color))
4050     success_colorp = &success_color;
4051
4052   if (gtk_style_context_lookup_color (context, "warning_color", &warning_color))
4053     warning_colorp = &warning_color;
4054
4055   if (gtk_style_context_lookup_color (context, "error_color", &error_color))
4056     error_colorp = &error_color;
4057
4058   return _gtk_icon_info_load_symbolic_internal (icon_info,
4059                                                 fgp, success_colorp,
4060                                                 warning_colorp, error_colorp,
4061                                                 TRUE,
4062                                                 error);
4063 }
4064
4065 typedef struct {
4066   gboolean is_symbolic;
4067   GtkIconInfo *dup;
4068   GdkRGBA fg;
4069   gboolean fg_set;
4070   GdkRGBA success_color;
4071   gboolean success_color_set;
4072   GdkRGBA warning_color;
4073   gboolean warning_color_set;
4074   GdkRGBA error_color;
4075   gboolean error_color_set;
4076 } AsyncSymbolicData;
4077
4078 static void
4079 async_symbolic_data_free (AsyncSymbolicData *data)
4080 {
4081   if (data->dup)
4082     g_object_unref (data->dup);
4083   g_slice_free (AsyncSymbolicData, data);
4084 }
4085
4086 static void
4087 async_load_no_symbolic_cb (GObject *source_object,
4088                            GAsyncResult *res,
4089                            gpointer user_data)
4090 {
4091   GtkIconInfo *icon_info = GTK_ICON_INFO (source_object);
4092   GTask *task = user_data;
4093   GError *error = NULL;
4094   GdkPixbuf *pixbuf;
4095
4096   pixbuf = gtk_icon_info_load_icon_finish (icon_info, res, &error);
4097   if (pixbuf == NULL)
4098     g_task_return_error (task, error);
4099   else
4100     g_task_return_pointer (task, pixbuf, g_object_unref);
4101   g_object_unref (task);
4102 }
4103
4104 static void
4105 load_symbolic_icon_thread  (GTask           *task,
4106                             gpointer         source_object,
4107                             gpointer         task_data,
4108                             GCancellable    *cancellable)
4109 {
4110   AsyncSymbolicData *data = task_data;
4111   GError *error;
4112   GdkPixbuf *pixbuf;
4113
4114   error = NULL;
4115   pixbuf =
4116     _gtk_icon_info_load_symbolic_internal (data->dup,
4117                                            data->fg_set ? &data->fg : NULL,
4118                                            data->success_color_set ? &data->success_color : NULL,
4119                                            data->warning_color_set ? &data->warning_color : NULL,
4120                                            data->error_color_set ? &data->error_color : NULL,
4121                                            FALSE,
4122                                            &error);
4123   if (pixbuf == NULL)
4124     g_task_return_error (task, error);
4125   else
4126     g_task_return_pointer (task, pixbuf, g_object_unref);
4127 }
4128
4129 /**
4130  * gtk_icon_info_load_symbolic_async:
4131  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
4132  * @fg: a #GdkRGBA representing the foreground color of the icon
4133  * @success_color: (allow-none): a #GdkRGBA representing the warning color
4134  *     of the icon or %NULL to use the default color
4135  * @warning_color: (allow-none): a #GdkRGBA representing the warning color
4136  *     of the icon or %NULL to use the default color
4137  * @error_color: (allow-none): a #GdkRGBA representing the error color
4138  *     of the icon or %NULL to use the default color (allow-none)
4139  * @cancellable: (allow-none): optional #GCancellable object,
4140  *     %NULL to ignore
4141  * @callback: (scope async): a #GAsyncReadyCallback to call when the
4142  *     request is satisfied
4143  * @user_data: (closure): the data to pass to callback function
4144  *
4145  * Asynchronously load, render and scale a symbolic icon previously looked up
4146  * from the icon theme using gtk_icon_theme_lookup_icon().
4147  *
4148  * For more details, see gtk_icon_info_load_symbolic() which is the synchronous
4149  * version of this call.
4150  *
4151  * Since: 3.8
4152  **/
4153 void
4154 gtk_icon_info_load_symbolic_async (GtkIconInfo   *icon_info,
4155                                    const GdkRGBA *fg,
4156                                    const GdkRGBA *success_color,
4157                                    const GdkRGBA *warning_color,
4158                                    const GdkRGBA *error_color,
4159                                    GCancellable         *cancellable,
4160                                    GAsyncReadyCallback   callback,
4161                                    gpointer              user_data)
4162 {
4163   GTask *task;
4164   AsyncSymbolicData *data;
4165   gchar *icon_uri;
4166   SymbolicPixbufCache *symbolic_cache;
4167   GdkPixbuf *pixbuf;
4168
4169   g_return_if_fail (icon_info != NULL);
4170   g_return_if_fail (fg != NULL);
4171
4172   task = g_task_new (icon_info, cancellable, callback, user_data);
4173
4174   data = g_slice_new0 (AsyncSymbolicData);
4175   g_task_set_task_data (task, data, (GDestroyNotify) async_symbolic_data_free);
4176
4177   icon_uri = NULL;
4178   if (icon_info->icon_file)
4179     icon_uri = g_file_get_uri (icon_info->icon_file);
4180
4181   data->is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
4182   g_free (icon_uri);
4183
4184   if (!data->is_symbolic)
4185     {
4186       gtk_icon_info_load_icon_async (icon_info, cancellable, async_load_no_symbolic_cb, g_object_ref (task));
4187     }
4188   else
4189     {
4190       symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
4191                                                       fg, success_color, warning_color, error_color);
4192       if (symbolic_cache)
4193         {
4194           pixbuf = symbolic_cache_get_proxy (symbolic_cache, icon_info);
4195           g_task_return_pointer (task, pixbuf, g_object_unref);
4196         }
4197       else
4198         {
4199           if (fg)
4200             {
4201               data->fg = *fg;
4202               data->fg_set = TRUE;
4203             }
4204
4205           if (success_color)
4206             {
4207               data->success_color = *success_color;
4208               data->success_color_set = TRUE;
4209             }
4210
4211           if (warning_color)
4212             {
4213               data->warning_color = *warning_color;
4214               data->warning_color_set = TRUE;
4215             }
4216
4217           if (error_color)
4218             {
4219               data->error_color = *error_color;
4220               data->error_color_set = TRUE;
4221             }
4222
4223           data->dup = icon_info_dup (icon_info);
4224           g_task_run_in_thread (task, load_symbolic_icon_thread);
4225         }
4226     }
4227   g_object_unref (task);
4228 }
4229
4230 /**
4231  * gtk_icon_info_load_symbolic_finish:
4232  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
4233  * @res: a #GAsyncResult
4234  * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
4235  *     loaded icon was a symbolic one and whether the @fg color was
4236  *     applied to it.
4237  * @error: (allow-none): location to store error information on failure,
4238  *     or %NULL.
4239  *
4240  * Finishes an async icon load, see gtk_icon_info_load_symbolic_async().
4241  *
4242  * Return value: (transfer full): the rendered icon; this may be a newly
4243  *     created icon or a new reference to an internal icon, so you must
4244  *     not modify the icon. Use g_object_unref() to release your reference
4245  *     to the icon.
4246  *
4247  * Since: 3.8
4248  **/
4249 GdkPixbuf *
4250 gtk_icon_info_load_symbolic_finish (GtkIconInfo   *icon_info,
4251                                     GAsyncResult  *result,
4252                                     gboolean      *was_symbolic,
4253                                     GError       **error)
4254 {
4255   GTask *task = G_TASK (result);
4256   AsyncSymbolicData *data = g_task_get_task_data (task);
4257   SymbolicPixbufCache *symbolic_cache;
4258   GdkPixbuf *pixbuf;
4259
4260   if (was_symbolic)
4261     *was_symbolic = data->is_symbolic;
4262
4263   if (data->dup && !g_task_had_error (task))
4264     {
4265       pixbuf = g_task_propagate_pointer (task, NULL);
4266
4267       g_assert (pixbuf != NULL); /* we checked for !had_error above */
4268
4269       symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
4270                                                       data->fg_set ? &data->fg : NULL,
4271                                                       data->success_color_set ? &data->success_color : NULL,
4272                                                       data->warning_color_set ? &data->warning_color : NULL,
4273                                                       data->error_color_set ? &data->error_color : NULL);
4274
4275       if (symbolic_cache == NULL)
4276         {
4277           symbolic_cache = icon_info->symbolic_pixbuf_cache =
4278             symbolic_pixbuf_cache_new (pixbuf,
4279                                        data->fg_set ? &data->fg : NULL,
4280                                        data->success_color_set ? &data->success_color : NULL,
4281                                        data->warning_color_set ? &data->warning_color : NULL,
4282                                        data->error_color_set ? &data->error_color : NULL,
4283                                        icon_info->symbolic_pixbuf_cache);
4284         }
4285
4286       g_object_unref (pixbuf);
4287
4288       return symbolic_cache_get_proxy (symbolic_cache, icon_info);
4289     }
4290
4291   return g_task_propagate_pointer (task, error);
4292 }
4293
4294 /**
4295  * gtk_icon_info_load_symbolic_for_context_async:
4296  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
4297  * @context: a #GtkStyleContext
4298  * @cancellable: (allow-none): optional #GCancellable object,
4299  *     %NULL to ignore
4300  * @callback: (scope async): a #GAsyncReadyCallback to call when the
4301  *     request is satisfied
4302  * @user_data: (closure): the data to pass to callback function
4303  *
4304  * Asynchronously load, render and scale a symbolic icon previously looked up
4305  * from the icon theme using gtk_icon_theme_lookup_icon().
4306  *
4307  * For more details, see gtk_icon_info_load_symbolic_for_context() which is the synchronous
4308  * version of this call.
4309  *
4310  * Since: 3.8
4311  **/
4312 void
4313 gtk_icon_info_load_symbolic_for_context_async (GtkIconInfo      *icon_info,
4314                                                GtkStyleContext  *context,
4315                                                GCancellable     *cancellable,
4316                                                GAsyncReadyCallback callback,
4317                                                gpointer          user_data)
4318 {
4319   GdkRGBA *color = NULL;
4320   GdkRGBA fg;
4321   GdkRGBA *fgp;
4322   GdkRGBA success_color;
4323   GdkRGBA *success_colorp;
4324   GdkRGBA warning_color;
4325   GdkRGBA *warning_colorp;
4326   GdkRGBA error_color;
4327   GdkRGBA *error_colorp;
4328   GtkStateFlags state;
4329
4330   g_return_if_fail (icon_info != NULL);
4331   g_return_if_fail (context != NULL);
4332
4333   fgp = success_colorp = warning_colorp = error_colorp = NULL;
4334
4335   state = gtk_style_context_get_state (context);
4336   gtk_style_context_get (context, state, "color", &color, NULL);
4337   if (color)
4338     {
4339       fg = *color;
4340       fgp = &fg;
4341       gdk_rgba_free (color);
4342     }
4343
4344   if (gtk_style_context_lookup_color (context, "success_color", &success_color))
4345     success_colorp = &success_color;
4346
4347   if (gtk_style_context_lookup_color (context, "warning_color", &warning_color))
4348     warning_colorp = &warning_color;
4349
4350   if (gtk_style_context_lookup_color (context, "error_color", &error_color))
4351     error_colorp = &error_color;
4352
4353   gtk_icon_info_load_symbolic_async (icon_info,
4354                                      fgp, success_colorp,
4355                                      warning_colorp, error_colorp,
4356                                      cancellable, callback, user_data);
4357 }
4358
4359 /**
4360  * gtk_icon_info_load_symbolic_for_context_finish:
4361  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
4362  * @res: a #GAsyncResult
4363  * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
4364  *     loaded icon was a symbolic one and whether the @fg color was
4365  *     applied to it.
4366  * @error: (allow-none): location to store error information on failure,
4367  *     or %NULL.
4368  *
4369  * Finishes an async icon load, see gtk_icon_info_load_symbolic_for_context_async().
4370  *
4371  * Return value: (transfer full): the rendered icon; this may be a newly
4372  *     created icon or a new reference to an internal icon, so you must
4373  *     not modify the icon. Use g_object_unref() to release your reference
4374  *     to the icon.
4375  *
4376  * Since: 3.8
4377  **/
4378 GdkPixbuf *
4379 gtk_icon_info_load_symbolic_for_context_finish (GtkIconInfo      *icon_info,
4380                                                 GAsyncResult     *result,
4381                                                 gboolean         *was_symbolic,
4382                                                 GError          **error)
4383 {
4384   return gtk_icon_info_load_symbolic_finish (icon_info, result, was_symbolic, error);
4385 }
4386
4387 static GdkRGBA *
4388 color_to_rgba (GdkColor *color, GdkRGBA *rgba)
4389 {
4390   rgba->red = color->red / 65535.0;
4391   rgba->green = color->green / 65535.0;
4392   rgba->blue = color->blue / 65535.0;
4393   rgba->alpha = 1.0;
4394   return rgba;
4395 }
4396
4397 /**
4398  * gtk_icon_info_load_symbolic_for_style:
4399  * @icon_info: a #GtkIconInfo
4400  * @style: a #GtkStyle to take the colors from
4401  * @state: the widget state to use for colors
4402  * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
4403  *     loaded icon was a symbolic one and whether the @fg color was
4404  *     applied to it.
4405  * @error: (allow-none): location to store error information on failure,
4406  *     or %NULL.
4407  *
4408  * Loads an icon, modifying it to match the system colours for the foreground,
4409  * success, warning and error colors provided. If the icon is not a symbolic
4410  * one, the function will return the result from gtk_icon_info_load_icon().
4411  *
4412  * This allows loading symbolic icons that will match the system theme.
4413  *
4414  * See gtk_icon_info_load_symbolic() for more details.
4415  *
4416  * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
4417  *
4418  * Since: 3.0
4419  *
4420  * Deprecated: 3.0: Use gtk_icon_info_load_symbolic_for_context() instead
4421  **/
4422 GdkPixbuf *
4423 gtk_icon_info_load_symbolic_for_style (GtkIconInfo   *icon_info,
4424                                        GtkStyle      *style,
4425                                        GtkStateType   state,
4426                                        gboolean      *was_symbolic,
4427                                        GError       **error)
4428 {
4429   GdkColor color;
4430   GdkRGBA fg;
4431   GdkRGBA success_color;
4432   GdkRGBA *success_colorp;
4433   GdkRGBA warning_color;
4434   GdkRGBA *warning_colorp;
4435   GdkRGBA error_color;
4436   GdkRGBA *error_colorp;
4437   gchar *icon_uri;
4438   gboolean is_symbolic;
4439
4440   g_return_val_if_fail (icon_info != NULL, NULL);
4441   g_return_val_if_fail (style != NULL, NULL);
4442
4443   icon_uri = NULL;
4444   if (icon_info->icon_file)
4445     icon_uri = g_file_get_uri (icon_info->icon_file);
4446
4447   is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
4448   g_free (icon_uri);
4449
4450   if (was_symbolic)
4451     *was_symbolic = is_symbolic;
4452
4453   if (!is_symbolic)
4454     return gtk_icon_info_load_icon (icon_info, error);
4455
4456   color_to_rgba (&style->fg[state], &fg);
4457
4458   success_colorp = warning_colorp = error_colorp = NULL;
4459
4460   if (gtk_style_lookup_color (style, "success_color", &color))
4461     success_colorp = color_to_rgba (&color, &success_color);
4462
4463   if (gtk_style_lookup_color (style, "warning_color", &color))
4464     warning_colorp = color_to_rgba (&color, &warning_color);
4465
4466   if (gtk_style_lookup_color (style, "error_color", &color))
4467     error_colorp = color_to_rgba (&color, &error_color);
4468
4469   return _gtk_icon_info_load_symbolic_internal (icon_info,
4470                                                 &fg, success_colorp,
4471                                                 warning_colorp, error_colorp,
4472                                                 TRUE,
4473                                                 error);
4474 }
4475
4476 /**
4477  * gtk_icon_info_set_raw_coordinates:
4478  * @icon_info: a #GtkIconInfo
4479  * @raw_coordinates: whether the coordinates of embedded rectangles
4480  *   and attached points should be returned in their original
4481  *   (unscaled) form.
4482  * 
4483  * Sets whether the coordinates returned by gtk_icon_info_get_embedded_rect()
4484  * and gtk_icon_info_get_attach_points() should be returned in their
4485  * original form as specified in the icon theme, instead of scaled
4486  * appropriately for the pixbuf returned by gtk_icon_info_load_icon().
4487  *
4488  * Raw coordinates are somewhat strange; they are specified to be with
4489  * respect to the unscaled pixmap for PNG and XPM icons, but for SVG
4490  * icons, they are in a 1000x1000 coordinate space that is scaled
4491  * to the final size of the icon.  You can determine if the icon is an SVG
4492  * icon by using gtk_icon_info_get_filename(), and seeing if it is non-%NULL
4493  * and ends in '.svg'.
4494  *
4495  * This function is provided primarily to allow compatibility wrappers
4496  * for older API's, and is not expected to be useful for applications.
4497  *
4498  * Since: 2.4
4499  **/
4500 void
4501 gtk_icon_info_set_raw_coordinates (GtkIconInfo *icon_info,
4502                                    gboolean     raw_coordinates)
4503 {
4504   g_return_if_fail (icon_info != NULL);
4505   
4506   icon_info->raw_coordinates = raw_coordinates != FALSE;
4507 }
4508
4509 /* Scale coordinates from the icon data prior to returning
4510  * them to the user.
4511  */
4512 static gboolean
4513 icon_info_scale_point (GtkIconInfo  *icon_info,
4514                        gint          x,
4515                        gint          y,
4516                        gint         *x_out,
4517                        gint         *y_out)
4518 {
4519   if (icon_info->raw_coordinates)
4520     {
4521       *x_out = x;
4522       *y_out = y;
4523     }
4524   else
4525     {
4526       if (!icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4527         return FALSE;
4528
4529       *x_out = 0.5 + x * icon_info->scale;
4530       *y_out = 0.5 + y * icon_info->scale;
4531     }
4532
4533   return TRUE;
4534 }
4535
4536 /**
4537  * gtk_icon_info_get_embedded_rect:
4538  * @icon_info: a #GtkIconInfo
4539  * @rectangle: (out): #GdkRectangle in which to store embedded
4540  *   rectangle coordinates; coordinates are only stored
4541  *   when this function returns %TRUE.
4542  *
4543  * Gets the coordinates of a rectangle within the icon
4544  * that can be used for display of information such
4545  * as a preview of the contents of a text file.
4546  * See gtk_icon_info_set_raw_coordinates() for further
4547  * information about the coordinate system.
4548  * 
4549  * Return value: %TRUE if the icon has an embedded rectangle
4550  *
4551  * Since: 2.4
4552  **/
4553 gboolean
4554 gtk_icon_info_get_embedded_rect (GtkIconInfo  *icon_info,
4555                                  GdkRectangle *rectangle)
4556 {
4557   g_return_val_if_fail (icon_info != NULL, FALSE);
4558
4559   if (icon_info->data && icon_info->data->has_embedded_rect &&
4560       icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4561     {
4562       gint scaled_x0, scaled_y0;
4563       gint scaled_x1, scaled_y1;
4564       
4565       if (rectangle)
4566         {
4567           icon_info_scale_point (icon_info,
4568                                  icon_info->data->x0, icon_info->data->y0,
4569                                  &scaled_x0, &scaled_y0);
4570           icon_info_scale_point (icon_info,
4571                                  icon_info->data->x1, icon_info->data->y1,
4572                                  &scaled_x1, &scaled_y1);
4573           
4574           rectangle->x = scaled_x0;
4575           rectangle->y = scaled_y0;
4576           rectangle->width = scaled_x1 - rectangle->x;
4577           rectangle->height = scaled_y1 - rectangle->y;
4578         }
4579
4580       return TRUE;
4581     }
4582   else
4583     return FALSE;
4584 }
4585
4586 /**
4587  * gtk_icon_info_get_attach_points:
4588  * @icon_info: a #GtkIconInfo
4589  * @points: (allow-none) (array length=n_points) (out): location to store pointer to an array of points, or %NULL
4590  *          free the array of points with g_free().
4591  * @n_points: (allow-none): location to store the number of points in @points, or %NULL
4592  * 
4593  * Fetches the set of attach points for an icon. An attach point
4594  * is a location in the icon that can be used as anchor points for attaching
4595  * emblems or overlays to the icon.
4596  * 
4597  * Return value: %TRUE if there are any attach points for the icon.
4598  *
4599  * Since: 2.4
4600  **/
4601 gboolean
4602 gtk_icon_info_get_attach_points (GtkIconInfo *icon_info,
4603                                  GdkPoint   **points,
4604                                  gint        *n_points)
4605 {
4606   g_return_val_if_fail (icon_info != NULL, FALSE);
4607   
4608   if (icon_info->data && icon_info->data->n_attach_points &&
4609       icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4610     {
4611       if (points)
4612         {
4613           gint i;
4614           
4615           *points = g_new (GdkPoint, icon_info->data->n_attach_points);
4616           for (i = 0; i < icon_info->data->n_attach_points; i++)
4617             icon_info_scale_point (icon_info,
4618                                    icon_info->data->attach_points[i].x,
4619                                    icon_info->data->attach_points[i].y,
4620                                    &(*points)[i].x,
4621                                    &(*points)[i].y);
4622         }
4623           
4624       if (n_points)
4625         *n_points = icon_info->data->n_attach_points;
4626
4627       return TRUE;
4628     }
4629   else
4630     {
4631       if (points)
4632         *points = NULL;
4633       if (n_points)
4634         *n_points = 0;
4635       
4636       return FALSE;
4637     }
4638 }
4639
4640 /**
4641  * gtk_icon_info_get_display_name:
4642  * @icon_info: a #GtkIconInfo
4643  * 
4644  * Gets the display name for an icon. A display name is a
4645  * string to be used in place of the icon name in a user
4646  * visible context like a list of icons.
4647  * 
4648  * Return value: the display name for the icon or %NULL, if
4649  *  the icon doesn't have a specified display name. This value
4650  *  is owned @icon_info and must not be modified or free.
4651  *
4652  * Since: 2.4
4653  **/
4654 const gchar *
4655 gtk_icon_info_get_display_name (GtkIconInfo *icon_info)
4656 {
4657   g_return_val_if_fail (icon_info != NULL, NULL);
4658
4659   if (icon_info->data)
4660     return icon_info->data->display_name;
4661   else
4662     return NULL;
4663 }
4664
4665 /*
4666  * Builtin icons
4667  */
4668
4669
4670 /**
4671  * gtk_icon_theme_add_builtin_icon:
4672  * @icon_name: the name of the icon to register
4673  * @size: the size at which to register the icon (different
4674  *        images can be registered for the same icon name
4675  *        at different sizes.)
4676  * @pixbuf: #GdkPixbuf that contains the image to use
4677  *          for @icon_name.
4678  * 
4679  * Registers a built-in icon for icon theme lookups. The idea
4680  * of built-in icons is to allow an application or library
4681  * that uses themed icons to function requiring files to
4682  * be present in the file system. For instance, the default
4683  * images for all of GTK+'s stock icons are registered
4684  * as built-icons.
4685  *
4686  * In general, if you use gtk_icon_theme_add_builtin_icon()
4687  * you should also install the icon in the icon theme, so
4688  * that the icon is generally available.
4689  *
4690  * This function will generally be used with pixbufs loaded
4691  * via gdk_pixbuf_new_from_inline().
4692  *
4693  * Since: 2.4
4694  **/
4695 void
4696 gtk_icon_theme_add_builtin_icon (const gchar *icon_name,
4697                                  gint         size,
4698                                  GdkPixbuf   *pixbuf)
4699 {
4700   BuiltinIcon *default_icon;
4701   GSList *icons;
4702   gpointer key;
4703
4704   g_return_if_fail (icon_name != NULL);
4705   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
4706   
4707   if (!icon_theme_builtin_icons)
4708     icon_theme_builtin_icons = g_hash_table_new (g_str_hash, g_str_equal);
4709
4710   icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
4711   if (!icons)
4712     key = g_strdup (icon_name);
4713   else
4714     key = (gpointer)icon_name;  /* Won't get stored */
4715
4716   default_icon = g_new (BuiltinIcon, 1);
4717   default_icon->size = size;
4718   default_icon->pixbuf = g_object_ref (pixbuf);
4719   icons = g_slist_prepend (icons, default_icon);
4720
4721   /* Replaces value, leaves key untouched
4722    */
4723   g_hash_table_insert (icon_theme_builtin_icons, key, icons);
4724 }
4725
4726 /* Look up a builtin icon; the min_difference_p and
4727  * has_larger_p out parameters allow us to combine
4728  * this lookup with searching through the actual directories
4729  * of the "hicolor" icon theme. See theme_lookup_icon()
4730  * for how they are used.
4731  */
4732 static BuiltinIcon *
4733 find_builtin_icon (const gchar *icon_name,
4734                    gint         size,
4735                    gint        *min_difference_p,
4736                    gboolean    *has_larger_p)
4737 {
4738   GSList *icons = NULL;
4739   gint min_difference = G_MAXINT;
4740   gboolean has_larger = FALSE;
4741   BuiltinIcon *min_icon = NULL;
4742   
4743   if (!icon_theme_builtin_icons)
4744     return NULL;
4745
4746   icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
4747
4748   while (icons)
4749     {
4750       BuiltinIcon *default_icon = icons->data;
4751       int min, max, difference;
4752       gboolean smaller;
4753       
4754       min = default_icon->size - 2;
4755       max = default_icon->size + 2;
4756       smaller = size < min;
4757       if (size < min)
4758         difference = min - size;
4759       else if (size > max)
4760         difference = size - max;
4761       else
4762         difference = 0;
4763       
4764       if (difference == 0)
4765         {
4766           min_icon = default_icon;
4767           break;
4768         }
4769       
4770       if (!has_larger)
4771         {
4772           if (difference < min_difference || smaller)
4773             {
4774               min_difference = difference;
4775               min_icon = default_icon;
4776               has_larger = smaller;
4777             }
4778         }
4779       else
4780         {
4781           if (difference < min_difference && smaller)
4782             {
4783               min_difference = difference;
4784               min_icon = default_icon;
4785             }
4786         }
4787       
4788       icons = icons->next;
4789     }
4790
4791   if (min_difference_p)
4792     *min_difference_p = min_difference;
4793   if (has_larger_p)
4794     *has_larger_p = has_larger;
4795
4796   return min_icon;
4797 }
4798
4799 void
4800 _gtk_icon_theme_check_reload (GdkDisplay *display)
4801 {
4802   gint n_screens, i;
4803   GdkScreen *screen;
4804   GtkIconTheme *icon_theme;
4805
4806   n_screens = gdk_display_get_n_screens (display);
4807   
4808   for (i = 0; i < n_screens; i++)
4809     {
4810       screen = gdk_display_get_screen (display, i);
4811
4812       icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
4813       if (icon_theme)
4814         {
4815           icon_theme->priv->check_reload = TRUE;
4816           ensure_valid_themes (icon_theme);
4817           icon_theme->priv->check_reload = FALSE;
4818         }
4819     }
4820 }
4821
4822
4823 /**
4824  * gtk_icon_theme_lookup_by_gicon:
4825  * @icon_theme: a #GtkIconTheme
4826  * @icon: the #GIcon to look up
4827  * @size: desired icon size
4828  * @flags: flags modifying the behavior of the icon lookup
4829  * 
4830  * Looks up an icon and returns a structure containing
4831  * information such as the filename of the icon. 
4832  * The icon can then be rendered into a pixbuf using
4833  * gtk_icon_info_load_icon().
4834  *
4835  * Return value: (transfer full): a #GtkIconInfo structure containing 
4836  *     information about the icon, or %NULL if the icon 
4837  *     wasn't found. Unref with g_object_unref()
4838  *
4839  * Since: 2.14
4840  */
4841 GtkIconInfo *
4842 gtk_icon_theme_lookup_by_gicon (GtkIconTheme       *icon_theme,
4843                                 GIcon              *icon,
4844                                 gint                size,
4845                                 GtkIconLookupFlags  flags)
4846 {
4847   GtkIconInfo *info;
4848
4849   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
4850   g_return_val_if_fail (G_IS_ICON (icon), NULL);
4851
4852   if (G_IS_LOADABLE_ICON (icon))
4853     {
4854       info = icon_info_new ();
4855       info->loadable = G_LOADABLE_ICON (g_object_ref (icon));
4856
4857       if (G_IS_FILE_ICON (icon))
4858         {
4859           GFile *file = g_file_icon_get_file (G_FILE_ICON (icon));
4860           if (file != NULL)
4861             {
4862               info->icon_file = g_object_ref (file);
4863               info->filename = g_file_get_path (file);
4864             }
4865         }
4866
4867       info->dir_type = ICON_THEME_DIR_UNTHEMED;
4868       info->dir_size = size;
4869       info->desired_size = size;
4870       info->threshold = 2;
4871       info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
4872
4873       return info;
4874     }
4875   else if (G_IS_THEMED_ICON (icon))
4876     {
4877       const gchar **names;
4878
4879       names = (const gchar **)g_themed_icon_get_names (G_THEMED_ICON (icon));
4880       info = gtk_icon_theme_choose_icon (icon_theme, names, size, flags);
4881
4882       return info;
4883     }
4884   else if (G_IS_EMBLEMED_ICON (icon))
4885     {
4886       GIcon *base, *emblem;
4887       GList *list, *l;
4888       GtkIconInfo *emblem_info;
4889
4890       if (GTK_IS_NUMERABLE_ICON (icon))
4891         _gtk_numerable_icon_set_background_icon_size (GTK_NUMERABLE_ICON (icon), size / 2);
4892
4893       base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
4894       info = gtk_icon_theme_lookup_by_gicon (icon_theme, base, size, flags);
4895       if (info)
4896         {
4897           list = g_emblemed_icon_get_emblems (G_EMBLEMED_ICON (icon));
4898           for (l = list; l; l = l->next)
4899             {
4900               emblem = g_emblem_get_icon (G_EMBLEM (l->data));
4901               /* always force size for emblems */
4902               emblem_info = gtk_icon_theme_lookup_by_gicon (icon_theme, emblem, size / 2, flags | GTK_ICON_LOOKUP_FORCE_SIZE);
4903               if (emblem_info)
4904                 info->emblem_infos = g_slist_prepend (info->emblem_infos, emblem_info);
4905             }
4906         }
4907
4908       return info;
4909     }
4910   else if (GDK_IS_PIXBUF (icon))
4911     {
4912       GdkPixbuf *pixbuf;
4913
4914       pixbuf = GDK_PIXBUF (icon);
4915
4916       if ((flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0)
4917         {
4918           gint width, height, max;
4919           gdouble scale;
4920           GdkPixbuf *scaled;
4921
4922           width = gdk_pixbuf_get_width (pixbuf);
4923           height = gdk_pixbuf_get_height (pixbuf);
4924           max = MAX (width, height);
4925           scale = (gdouble) size / (gdouble) max;
4926
4927           scaled = gdk_pixbuf_scale_simple (pixbuf,
4928                                             0.5 + width * scale,
4929                                             0.5 + height * scale,
4930                                             GDK_INTERP_BILINEAR);
4931
4932           info = gtk_icon_info_new_for_pixbuf (icon_theme, scaled);
4933
4934           g_object_unref (scaled);
4935         }
4936       else
4937         {
4938           info = gtk_icon_info_new_for_pixbuf (icon_theme, pixbuf);
4939         }
4940
4941       return info;
4942     }
4943
4944   return NULL;
4945 }
4946
4947 /**
4948  * gtk_icon_info_new_for_pixbuf:
4949  * @icon_theme: a #GtkIconTheme
4950  * @pixbuf: the pixbuf to wrap in a #GtkIconInfo
4951  *
4952  * Creates a #GtkIconInfo for a #GdkPixbuf.
4953  *
4954  * Return value: (transfer full): a #GtkIconInfo
4955  *
4956  * Since: 2.14
4957  */
4958 GtkIconInfo *
4959 gtk_icon_info_new_for_pixbuf (GtkIconTheme *icon_theme,
4960                               GdkPixbuf    *pixbuf)
4961 {
4962   GtkIconInfo *info;
4963
4964   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
4965   g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
4966
4967   info = icon_info_new ();
4968   info->pixbuf = g_object_ref (pixbuf);
4969   info->scale = 1.0;
4970   info->dir_type = ICON_THEME_DIR_UNTHEMED;
4971
4972   return info;
4973 }