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