]> Pileus Git - ~andy/gtk/blob - gtk/gtkicontheme.c
stylecontext: Do invalidation on first resize container
[~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 it's much more
113  * efficient to use the standard icon theme for the #GdkScreen
114  * so that the icon information is shared with other people
115  * looking up icons. In the case where the default screen is
116  * being used, looking up an icon can be as simple as:
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   if (icon_info->pixbuf)
3031     dup->pixbuf = g_object_ref (icon_info->pixbuf);
3032
3033   for (l = icon_info->emblem_infos; l != NULL; l = l->next)
3034     {
3035       dup->emblem_infos =
3036         g_slist_append (dup->emblem_infos,
3037                         icon_info_dup (l->data));
3038     }
3039
3040   if (icon_info->cache_pixbuf)
3041     dup->cache_pixbuf = g_object_ref (icon_info->cache_pixbuf);
3042
3043   dup->data = icon_data_dup (icon_info->data);
3044   dup->dir_type = icon_info->dir_type;
3045   dup->dir_size = icon_info->dir_size;
3046   dup->threshold = icon_info->threshold;
3047   dup->desired_size = icon_info->desired_size;
3048   dup->raw_coordinates = icon_info->raw_coordinates;
3049   dup->forced_size = icon_info->forced_size;
3050   dup->emblems_applied = icon_info->emblems_applied;
3051
3052   return dup;
3053 }
3054
3055 static GtkIconInfo *
3056 icon_info_new_builtin (BuiltinIcon *icon)
3057 {
3058   GtkIconInfo *icon_info = icon_info_new ();
3059
3060   icon_info->cache_pixbuf = g_object_ref (icon->pixbuf);
3061   icon_info->dir_type = ICON_THEME_DIR_THRESHOLD;
3062   icon_info->dir_size = icon->size;
3063   icon_info->threshold = 2;
3064
3065   return icon_info;
3066 }
3067
3068 /**
3069  * gtk_icon_info_copy: (skip)
3070  * @icon_info: a #GtkIconInfo
3071  * 
3072  * Make a copy of a #GtkIconInfo.
3073  * 
3074  * Return value: the new GtkIconInfo
3075  *
3076  * Since: 2.4
3077  *
3078  * Deprecated: 3.8: Use g_object_ref()
3079  **/
3080 GtkIconInfo *
3081 gtk_icon_info_copy (GtkIconInfo *icon_info)
3082 {
3083   
3084   g_return_val_if_fail (icon_info != NULL, NULL);
3085
3086   return g_object_ref (icon_info);
3087 }
3088
3089 /**
3090  * gtk_icon_info_free: (skip)
3091  * @icon_info: a #GtkIconInfo
3092  * 
3093  * Free a #GtkIconInfo and associated information
3094  *
3095  * Since: 2.4
3096  *
3097  * Deprecated: 3.8: Use g_object_unref()
3098  **/
3099 void
3100 gtk_icon_info_free (GtkIconInfo *icon_info)
3101 {
3102   g_return_if_fail (icon_info != NULL);
3103
3104   g_object_unref (icon_info);
3105 }
3106
3107 static void
3108 gtk_icon_info_finalize (GObject *object)
3109 {
3110   GtkIconInfo *icon_info = (GtkIconInfo *) object;
3111
3112   if (icon_info->in_cache)
3113     g_hash_table_remove (icon_info->in_cache->priv->info_cache, &icon_info->key);
3114
3115   g_strfreev (icon_info->key.icon_names);
3116
3117   g_free (icon_info->filename);
3118   g_clear_object (&icon_info->icon_file);
3119
3120   if (icon_info->loadable)
3121     g_object_unref (icon_info->loadable);
3122   g_slist_free_full (icon_info->emblem_infos, (GDestroyNotify) g_object_unref);
3123   if (icon_info->pixbuf)
3124     g_object_unref (icon_info->pixbuf);
3125   if (icon_info->cache_pixbuf)
3126     g_object_unref (icon_info->cache_pixbuf);
3127   if (icon_info->symbolic_pixbuf_size)
3128     gtk_requisition_free (icon_info->symbolic_pixbuf_size);
3129
3130   symbolic_pixbuf_cache_free (icon_info->symbolic_pixbuf_cache);
3131
3132   G_OBJECT_CLASS (gtk_icon_info_parent_class)->finalize (object);
3133 }
3134
3135 static void
3136 gtk_icon_info_class_init (GtkIconInfoClass *klass)
3137 {
3138   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3139
3140   gobject_class->finalize = gtk_icon_info_finalize;
3141 }
3142
3143 /**
3144  * gtk_icon_info_get_base_size:
3145  * @icon_info: a #GtkIconInfo
3146  * 
3147  * Gets the base size for the icon. The base size
3148  * is a size for the icon that was specified by
3149  * the icon theme creator. This may be different
3150  * than the actual size of image; an example of
3151  * this is small emblem icons that can be attached
3152  * to a larger icon. These icons will be given
3153  * the same base size as the larger icons to which
3154  * they are attached.
3155  * 
3156  * Return value: the base size, or 0, if no base
3157  *  size is known for the icon.
3158  *
3159  * Since: 2.4
3160  **/
3161 gint
3162 gtk_icon_info_get_base_size (GtkIconInfo *icon_info)
3163 {
3164   g_return_val_if_fail (icon_info != NULL, 0);
3165
3166   return icon_info->dir_size;
3167 }
3168
3169 /**
3170  * gtk_icon_info_get_filename:
3171  * @icon_info: a #GtkIconInfo
3172  * 
3173  * Gets the filename for the icon. If the
3174  * %GTK_ICON_LOOKUP_USE_BUILTIN flag was passed
3175  * to gtk_icon_theme_lookup_icon(), there may be
3176  * no filename if a builtin icon is returned; in this
3177  * case, you should use gtk_icon_info_get_builtin_pixbuf().
3178  * 
3179  * Return value: (type filename): the filename for the icon, or %NULL
3180  *  if gtk_icon_info_get_builtin_pixbuf() should be used instead. The
3181  *  return value is owned by GTK+ and should not be modified or freed.
3182  *
3183  * Since: 2.4
3184  **/
3185 const gchar *
3186 gtk_icon_info_get_filename (GtkIconInfo *icon_info)
3187 {
3188   g_return_val_if_fail (icon_info != NULL, NULL);
3189
3190   return icon_info->filename;
3191 }
3192
3193 /**
3194  * gtk_icon_info_get_builtin_pixbuf:
3195  * @icon_info: a #GtkIconInfo structure
3196  * 
3197  * Gets the built-in image for this icon, if any. To allow
3198  * GTK+ to use built in icon images, you must pass the
3199  * %GTK_ICON_LOOKUP_USE_BUILTIN to
3200  * gtk_icon_theme_lookup_icon().
3201  *
3202  * Return value: (transfer none): the built-in image pixbuf, or %NULL. No
3203  *  extra reference is added to the returned pixbuf, so if
3204  *  you want to keep it around, you must use g_object_ref().
3205  *  The returned image must not be modified.
3206  *
3207  * Since: 2.4
3208  **/
3209 GdkPixbuf *
3210 gtk_icon_info_get_builtin_pixbuf (GtkIconInfo *icon_info)
3211 {
3212   g_return_val_if_fail (icon_info != NULL, NULL);
3213
3214   if (icon_info->filename)
3215     return NULL;
3216   
3217   return icon_info->cache_pixbuf;
3218 }
3219
3220 static gboolean icon_info_ensure_scale_and_pixbuf (GtkIconInfo*, gboolean);
3221
3222 /* Combine the icon with all emblems, the first emblem is placed 
3223  * in the southeast corner. Scale emblems to be at most 3/4 of the
3224  * size of the icon itself.
3225  */
3226 static void 
3227 apply_emblems (GtkIconInfo *info)
3228 {
3229   GdkPixbuf *icon = NULL;
3230   gint w, h, pos;
3231   GSList *l;
3232
3233   if (info->emblem_infos == NULL)
3234     return;
3235
3236   if (info->emblems_applied)
3237     return;
3238
3239   w = gdk_pixbuf_get_width (info->pixbuf);
3240   h = gdk_pixbuf_get_height (info->pixbuf);
3241
3242   for (l = info->emblem_infos, pos = 0; l; l = l->next, pos++)
3243     {
3244       GtkIconInfo *emblem_info = l->data;
3245
3246       if (icon_info_ensure_scale_and_pixbuf (emblem_info, FALSE))
3247         {
3248           GdkPixbuf *emblem = emblem_info->pixbuf;
3249           gint ew, eh;
3250           gint x = 0, y = 0; /* silence compiler */
3251           gdouble scale;
3252
3253           ew = gdk_pixbuf_get_width (emblem);
3254           eh = gdk_pixbuf_get_height (emblem);
3255           if (ew >= w)
3256             {
3257               scale = 0.75;
3258               ew = ew * 0.75;
3259               eh = eh * 0.75;
3260             }
3261           else
3262             scale = 1.0;
3263
3264           switch (pos % 4)
3265             {
3266             case 0:
3267               x = w - ew;
3268               y = h - eh;
3269               break;
3270             case 1:
3271               x = w - ew;
3272               y = 0;
3273               break;
3274             case 2:
3275               x = 0;
3276               y = h - eh;
3277               break;
3278             case 3:
3279               x = 0;
3280               y = 0;
3281               break;
3282             }
3283
3284           if (icon == NULL)
3285             {
3286               icon = gdk_pixbuf_copy (info->pixbuf);
3287               if (icon == NULL)
3288                 break;
3289             }
3290
3291           gdk_pixbuf_composite (emblem, icon, x, y, ew, eh, x, y,
3292                                 scale, scale, GDK_INTERP_BILINEAR, 255);
3293        }
3294    }
3295
3296   if (icon)
3297     {
3298       g_object_unref (info->pixbuf);
3299       info->pixbuf = icon;
3300     }
3301
3302   info->emblems_applied = TRUE;
3303 }
3304
3305 /* If this returns TRUE, its safe to call
3306    icon_info_ensure_scale_and_pixbuf without blocking */
3307 static gboolean
3308 icon_info_get_pixbuf_ready (GtkIconInfo *icon_info)
3309 {
3310   if (icon_info->pixbuf &&
3311       (icon_info->emblem_infos == NULL || icon_info->emblems_applied))
3312     return TRUE;
3313
3314   if (icon_info->load_error)
3315     return TRUE;
3316
3317   return FALSE;
3318 }
3319
3320 /* This function contains the complicated logic for deciding
3321  * on the size at which to load the icon and loading it at
3322  * that size.
3323  */
3324 static gboolean
3325 icon_info_ensure_scale_and_pixbuf (GtkIconInfo  *icon_info,
3326                                    gboolean      scale_only)
3327 {
3328   int image_width, image_height;
3329   GdkPixbuf *source_pixbuf;
3330   gboolean is_svg;
3331
3332   /* First check if we already succeeded have the necessary
3333    * information (or failed earlier)
3334    */
3335   if (scale_only && icon_info->scale >= 0)
3336     return TRUE;
3337
3338   if (icon_info->pixbuf)
3339     {
3340       apply_emblems (icon_info);
3341       return TRUE;
3342     }
3343
3344   if (icon_info->load_error)
3345     return FALSE;
3346
3347   /* SVG icons are a special case - we just immediately scale them
3348    * to the desired size
3349    */
3350   if (icon_info->icon_file && !icon_info->loadable)
3351     icon_info->loadable = G_LOADABLE_ICON (g_file_icon_new (icon_info->icon_file));
3352
3353   is_svg = FALSE;
3354   if (G_IS_FILE_ICON (icon_info->loadable))
3355     {
3356       GFile *file;
3357       GFileInfo *file_info;
3358       const gchar *content_type;
3359
3360       file = g_file_icon_get_file (G_FILE_ICON (icon_info->loadable));
3361       file_info = g_file_query_info (file, 
3362                                      G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
3363                                      G_FILE_QUERY_INFO_NONE,
3364                                      NULL, NULL);
3365       if (file_info) 
3366         {
3367           content_type = g_file_info_get_content_type (file_info);
3368
3369           if (content_type && strcmp (content_type, "image/svg+xml") == 0)
3370             is_svg = TRUE;
3371
3372           g_object_unref (file_info);
3373        }
3374     }
3375
3376   if (is_svg)
3377     {
3378       GInputStream *stream;
3379
3380       icon_info->scale = icon_info->desired_size / 1000.;
3381
3382       if (scale_only)
3383         return TRUE;
3384       
3385       stream = g_loadable_icon_load (icon_info->loadable,
3386                                      icon_info->desired_size,
3387                                      NULL, NULL,
3388                                      &icon_info->load_error);
3389       if (stream)
3390         {
3391           icon_info->pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3392                                                                    icon_info->desired_size,
3393                                                                    icon_info->desired_size,
3394                                                                    TRUE,
3395                                                                    NULL,
3396                                                                    &icon_info->load_error);
3397           g_object_unref (stream);
3398         }
3399
3400       if (!icon_info->pixbuf)
3401         return FALSE;
3402
3403       apply_emblems (icon_info);
3404         
3405       return TRUE;
3406     }
3407
3408   /* In many cases, the scale can be determined without actual access
3409    * to the icon file. This is generally true when we have a size
3410    * for the directory where the icon is; the image size doesn't
3411    * matter in that case.
3412    */
3413   if (icon_info->forced_size)
3414     icon_info->scale = -1;
3415   else if (icon_info->dir_type == ICON_THEME_DIR_FIXED)
3416     icon_info->scale = 1.0;
3417   else if (icon_info->dir_type == ICON_THEME_DIR_THRESHOLD)
3418     {
3419       if (icon_info->desired_size >= icon_info->dir_size - icon_info->threshold &&
3420           icon_info->desired_size <= icon_info->dir_size + icon_info->threshold)
3421         icon_info->scale = 1.0;
3422       else if (icon_info->dir_size > 0)
3423         icon_info->scale =(gdouble) icon_info->desired_size / icon_info->dir_size;
3424     }
3425   else if (icon_info->dir_type == ICON_THEME_DIR_SCALABLE)
3426     {
3427       if (icon_info->dir_size > 0)
3428         icon_info->scale = (gdouble) icon_info->desired_size / icon_info->dir_size;
3429     }
3430
3431   if (icon_info->scale >= 0. && scale_only)
3432     return TRUE;
3433
3434   /* At this point, we need to actually get the icon; either from the
3435    * builtin image or by loading the file
3436    */
3437   source_pixbuf = NULL;
3438   if (icon_info->cache_pixbuf)
3439     source_pixbuf = g_object_ref (icon_info->cache_pixbuf);
3440   else
3441     {
3442       GInputStream *stream;
3443
3444       stream = g_loadable_icon_load (icon_info->loadable,
3445                                      icon_info->desired_size,
3446                                      NULL, NULL,
3447                                      &icon_info->load_error);
3448       if (stream)
3449         {
3450           source_pixbuf = gdk_pixbuf_new_from_stream (stream,
3451                                                       NULL,
3452                                                       &icon_info->load_error);
3453           g_object_unref (stream);
3454         }
3455     }
3456
3457   if (!source_pixbuf)
3458     return FALSE;
3459
3460   /* Do scale calculations that depend on the image size
3461    */
3462   image_width = gdk_pixbuf_get_width (source_pixbuf);
3463   image_height = gdk_pixbuf_get_height (source_pixbuf);
3464
3465   if (icon_info->scale < 0.0)
3466     {
3467       gint image_size = MAX (image_width, image_height);
3468       if (image_size > 0)
3469         icon_info->scale = (gdouble)icon_info->desired_size / (gdouble)image_size;
3470       else
3471         icon_info->scale = 1.0;
3472       
3473       if (icon_info->dir_type == ICON_THEME_DIR_UNTHEMED && 
3474           !icon_info->forced_size)
3475         icon_info->scale = MIN (icon_info->scale, 1.0);
3476     }
3477
3478   /* We don't short-circuit out here for scale_only, since, now
3479    * we've loaded the icon, we might as well go ahead and finish
3480    * the job. This is a bit of a waste when we scale here
3481    * and never get the final pixbuf; at the cost of a bit of
3482    * extra complexity, we could keep the source pixbuf around
3483    * but not actually scale it until needed.
3484    */
3485   if (icon_info->scale == 1.0)
3486     icon_info->pixbuf = source_pixbuf;
3487   else
3488     {
3489       icon_info->pixbuf = gdk_pixbuf_scale_simple (source_pixbuf,
3490                                                    0.5 + image_width * icon_info->scale,
3491                                                    0.5 + image_height * icon_info->scale,
3492                                                    GDK_INTERP_BILINEAR);
3493       g_object_unref (source_pixbuf);
3494     }
3495
3496   apply_emblems (icon_info);
3497
3498   return TRUE;
3499 }
3500
3501 static void
3502 proxy_pixbuf_destroy (guchar *pixels, gpointer data)
3503 {
3504   GtkIconInfo *icon_info = data;
3505   GtkIconTheme *icon_theme = icon_info->in_cache;
3506
3507   g_assert (icon_info->proxy_pixbuf != NULL);
3508   icon_info->proxy_pixbuf = NULL;
3509
3510   /* Keep it alive a bit longer */
3511   if (icon_theme != NULL)
3512     ensure_in_lru_cache (icon_theme, icon_info);
3513
3514   g_object_unref (icon_info);
3515 }
3516
3517 /**
3518  * gtk_icon_info_load_icon:
3519  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3520  * @error: (allow-none): location to store error information on failure,
3521  *     or %NULL.
3522  *
3523  * Renders an icon previously looked up in an icon theme using
3524  * gtk_icon_theme_lookup_icon(); the size will be based on the size
3525  * passed to gtk_icon_theme_lookup_icon(). Note that the resulting
3526  * pixbuf may not be exactly this size; an icon theme may have icons
3527  * that differ slightly from their nominal sizes, and in addition GTK+
3528  * will avoid scaling icons that it considers sufficiently close to the
3529  * requested size or for which the source image would have to be scaled
3530  * up too far. (This maintains sharpness.). This behaviour can be changed
3531  * by passing the %GTK_ICON_LOOKUP_FORCE_SIZE flag when obtaining
3532  * the #GtkIconInfo. If this flag has been specified, the pixbuf
3533  * returned by this function will be scaled to the exact size.
3534  *
3535  * Return value: (transfer full): the rendered icon; this may be a newly
3536  *     created icon or a new reference to an internal icon, so you must
3537  *     not modify the icon. Use g_object_unref() to release your reference
3538  *     to the icon.
3539  *
3540  * Since: 2.4
3541  **/
3542 GdkPixbuf *
3543 gtk_icon_info_load_icon (GtkIconInfo *icon_info,
3544                          GError     **error)
3545 {
3546   g_return_val_if_fail (icon_info != NULL, NULL);
3547   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3548
3549   if (!icon_info_ensure_scale_and_pixbuf (icon_info, FALSE))
3550     {
3551       if (icon_info->load_error)
3552         g_propagate_error (error, icon_info->load_error);
3553       else
3554         g_set_error_literal (error,  
3555                              GTK_ICON_THEME_ERROR,  
3556                              GTK_ICON_THEME_NOT_FOUND,
3557                              _("Failed to load icon"));
3558  
3559       return NULL;
3560     }
3561
3562   /* Instead of returning the pixbuf directly we
3563      return a proxy to it that we don't own (but that
3564      shares the data with the one we own). This way
3565      we can know when it is freed and ensure the
3566      IconInfo is alive (and thus cached) while
3567      the pixbuf is still alive. */
3568
3569   if (icon_info->proxy_pixbuf != NULL)
3570     return g_object_ref (icon_info->proxy_pixbuf);
3571
3572   icon_info->proxy_pixbuf =
3573     gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (icon_info->pixbuf),
3574                               gdk_pixbuf_get_colorspace (icon_info->pixbuf),
3575                               gdk_pixbuf_get_has_alpha (icon_info->pixbuf),
3576                               gdk_pixbuf_get_bits_per_sample (icon_info->pixbuf),
3577                               gdk_pixbuf_get_width (icon_info->pixbuf),
3578                               gdk_pixbuf_get_height (icon_info->pixbuf),
3579                               gdk_pixbuf_get_rowstride (icon_info->pixbuf),
3580                               proxy_pixbuf_destroy,
3581                               g_object_ref (icon_info));
3582
3583   return icon_info->proxy_pixbuf;
3584 }
3585
3586 static void
3587 load_icon_thread  (GTask           *task,
3588                    gpointer         source_object,
3589                    gpointer         task_data,
3590                    GCancellable    *cancellable)
3591 {
3592   GtkIconInfo *dup = task_data;
3593
3594   icon_info_ensure_scale_and_pixbuf (dup, FALSE);
3595   g_task_return_pointer (task, NULL, NULL);
3596 }
3597
3598 /**
3599  * gtk_icon_info_load_icon_async:
3600  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3601  * @cancellable: (allow-none): optional #GCancellable object,
3602  *     %NULL to ignore
3603  * @callback: (scope async): a #GAsyncReadyCallback to call when the
3604  *     request is satisfied
3605  * @user_data: (closure): the data to pass to callback function
3606  *
3607  * Asynchronously load, render and scale an icon previously looked up
3608  * from the icon theme using gtk_icon_theme_lookup_icon().
3609  *
3610  * For more details, see gtk_icon_info_load_icon() which is the synchronous
3611  * version of this call.
3612  *
3613  * Since: 3.8
3614  **/
3615 void
3616 gtk_icon_info_load_icon_async (GtkIconInfo          *icon_info,
3617                                GCancellable         *cancellable,
3618                                GAsyncReadyCallback   callback,
3619                                gpointer              user_data)
3620 {
3621   GTask *task;
3622   GdkPixbuf *pixbuf;
3623   GtkIconInfo *dup;
3624   GError *error = NULL;
3625
3626   task = g_task_new (icon_info, cancellable, callback, user_data);
3627
3628   if (icon_info_get_pixbuf_ready (icon_info))
3629     {
3630       pixbuf = gtk_icon_info_load_icon (icon_info, &error);
3631       if (pixbuf == NULL)
3632         g_task_return_error (task, error);
3633       else
3634         g_task_return_pointer (task, pixbuf, g_object_unref);
3635       g_object_unref (task);
3636     }
3637   else
3638     {
3639       dup = icon_info_dup (icon_info);
3640       g_task_set_task_data (task, dup, g_object_unref);
3641       g_task_run_in_thread (task, load_icon_thread);
3642       g_object_unref (task);
3643     }
3644 }
3645
3646 /**
3647  * gtk_icon_info_load_icon_finish:
3648  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3649  * @res: a #GAsyncResult
3650  * @error: (allow-none): location to store error information on failure,
3651  *     or %NULL.
3652  *
3653  * Finishes an async icon load, see gtk_icon_info_load_icon_async().
3654  *
3655  * Return value: (transfer full): the rendered icon; this may be a newly
3656  *     created icon or a new reference to an internal icon, so you must
3657  *     not modify the icon. Use g_object_unref() to release your reference
3658  *     to the icon.
3659  *
3660  * Since: 3.8
3661  **/
3662 GdkPixbuf *
3663 gtk_icon_info_load_icon_finish (GtkIconInfo          *icon_info,
3664                                 GAsyncResult         *result,
3665                                 GError              **error)
3666 {
3667   GTask *task = G_TASK (result);
3668   GtkIconInfo *dup;
3669
3670   g_return_val_if_fail (g_task_is_valid (result, icon_info), NULL);
3671
3672   dup = g_task_get_task_data (task);
3673   if (dup == NULL || g_task_had_error (task))
3674     return g_task_propagate_pointer (task, error);
3675
3676   /* We ran the thread and it was not cancelled */
3677
3678   /* Check if someone else updated the icon_info in between */
3679   if (!icon_info_get_pixbuf_ready (icon_info))
3680     {
3681       /* If not, copy results from dup back to icon_info */
3682
3683       icon_info->emblems_applied = dup->emblems_applied;
3684       icon_info->scale = dup->scale;
3685       g_clear_object (&icon_info->pixbuf);
3686       if (dup->pixbuf)
3687         icon_info->pixbuf = g_object_ref (dup->pixbuf);
3688       g_clear_error (&icon_info->load_error);
3689       if (dup->load_error)
3690         icon_info->load_error = g_error_copy (dup->load_error);
3691     }
3692
3693   g_assert (icon_info_get_pixbuf_ready (icon_info));
3694
3695   /* This is now guaranteed to not block */
3696   return gtk_icon_info_load_icon (icon_info, error);
3697 }
3698
3699 static gchar *
3700 gdk_color_to_css (GdkColor *color)
3701 {
3702   return g_strdup_printf ("rgb(%d,%d,%d)",
3703                           color->red >> 8,
3704                           color->green >> 8,
3705                           color->blue >> 8);
3706 }
3707
3708 static gchar *
3709 gdk_rgba_to_css (const GdkRGBA *color)
3710 {
3711   /* drop alpha for now, since librsvg does not understand rgba() */
3712   return g_strdup_printf ("rgb(%d,%d,%d)",
3713                           (gint)(color->red * 255),
3714                           (gint)(color->green * 255),
3715                           (gint)(color->blue * 255));
3716 }
3717
3718 static void
3719 proxy_symbolic_pixbuf_destroy (guchar *pixels, gpointer data)
3720 {
3721   GtkIconInfo *icon_info = data;
3722   GtkIconTheme *icon_theme = icon_info->in_cache;
3723   SymbolicPixbufCache *symbolic_cache;
3724
3725   for (symbolic_cache = icon_info->symbolic_pixbuf_cache;
3726        symbolic_cache != NULL;
3727        symbolic_cache = symbolic_cache->next)
3728     {
3729       if (symbolic_cache->proxy_pixbuf != NULL &&
3730           gdk_pixbuf_get_pixels (symbolic_cache->proxy_pixbuf) == pixels)
3731         break;
3732     }
3733
3734   g_assert (symbolic_cache != NULL);
3735   g_assert (symbolic_cache->proxy_pixbuf != NULL);
3736
3737   symbolic_cache->proxy_pixbuf = NULL;
3738
3739   /* Keep it alive a bit longer */
3740   if (icon_theme != NULL)
3741     ensure_in_lru_cache (icon_theme, icon_info);
3742
3743   g_object_unref (icon_info);
3744 }
3745
3746 static GdkPixbuf *
3747 symbolic_cache_get_proxy (SymbolicPixbufCache *symbolic_cache,
3748                           GtkIconInfo  *icon_info)
3749 {
3750   if (symbolic_cache->proxy_pixbuf)
3751     return g_object_ref (symbolic_cache->proxy_pixbuf);
3752
3753   symbolic_cache->proxy_pixbuf =
3754     gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (symbolic_cache->pixbuf),
3755                               gdk_pixbuf_get_colorspace (symbolic_cache->pixbuf),
3756                               gdk_pixbuf_get_has_alpha (symbolic_cache->pixbuf),
3757                               gdk_pixbuf_get_bits_per_sample (symbolic_cache->pixbuf),
3758                               gdk_pixbuf_get_width (symbolic_cache->pixbuf),
3759                               gdk_pixbuf_get_height (symbolic_cache->pixbuf),
3760                               gdk_pixbuf_get_rowstride (symbolic_cache->pixbuf),
3761                               proxy_symbolic_pixbuf_destroy,
3762                               g_object_ref (icon_info));
3763
3764   return symbolic_cache->proxy_pixbuf;
3765 }
3766
3767 static GdkPixbuf *
3768 _gtk_icon_info_load_symbolic_internal (GtkIconInfo  *icon_info,
3769                                        const GdkRGBA  *fg,
3770                                        const GdkRGBA  *success_color,
3771                                        const GdkRGBA  *warning_color,
3772                                        const GdkRGBA  *error_color,
3773                                        gboolean        use_cache,
3774                                        GError        **error)
3775 {
3776   GInputStream *stream;
3777   GdkPixbuf *pixbuf;
3778   gchar *css_fg;
3779   gchar *css_success;
3780   gchar *css_warning;
3781   gchar *css_error;
3782   gchar *data;
3783   gchar *width, *height, *uri;
3784   SymbolicPixbufCache *symbolic_cache;
3785
3786   if (use_cache)
3787     {
3788       symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
3789                                                       fg, success_color, warning_color, error_color);
3790       if (symbolic_cache)
3791         return symbolic_cache_get_proxy (symbolic_cache, icon_info);
3792     }
3793
3794   /* css_fg can't possibly have failed, otherwise
3795    * that would mean we have a broken style */
3796   g_return_val_if_fail (fg != NULL, NULL);
3797
3798   css_fg = gdk_rgba_to_css (fg);
3799
3800   css_success = css_warning = css_error = NULL;
3801
3802   if (warning_color)
3803     css_warning = gdk_rgba_to_css (warning_color);
3804
3805   if (error_color)
3806     css_error = gdk_rgba_to_css (error_color);
3807
3808   if (success_color)
3809     css_success = gdk_rgba_to_css (success_color);
3810
3811   if (!css_success)
3812     {
3813       GdkColor success_default_color = { 0, 0x4e00, 0x9a00, 0x0600 };
3814       css_success = gdk_color_to_css (&success_default_color);
3815     }
3816   if (!css_warning)
3817     {
3818       GdkColor warning_default_color = { 0, 0xf500, 0x7900, 0x3e00 };
3819       css_warning = gdk_color_to_css (&warning_default_color);
3820     }
3821   if (!css_error)
3822     {
3823       GdkColor error_default_color = { 0, 0xcc00, 0x0000, 0x0000 };
3824       css_error = gdk_color_to_css (&error_default_color);
3825     }
3826
3827   if (!icon_info->symbolic_pixbuf_size)
3828     {
3829       stream = G_INPUT_STREAM (g_file_read (icon_info->icon_file, NULL, error));
3830
3831       if (!stream)
3832         return NULL;
3833
3834       /* Fetch size from the original icon */
3835       pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
3836       g_object_unref (stream);
3837
3838       if (!pixbuf)
3839         return NULL;
3840
3841       icon_info->symbolic_pixbuf_size = gtk_requisition_new ();
3842       icon_info->symbolic_pixbuf_size->width = gdk_pixbuf_get_width (pixbuf);
3843       icon_info->symbolic_pixbuf_size->height = gdk_pixbuf_get_height (pixbuf);
3844       g_object_unref (pixbuf);
3845     }
3846
3847   width = g_strdup_printf ("%d", icon_info->symbolic_pixbuf_size->width);
3848   height = g_strdup_printf ("%d", icon_info->symbolic_pixbuf_size->height);
3849   uri = g_file_get_uri (icon_info->icon_file);
3850
3851   data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
3852                       "<svg version=\"1.1\"\n"
3853                       "     xmlns=\"http://www.w3.org/2000/svg\"\n"
3854                       "     xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n"
3855                       "     width=\"", width, "\"\n"
3856                       "     height=\"", height, "\">\n"
3857                       "  <style type=\"text/css\">\n"
3858                       "    rect,path {\n"
3859                       "      fill: ", css_fg," !important;\n"
3860                       "    }\n"
3861                       "    .warning {\n"
3862                       "      fill: ", css_warning, " !important;\n"
3863                       "    }\n"
3864                       "    .error {\n"
3865                       "      fill: ", css_error ," !important;\n"
3866                       "    }\n"
3867                       "    .success {\n"
3868                       "      fill: ", css_success, " !important;\n"
3869                       "    }\n"
3870                       "  </style>\n"
3871                       "  <xi:include href=\"", uri, "\"/>\n"
3872                       "</svg>",
3873                       NULL);
3874   g_free (css_fg);
3875   g_free (css_warning);
3876   g_free (css_error);
3877   g_free (css_success);
3878   g_free (width);
3879   g_free (height);
3880   g_free (uri);
3881
3882   stream = g_memory_input_stream_new_from_data (data, -1, g_free);
3883   pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3884                                                 icon_info->desired_size,
3885                                                 icon_info->desired_size,
3886                                                 TRUE,
3887                                                 NULL,
3888                                                 error);
3889   g_object_unref (stream);
3890
3891   if (pixbuf != NULL)
3892     {
3893       if (use_cache)
3894         {
3895           icon_info->symbolic_pixbuf_cache =
3896             symbolic_pixbuf_cache_new (pixbuf, fg, success_color, warning_color, error_color,
3897                                        icon_info->symbolic_pixbuf_cache);
3898           g_object_unref (pixbuf);
3899           return symbolic_cache_get_proxy (icon_info->symbolic_pixbuf_cache, icon_info);
3900         }
3901       else
3902         return pixbuf;
3903     }
3904
3905   return NULL;
3906 }
3907
3908 /**
3909  * gtk_icon_info_load_symbolic:
3910  * @icon_info: a #GtkIconInfo
3911  * @fg: a #GdkRGBA representing the foreground color of the icon
3912  * @success_color: (allow-none): a #GdkRGBA representing the warning color
3913  *     of the icon or %NULL to use the default color
3914  * @warning_color: (allow-none): a #GdkRGBA representing the warning color
3915  *     of the icon or %NULL to use the default color
3916  * @error_color: (allow-none): a #GdkRGBA representing the error color
3917  *     of the icon or %NULL to use the default color (allow-none)
3918  * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3919  *     loaded icon was a symbolic one and whether the @fg color was
3920  *     applied to it.
3921  * @error: (allow-none): location to store error information on failure,
3922  *     or %NULL.
3923  *
3924  * Loads an icon, modifying it to match the system colours for the foreground,
3925  * success, warning and error colors provided. If the icon is not a symbolic
3926  * one, the function will return the result from gtk_icon_info_load_icon().
3927  *
3928  * This allows loading symbolic icons that will match the system theme.
3929  *
3930  * Unless you are implementing a widget, you will want to use
3931  * g_themed_icon_new_with_default_fallbacks() to load the icon.
3932  *
3933  * As implementation details, the icon loaded needs to be of SVG type,
3934  * contain the "symbolic" term as the last component of the icon name,
3935  * and use the 'fg', 'success', 'warning' and 'error' CSS styles in the
3936  * SVG file itself.
3937  *
3938  * See the <ulink url="http://www.freedesktop.org/wiki/SymbolicIcons">Symbolic Icons spec</ulink>
3939  * for more information about symbolic icons.
3940  *
3941  * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
3942  *
3943  * Since: 3.0
3944  **/
3945 GdkPixbuf *
3946 gtk_icon_info_load_symbolic (GtkIconInfo    *icon_info,
3947                              const GdkRGBA  *fg,
3948                              const GdkRGBA  *success_color,
3949                              const GdkRGBA  *warning_color,
3950                              const GdkRGBA  *error_color,
3951                              gboolean       *was_symbolic,
3952                              GError        **error)
3953 {
3954   gchar *icon_uri;
3955   gboolean is_symbolic;
3956
3957   g_return_val_if_fail (icon_info != NULL, NULL);
3958   g_return_val_if_fail (fg != NULL, NULL);
3959
3960   icon_uri = NULL;
3961   if (icon_info->icon_file)
3962     icon_uri = g_file_get_uri (icon_info->icon_file);
3963
3964   is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
3965   g_free (icon_uri);
3966
3967   if (was_symbolic)
3968     *was_symbolic = is_symbolic;
3969
3970   if (!is_symbolic)
3971     return gtk_icon_info_load_icon (icon_info, error);
3972
3973   return _gtk_icon_info_load_symbolic_internal (icon_info,
3974                                                 fg, success_color,
3975                                                 warning_color, error_color,
3976                                                 TRUE,
3977                                                 error);
3978 }
3979
3980 /**
3981  * gtk_icon_info_load_symbolic_for_context:
3982  * @icon_info: a #GtkIconInfo
3983  * @context: a #GtkStyleContext
3984  * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3985  *     loaded icon was a symbolic one and whether the @fg color was
3986  *     applied to it.
3987  * @error: (allow-none): location to store error information on failure,
3988  *     or %NULL.
3989  *
3990  * Loads an icon, modifying it to match the system colors for the foreground,
3991  * success, warning and error colors provided. If the icon is not a symbolic
3992  * one, the function will return the result from gtk_icon_info_load_icon().
3993  * This function uses the regular foreground color and the symbolic colors
3994  * with the names "success_color", "warning_color" and "error_color" from
3995  * the context.
3996  *
3997  * This allows loading symbolic icons that will match the system theme.
3998  *
3999  * See gtk_icon_info_load_symbolic() for more details.
4000  *
4001  * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
4002  *
4003  * Since: 3.0
4004  **/
4005 GdkPixbuf *
4006 gtk_icon_info_load_symbolic_for_context (GtkIconInfo      *icon_info,
4007                                          GtkStyleContext  *context,
4008                                          gboolean         *was_symbolic,
4009                                          GError          **error)
4010 {
4011   GdkRGBA *color = NULL;
4012   GdkRGBA fg;
4013   GdkRGBA *fgp;
4014   GdkRGBA success_color;
4015   GdkRGBA *success_colorp;
4016   GdkRGBA warning_color;
4017   GdkRGBA *warning_colorp;
4018   GdkRGBA error_color;
4019   GdkRGBA *error_colorp;
4020   GtkStateFlags state;
4021   gchar *icon_uri;
4022   gboolean is_symbolic;
4023
4024   g_return_val_if_fail (icon_info != NULL, NULL);
4025   g_return_val_if_fail (context != NULL, NULL);
4026
4027   icon_uri = NULL;
4028   if (icon_info->icon_file)
4029     icon_uri = g_file_get_uri (icon_info->icon_file);
4030
4031   is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
4032   g_free (icon_uri);
4033
4034   if (was_symbolic)
4035     *was_symbolic = is_symbolic;
4036
4037   if (!is_symbolic)
4038     return gtk_icon_info_load_icon (icon_info, error);
4039
4040   fgp = success_colorp = warning_colorp = error_colorp = NULL;
4041
4042   state = gtk_style_context_get_state (context);
4043   gtk_style_context_get (context, state, "color", &color, NULL);
4044   if (color)
4045     {
4046       fg = *color;
4047       fgp = &fg;
4048       gdk_rgba_free (color);
4049     }
4050
4051   if (gtk_style_context_lookup_color (context, "success_color", &success_color))
4052     success_colorp = &success_color;
4053
4054   if (gtk_style_context_lookup_color (context, "warning_color", &warning_color))
4055     warning_colorp = &warning_color;
4056
4057   if (gtk_style_context_lookup_color (context, "error_color", &error_color))
4058     error_colorp = &error_color;
4059
4060   return _gtk_icon_info_load_symbolic_internal (icon_info,
4061                                                 fgp, success_colorp,
4062                                                 warning_colorp, error_colorp,
4063                                                 TRUE,
4064                                                 error);
4065 }
4066
4067 typedef struct {
4068   gboolean is_symbolic;
4069   GtkIconInfo *dup;
4070   GdkRGBA fg;
4071   gboolean fg_set;
4072   GdkRGBA success_color;
4073   gboolean success_color_set;
4074   GdkRGBA warning_color;
4075   gboolean warning_color_set;
4076   GdkRGBA error_color;
4077   gboolean error_color_set;
4078 } AsyncSymbolicData;
4079
4080 static void
4081 async_symbolic_data_free (AsyncSymbolicData *data)
4082 {
4083   if (data->dup)
4084     g_object_unref (data->dup);
4085   g_slice_free (AsyncSymbolicData, data);
4086 }
4087
4088 static void
4089 async_load_no_symbolic_cb (GObject *source_object,
4090                            GAsyncResult *res,
4091                            gpointer user_data)
4092 {
4093   GtkIconInfo *icon_info = GTK_ICON_INFO (source_object);
4094   GTask *task = user_data;
4095   GError *error = NULL;
4096   GdkPixbuf *pixbuf;
4097
4098   pixbuf = gtk_icon_info_load_icon_finish (icon_info, res, &error);
4099   if (pixbuf == NULL)
4100     g_task_return_error (task, error);
4101   else
4102     g_task_return_pointer (task, pixbuf, g_object_unref);
4103   g_object_unref (task);
4104 }
4105
4106 static void
4107 load_symbolic_icon_thread  (GTask           *task,
4108                             gpointer         source_object,
4109                             gpointer         task_data,
4110                             GCancellable    *cancellable)
4111 {
4112   AsyncSymbolicData *data = task_data;
4113   GError *error;
4114   GdkPixbuf *pixbuf;
4115
4116   error = NULL;
4117   pixbuf =
4118     _gtk_icon_info_load_symbolic_internal (data->dup,
4119                                            data->fg_set ? &data->fg : NULL,
4120                                            data->success_color_set ? &data->success_color : NULL,
4121                                            data->warning_color_set ? &data->warning_color : NULL,
4122                                            data->error_color_set ? &data->error_color : NULL,
4123                                            FALSE,
4124                                            &error);
4125   if (pixbuf == NULL)
4126     g_task_return_error (task, error);
4127   else
4128     g_task_return_pointer (task, pixbuf, g_object_unref);
4129 }
4130
4131 /**
4132  * gtk_icon_info_load_symbolic_async:
4133  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
4134  * @fg: a #GdkRGBA representing the foreground color of the icon
4135  * @success_color: (allow-none): a #GdkRGBA representing the warning color
4136  *     of the icon or %NULL to use the default color
4137  * @warning_color: (allow-none): a #GdkRGBA representing the warning color
4138  *     of the icon or %NULL to use the default color
4139  * @error_color: (allow-none): a #GdkRGBA representing the error color
4140  *     of the icon or %NULL to use the default color (allow-none)
4141  * @cancellable: (allow-none): optional #GCancellable object,
4142  *     %NULL to ignore
4143  * @callback: (scope async): a #GAsyncReadyCallback to call when the
4144  *     request is satisfied
4145  * @user_data: (closure): the data to pass to callback function
4146  *
4147  * Asynchronously load, render and scale a symbolic icon previously looked up
4148  * from the icon theme using gtk_icon_theme_lookup_icon().
4149  *
4150  * For more details, see gtk_icon_info_load_symbolic() which is the synchronous
4151  * version of this call.
4152  *
4153  * Since: 3.8
4154  **/
4155 void
4156 gtk_icon_info_load_symbolic_async (GtkIconInfo   *icon_info,
4157                                    const GdkRGBA *fg,
4158                                    const GdkRGBA *success_color,
4159                                    const GdkRGBA *warning_color,
4160                                    const GdkRGBA *error_color,
4161                                    GCancellable         *cancellable,
4162                                    GAsyncReadyCallback   callback,
4163                                    gpointer              user_data)
4164 {
4165   GTask *task;
4166   AsyncSymbolicData *data;
4167   gchar *icon_uri;
4168   SymbolicPixbufCache *symbolic_cache;
4169   GdkPixbuf *pixbuf;
4170
4171   g_return_if_fail (icon_info != NULL);
4172   g_return_if_fail (fg != NULL);
4173
4174   task = g_task_new (icon_info, cancellable, callback, user_data);
4175
4176   data = g_slice_new0 (AsyncSymbolicData);
4177   g_task_set_task_data (task, data, (GDestroyNotify) async_symbolic_data_free);
4178
4179   icon_uri = NULL;
4180   if (icon_info->icon_file)
4181     icon_uri = g_file_get_uri (icon_info->icon_file);
4182
4183   data->is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
4184   g_free (icon_uri);
4185
4186   if (!data->is_symbolic)
4187     {
4188       gtk_icon_info_load_icon_async (icon_info, cancellable, async_load_no_symbolic_cb, g_object_ref (task));
4189     }
4190   else
4191     {
4192       symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
4193                                                       fg, success_color, warning_color, error_color);
4194       if (symbolic_cache)
4195         {
4196           pixbuf = symbolic_cache_get_proxy (symbolic_cache, icon_info);
4197           g_task_return_pointer (task, pixbuf, g_object_unref);
4198         }
4199       else
4200         {
4201           if (fg)
4202             {
4203               data->fg = *fg;
4204               data->fg_set = TRUE;
4205             }
4206
4207           if (success_color)
4208             {
4209               data->success_color = *success_color;
4210               data->success_color_set = TRUE;
4211             }
4212
4213           if (warning_color)
4214             {
4215               data->warning_color = *warning_color;
4216               data->warning_color_set = TRUE;
4217             }
4218
4219           if (error_color)
4220             {
4221               data->error_color = *error_color;
4222               data->error_color_set = TRUE;
4223             }
4224
4225           data->dup = icon_info_dup (icon_info);
4226           g_task_run_in_thread (task, load_symbolic_icon_thread);
4227         }
4228     }
4229   g_object_unref (task);
4230 }
4231
4232 /**
4233  * gtk_icon_info_load_symbolic_finish:
4234  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
4235  * @res: a #GAsyncResult
4236  * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
4237  *     loaded icon was a symbolic one and whether the @fg color was
4238  *     applied to it.
4239  * @error: (allow-none): location to store error information on failure,
4240  *     or %NULL.
4241  *
4242  * Finishes an async icon load, see gtk_icon_info_load_symbolic_async().
4243  *
4244  * Return value: (transfer full): the rendered icon; this may be a newly
4245  *     created icon or a new reference to an internal icon, so you must
4246  *     not modify the icon. Use g_object_unref() to release your reference
4247  *     to the icon.
4248  *
4249  * Since: 3.8
4250  **/
4251 GdkPixbuf *
4252 gtk_icon_info_load_symbolic_finish (GtkIconInfo   *icon_info,
4253                                     GAsyncResult  *result,
4254                                     gboolean      *was_symbolic,
4255                                     GError       **error)
4256 {
4257   GTask *task = G_TASK (result);
4258   AsyncSymbolicData *data = g_task_get_task_data (task);
4259   SymbolicPixbufCache *symbolic_cache;
4260   GdkPixbuf *pixbuf;
4261
4262   if (was_symbolic)
4263     *was_symbolic = data->is_symbolic;
4264
4265   if (data->dup && !g_task_had_error (task))
4266     {
4267       pixbuf = g_task_propagate_pointer (task, NULL);
4268
4269       g_assert (pixbuf != NULL); /* we checked for !had_error above */
4270
4271       symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
4272                                                       data->fg_set ? &data->fg : NULL,
4273                                                       data->success_color_set ? &data->success_color : NULL,
4274                                                       data->warning_color_set ? &data->warning_color : NULL,
4275                                                       data->error_color_set ? &data->error_color : NULL);
4276
4277       if (symbolic_cache == NULL)
4278         {
4279           symbolic_cache = icon_info->symbolic_pixbuf_cache =
4280             symbolic_pixbuf_cache_new (pixbuf,
4281                                        data->fg_set ? &data->fg : NULL,
4282                                        data->success_color_set ? &data->success_color : NULL,
4283                                        data->warning_color_set ? &data->warning_color : NULL,
4284                                        data->error_color_set ? &data->error_color : NULL,
4285                                        icon_info->symbolic_pixbuf_cache);
4286         }
4287
4288       g_object_unref (pixbuf);
4289
4290       return symbolic_cache_get_proxy (symbolic_cache, icon_info);
4291     }
4292
4293   return g_task_propagate_pointer (task, error);
4294 }
4295
4296 /**
4297  * gtk_icon_info_load_symbolic_for_context_async:
4298  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
4299  * @context: a #GtkStyleContext
4300  * @cancellable: (allow-none): optional #GCancellable object,
4301  *     %NULL to ignore
4302  * @callback: (scope async): a #GAsyncReadyCallback to call when the
4303  *     request is satisfied
4304  * @user_data: (closure): the data to pass to callback function
4305  *
4306  * Asynchronously load, render and scale a symbolic icon previously looked up
4307  * from the icon theme using gtk_icon_theme_lookup_icon().
4308  *
4309  * For more details, see gtk_icon_info_load_symbolic_for_context() which is the synchronous
4310  * version of this call.
4311  *
4312  * Since: 3.8
4313  **/
4314 void
4315 gtk_icon_info_load_symbolic_for_context_async (GtkIconInfo      *icon_info,
4316                                                GtkStyleContext  *context,
4317                                                GCancellable     *cancellable,
4318                                                GAsyncReadyCallback callback,
4319                                                gpointer          user_data)
4320 {
4321   GdkRGBA *color = NULL;
4322   GdkRGBA fg;
4323   GdkRGBA *fgp;
4324   GdkRGBA success_color;
4325   GdkRGBA *success_colorp;
4326   GdkRGBA warning_color;
4327   GdkRGBA *warning_colorp;
4328   GdkRGBA error_color;
4329   GdkRGBA *error_colorp;
4330   GtkStateFlags state;
4331
4332   g_return_if_fail (icon_info != NULL);
4333   g_return_if_fail (context != NULL);
4334
4335   fgp = success_colorp = warning_colorp = error_colorp = NULL;
4336
4337   state = gtk_style_context_get_state (context);
4338   gtk_style_context_get (context, state, "color", &color, NULL);
4339   if (color)
4340     {
4341       fg = *color;
4342       fgp = &fg;
4343       gdk_rgba_free (color);
4344     }
4345
4346   if (gtk_style_context_lookup_color (context, "success_color", &success_color))
4347     success_colorp = &success_color;
4348
4349   if (gtk_style_context_lookup_color (context, "warning_color", &warning_color))
4350     warning_colorp = &warning_color;
4351
4352   if (gtk_style_context_lookup_color (context, "error_color", &error_color))
4353     error_colorp = &error_color;
4354
4355   gtk_icon_info_load_symbolic_async (icon_info,
4356                                      fgp, success_colorp,
4357                                      warning_colorp, error_colorp,
4358                                      cancellable, callback, user_data);
4359 }
4360
4361 /**
4362  * gtk_icon_info_load_symbolic_for_context_finish:
4363  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
4364  * @res: a #GAsyncResult
4365  * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
4366  *     loaded icon was a symbolic one and whether the @fg color was
4367  *     applied to it.
4368  * @error: (allow-none): location to store error information on failure,
4369  *     or %NULL.
4370  *
4371  * Finishes an async icon load, see gtk_icon_info_load_symbolic_for_context_async().
4372  *
4373  * Return value: (transfer full): the rendered icon; this may be a newly
4374  *     created icon or a new reference to an internal icon, so you must
4375  *     not modify the icon. Use g_object_unref() to release your reference
4376  *     to the icon.
4377  *
4378  * Since: 3.8
4379  **/
4380 GdkPixbuf *
4381 gtk_icon_info_load_symbolic_for_context_finish (GtkIconInfo      *icon_info,
4382                                                 GAsyncResult     *result,
4383                                                 gboolean         *was_symbolic,
4384                                                 GError          **error)
4385 {
4386   return gtk_icon_info_load_symbolic_finish (icon_info, result, was_symbolic, error);
4387 }
4388
4389 static GdkRGBA *
4390 color_to_rgba (GdkColor *color, GdkRGBA *rgba)
4391 {
4392   rgba->red = color->red / 65535.0;
4393   rgba->green = color->green / 65535.0;
4394   rgba->blue = color->blue / 65535.0;
4395   rgba->alpha = 1.0;
4396   return rgba;
4397 }
4398
4399 /**
4400  * gtk_icon_info_load_symbolic_for_style:
4401  * @icon_info: a #GtkIconInfo
4402  * @style: a #GtkStyle to take the colors from
4403  * @state: the widget state to use for colors
4404  * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
4405  *     loaded icon was a symbolic one and whether the @fg color was
4406  *     applied to it.
4407  * @error: (allow-none): location to store error information on failure,
4408  *     or %NULL.
4409  *
4410  * Loads an icon, modifying it to match the system colours for the foreground,
4411  * success, warning and error colors provided. If the icon is not a symbolic
4412  * one, the function will return the result from gtk_icon_info_load_icon().
4413  *
4414  * This allows loading symbolic icons that will match the system theme.
4415  *
4416  * See gtk_icon_info_load_symbolic() for more details.
4417  *
4418  * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
4419  *
4420  * Since: 3.0
4421  *
4422  * Deprecated: 3.0: Use gtk_icon_info_load_symbolic_for_context() instead
4423  **/
4424 GdkPixbuf *
4425 gtk_icon_info_load_symbolic_for_style (GtkIconInfo   *icon_info,
4426                                        GtkStyle      *style,
4427                                        GtkStateType   state,
4428                                        gboolean      *was_symbolic,
4429                                        GError       **error)
4430 {
4431   GdkColor color;
4432   GdkRGBA fg;
4433   GdkRGBA success_color;
4434   GdkRGBA *success_colorp;
4435   GdkRGBA warning_color;
4436   GdkRGBA *warning_colorp;
4437   GdkRGBA error_color;
4438   GdkRGBA *error_colorp;
4439   gchar *icon_uri;
4440   gboolean is_symbolic;
4441
4442   g_return_val_if_fail (icon_info != NULL, NULL);
4443   g_return_val_if_fail (style != NULL, NULL);
4444
4445   icon_uri = NULL;
4446   if (icon_info->icon_file)
4447     icon_uri = g_file_get_uri (icon_info->icon_file);
4448
4449   is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
4450   g_free (icon_uri);
4451
4452   if (was_symbolic)
4453     *was_symbolic = is_symbolic;
4454
4455   if (!is_symbolic)
4456     return gtk_icon_info_load_icon (icon_info, error);
4457
4458   color_to_rgba (&style->fg[state], &fg);
4459
4460   success_colorp = warning_colorp = error_colorp = NULL;
4461
4462   if (gtk_style_lookup_color (style, "success_color", &color))
4463     success_colorp = color_to_rgba (&color, &success_color);
4464
4465   if (gtk_style_lookup_color (style, "warning_color", &color))
4466     warning_colorp = color_to_rgba (&color, &warning_color);
4467
4468   if (gtk_style_lookup_color (style, "error_color", &color))
4469     error_colorp = color_to_rgba (&color, &error_color);
4470
4471   return _gtk_icon_info_load_symbolic_internal (icon_info,
4472                                                 &fg, success_colorp,
4473                                                 warning_colorp, error_colorp,
4474                                                 TRUE,
4475                                                 error);
4476 }
4477
4478 /**
4479  * gtk_icon_info_set_raw_coordinates:
4480  * @icon_info: a #GtkIconInfo
4481  * @raw_coordinates: whether the coordinates of embedded rectangles
4482  *   and attached points should be returned in their original
4483  *   (unscaled) form.
4484  * 
4485  * Sets whether the coordinates returned by gtk_icon_info_get_embedded_rect()
4486  * and gtk_icon_info_get_attach_points() should be returned in their
4487  * original form as specified in the icon theme, instead of scaled
4488  * appropriately for the pixbuf returned by gtk_icon_info_load_icon().
4489  *
4490  * Raw coordinates are somewhat strange; they are specified to be with
4491  * respect to the unscaled pixmap for PNG and XPM icons, but for SVG
4492  * icons, they are in a 1000x1000 coordinate space that is scaled
4493  * to the final size of the icon.  You can determine if the icon is an SVG
4494  * icon by using gtk_icon_info_get_filename(), and seeing if it is non-%NULL
4495  * and ends in '.svg'.
4496  *
4497  * This function is provided primarily to allow compatibility wrappers
4498  * for older API's, and is not expected to be useful for applications.
4499  *
4500  * Since: 2.4
4501  **/
4502 void
4503 gtk_icon_info_set_raw_coordinates (GtkIconInfo *icon_info,
4504                                    gboolean     raw_coordinates)
4505 {
4506   g_return_if_fail (icon_info != NULL);
4507   
4508   icon_info->raw_coordinates = raw_coordinates != FALSE;
4509 }
4510
4511 /* Scale coordinates from the icon data prior to returning
4512  * them to the user.
4513  */
4514 static gboolean
4515 icon_info_scale_point (GtkIconInfo  *icon_info,
4516                        gint          x,
4517                        gint          y,
4518                        gint         *x_out,
4519                        gint         *y_out)
4520 {
4521   if (icon_info->raw_coordinates)
4522     {
4523       *x_out = x;
4524       *y_out = y;
4525     }
4526   else
4527     {
4528       if (!icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4529         return FALSE;
4530
4531       *x_out = 0.5 + x * icon_info->scale;
4532       *y_out = 0.5 + y * icon_info->scale;
4533     }
4534
4535   return TRUE;
4536 }
4537
4538 /**
4539  * gtk_icon_info_get_embedded_rect:
4540  * @icon_info: a #GtkIconInfo
4541  * @rectangle: (out): #GdkRectangle in which to store embedded
4542  *   rectangle coordinates; coordinates are only stored
4543  *   when this function returns %TRUE.
4544  *
4545  * Gets the coordinates of a rectangle within the icon
4546  * that can be used for display of information such
4547  * as a preview of the contents of a text file.
4548  * See gtk_icon_info_set_raw_coordinates() for further
4549  * information about the coordinate system.
4550  * 
4551  * Return value: %TRUE if the icon has an embedded rectangle
4552  *
4553  * Since: 2.4
4554  **/
4555 gboolean
4556 gtk_icon_info_get_embedded_rect (GtkIconInfo  *icon_info,
4557                                  GdkRectangle *rectangle)
4558 {
4559   g_return_val_if_fail (icon_info != NULL, FALSE);
4560
4561   if (icon_info->data && icon_info->data->has_embedded_rect &&
4562       icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4563     {
4564       gint scaled_x0, scaled_y0;
4565       gint scaled_x1, scaled_y1;
4566       
4567       if (rectangle)
4568         {
4569           icon_info_scale_point (icon_info,
4570                                  icon_info->data->x0, icon_info->data->y0,
4571                                  &scaled_x0, &scaled_y0);
4572           icon_info_scale_point (icon_info,
4573                                  icon_info->data->x1, icon_info->data->y1,
4574                                  &scaled_x1, &scaled_y1);
4575           
4576           rectangle->x = scaled_x0;
4577           rectangle->y = scaled_y0;
4578           rectangle->width = scaled_x1 - rectangle->x;
4579           rectangle->height = scaled_y1 - rectangle->y;
4580         }
4581
4582       return TRUE;
4583     }
4584   else
4585     return FALSE;
4586 }
4587
4588 /**
4589  * gtk_icon_info_get_attach_points:
4590  * @icon_info: a #GtkIconInfo
4591  * @points: (allow-none) (array length=n_points) (out): location to store pointer to an array of points, or %NULL
4592  *          free the array of points with g_free().
4593  * @n_points: (allow-none): location to store the number of points in @points, or %NULL
4594  * 
4595  * Fetches the set of attach points for an icon. An attach point
4596  * is a location in the icon that can be used as anchor points for attaching
4597  * emblems or overlays to the icon.
4598  * 
4599  * Return value: %TRUE if there are any attach points for the icon.
4600  *
4601  * Since: 2.4
4602  **/
4603 gboolean
4604 gtk_icon_info_get_attach_points (GtkIconInfo *icon_info,
4605                                  GdkPoint   **points,
4606                                  gint        *n_points)
4607 {
4608   g_return_val_if_fail (icon_info != NULL, FALSE);
4609   
4610   if (icon_info->data && icon_info->data->n_attach_points &&
4611       icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4612     {
4613       if (points)
4614         {
4615           gint i;
4616           
4617           *points = g_new (GdkPoint, icon_info->data->n_attach_points);
4618           for (i = 0; i < icon_info->data->n_attach_points; i++)
4619             icon_info_scale_point (icon_info,
4620                                    icon_info->data->attach_points[i].x,
4621                                    icon_info->data->attach_points[i].y,
4622                                    &(*points)[i].x,
4623                                    &(*points)[i].y);
4624         }
4625           
4626       if (n_points)
4627         *n_points = icon_info->data->n_attach_points;
4628
4629       return TRUE;
4630     }
4631   else
4632     {
4633       if (points)
4634         *points = NULL;
4635       if (n_points)
4636         *n_points = 0;
4637       
4638       return FALSE;
4639     }
4640 }
4641
4642 /**
4643  * gtk_icon_info_get_display_name:
4644  * @icon_info: a #GtkIconInfo
4645  * 
4646  * Gets the display name for an icon. A display name is a
4647  * string to be used in place of the icon name in a user
4648  * visible context like a list of icons.
4649  * 
4650  * Return value: the display name for the icon or %NULL, if
4651  *  the icon doesn't have a specified display name. This value
4652  *  is owned @icon_info and must not be modified or free.
4653  *
4654  * Since: 2.4
4655  **/
4656 const gchar *
4657 gtk_icon_info_get_display_name (GtkIconInfo *icon_info)
4658 {
4659   g_return_val_if_fail (icon_info != NULL, NULL);
4660
4661   if (icon_info->data)
4662     return icon_info->data->display_name;
4663   else
4664     return NULL;
4665 }
4666
4667 /*
4668  * Builtin icons
4669  */
4670
4671
4672 /**
4673  * gtk_icon_theme_add_builtin_icon:
4674  * @icon_name: the name of the icon to register
4675  * @size: the size at which to register the icon (different
4676  *        images can be registered for the same icon name
4677  *        at different sizes.)
4678  * @pixbuf: #GdkPixbuf that contains the image to use
4679  *          for @icon_name.
4680  * 
4681  * Registers a built-in icon for icon theme lookups. The idea
4682  * of built-in icons is to allow an application or library
4683  * that uses themed icons to function requiring files to
4684  * be present in the file system. For instance, the default
4685  * images for all of GTK+'s stock icons are registered
4686  * as built-icons.
4687  *
4688  * In general, if you use gtk_icon_theme_add_builtin_icon()
4689  * you should also install the icon in the icon theme, so
4690  * that the icon is generally available.
4691  *
4692  * This function will generally be used with pixbufs loaded
4693  * via gdk_pixbuf_new_from_inline().
4694  *
4695  * Since: 2.4
4696  **/
4697 void
4698 gtk_icon_theme_add_builtin_icon (const gchar *icon_name,
4699                                  gint         size,
4700                                  GdkPixbuf   *pixbuf)
4701 {
4702   BuiltinIcon *default_icon;
4703   GSList *icons;
4704   gpointer key;
4705
4706   g_return_if_fail (icon_name != NULL);
4707   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
4708   
4709   if (!icon_theme_builtin_icons)
4710     icon_theme_builtin_icons = g_hash_table_new (g_str_hash, g_str_equal);
4711
4712   icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
4713   if (!icons)
4714     key = g_strdup (icon_name);
4715   else
4716     key = (gpointer)icon_name;  /* Won't get stored */
4717
4718   default_icon = g_new (BuiltinIcon, 1);
4719   default_icon->size = size;
4720   default_icon->pixbuf = g_object_ref (pixbuf);
4721   icons = g_slist_prepend (icons, default_icon);
4722
4723   /* Replaces value, leaves key untouched
4724    */
4725   g_hash_table_insert (icon_theme_builtin_icons, key, icons);
4726 }
4727
4728 /* Look up a builtin icon; the min_difference_p and
4729  * has_larger_p out parameters allow us to combine
4730  * this lookup with searching through the actual directories
4731  * of the "hicolor" icon theme. See theme_lookup_icon()
4732  * for how they are used.
4733  */
4734 static BuiltinIcon *
4735 find_builtin_icon (const gchar *icon_name,
4736                    gint         size,
4737                    gint        *min_difference_p,
4738                    gboolean    *has_larger_p)
4739 {
4740   GSList *icons = NULL;
4741   gint min_difference = G_MAXINT;
4742   gboolean has_larger = FALSE;
4743   BuiltinIcon *min_icon = NULL;
4744   
4745   if (!icon_theme_builtin_icons)
4746     return NULL;
4747
4748   icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
4749
4750   while (icons)
4751     {
4752       BuiltinIcon *default_icon = icons->data;
4753       int min, max, difference;
4754       gboolean smaller;
4755       
4756       min = default_icon->size - 2;
4757       max = default_icon->size + 2;
4758       smaller = size < min;
4759       if (size < min)
4760         difference = min - size;
4761       else if (size > max)
4762         difference = size - max;
4763       else
4764         difference = 0;
4765       
4766       if (difference == 0)
4767         {
4768           min_icon = default_icon;
4769           break;
4770         }
4771       
4772       if (!has_larger)
4773         {
4774           if (difference < min_difference || smaller)
4775             {
4776               min_difference = difference;
4777               min_icon = default_icon;
4778               has_larger = smaller;
4779             }
4780         }
4781       else
4782         {
4783           if (difference < min_difference && smaller)
4784             {
4785               min_difference = difference;
4786               min_icon = default_icon;
4787             }
4788         }
4789       
4790       icons = icons->next;
4791     }
4792
4793   if (min_difference_p)
4794     *min_difference_p = min_difference;
4795   if (has_larger_p)
4796     *has_larger_p = has_larger;
4797
4798   return min_icon;
4799 }
4800
4801 void
4802 _gtk_icon_theme_check_reload (GdkDisplay *display)
4803 {
4804   gint n_screens, i;
4805   GdkScreen *screen;
4806   GtkIconTheme *icon_theme;
4807
4808   n_screens = gdk_display_get_n_screens (display);
4809   
4810   for (i = 0; i < n_screens; i++)
4811     {
4812       screen = gdk_display_get_screen (display, i);
4813
4814       icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
4815       if (icon_theme)
4816         {
4817           icon_theme->priv->check_reload = TRUE;
4818           ensure_valid_themes (icon_theme);
4819           icon_theme->priv->check_reload = FALSE;
4820         }
4821     }
4822 }
4823
4824
4825 /**
4826  * gtk_icon_theme_lookup_by_gicon:
4827  * @icon_theme: a #GtkIconTheme
4828  * @icon: the #GIcon to look up
4829  * @size: desired icon size
4830  * @flags: flags modifying the behavior of the icon lookup
4831  * 
4832  * Looks up an icon and returns a structure containing
4833  * information such as the filename of the icon. 
4834  * The icon can then be rendered into a pixbuf using
4835  * gtk_icon_info_load_icon().
4836  *
4837  * Return value: (transfer full): a #GtkIconInfo structure containing 
4838  *     information about the icon, or %NULL if the icon 
4839  *     wasn't found. Unref with g_object_unref()
4840  *
4841  * Since: 2.14
4842  */
4843 GtkIconInfo *
4844 gtk_icon_theme_lookup_by_gicon (GtkIconTheme       *icon_theme,
4845                                 GIcon              *icon,
4846                                 gint                size,
4847                                 GtkIconLookupFlags  flags)
4848 {
4849   GtkIconInfo *info;
4850
4851   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
4852   g_return_val_if_fail (G_IS_ICON (icon), NULL);
4853
4854   if (G_IS_LOADABLE_ICON (icon))
4855     {
4856       info = icon_info_new ();
4857       info->loadable = G_LOADABLE_ICON (g_object_ref (icon));
4858
4859       if (G_IS_FILE_ICON (icon))
4860         {
4861           GFile *file = g_file_icon_get_file (G_FILE_ICON (icon));
4862           if (file != NULL)
4863             {
4864               info->icon_file = g_object_ref (file);
4865               info->filename = g_file_get_path (file);
4866             }
4867         }
4868
4869       info->dir_type = ICON_THEME_DIR_UNTHEMED;
4870       info->dir_size = size;
4871       info->desired_size = size;
4872       info->threshold = 2;
4873       info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
4874
4875       return info;
4876     }
4877   else if (G_IS_THEMED_ICON (icon))
4878     {
4879       const gchar **names;
4880
4881       names = (const gchar **)g_themed_icon_get_names (G_THEMED_ICON (icon));
4882       info = gtk_icon_theme_choose_icon (icon_theme, names, size, flags);
4883
4884       return info;
4885     }
4886   else if (G_IS_EMBLEMED_ICON (icon))
4887     {
4888       GIcon *base, *emblem;
4889       GList *list, *l;
4890       GtkIconInfo *base_info, *emblem_info;
4891
4892       if (GTK_IS_NUMERABLE_ICON (icon))
4893         _gtk_numerable_icon_set_background_icon_size (GTK_NUMERABLE_ICON (icon), size / 2);
4894
4895       base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
4896       base_info = gtk_icon_theme_lookup_by_gicon (icon_theme, base, size, flags);
4897       if (base_info)
4898         {
4899           info = icon_info_dup (base_info);
4900           g_object_unref (base_info);
4901
4902           list = g_emblemed_icon_get_emblems (G_EMBLEMED_ICON (icon));
4903           for (l = list; l; l = l->next)
4904             {
4905               emblem = g_emblem_get_icon (G_EMBLEM (l->data));
4906               /* always force size for emblems */
4907               emblem_info = gtk_icon_theme_lookup_by_gicon (icon_theme, emblem, size / 2, flags | GTK_ICON_LOOKUP_FORCE_SIZE);
4908               if (emblem_info)
4909                 info->emblem_infos = g_slist_prepend (info->emblem_infos, emblem_info);
4910             }
4911
4912           return info;
4913         }
4914       else
4915         return NULL;
4916     }
4917   else if (GDK_IS_PIXBUF (icon))
4918     {
4919       GdkPixbuf *pixbuf;
4920
4921       pixbuf = GDK_PIXBUF (icon);
4922
4923       if ((flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0)
4924         {
4925           gint width, height, max;
4926           gdouble scale;
4927           GdkPixbuf *scaled;
4928
4929           width = gdk_pixbuf_get_width (pixbuf);
4930           height = gdk_pixbuf_get_height (pixbuf);
4931           max = MAX (width, height);
4932           scale = (gdouble) size / (gdouble) max;
4933
4934           scaled = gdk_pixbuf_scale_simple (pixbuf,
4935                                             0.5 + width * scale,
4936                                             0.5 + height * scale,
4937                                             GDK_INTERP_BILINEAR);
4938
4939           info = gtk_icon_info_new_for_pixbuf (icon_theme, scaled);
4940
4941           g_object_unref (scaled);
4942         }
4943       else
4944         {
4945           info = gtk_icon_info_new_for_pixbuf (icon_theme, pixbuf);
4946         }
4947
4948       return info;
4949     }
4950
4951   return NULL;
4952 }
4953
4954 /**
4955  * gtk_icon_info_new_for_pixbuf:
4956  * @icon_theme: a #GtkIconTheme
4957  * @pixbuf: the pixbuf to wrap in a #GtkIconInfo
4958  *
4959  * Creates a #GtkIconInfo for a #GdkPixbuf.
4960  *
4961  * Return value: (transfer full): a #GtkIconInfo
4962  *
4963  * Since: 2.14
4964  */
4965 GtkIconInfo *
4966 gtk_icon_info_new_for_pixbuf (GtkIconTheme *icon_theme,
4967                               GdkPixbuf    *pixbuf)
4968 {
4969   GtkIconInfo *info;
4970
4971   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
4972   g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
4973
4974   info = icon_info_new ();
4975   info->pixbuf = g_object_ref (pixbuf);
4976   info->scale = 1.0;
4977   info->dir_type = ICON_THEME_DIR_UNTHEMED;
4978
4979   return info;
4980 }