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