]> Pileus Git - ~andy/gtk/blob - gtk/gtkicontheme.c
Store builtin stock icons in an icon cache, instead of populating a hash
[~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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #ifdef HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27 #include <string.h>
28 #include <stdlib.h>
29 #include <glib.h>
30 #include <glib/gstdio.h>
31
32 #ifdef G_OS_WIN32
33 #ifndef S_ISDIR
34 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
35 #endif
36 #endif /* G_OS_WIN32 */
37
38 #include "gtkicontheme.h"
39 #include "gtkiconfactory.h"
40 #include "gtkiconcache.h"
41 #include "gtkbuiltincache.h"
42 #include "gtkintl.h"
43 #include "gtksettings.h"
44 #include "gtkprivate.h"
45 #include "gtkalias.h"
46
47 #define DEFAULT_THEME_NAME "hicolor"
48
49 typedef enum
50 {
51   ICON_THEME_DIR_FIXED,  
52   ICON_THEME_DIR_SCALABLE,  
53   ICON_THEME_DIR_THRESHOLD,
54   ICON_THEME_DIR_UNTHEMED
55 } IconThemeDirType;
56
57 /* In reverse search order: */
58 typedef enum
59 {
60   ICON_SUFFIX_NONE = 0,
61   ICON_SUFFIX_XPM = 1 << 0,
62   ICON_SUFFIX_SVG = 1 << 1,
63   ICON_SUFFIX_PNG = 1 << 2,
64   HAS_ICON_FILE = 1 << 3
65 } IconSuffix;
66
67
68 struct _GtkIconThemePrivate
69 {
70   guint custom_theme : 1;
71   guint is_screen_singleton : 1;
72   guint pixbuf_supports_svg : 1;
73   
74   char *current_theme;
75   char **search_path;
76   int search_path_len;
77
78   gboolean themes_valid;
79   /* A list of all the themes needed to look up icons.
80    * In search order, without duplicates
81    */
82   GList *themes;
83   GHashTable *unthemed_icons;
84   
85   /* Note: The keys of this hashtable are owned by the
86    * themedir and unthemed hashtables.
87    */
88   GHashTable *all_icons;
89
90   /* GdkScreen for the icon theme (may be NULL)
91    */
92   GdkScreen *screen;
93   
94   /* time when we last stat:ed for theme changes */
95   long last_stat_time;
96   GList *dir_mtimes;
97
98   gulong reset_styles_idle;
99
100   gboolean check_reload;
101 };
102
103 struct _GtkIconInfo
104 {
105   /* Information about the source
106    */
107   gchar *filename;
108 #ifdef G_OS_WIN32
109   /* System codepage version of filename, for DLL ABI backward
110    * compatibility functions.
111    */
112   gchar *cp_filename;
113 #endif
114   /* Cache pixbuf (if there is any) */
115   GdkPixbuf *cache_pixbuf;
116
117   GtkIconData *data;
118   
119   /* Information about the directory where
120    * the source was found
121    */
122   IconThemeDirType dir_type;
123   gint dir_size;
124   gint threshold;
125
126   /* Parameters influencing the scaled icon
127    */
128   gint desired_size;
129   gboolean raw_coordinates;
130
131   /* Cached information if we go ahead and try to load
132    * the icon.
133    */
134   GdkPixbuf *pixbuf;
135   GError *load_error;
136   gdouble scale;
137 };
138
139 typedef struct
140 {
141   char *name;
142   char *display_name;
143   char *comment;
144   char *example;
145
146   /* In search order */
147   GList *dirs;
148 } IconTheme;
149
150 typedef struct
151 {
152   IconThemeDirType type;
153   GQuark context;
154
155   int size;
156   int min_size;
157   int max_size;
158   int threshold;
159
160   char *dir;
161   char *subdir;
162   
163   GtkIconCache *cache;
164   
165   GHashTable *icons;
166   GHashTable *icon_data;
167 } IconThemeDir;
168
169 typedef struct
170 {
171   char *svg_filename;
172   char *no_svg_filename;
173 } UnthemedIcon;
174
175 typedef struct
176 {
177   gint size;
178   GdkPixbuf *pixbuf;
179 } BuiltinIcon;
180
181 typedef struct 
182 {
183   char *dir;
184   time_t mtime; /* 0 == not existing or not a dir */
185
186   GtkIconCache *cache;
187 } IconThemeDirMtime;
188
189 static void  gtk_icon_theme_class_init (GtkIconThemeClass    *klass);
190 static void  gtk_icon_theme_init       (GtkIconTheme         *icon_theme);
191 static void  gtk_icon_theme_finalize   (GObject              *object);
192 static void  theme_dir_destroy         (IconThemeDir         *dir);
193
194 static void         theme_destroy     (IconTheme        *theme);
195 static GtkIconInfo *theme_lookup_icon (IconTheme        *theme,
196                                        const char       *icon_name,
197                                        int               size,
198                                        gboolean          allow_svg,
199                                        gboolean          use_default_icons);
200 static void         theme_list_icons  (IconTheme        *theme,
201                                        GHashTable       *icons,
202                                        GQuark            context);
203 static void         theme_subdir_load (GtkIconTheme     *icon_theme,
204                                        IconTheme        *theme,
205                                        GKeyFile         *theme_file,
206                                        char             *subdir);
207 static void         do_theme_change   (GtkIconTheme     *icon_theme);
208
209 static void  blow_themes               (GtkIconTheme    *icon_themes);
210
211 static void  icon_data_free            (GtkIconData     *icon_data);
212 static void load_icon_data             (IconThemeDir    *dir,
213                                         const char      *path,
214                                         const char      *name);
215
216 static IconSuffix theme_dir_get_icon_suffix (IconThemeDir *dir,
217                                              const gchar  *icon_name,
218                                              gboolean     *has_icon_file);
219
220
221 static GtkIconInfo *icon_info_new             (void);
222 static GtkIconInfo *icon_info_new_builtin     (BuiltinIcon *icon);
223
224 static IconSuffix suffix_from_name (const char *name);
225
226 static BuiltinIcon *find_builtin_icon (const gchar *icon_name,
227                                        gint        size,
228                                        gint        *min_difference_p,
229                                        gboolean    *has_larger_p);
230
231 static GObjectClass *parent_class = NULL;
232
233 static guint signal_changed = 0;
234
235 static GHashTable *icon_theme_builtin_icons;
236
237 /* also used in gtkiconfactory.c */
238 GtkIconCache *_builtin_cache = NULL;
239 static GList *builtin_dirs = NULL;
240
241
242 GType
243 gtk_icon_theme_get_type (void)
244 {
245   static GType type = 0;
246
247   if (type == 0)
248     {
249       static const GTypeInfo info =
250         {
251           sizeof (GtkIconThemeClass),
252           NULL,           /* base_init */
253           NULL,           /* base_finalize */
254           (GClassInitFunc) gtk_icon_theme_class_init,
255           NULL,           /* class_finalize */
256           NULL,           /* class_data */
257           sizeof (GtkIconTheme),
258           0,              /* n_preallocs */
259           (GInstanceInitFunc) gtk_icon_theme_init,
260         };
261
262       type = g_type_register_static (G_TYPE_OBJECT, I_("GtkIconTheme"), &info, 0);
263     }
264
265   return type;
266 }
267
268 /**
269  * gtk_icon_theme_new:
270  * 
271  * Creates a new icon theme object. Icon theme objects are used
272  * to lookup up an icon by name in a particular icon theme.
273  * Usually, you'll want to use gtk_icon_theme_get_default()
274  * or gtk_icon_theme_get_for_screen() rather than creating
275  * a new icon theme object for scratch.
276  * 
277  * Return value: the newly created #GtkIconTheme object.
278  *
279  * Since: 2.4
280  **/
281 GtkIconTheme *
282 gtk_icon_theme_new (void)
283 {
284   return g_object_new (GTK_TYPE_ICON_THEME, NULL);
285 }
286
287 /**
288  * gtk_icon_theme_get_default:
289  * 
290  * Gets the icon theme for the default screen. See
291  * gtk_icon_theme_get_for_screen().
292  * 
293  * Return value: A unique #GtkIconTheme associated with
294  *  the default screen. This icon theme is associated with
295  *  the screen and can be used as long as the screen
296  *  is open. Do not ref or unref it.
297  *
298  * Since: 2.4
299  **/
300 GtkIconTheme *
301 gtk_icon_theme_get_default (void)
302 {
303   return gtk_icon_theme_get_for_screen (gdk_screen_get_default ());
304 }
305
306 /**
307  * gtk_icon_theme_get_for_screen:
308  * @screen: a #GdkScreen
309  * 
310  * Gets the icon theme object associated with @screen; if this
311  * function has not previously been called for the given
312  * screen, a new icon theme object will be created and
313  * associated with the screen. Icon theme objects are
314  * fairly expensive to create, so using this function
315  * is usually a better choice than calling than gtk_icon_theme_new()
316  * and setting the screen yourself; by using this function
317  * a single icon theme object will be shared between users.
318  * 
319  * Return value: A unique #GtkIconTheme associated with
320  *  the given screen. This icon theme is associated with
321  *  the screen and can be used as long as the screen
322  *  is open. Do not ref or unref it.
323  *
324  * Since: 2.4
325  **/
326 GtkIconTheme *
327 gtk_icon_theme_get_for_screen (GdkScreen *screen)
328 {
329   GtkIconTheme *icon_theme;
330
331   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
332   g_return_val_if_fail (!screen->closed, NULL);
333
334   icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
335   if (!icon_theme)
336     {
337       GtkIconThemePrivate *priv;
338
339       icon_theme = gtk_icon_theme_new ();
340       gtk_icon_theme_set_screen (icon_theme, screen);
341
342       priv = icon_theme->priv;
343       priv->is_screen_singleton = TRUE;
344
345       g_object_set_data (G_OBJECT (screen), I_("gtk-icon-theme"), icon_theme);
346     }
347
348   return icon_theme;
349 }
350
351 static void
352 gtk_icon_theme_class_init (GtkIconThemeClass *klass)
353 {
354   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
355
356   parent_class = g_type_class_peek_parent (klass);
357
358   gobject_class->finalize = gtk_icon_theme_finalize;
359
360 /**
361  * GtkIconTheme::changed
362  * @icon_theme: the icon theme
363  * 
364  * Emitted when the current icon theme is switched or GTK+ detects
365  * that a change has occurred in the contents of the current
366  * icon theme.
367  **/
368   signal_changed = g_signal_new (I_("changed"),
369                                  G_TYPE_FROM_CLASS (klass),
370                                  G_SIGNAL_RUN_LAST,
371                                  G_STRUCT_OFFSET (GtkIconThemeClass, changed),
372                                  NULL, NULL,
373                                  g_cclosure_marshal_VOID__VOID,
374                                  G_TYPE_NONE, 0);
375
376   g_type_class_add_private (klass, sizeof (GtkIconThemePrivate));
377 }
378
379
380 /* Callback when the display that the icon theme is attached to
381  * is closed; unset the screen, and if it's the unique theme
382  * for the screen, drop the reference
383  */
384 static void
385 display_closed (GdkDisplay   *display,
386                 gboolean      is_error,
387                 GtkIconTheme *icon_theme)
388 {
389   GtkIconThemePrivate *priv = icon_theme->priv;
390   GdkScreen *screen = priv->screen;
391   gboolean was_screen_singleton = priv->is_screen_singleton;
392
393   if (was_screen_singleton)
394     {
395       g_object_set_data (G_OBJECT (screen), I_("gtk-icon-theme"), NULL);
396       priv->is_screen_singleton = FALSE;
397     }
398
399   gtk_icon_theme_set_screen (icon_theme, NULL);
400
401   if (was_screen_singleton)
402     {
403       g_object_unref (icon_theme);
404     }
405 }
406
407 static void
408 update_current_theme (GtkIconTheme *icon_theme)
409 {
410   GtkIconThemePrivate *priv = icon_theme->priv;
411
412   if (!priv->custom_theme)
413     {
414       gchar *theme = NULL;
415
416       if (priv->screen)
417         {
418           GtkSettings *settings = gtk_settings_get_for_screen (priv->screen);
419           g_object_get (settings, "gtk-icon-theme-name", &theme, NULL);
420         }
421
422       if (!theme)
423         theme = g_strdup (DEFAULT_THEME_NAME);
424
425       if (strcmp (priv->current_theme, theme) != 0)
426         {
427           g_free (priv->current_theme);
428           priv->current_theme = theme;
429
430           do_theme_change (icon_theme);
431         }
432       else
433         g_free (theme);
434     }
435 }
436
437 /* Callback when the icon theme GtkSetting changes
438  */
439 static void
440 theme_changed (GtkSettings  *settings,
441                GParamSpec   *pspec,
442                GtkIconTheme *icon_theme)
443 {
444   update_current_theme (icon_theme);
445 }
446
447 static void
448 unset_screen (GtkIconTheme *icon_theme)
449 {
450   GtkIconThemePrivate *priv = icon_theme->priv;
451   GtkSettings *settings;
452   GdkDisplay *display;
453   
454   if (priv->screen)
455     {
456       settings = gtk_settings_get_for_screen (priv->screen);
457       display = gdk_screen_get_display (priv->screen);
458       
459       g_signal_handlers_disconnect_by_func (display,
460                                             (gpointer) display_closed,
461                                             icon_theme);
462       g_signal_handlers_disconnect_by_func (settings,
463                                             (gpointer) theme_changed,
464                                             icon_theme);
465
466       priv->screen = NULL;
467     }
468 }
469
470 /**
471  * gtk_icon_theme_set_screen:
472  * @icon_theme: a #GtkIconTheme
473  * @screen: a #GdkScreen
474  * 
475  * Sets the screen for an icon theme; the screen is used
476  * to track the user's currently configured icon theme,
477  * which might be different for different screens.
478  *
479  * Since: 2.4
480  **/
481 void
482 gtk_icon_theme_set_screen (GtkIconTheme *icon_theme,
483                            GdkScreen    *screen)
484 {
485   GtkIconThemePrivate *priv;
486   GtkSettings *settings;
487   GdkDisplay *display;
488
489   g_return_if_fail (GTK_ICON_THEME (icon_theme));
490   g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
491
492   priv = icon_theme->priv;
493
494   unset_screen (icon_theme);
495   
496   if (screen)
497     {
498       display = gdk_screen_get_display (screen);
499       settings = gtk_settings_get_for_screen (screen);
500       
501       priv->screen = screen;
502       
503       g_signal_connect (display, "closed",
504                         G_CALLBACK (display_closed), icon_theme);
505       g_signal_connect (settings, "notify::gtk-icon-theme-name",
506                         G_CALLBACK (theme_changed), icon_theme);
507     }
508
509   update_current_theme (icon_theme);
510 }
511
512 /* Checks whether a loader for SVG files has been registered
513  * with GdkPixbuf.
514  */
515 static gboolean
516 pixbuf_supports_svg (void)
517 {
518   GSList *formats = gdk_pixbuf_get_formats ();
519   GSList *tmp_list;
520   static gboolean found_svg = FALSE;
521   static gboolean value_known = FALSE;
522
523   if (value_known)
524     return found_svg;
525   
526   for (tmp_list = formats; tmp_list && !found_svg; tmp_list = tmp_list->next)
527     {
528       gchar **mime_types = gdk_pixbuf_format_get_mime_types (tmp_list->data);
529       gchar **mime_type;
530       
531       for (mime_type = mime_types; *mime_type && !found_svg; mime_type++)
532         {
533           if (strcmp (*mime_type, "image/svg") == 0)
534             found_svg = TRUE;
535         }
536
537       g_strfreev (mime_types);
538     }
539
540   g_slist_free (formats);
541   value_known = TRUE;
542   
543   return found_svg;
544 }
545
546 static void
547 gtk_icon_theme_init (GtkIconTheme *icon_theme)
548 {
549   GtkIconThemePrivate *priv;
550   const gchar * const *xdg_data_dirs;
551   int i, j;
552   
553   priv = g_type_instance_get_private ((GTypeInstance *)icon_theme,
554                                       GTK_TYPE_ICON_THEME);
555   icon_theme->priv = priv;
556
557   priv->custom_theme = FALSE;
558   priv->current_theme = g_strdup (DEFAULT_THEME_NAME);
559
560   xdg_data_dirs = g_get_system_data_dirs ();
561   for (i = 0; xdg_data_dirs[i]; i++) ;
562
563   priv->search_path_len = 2 * i + 2;
564   
565   priv->search_path = g_new (char *, priv->search_path_len);
566   
567   i = 0;
568   priv->search_path[i++] = g_build_filename (g_get_home_dir (), ".icons", NULL);
569   priv->search_path[i++] = g_build_filename (g_get_user_data_dir (), "icons", NULL);
570   
571   for (j = 0; xdg_data_dirs[j]; j++) 
572     priv->search_path[i++] = g_build_filename (xdg_data_dirs[j], "icons", NULL);
573
574   for (j = 0; xdg_data_dirs[j]; j++) 
575     priv->search_path[i++] = g_build_filename (xdg_data_dirs[j], "pixmaps", NULL);
576
577   priv->themes_valid = FALSE;
578   priv->themes = NULL;
579   priv->unthemed_icons = NULL;
580   
581   priv->pixbuf_supports_svg = pixbuf_supports_svg ();
582 }
583
584 static void
585 free_dir_mtime (IconThemeDirMtime *dir_mtime)
586 {
587   if (dir_mtime->cache)
588     _gtk_icon_cache_unref (dir_mtime->cache);
589
590   g_free (dir_mtime->dir);
591   g_free (dir_mtime);
592
593 }
594
595 static gboolean
596 reset_styles_idle (gpointer user_data)
597 {
598   GtkIconTheme *icon_theme;
599   GtkIconThemePrivate *priv;
600
601   GDK_THREADS_ENTER ();
602
603   icon_theme = GTK_ICON_THEME (user_data);
604   priv = icon_theme->priv;
605
606   if (priv->screen && priv->is_screen_singleton)
607     {
608       GtkSettings *settings = gtk_settings_get_for_screen (priv->screen);
609       gtk_rc_reset_styles (settings);
610     }
611
612   priv->reset_styles_idle = 0;
613
614   GDK_THREADS_LEAVE ();
615
616   return FALSE;
617 }
618
619 static void
620 do_theme_change (GtkIconTheme *icon_theme)
621 {
622   GtkIconThemePrivate *priv = icon_theme->priv;
623   
624   GTK_NOTE (ICONTHEME, 
625             g_print ("change to icon theme \"%s\"\n", priv->current_theme));
626   blow_themes (icon_theme);
627   g_signal_emit (icon_theme, signal_changed, 0);
628
629   if (!priv->reset_styles_idle)
630     priv->reset_styles_idle = 
631       g_idle_add (reset_styles_idle, icon_theme);
632 }
633
634 static void
635 blow_themes (GtkIconTheme *icon_theme)
636 {
637   GtkIconThemePrivate *priv = icon_theme->priv;
638   
639   if (priv->themes_valid)
640     {
641       g_hash_table_destroy (priv->all_icons);
642       g_list_foreach (priv->themes, (GFunc)theme_destroy, NULL);
643       g_list_free (priv->themes);
644       g_list_foreach (priv->dir_mtimes, (GFunc)free_dir_mtime, NULL);
645       g_list_free (priv->dir_mtimes);
646       g_hash_table_destroy (priv->unthemed_icons);
647     }
648   priv->themes = NULL;
649   priv->unthemed_icons = NULL;
650   priv->dir_mtimes = NULL;
651   priv->all_icons = NULL;
652   priv->themes_valid = FALSE;
653 }
654
655 static void
656 gtk_icon_theme_finalize (GObject *object)
657 {
658   GtkIconTheme *icon_theme;
659   GtkIconThemePrivate *priv;
660   int i;
661
662   icon_theme = GTK_ICON_THEME (object);
663   priv = icon_theme->priv;
664
665   if (priv->reset_styles_idle)
666     {
667       g_source_remove (priv->reset_styles_idle);
668       priv->reset_styles_idle = 0;
669     }
670
671   unset_screen (icon_theme);
672
673   g_free (priv->current_theme);
674   priv->current_theme = NULL;
675
676   for (i=0; i < priv->search_path_len; i++)
677     g_free (priv->search_path[i]);
678
679   g_free (priv->search_path);
680   priv->search_path = NULL;
681
682   blow_themes (icon_theme);
683
684   G_OBJECT_CLASS (parent_class)->finalize (object);  
685 }
686
687 /**
688  * gtk_icon_theme_set_search_path:
689  * @icon_theme: a #GtkIconTheme
690  * @path: array of directories that are searched for icon themes
691  * @n_elements: number of elements in @path.
692  * 
693  * Sets the search path for the icon theme object. When looking
694  * for an icon theme, GTK+ will search for a subdirectory of
695  * one or more of the directories in @path with the same name
696  * as the icon theme. (Themes from multiple of the path elements
697  * are combined to allow themes to be extended by adding icons
698  * in the user's home directory.)
699  *
700  * In addition if an icon found isn't found either in the current
701  * icon theme or the default icon theme, and an image file with
702  * the right name is found directly in one of the elements of
703  * @path, then that image will be used for the icon name.
704  * (This is legacy feature, and new icons should be put
705  * into the default icon theme, which is called DEFAULT_THEME_NAME,
706  * rather than directly on the icon path.)
707  *
708  * Since: 2.4
709  **/
710 void
711 gtk_icon_theme_set_search_path (GtkIconTheme *icon_theme,
712                                 const gchar  *path[],
713                                 gint          n_elements)
714 {
715   GtkIconThemePrivate *priv;
716   gint i;
717
718   g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
719
720   priv = icon_theme->priv;
721   for (i = 0; i < priv->search_path_len; i++)
722     g_free (priv->search_path[i]);
723
724   g_free (priv->search_path);
725
726   priv->search_path = g_new (gchar *, n_elements);
727   priv->search_path_len = n_elements;
728   for (i = 0; i < priv->search_path_len; i++)
729     priv->search_path[i] = g_strdup (path[i]);
730
731   do_theme_change (icon_theme);
732 }
733
734
735 /**
736  * gtk_icon_theme_get_search_path:
737  * @icon_theme: a #GtkIconTheme
738  * @path: location to store a list of icon theme path directories or %NULL
739  *        The stored value should be freed with g_strfreev().
740  * @n_elements: location to store number of elements
741  *              in @path, or %NULL
742  * 
743  * Gets the current search path. See gtk_icon_theme_set_search_path().
744  *
745  * Since: 2.4
746  **/
747 void
748 gtk_icon_theme_get_search_path (GtkIconTheme      *icon_theme,
749                                 gchar            **path[],
750                                 gint              *n_elements)
751 {
752   GtkIconThemePrivate *priv;
753   int i;
754
755   g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
756
757   priv = icon_theme->priv;
758
759   if (n_elements)
760     *n_elements = priv->search_path_len;
761   
762   if (path)
763     {
764       *path = g_new (gchar *, priv->search_path_len + 1);
765       for (i = 0; i < priv->search_path_len; i++)
766         (*path)[i] = g_strdup (priv->search_path[i]);
767       (*path)[i] = NULL;
768     }
769 }
770
771 /**
772  * gtk_icon_theme_append_search_path:
773  * @icon_theme: a #GtkIconTheme
774  * @path: directory name to append to the icon path
775  * 
776  * Appends a directory to the search path. 
777  * See gtk_icon_theme_set_search_path(). 
778  *
779  * Since: 2.4
780  **/
781 void
782 gtk_icon_theme_append_search_path (GtkIconTheme *icon_theme,
783                                    const gchar  *path)
784 {
785   GtkIconThemePrivate *priv;
786
787   g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
788   g_return_if_fail (path != NULL);
789
790   priv = icon_theme->priv;
791   
792   priv->search_path_len++;
793   priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
794   priv->search_path[priv->search_path_len-1] = g_strdup (path);
795
796   do_theme_change (icon_theme);
797 }
798
799 /**
800  * gtk_icon_theme_prepend_search_path:
801  * @icon_theme: a #GtkIconTheme
802  * @path: directory name to prepend to the icon path
803  * 
804  * Prepends a directory to the search path. 
805  * See gtk_icon_theme_set_search_path().
806  *
807  * Since: 2.4
808  **/
809 void
810 gtk_icon_theme_prepend_search_path (GtkIconTheme *icon_theme,
811                                     const gchar  *path)
812 {
813   GtkIconThemePrivate *priv;
814   int i;
815
816   g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
817   g_return_if_fail (path != NULL);
818
819   priv = icon_theme->priv;
820   
821   priv->search_path_len++;
822   priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
823
824   for (i = priv->search_path_len - 1; i > 0; i--)
825     priv->search_path[i] = priv->search_path[i - 1];
826   
827   priv->search_path[0] = g_strdup (path);
828
829   do_theme_change (icon_theme);
830 }
831
832 /**
833  * gtk_icon_theme_set_custom_theme:
834  * @icon_theme: a #GtkIconTheme
835  * @theme_name: name of icon theme to use instead of configured theme,
836  *   or %NULL to unset a previously set custom theme
837  * 
838  * Sets the name of the icon theme that the #GtkIconTheme object uses
839  * overriding system configuration. This function cannot be called
840  * on the icon theme objects returned from gtk_icon_theme_get_default()
841  * and gtk_icon_theme_get_for_screen().
842  *
843  * Since: 2.4
844  **/
845 void
846 gtk_icon_theme_set_custom_theme (GtkIconTheme *icon_theme,
847                                  const gchar  *theme_name)
848 {
849   GtkIconThemePrivate *priv;
850
851   g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
852
853   priv = icon_theme->priv;
854
855   g_return_if_fail (!priv->is_screen_singleton);
856   
857   if (theme_name != NULL)
858     {
859       priv->custom_theme = TRUE;
860       if (strcmp (theme_name, priv->current_theme) != 0)
861         {
862           g_free (priv->current_theme);
863           priv->current_theme = g_strdup (theme_name);
864
865           do_theme_change (icon_theme);
866         }
867     }
868   else
869     {
870       priv->custom_theme = FALSE;
871
872       update_current_theme (icon_theme);
873     }
874 }
875
876 static void
877 insert_theme (GtkIconTheme *icon_theme, const char *theme_name)
878 {
879   int i;
880   GList *l;
881   char **dirs;
882   char **themes;
883   GtkIconThemePrivate *priv;
884   IconTheme *theme;
885   char *path;
886   GKeyFile *theme_file;
887   GError *error = NULL;
888   IconThemeDirMtime *dir_mtime;
889   struct stat stat_buf;
890   
891   priv = icon_theme->priv;
892
893   for (l = priv->themes; l != NULL; l = l->next)
894     {
895       theme = l->data;
896       if (strcmp (theme->name, theme_name) == 0)
897         return;
898     }
899   
900   for (i = 0; i < priv->search_path_len; i++)
901     {
902       path = g_build_filename (priv->search_path[i],
903                                theme_name,
904                                NULL);
905       dir_mtime = g_new (IconThemeDirMtime, 1);
906       dir_mtime->cache = NULL;
907       dir_mtime->dir = path;
908       if (g_stat (path, &stat_buf) == 0 && S_ISDIR (stat_buf.st_mode))
909         dir_mtime->mtime = stat_buf.st_mtime;
910       else
911         dir_mtime->mtime = 0;
912
913       priv->dir_mtimes = g_list_prepend (priv->dir_mtimes, dir_mtime);
914     }
915   priv->dir_mtimes = g_list_reverse (priv->dir_mtimes);
916
917   theme_file = NULL;
918   for (i = 0; i < priv->search_path_len && !theme_file; i++)
919     {
920       path = g_build_filename (priv->search_path[i],
921                                theme_name,
922                                "index.theme",
923                                NULL);
924       if (g_file_test (path, G_FILE_TEST_IS_REGULAR)) 
925         {
926           theme_file = g_key_file_new ();
927           g_key_file_set_list_separator (theme_file, ',');
928           g_key_file_load_from_file (theme_file, path, 0, &error);
929           if (error)
930             {
931               g_key_file_free (theme_file);
932               theme_file = NULL;
933               g_error_free (error);
934               error = NULL;
935             }
936         }
937       g_free (path);
938     }
939
940   if (theme_file == NULL)
941     return;
942   
943   theme = g_new (IconTheme, 1);
944   theme->display_name = 
945     g_key_file_get_locale_string (theme_file, "Icon Theme", "Name", NULL, NULL);
946   if (!theme->display_name)
947     {
948       g_warning ("Theme file for %s has no name\n", theme_name);
949       g_free (theme);
950       g_key_file_free (theme_file);
951       return;
952     }
953
954   dirs = g_key_file_get_string_list (theme_file, "Icon Theme", "Directories", NULL, NULL);
955   if (!dirs)
956     {
957       g_warning ("Theme file for %s has no directories\n", theme_name);
958       g_free (theme->display_name);
959       g_free (theme);
960       g_key_file_free (theme_file);
961       return;
962     }
963   
964   theme->name = g_strdup (theme_name);
965   theme->comment = 
966     g_key_file_get_locale_string (theme_file, 
967                                   "Icon Theme", "Comment",
968                                   NULL, NULL);
969   theme->example = 
970     g_key_file_get_string (theme_file, 
971                            "Icon Theme", "Example",
972                            NULL);
973
974   theme->dirs = NULL;
975   for (i = 0; dirs[i] != NULL; i++)
976     theme_subdir_load (icon_theme, theme, theme_file, dirs[i]);
977   
978   g_strfreev (dirs);
979   
980   theme->dirs = g_list_reverse (theme->dirs);
981
982   /* Prepend the finished theme */
983   priv->themes = g_list_prepend (priv->themes, theme);
984
985   themes = g_key_file_get_string_list (theme_file,
986                                        "Icon Theme",
987                                        "Inherits",
988                                        NULL,
989                                        NULL);
990   if (themes)
991     {
992       for (i = 0; themes[i] != NULL; i++)
993         insert_theme (icon_theme, themes[i]);
994       
995       g_strfreev (themes);
996     }
997
998   g_key_file_free (theme_file);
999 }
1000
1001 static void
1002 free_unthemed_icon (UnthemedIcon *unthemed_icon)
1003 {
1004   if (unthemed_icon->svg_filename)
1005     g_free (unthemed_icon->svg_filename);
1006   if (unthemed_icon->no_svg_filename)
1007     g_free (unthemed_icon->no_svg_filename);
1008   g_free (unthemed_icon);
1009 }
1010
1011 static void
1012 load_themes (GtkIconTheme *icon_theme)
1013 {
1014   GtkIconThemePrivate *priv;
1015   GDir *gdir;
1016   int base;
1017   char *dir, *base_name, *dot;
1018   const char *file;
1019   char *abs_file;
1020   UnthemedIcon *unthemed_icon;
1021   IconSuffix old_suffix, new_suffix;
1022   GTimeVal tv;
1023   IconThemeDirMtime *dir_mtime;
1024   struct stat stat_buf;
1025   
1026   priv = icon_theme->priv;
1027
1028   priv->all_icons = g_hash_table_new (g_str_hash, g_str_equal);
1029   
1030   insert_theme (icon_theme, priv->current_theme);
1031   
1032   /* Always look in the "default" icon theme */
1033   insert_theme (icon_theme, DEFAULT_THEME_NAME);
1034   priv->themes = g_list_reverse (priv->themes);
1035
1036
1037   priv->unthemed_icons = g_hash_table_new_full (g_str_hash, g_str_equal,
1038                                                 g_free, (GDestroyNotify)free_unthemed_icon);
1039
1040   for (base = 0; base < icon_theme->priv->search_path_len; base++)
1041     {
1042       dir = icon_theme->priv->search_path[base];
1043
1044       dir_mtime = g_new (IconThemeDirMtime, 1);
1045       dir_mtime->cache = _gtk_icon_cache_new_for_path (dir);
1046       dir_mtime->dir = g_strdup (dir);
1047       if (g_stat (dir, &stat_buf) == 0 && S_ISDIR (stat_buf.st_mode))
1048         dir_mtime->mtime = stat_buf.st_mtime;
1049       else
1050         dir_mtime->mtime = 0;
1051
1052       priv->dir_mtimes = g_list_append (priv->dir_mtimes, dir_mtime);
1053
1054       if (dir_mtime->cache != NULL)
1055         continue;
1056
1057       gdir = g_dir_open (dir, 0, NULL);
1058
1059       if (gdir == NULL)
1060         continue;
1061       
1062       while ((file = g_dir_read_name (gdir)))
1063         {
1064           new_suffix = suffix_from_name (file);
1065
1066           if (new_suffix != ICON_SUFFIX_NONE)
1067             {
1068               abs_file = g_build_filename (dir, file, NULL);
1069
1070               base_name = g_strdup (file);
1071                   
1072               dot = strrchr (base_name, '.');
1073               if (dot)
1074                 *dot = 0;
1075
1076               if ((unthemed_icon = g_hash_table_lookup (priv->unthemed_icons,
1077                                                         base_name)))
1078                 {
1079                   if (new_suffix == ICON_SUFFIX_SVG)
1080                     {
1081                       if (unthemed_icon->svg_filename)
1082                         g_free (abs_file);
1083                       else
1084                         unthemed_icon->svg_filename = abs_file;
1085                     }
1086                   else
1087                     {
1088                       if (unthemed_icon->no_svg_filename)
1089                         {
1090                           old_suffix = suffix_from_name (unthemed_icon->no_svg_filename);
1091                           if (new_suffix > old_suffix)
1092                             {
1093                               g_free (unthemed_icon->no_svg_filename);
1094                               unthemed_icon->no_svg_filename = abs_file;                              
1095                             }
1096                           else
1097                             g_free (abs_file);
1098                         }
1099                       else
1100                         unthemed_icon->no_svg_filename = abs_file;                            
1101                     }
1102
1103                   g_free (base_name);
1104                 }
1105               else
1106                 {
1107                   unthemed_icon = g_new0 (UnthemedIcon, 1);
1108                   
1109                   if (new_suffix == ICON_SUFFIX_SVG)
1110                     unthemed_icon->svg_filename = abs_file;
1111                   else
1112                     unthemed_icon->no_svg_filename = abs_file;
1113
1114                   g_hash_table_insert (priv->unthemed_icons,
1115                                        base_name,
1116                                        unthemed_icon);
1117                   g_hash_table_insert (priv->all_icons,
1118                                        base_name, NULL);
1119                 }
1120             }
1121         }
1122       g_dir_close (gdir);
1123     }
1124
1125   priv->themes_valid = TRUE;
1126   
1127   g_get_current_time(&tv);
1128   priv->last_stat_time = tv.tv_sec;
1129 }
1130
1131 static void
1132 ensure_valid_themes (GtkIconTheme *icon_theme)
1133 {
1134   GtkIconThemePrivate *priv = icon_theme->priv;
1135   GTimeVal tv;
1136
1137   if (priv->themes_valid)
1138     {
1139       g_get_current_time (&tv);
1140
1141       if (ABS (tv.tv_sec - priv->last_stat_time) > 5)
1142         gtk_icon_theme_rescan_if_needed (icon_theme);
1143     }
1144   
1145   if (!priv->themes_valid)
1146     {
1147       load_themes (icon_theme);
1148       
1149       if (!priv->check_reload && priv->screen)
1150         {         
1151           static GdkAtom atom_iconthemes = GDK_NONE;
1152           GdkEvent *event = gdk_event_new (GDK_CLIENT_EVENT);
1153           int i;
1154
1155           if (!atom_iconthemes)
1156             atom_iconthemes = gdk_atom_intern_static_string ("_GTK_LOAD_ICONTHEMES");
1157
1158           for (i = 0; i < 5; i++)
1159             event->client.data.l[i] = 0;
1160           event->client.data_format = 32;
1161           event->client.message_type = atom_iconthemes;
1162
1163           gdk_screen_broadcast_client_message (priv->screen, event);
1164         }
1165     }
1166 }
1167
1168 /**
1169  * gtk_icon_theme_lookup_icon:
1170  * @icon_theme: a #GtkIconTheme
1171  * @icon_name: the name of the icon to lookup
1172  * @size: desired icon size
1173  * @flags: flags modifying the behavior of the icon lookup
1174  * 
1175  * Looks up a named icon and returns a structure containing
1176  * information such as the filename of the icon. The icon
1177  * can then be rendered into a pixbuf using
1178  * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
1179  * combines these two steps if all you need is the pixbuf.)
1180  * 
1181  * Return value: a #GtkIconInfo structure containing information
1182  * about the icon, or %NULL if the icon wasn't found. Free with
1183  * gtk_icon_info_free()
1184  *
1185  * Since: 2.4
1186  **/
1187 GtkIconInfo *
1188 gtk_icon_theme_lookup_icon (GtkIconTheme       *icon_theme,
1189                             const gchar        *icon_name,
1190                             gint                size,
1191                             GtkIconLookupFlags  flags)
1192 {
1193   GtkIconThemePrivate *priv;
1194   GList *l;
1195   GtkIconInfo *icon_info = NULL;
1196   UnthemedIcon *unthemed_icon;
1197   gboolean allow_svg;
1198   gboolean use_builtin;
1199   gboolean found_default;
1200
1201   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1202   g_return_val_if_fail (icon_name != NULL, NULL);
1203   g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1204                         (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1205
1206   priv = icon_theme->priv;
1207
1208   GTK_NOTE (ICONTHEME, 
1209             g_print ("gtk_icon_theme_lookup_icon %s\n", icon_name));
1210   if (flags & GTK_ICON_LOOKUP_NO_SVG)
1211     allow_svg = FALSE;
1212   else if (flags & GTK_ICON_LOOKUP_FORCE_SVG)
1213     allow_svg = TRUE;
1214   else
1215     allow_svg = priv->pixbuf_supports_svg;
1216
1217   use_builtin = (flags & GTK_ICON_LOOKUP_USE_BUILTIN);
1218
1219   ensure_valid_themes (icon_theme);
1220
1221   found_default = FALSE;
1222   l = priv->themes;
1223   while (l != NULL)
1224     {
1225       IconTheme *theme = l->data;
1226       
1227       if (strcmp (theme->name, DEFAULT_THEME_NAME) == 0)
1228         found_default = TRUE;
1229       
1230       icon_info = theme_lookup_icon (theme, icon_name, size, allow_svg, use_builtin);
1231       if (icon_info)
1232         goto out;
1233       
1234       l = l->next;
1235     }
1236
1237   g_assert (found_default);
1238
1239   unthemed_icon = g_hash_table_lookup (priv->unthemed_icons, icon_name);
1240   if (unthemed_icon)
1241     {
1242       icon_info = icon_info_new ();
1243
1244       /* A SVG icon, when allowed, beats out a XPM icon, but not
1245        * a PNG icon
1246        */
1247       if (allow_svg &&
1248           unthemed_icon->svg_filename &&
1249           (!unthemed_icon->no_svg_filename ||
1250            suffix_from_name (unthemed_icon->no_svg_filename) != ICON_SUFFIX_PNG))
1251         icon_info->filename = g_strdup (unthemed_icon->svg_filename);
1252       else if (unthemed_icon->no_svg_filename)
1253         icon_info->filename = g_strdup (unthemed_icon->no_svg_filename);
1254 #ifdef G_OS_WIN32
1255       icon_info->cp_filename = g_locale_from_utf8 (icon_info->filename,
1256                                                    -1, NULL, NULL, NULL);
1257 #endif
1258
1259       icon_info->dir_type = ICON_THEME_DIR_UNTHEMED;
1260     }
1261
1262  out:
1263   if (icon_info)
1264     icon_info->desired_size = size;
1265   else
1266     {
1267       static gboolean check_for_default_theme = TRUE;
1268       char *default_theme_path;
1269       gboolean found = FALSE;
1270       unsigned i;
1271
1272       if (check_for_default_theme)
1273         {
1274           check_for_default_theme = FALSE;
1275
1276           for (i = 0; !found && i < priv->search_path_len; i++)
1277             {
1278               default_theme_path = g_build_filename (priv->search_path[i],
1279                                                      DEFAULT_THEME_NAME,
1280                                                      "index.theme",
1281                                                      NULL);
1282               found = g_file_test (default_theme_path, G_FILE_TEST_IS_REGULAR);
1283               g_free (default_theme_path);
1284             }
1285           if (!found)
1286             {
1287               g_warning (_("Could not find the icon '%s'. The '%s' theme\n"
1288                            "was not found either, perhaps you need to install it.\n"
1289                            "You can get a copy from:\n"
1290                            "\t%s"),
1291                          icon_name, DEFAULT_THEME_NAME, "http://icon-theme.freedesktop.org/releases");
1292             }
1293         }
1294     }
1295
1296   return icon_info;
1297 }
1298
1299 /* Error quark */
1300 GQuark
1301 gtk_icon_theme_error_quark (void)
1302 {
1303   static GQuark q = 0;
1304   if (q == 0)
1305     q = g_quark_from_static_string ("gtk-icon-theme-error-quark");
1306
1307   return q;
1308 }
1309
1310 /**
1311  * gtk_icon_theme_load_icon:
1312  * @icon_theme: a #GtkIconTheme
1313  * @icon_name: the name of the icon to lookup
1314  * @size: the desired icon size. The resulting icon may not be
1315  *        exactly this size; see gtk_icon_info_load_icon().
1316  * @flags: flags modifying the behavior of the icon lookup
1317  * @error: Location to store error information on failure, or %NULL.
1318  * 
1319  * Looks up an icon in an icon theme, scales it to the given size
1320  * and renders it into a pixbuf. This is a convenience function;
1321  * if more details about the icon are needed, use
1322  * gtk_icon_theme_lookup_icon() followed by gtk_icon_info_load_icon().
1323  *
1324  * Note that you probably want to listen for icon theme changes and
1325  * update the icon. This is usually done by connecting to the 
1326  * GtkWidget::style-set signal. If for some reason you do not want to
1327  * update the icon when the icon theme changes, you should consider
1328  * using gdk_pixbuf_copy() to make a private copy of the pixbuf
1329  * returned by this function. Otherwise GTK+ may need to keep the old 
1330  * icon theme loaded, which would be a waste of memory.
1331  * 
1332  * Return value: the rendered icon; this may be a newly created icon
1333  *  or a new reference to an internal icon, so you must not modify
1334  *  the icon. Use g_object_unref() to release your reference to the
1335  *  icon. %NULL if the icon isn't found.
1336  *
1337  * Since: 2.4
1338  **/
1339 GdkPixbuf *
1340 gtk_icon_theme_load_icon (GtkIconTheme         *icon_theme,
1341                           const gchar          *icon_name,
1342                           gint                  size,
1343                           GtkIconLookupFlags    flags,
1344                           GError              **error)
1345 {
1346   GtkIconInfo *icon_info;
1347   GdkPixbuf *pixbuf = NULL;
1348   
1349   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1350   g_return_val_if_fail (icon_name != NULL, NULL);
1351   g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1352                         (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1353   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1354   
1355   icon_info  = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size,
1356                                            flags | GTK_ICON_LOOKUP_USE_BUILTIN);
1357   if (!icon_info)
1358     {
1359       g_set_error (error, GTK_ICON_THEME_ERROR,  GTK_ICON_THEME_NOT_FOUND,
1360                    _("Icon '%s' not present in theme"), icon_name);
1361       return NULL;
1362     }
1363
1364   pixbuf = gtk_icon_info_load_icon (icon_info, error);
1365   gtk_icon_info_free (icon_info);
1366
1367   return pixbuf;
1368 }
1369
1370 /**
1371  * gtk_icon_theme_has_icon:
1372  * @icon_theme: a #GtkIconTheme
1373  * @icon_name: the name of an icon
1374  * 
1375  * Checks whether an icon theme includes an icon
1376  * for a particular name.
1377  * 
1378  * Return value: %TRUE if @icon_theme includes an
1379  *  icon for @icon_name.
1380  *
1381  * Since: 2.4
1382  **/
1383 gboolean 
1384 gtk_icon_theme_has_icon (GtkIconTheme *icon_theme,
1385                          const char   *icon_name)
1386 {
1387   GtkIconThemePrivate *priv;
1388   GList *l;
1389
1390   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
1391   
1392   priv = icon_theme->priv;
1393   
1394   ensure_valid_themes (icon_theme);
1395
1396   for (l = priv->dir_mtimes; l; l = l->next)
1397     {
1398       IconThemeDirMtime *dir_mtime = l->data;
1399       GtkIconCache *cache = dir_mtime->cache;
1400       
1401       if (cache && _gtk_icon_cache_has_icon (cache, icon_name))
1402         return TRUE;
1403     }
1404
1405   if (g_hash_table_lookup_extended (priv->all_icons,
1406                                     icon_name, NULL, NULL))
1407     return TRUE;
1408
1409   if (_builtin_cache &&
1410       _gtk_icon_cache_has_icon (_builtin_cache, icon_name))
1411     return TRUE;
1412
1413   if (icon_theme_builtin_icons &&
1414       g_hash_table_lookup_extended (icon_theme_builtin_icons,
1415                                     icon_name, NULL, NULL))
1416     return TRUE;
1417
1418   return FALSE;
1419 }
1420
1421 static void
1422 add_size (gpointer  key,
1423           gpointer  value,
1424           gpointer  user_data)
1425 {
1426   gint **res_p = user_data;
1427
1428   **res_p = GPOINTER_TO_INT (key);
1429
1430   (*res_p)++;
1431 }
1432
1433 /**
1434  * gtk_icon_theme_get_icon_sizes:
1435  * @icon_theme: a #GtkIconTheme
1436  * @icon_name: the name of an icon
1437  * 
1438  * Returns an array of integers describing the sizes at which
1439  * the icon is available without scaling. A size of -1 means 
1440  * that the icon is available in a scalable format. The array 
1441  * is zero-terminated.
1442  * 
1443  * Return value: An newly allocated array describing the sizes at
1444  * which the icon is available. The array should be freed with g_free()
1445  * when it is no longer needed.
1446  *
1447  * Since: 2.6
1448  **/
1449 gint *
1450 gtk_icon_theme_get_icon_sizes (GtkIconTheme *icon_theme,
1451                                const char   *icon_name)
1452 {
1453   GList *l, *d;
1454   GHashTable *sizes;
1455   gint *result, *r;
1456   guint suffix;
1457   
1458   GtkIconThemePrivate *priv;
1459
1460   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1461   
1462   priv = icon_theme->priv;
1463
1464   ensure_valid_themes (icon_theme);
1465
1466   sizes = g_hash_table_new (g_direct_hash, g_direct_equal);
1467
1468   for (l = priv->themes; l; l = l->next)
1469     {
1470       IconTheme *theme = l->data;
1471       for (d = theme->dirs; d; d = d->next)
1472         {
1473           IconThemeDir *dir = d->data;
1474
1475           suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);      
1476           if (suffix != ICON_SUFFIX_NONE)
1477             {
1478               if (suffix == ICON_SUFFIX_SVG)
1479                 g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL);
1480               else
1481                 g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL);
1482             }
1483         }
1484     }
1485
1486   r = result = g_new0 (gint, g_hash_table_size (sizes) + 1);
1487
1488   g_hash_table_foreach (sizes, add_size, &r);
1489   g_hash_table_destroy (sizes);
1490   
1491   return result;
1492 }
1493
1494 static void
1495 add_key_to_hash (gpointer  key,
1496                  gpointer  value,
1497                  gpointer  user_data)
1498 {
1499   GHashTable *hash = user_data;
1500
1501   g_hash_table_insert (hash, key, NULL);
1502 }
1503
1504 static void
1505 add_key_to_list (gpointer  key,
1506                  gpointer  value,
1507                  gpointer  user_data)
1508 {
1509   GList **list = user_data;
1510
1511   *list = g_list_prepend (*list, g_strdup (key));
1512 }
1513
1514 /**
1515  * gtk_icon_theme_list_icons:
1516  * @icon_theme: a #GtkIconTheme
1517  * @context: a string identifying a particular type of icon,
1518  *           or %NULL to list all icons.
1519  * 
1520  * Lists the icons in the current icon theme. Only a subset
1521  * of the icons can be listed by providing a context string.
1522  * The set of values for the context string is system dependent,
1523  * but will typically include such values as 'apps' and
1524  * 'mimetypes'.
1525  * 
1526  * Return value: a #GList list holding the names of all the
1527  *  icons in the theme. You must first free each element
1528  *  in the list with g_free(), then free the list itself
1529  *  with g_list_free().
1530  *
1531  * Since: 2.4
1532  **/
1533 GList *
1534 gtk_icon_theme_list_icons (GtkIconTheme *icon_theme,
1535                            const char   *context)
1536 {
1537   GtkIconThemePrivate *priv;
1538   GHashTable *icons;
1539   GList *list, *l;
1540   GQuark context_quark;
1541   
1542   priv = icon_theme->priv;
1543   
1544   ensure_valid_themes (icon_theme);
1545
1546   if (context)
1547     {
1548       context_quark = g_quark_try_string (context);
1549
1550       if (!context_quark)
1551         return NULL;
1552     }
1553   else
1554     context_quark = 0;
1555
1556   icons = g_hash_table_new (g_str_hash, g_str_equal);
1557   
1558   l = priv->themes;
1559   while (l != NULL)
1560     {
1561       theme_list_icons (l->data, icons, context_quark);
1562       l = l->next;
1563     }
1564
1565   if (context_quark == 0)
1566     g_hash_table_foreach (priv->unthemed_icons,
1567                           add_key_to_hash,
1568                           icons);
1569
1570   list = NULL;
1571   
1572   g_hash_table_foreach (icons,
1573                         add_key_to_list,
1574                         &list);
1575
1576   g_hash_table_destroy (icons);
1577   
1578   return list;
1579 }
1580
1581 /**
1582  * gtk_icon_theme_get_example_icon_name:
1583  * @icon_theme: a #GtkIconTheme
1584  * 
1585  * Gets the name of an icon that is representative of the
1586  * current theme (for instance, to use when presenting
1587  * a list of themes to the user.)
1588  * 
1589  * Return value: the name of an example icon or %NULL.
1590  *  Free with g_free().
1591  *
1592  * Since: 2.4
1593  **/
1594 char *
1595 gtk_icon_theme_get_example_icon_name (GtkIconTheme *icon_theme)
1596 {
1597   GtkIconThemePrivate *priv;
1598   GList *l;
1599   IconTheme *theme;
1600
1601   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1602   
1603   priv = icon_theme->priv;
1604   
1605   ensure_valid_themes (icon_theme);
1606
1607   l = priv->themes;
1608   while (l != NULL)
1609     {
1610       theme = l->data;
1611       if (theme->example)
1612         return g_strdup (theme->example);
1613       
1614       l = l->next;
1615     }
1616   
1617   return NULL;
1618 }
1619
1620 /**
1621  * gtk_icon_theme_rescan_if_needed:
1622  * @icon_theme: a #GtkIconTheme
1623  * 
1624  * Checks to see if the icon theme has changed; if it has, any
1625  * currently cached information is discarded and will be reloaded
1626  * next time @icon_theme is accessed.
1627  * 
1628  * Return value: %TRUE if the icon theme has changed and needed
1629  *   to be reloaded.
1630  *
1631  * Since: 2.4
1632  **/
1633 gboolean
1634 gtk_icon_theme_rescan_if_needed (GtkIconTheme *icon_theme)
1635 {
1636   GtkIconThemePrivate *priv;
1637   IconThemeDirMtime *dir_mtime;
1638   GList *d;
1639   int stat_res;
1640   struct stat stat_buf;
1641   GTimeVal tv;
1642
1643   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
1644
1645   priv = icon_theme->priv;
1646   
1647   for (d = priv->dir_mtimes; d != NULL; d = d->next)
1648     {
1649       dir_mtime = d->data;
1650
1651       stat_res = g_stat (dir_mtime->dir, &stat_buf);
1652
1653       /* dir mtime didn't change */
1654       if (stat_res == 0 && 
1655           S_ISDIR (stat_buf.st_mode) &&
1656           dir_mtime->mtime == stat_buf.st_mtime)
1657         continue;
1658       /* didn't exist before, and still doesn't */
1659       if (dir_mtime->mtime == 0 &&
1660           (stat_res != 0 || !S_ISDIR (stat_buf.st_mode)))
1661         continue;
1662           
1663       do_theme_change (icon_theme);
1664       return TRUE;
1665     }
1666   
1667   g_get_current_time (&tv);
1668   priv->last_stat_time = tv.tv_sec;
1669
1670   return FALSE;
1671 }
1672
1673 static void
1674 theme_destroy (IconTheme *theme)
1675 {
1676   g_free (theme->display_name);
1677   g_free (theme->comment);
1678   g_free (theme->name);
1679   g_free (theme->example);
1680
1681   g_list_foreach (theme->dirs, (GFunc)theme_dir_destroy, NULL);
1682   g_list_free (theme->dirs);
1683   
1684   g_free (theme);
1685 }
1686
1687 static void
1688 theme_dir_destroy (IconThemeDir *dir)
1689 {
1690   if (dir->cache)
1691       _gtk_icon_cache_unref (dir->cache);
1692   else
1693     g_hash_table_destroy (dir->icons);
1694   
1695   if (dir->icon_data)
1696     g_hash_table_destroy (dir->icon_data);
1697   g_free (dir->dir);
1698   g_free (dir->subdir);
1699   g_free (dir);
1700 }
1701
1702 static int
1703 theme_dir_size_difference (IconThemeDir *dir, int size, gboolean *smaller)
1704 {
1705   int min, max;
1706   switch (dir->type)
1707     {
1708     case ICON_THEME_DIR_FIXED:
1709       *smaller = size < dir->size;
1710       return abs (size - dir->size);
1711       break;
1712     case ICON_THEME_DIR_SCALABLE:
1713       *smaller = size < dir->min_size;
1714       if (size < dir->min_size)
1715         return dir->min_size - size;
1716       if (size > dir->max_size)
1717         return size - dir->max_size;
1718       return 0;
1719       break;
1720     case ICON_THEME_DIR_THRESHOLD:
1721       min = dir->size - dir->threshold;
1722       max = dir->size + dir->threshold;
1723       *smaller = size < min;
1724       if (size < min)
1725         return min - size;
1726       if (size > max)
1727         return size - max;
1728       return 0;
1729       break;
1730     case ICON_THEME_DIR_UNTHEMED:
1731       g_assert_not_reached ();
1732       break;
1733     }
1734   g_assert_not_reached ();
1735   return 1000;
1736 }
1737
1738 static const char *
1739 string_from_suffix (IconSuffix suffix)
1740 {
1741   switch (suffix)
1742     {
1743     case ICON_SUFFIX_XPM:
1744       return ".xpm";
1745     case ICON_SUFFIX_SVG:
1746       return ".svg";
1747     case ICON_SUFFIX_PNG:
1748       return ".png";
1749     default:
1750       g_assert_not_reached();
1751     }
1752   return NULL;
1753 }
1754
1755 static IconSuffix
1756 suffix_from_name (const char *name)
1757 {
1758   IconSuffix retval;
1759
1760   if (g_str_has_suffix (name, ".png"))
1761     retval = ICON_SUFFIX_PNG;
1762   else if (g_str_has_suffix (name, ".svg"))
1763     retval = ICON_SUFFIX_SVG;
1764   else if (g_str_has_suffix (name, ".xpm"))
1765     retval = ICON_SUFFIX_XPM;
1766   else
1767     retval = ICON_SUFFIX_NONE;
1768
1769   return retval;
1770 }
1771
1772 static IconSuffix
1773 best_suffix (IconSuffix suffix,
1774              gboolean   allow_svg)
1775 {
1776   if ((suffix & ICON_SUFFIX_PNG) != 0)
1777     return ICON_SUFFIX_PNG;
1778   else if (allow_svg && ((suffix & ICON_SUFFIX_SVG) != 0))
1779     return ICON_SUFFIX_SVG;
1780   else if ((suffix & ICON_SUFFIX_XPM) != 0)
1781     return ICON_SUFFIX_XPM;
1782   else
1783     return ICON_SUFFIX_NONE;
1784 }
1785
1786
1787 static IconSuffix
1788 theme_dir_get_icon_suffix (IconThemeDir *dir,
1789                            const gchar  *icon_name,
1790                            gboolean     *has_icon_file)
1791 {
1792   IconSuffix suffix;
1793
1794   if (dir->cache)
1795     {
1796       suffix = (IconSuffix)_gtk_icon_cache_get_icon_flags (dir->cache,
1797                                                            icon_name,
1798                                                            dir->subdir);
1799
1800       if (has_icon_file)
1801         *has_icon_file = suffix & HAS_ICON_FILE;
1802
1803       suffix = suffix & ~HAS_ICON_FILE;
1804     }
1805   else
1806     suffix = GPOINTER_TO_UINT (g_hash_table_lookup (dir->icons, icon_name));
1807
1808   GTK_NOTE (ICONTHEME, 
1809             g_print ("get_icon_suffix%s %d\n", dir->cache ? " (cached)" : "", suffix));
1810
1811   return suffix;
1812 }
1813
1814 static void
1815 _gtk_icon_theme_ensure_builtin_cache (void)
1816 {
1817   static gboolean initialized = FALSE;
1818   IconThemeDir *dir;
1819   gint sizes[5] = { 16, 20, 24, 32, 48 };
1820   gint n_sizes = G_N_ELEMENTS (sizes);
1821   gint i;
1822
1823   if (!initialized)
1824     {
1825       initialized = TRUE;
1826
1827       _builtin_cache = _gtk_icon_cache_new ((gchar *)builtin_icons);
1828
1829       for (i = 0; i < n_sizes; i++)
1830         {
1831           dir = g_new (IconThemeDir, 1);
1832           dir->type = ICON_THEME_DIR_THRESHOLD;
1833           dir->context = 0;
1834           dir->size = sizes[i];
1835           dir->min_size = sizes[i];
1836           dir->max_size = sizes[i];
1837           dir->threshold = 2;
1838           dir->dir = NULL;
1839           dir->icon_data = NULL;
1840           dir->subdir = g_strdup_printf ("%d", sizes[i]);
1841           dir->cache = _gtk_icon_cache_ref (_builtin_cache);
1842
1843           builtin_dirs = g_list_append (builtin_dirs, dir);
1844         }
1845     }
1846 }
1847
1848 static GtkIconInfo *
1849 theme_lookup_icon (IconTheme          *theme,
1850                    const char         *icon_name,
1851                    int                 size,
1852                    gboolean            allow_svg,
1853                    gboolean            use_builtin)
1854 {
1855   GList *dirs, *l;
1856   IconThemeDir *dir, *min_dir;
1857   char *file;
1858   int min_difference, difference;
1859   BuiltinIcon *closest_builtin = NULL;
1860   gboolean smaller, has_larger;
1861   IconSuffix suffix;
1862
1863   min_difference = G_MAXINT;
1864   min_dir = NULL;
1865   has_larger = FALSE;
1866
1867   /* Builtin icons are logically part of the default theme and
1868    * are searched before other subdirectories of the default theme.
1869    */
1870   _gtk_icon_theme_ensure_builtin_cache ();
1871
1872   if (strcmp (theme->name, DEFAULT_THEME_NAME) == 0 && use_builtin)
1873     {
1874       closest_builtin = find_builtin_icon (icon_name, 
1875                                            size,
1876                                            &min_difference,
1877                                            &has_larger);
1878
1879       if (min_difference == 0)
1880         return icon_info_new_builtin (closest_builtin);
1881
1882       dirs = builtin_dirs;
1883     }
1884   else
1885     dirs = theme->dirs;
1886
1887   l = dirs;
1888   while (l != NULL)
1889     {
1890       dir = l->data;
1891
1892       GTK_NOTE (ICONTHEME, 
1893                 g_print ("theme_lookup_icon dir %s\n", dir->dir));
1894       suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
1895       if (best_suffix (suffix, allow_svg) != ICON_SUFFIX_NONE)
1896         {
1897           difference = theme_dir_size_difference (dir, size, &smaller);
1898
1899           if (difference == 0)
1900             {
1901               min_dir = dir;
1902               break;
1903             }
1904
1905           if (!has_larger)
1906             {
1907               if (difference < min_difference || smaller)
1908                 {
1909                   min_difference = difference;
1910                   min_dir = dir;
1911                   closest_builtin = NULL;
1912                   has_larger = smaller;
1913                 }
1914             }
1915           else
1916             {
1917               if (difference < min_difference && smaller)
1918                 {
1919                   min_difference = difference;
1920                   min_dir = dir;
1921                   closest_builtin = NULL;
1922                 }
1923             }
1924
1925         }
1926
1927       l = l->next;
1928
1929       if (l == NULL && dirs == builtin_dirs)
1930         {
1931           dirs = theme->dirs;
1932           l = dirs;
1933         }
1934     }
1935
1936   if (closest_builtin)
1937     return icon_info_new_builtin (closest_builtin);
1938   
1939   if (min_dir)
1940     {
1941       GtkIconInfo *icon_info = icon_info_new ();
1942       gboolean has_icon_file = FALSE;
1943       
1944       suffix = theme_dir_get_icon_suffix (min_dir, icon_name, &has_icon_file);
1945       suffix = best_suffix (suffix, allow_svg);
1946       g_assert (suffix != ICON_SUFFIX_NONE);
1947       
1948       file = g_strconcat (icon_name, string_from_suffix (suffix), NULL);
1949       icon_info->filename = g_build_filename (min_dir->dir, file, NULL);
1950       g_free (file);
1951 #ifdef G_OS_WIN32
1952       icon_info->cp_filename = g_locale_from_utf8 (icon_info->filename,
1953                                                    -1, NULL, NULL, NULL);
1954 #endif
1955       
1956       if (min_dir->icon_data != NULL)
1957         icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
1958
1959       if (icon_info->data == NULL && min_dir->cache != NULL)
1960         {
1961           icon_info->data = _gtk_icon_cache_get_icon_data (min_dir->cache, icon_name, min_dir->subdir);
1962           if (icon_info->data)
1963             {
1964               if (min_dir->icon_data == NULL)
1965                 min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
1966                                                             g_free, (GDestroyNotify)icon_data_free);
1967
1968               g_hash_table_replace (min_dir->icon_data, g_strdup (icon_name), icon_info->data);
1969             }
1970         }
1971
1972       if (icon_info->data == NULL && has_icon_file)
1973         {
1974           gchar *icon_file_name, *icon_file_path;
1975
1976           icon_file_name = g_strconcat (icon_name, ".icon", NULL);
1977           icon_file_path = g_build_filename (min_dir->dir, icon_file_name, NULL);
1978
1979           if (g_file_test (icon_file_path, G_FILE_TEST_IS_REGULAR))
1980             {
1981               if (min_dir->icon_data == NULL)   
1982                 min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
1983                                                             g_free, (GDestroyNotify)icon_data_free);
1984               load_icon_data (min_dir, icon_file_path, icon_file_name);
1985               
1986               icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
1987             }
1988           g_free (icon_file_name);
1989           g_free (icon_file_path);
1990         }
1991
1992       if (min_dir->cache)
1993         {
1994           icon_info->cache_pixbuf = _gtk_icon_cache_get_icon (min_dir->cache, icon_name,
1995                                                               min_dir->subdir);
1996         }
1997
1998       icon_info->dir_type = min_dir->type;
1999       icon_info->dir_size = min_dir->size;
2000       icon_info->threshold = min_dir->threshold;
2001       
2002       return icon_info;
2003     }
2004  
2005   return NULL;
2006 }
2007
2008 static void
2009 theme_list_icons (IconTheme *theme, GHashTable *icons,
2010                   GQuark context)
2011 {
2012   GList *l = theme->dirs;
2013   IconThemeDir *dir;
2014   
2015   while (l != NULL)
2016     {
2017       dir = l->data;
2018
2019       if (context == dir->context ||
2020           context == 0)
2021         {
2022           if (dir->cache)
2023             {
2024               _gtk_icon_cache_add_icons (dir->cache,
2025                                          dir->subdir,
2026                                          icons);
2027                                          
2028             }
2029           else
2030             {
2031               g_hash_table_foreach (dir->icons,
2032                                     add_key_to_hash,
2033                                     icons);
2034             }
2035         }
2036       l = l->next;
2037     }
2038 }
2039
2040 static void
2041 load_icon_data (IconThemeDir *dir, const char *path, const char *name)
2042 {
2043   GKeyFile *icon_file;
2044   char *base_name;
2045   char **split;
2046   gsize length;
2047   char *dot;
2048   char *str;
2049   char *split_point;
2050   int i;
2051   gint *ivalues;
2052   GError *error = NULL;
2053   
2054   GtkIconData *data;
2055
2056   icon_file = g_key_file_new ();
2057   g_key_file_set_list_separator (icon_file, ',');
2058   g_key_file_load_from_file (icon_file, path, 0, &error);
2059   if (error)
2060     {
2061       g_error_free (error);
2062       return;
2063     }
2064   else
2065     {
2066       base_name = g_strdup (name);
2067       dot = strrchr (base_name, '.');
2068       *dot = 0;
2069       
2070       data = g_new0 (GtkIconData, 1);
2071       g_hash_table_replace (dir->icon_data, base_name, data);
2072       
2073       ivalues = g_key_file_get_integer_list (icon_file, 
2074                                              "Icon Data", "EmbeddedTextRectangle",
2075                                               &length, NULL);
2076       if (ivalues)
2077         {
2078           if (length == 4)
2079             {
2080               data->has_embedded_rect = TRUE;
2081               data->x0 = ivalues[0];
2082               data->y0 = ivalues[1];
2083               data->x1 = ivalues[2];
2084               data->y1 = ivalues[3];
2085             }
2086           
2087           g_free (ivalues);
2088         }
2089       
2090       str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
2091       if (str)
2092         {
2093           split = g_strsplit (str, "|", -1);
2094           
2095           data->n_attach_points = g_strv_length (split);
2096           data->attach_points = g_malloc (sizeof (GdkPoint) * data->n_attach_points);
2097
2098           i = 0;
2099           while (split[i] != NULL && i < data->n_attach_points)
2100             {
2101               split_point = strchr (split[i], ',');
2102               if (split_point)
2103                 {
2104                   *split_point = 0;
2105                   split_point++;
2106                   data->attach_points[i].x = atoi (split[i]);
2107                   data->attach_points[i].y = atoi (split_point);
2108                 }
2109               i++;
2110             }
2111           
2112           g_strfreev (split);
2113           g_free (str);
2114         }
2115       
2116       data->display_name = g_key_file_get_locale_string (icon_file, 
2117                                                          "Icon Data", "DisplayName",
2118                                                          NULL, NULL);
2119       g_key_file_free (icon_file);
2120     }
2121 }
2122
2123 static void
2124 scan_directory (GtkIconThemePrivate *icon_theme,
2125                 IconThemeDir *dir, char *full_dir)
2126 {
2127   GDir *gdir;
2128   const char *name;
2129   char *base_name, *dot;
2130   char *path;
2131   IconSuffix suffix, hash_suffix;
2132
2133   GTK_NOTE (ICONTHEME, 
2134             g_print ("scanning directory %s\n", full_dir));
2135   dir->icons = g_hash_table_new_full (g_str_hash, g_str_equal,
2136                                       g_free, NULL);
2137   
2138   gdir = g_dir_open (full_dir, 0, NULL);
2139
2140   if (gdir == NULL)
2141     return;
2142
2143   while ((name = g_dir_read_name (gdir)))
2144     {
2145       if (g_str_has_suffix (name, ".icon"))
2146         {
2147           if (dir->icon_data == NULL)
2148             dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2149                                                     g_free, (GDestroyNotify)icon_data_free);
2150           
2151           path = g_build_filename (full_dir, name, NULL);
2152           if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
2153             load_icon_data (dir, path, name);
2154           
2155           g_free (path);
2156           
2157           continue;
2158         }
2159
2160       suffix = suffix_from_name (name);
2161       if (suffix == ICON_SUFFIX_NONE)
2162         continue;
2163       
2164       base_name = g_strdup (name);
2165       dot = strrchr (base_name, '.');
2166       *dot = 0;
2167       
2168       hash_suffix = GPOINTER_TO_INT (g_hash_table_lookup (dir->icons, base_name));
2169       g_hash_table_replace (dir->icons, base_name, GUINT_TO_POINTER (hash_suffix| suffix));
2170       g_hash_table_insert (icon_theme->all_icons, base_name, NULL);
2171     }
2172   
2173   g_dir_close (gdir);
2174 }
2175
2176 static void
2177 theme_subdir_load (GtkIconTheme *icon_theme,
2178                    IconTheme    *theme,
2179                    GKeyFile     *theme_file,
2180                    char         *subdir)
2181 {
2182   GList *d;
2183   char *type_string;
2184   IconThemeDir *dir;
2185   IconThemeDirType type;
2186   char *context_string;
2187   GQuark context;
2188   int size;
2189   int min_size;
2190   int max_size;
2191   int threshold;
2192   char *full_dir;
2193   GError *error = NULL;
2194   IconThemeDirMtime *dir_mtime;
2195
2196   size = g_key_file_get_integer (theme_file, subdir, "Size", &error);
2197   if (error)
2198     {
2199       g_error_free (error);
2200       g_warning ("Theme directory %s of theme %s has no size field\n", 
2201                  subdir, theme->name);
2202       return;
2203     }
2204   
2205   type = ICON_THEME_DIR_THRESHOLD;
2206   type_string = g_key_file_get_string (theme_file, subdir, "Type", NULL);
2207   if (type_string)
2208     {
2209       if (strcmp (type_string, "Fixed") == 0)
2210         type = ICON_THEME_DIR_FIXED;
2211       else if (strcmp (type_string, "Scalable") == 0)
2212         type = ICON_THEME_DIR_SCALABLE;
2213       else if (strcmp (type_string, "Threshold") == 0)
2214         type = ICON_THEME_DIR_THRESHOLD;
2215
2216       g_free (type_string);
2217     }
2218   
2219   context = 0;
2220   context_string = g_key_file_get_string (theme_file, subdir, "Context", NULL);
2221   if (context_string)
2222     {
2223       context = g_quark_from_string (context_string);
2224       g_free (context_string);
2225     }
2226
2227   max_size = g_key_file_get_integer (theme_file, subdir, "MaxSize", &error);
2228   if (error)
2229     {
2230       max_size = size;
2231
2232       g_error_free (error);
2233       error = NULL;
2234     }
2235
2236   min_size = g_key_file_get_integer (theme_file, subdir, "MinSize", &error);
2237   if (error)
2238     {
2239       min_size = size;
2240
2241       g_error_free (error);
2242       error = NULL;
2243     }
2244   
2245   threshold = g_key_file_get_integer (theme_file, subdir, "Threshold", &error);
2246   if (error)
2247     {
2248       threshold = 2;
2249
2250       g_error_free (error);
2251       error = NULL;
2252     }
2253
2254   for (d = icon_theme->priv->dir_mtimes; d; d = d->next)
2255     {
2256       dir_mtime = (IconThemeDirMtime *)d->data;
2257
2258       if (dir_mtime->mtime == 0)
2259         continue; /* directory doesn't exist */
2260
2261        full_dir = g_build_filename (dir_mtime->dir, subdir, NULL);
2262
2263       /* First, see if we have a cache for the directory */
2264       if (dir_mtime->cache != NULL || g_file_test (full_dir, G_FILE_TEST_IS_DIR))
2265         {
2266           if (dir_mtime->cache == NULL)
2267             {
2268               /* This will return NULL if the cache doesn't exist or is outdated */
2269               dir_mtime->cache = _gtk_icon_cache_new_for_path (dir_mtime->dir);
2270             }
2271           
2272           dir = g_new (IconThemeDir, 1);
2273           dir->type = type;
2274           dir->context = context;
2275           dir->size = size;
2276           dir->min_size = min_size;
2277           dir->max_size = max_size;
2278           dir->threshold = threshold;
2279           dir->dir = full_dir;
2280           dir->icon_data = NULL;
2281           dir->subdir = g_strdup (subdir);
2282           if (dir_mtime->cache != NULL)
2283             dir->cache = _gtk_icon_cache_ref (dir_mtime->cache);
2284           else
2285             {
2286               dir->cache = NULL;
2287               scan_directory (icon_theme->priv, dir, full_dir);
2288             }
2289
2290           theme->dirs = g_list_prepend (theme->dirs, dir);
2291         }
2292       else
2293         g_free (full_dir);
2294     }
2295 }
2296
2297 static void
2298 icon_data_free (GtkIconData *icon_data)
2299 {
2300   g_free (icon_data->attach_points);
2301   g_free (icon_data->display_name);
2302   g_free (icon_data);
2303 }
2304
2305 /*
2306  * GtkIconInfo
2307  */
2308 GType
2309 gtk_icon_info_get_type (void)
2310 {
2311   static GType our_type = 0;
2312   
2313   if (our_type == 0)
2314     our_type = g_boxed_type_register_static (I_("GtkIconInfo"),
2315                                              (GBoxedCopyFunc) gtk_icon_info_copy,
2316                                              (GBoxedFreeFunc) gtk_icon_info_free);
2317
2318   return our_type;
2319 }
2320
2321 static GtkIconInfo *
2322 icon_info_new (void)
2323 {
2324   GtkIconInfo *icon_info = g_new0 (GtkIconInfo, 1);
2325
2326   icon_info->scale = -1.;
2327
2328   return icon_info;
2329 }
2330
2331 static GtkIconInfo *
2332 icon_info_new_builtin (BuiltinIcon *icon)
2333 {
2334   GtkIconInfo *icon_info = icon_info_new ();
2335
2336   icon_info->cache_pixbuf = g_object_ref (icon->pixbuf);
2337   icon_info->dir_type = ICON_THEME_DIR_THRESHOLD;
2338   icon_info->dir_size = icon->size;
2339   icon_info->threshold = 2;
2340   
2341   return icon_info;
2342 }
2343
2344 /**
2345  * gtk_icon_info_copy:
2346  * @icon_info: a #GtkIconInfo
2347  * 
2348  * Make a copy of a #GtkIconInfo.
2349  * 
2350  * Return value: the new GtkIconInfo
2351  *
2352  * Since: 2.4
2353  **/
2354 GtkIconInfo *
2355 gtk_icon_info_copy (GtkIconInfo *icon_info)
2356 {
2357   GtkIconInfo *copy;
2358   
2359   g_return_val_if_fail (icon_info != NULL, NULL);
2360
2361   copy = g_memdup (icon_info, sizeof (GtkIconInfo));
2362   if (copy->cache_pixbuf)
2363     g_object_ref (copy->cache_pixbuf);
2364   if (copy->pixbuf)
2365     g_object_ref (copy->pixbuf);
2366   if (copy->load_error)
2367     copy->load_error = g_error_copy (copy->load_error);
2368   if (copy->filename)
2369     copy->filename = g_strdup (copy->filename);
2370 #ifdef G_OS_WIN32
2371   if (copy->cp_filename)
2372     copy->cp_filename = g_strdup (copy->cp_filename);
2373 #endif
2374
2375   return copy;
2376 }
2377
2378 /**
2379  * gtk_icon_info_free:
2380  * @icon_info: a #GtkIconInfo
2381  * 
2382  * Free a #GtkIconInfo and associated information
2383  *
2384  * Since: 2.4
2385  **/
2386 void
2387 gtk_icon_info_free (GtkIconInfo *icon_info)
2388 {
2389   g_return_if_fail (icon_info != NULL);
2390
2391   if (icon_info->filename)
2392     g_free (icon_info->filename);
2393 #ifdef G_OS_WIN32
2394   if (icon_info->cp_filename)
2395     g_free (icon_info->cp_filename);
2396 #endif
2397   if (icon_info->pixbuf)
2398     g_object_unref (icon_info->pixbuf);
2399   if (icon_info->cache_pixbuf)
2400     g_object_unref (icon_info->cache_pixbuf);
2401
2402   g_free (icon_info);
2403 }
2404
2405 /**
2406  * gtk_icon_info_get_base_size:
2407  * @icon_info: a #GtkIconInfo
2408  * 
2409  * Gets the base size for the icon. The base size
2410  * is a size for the icon that was specified by
2411  * the icon theme creator. This may be different
2412  * than the actual size of image; an example of
2413  * this is small emblem icons that can be attached
2414  * to a larger icon. These icons will be given
2415  * the same base size as the larger icons to which
2416  * they are attached.
2417  * 
2418  * Return value: the base size, or 0, if no base
2419  *  size is known for the icon.
2420  *
2421  * Since: 2.4
2422  **/
2423 gint
2424 gtk_icon_info_get_base_size (GtkIconInfo *icon_info)
2425 {
2426   g_return_val_if_fail (icon_info != NULL, 0);
2427
2428   return icon_info->dir_size;
2429 }
2430
2431 /**
2432  * gtk_icon_info_get_filename:
2433  * @icon_info: a #GtkIconInfo
2434  * 
2435  * Gets the filename for the icon. If the
2436  * %GTK_ICON_LOOKUP_USE_BUILTIN flag was passed
2437  * to gtk_icon_theme_lookup_icon(), there may be
2438  * no filename if a builtin icon is returned; in this
2439  * case, you should use gtk_icon_info_get_builtin_pixbuf().
2440  * 
2441  * Return value: the filename for the icon, or %NULL
2442  *  if gtk_icon_info_get_builtin_pixbuf() should
2443  *  be used instead. The return value is owned by
2444  *  GTK+ and should not be modified or freed.
2445  *
2446  * Since: 2.4
2447  **/
2448 G_CONST_RETURN gchar *
2449 gtk_icon_info_get_filename (GtkIconInfo *icon_info)
2450 {
2451   g_return_val_if_fail (icon_info != NULL, NULL);
2452
2453   return icon_info->filename;
2454 }
2455
2456 /**
2457  * gtk_icon_info_get_builtin_pixbuf:
2458  * @icon_info: a #GtkIconInfo structure
2459  * 
2460  * Gets the built-in image for this icon, if any. To allow
2461  * GTK+ to use built in icon images, you must pass the
2462  * %GTK_ICON_LOOKUP_USE_BUILTIN to
2463  * gtk_icon_theme_lookup_icon().
2464  * 
2465  * Return value: the built-in image pixbuf, or %NULL. No
2466  *  extra reference is added to the returned pixbuf, so if
2467  *  you want to keep it around, you must use g_object_ref().
2468  *  The returned image must not be modified.
2469  *
2470  * Since: 2.4
2471  **/
2472 GdkPixbuf *
2473 gtk_icon_info_get_builtin_pixbuf (GtkIconInfo *icon_info)
2474 {
2475   g_return_val_if_fail (icon_info != NULL, NULL);
2476
2477   if (icon_info->filename)
2478     return NULL;
2479   
2480   return icon_info->cache_pixbuf;
2481 }
2482
2483 static GdkPixbuf *
2484 load_svg_at_size (const gchar *filename,
2485                   gint         size,
2486                   GError      **error)
2487 {
2488   GdkPixbuf *pixbuf = NULL;
2489   GdkPixbufLoader *loader = NULL;
2490   gchar *contents = NULL;
2491   gsize length;
2492   
2493   if (!g_file_get_contents (filename,
2494                             &contents, &length, error))
2495     goto bail;
2496   
2497   loader = gdk_pixbuf_loader_new_with_type ("svg", error);
2498   if (loader == NULL)
2499     goto bail;
2500
2501   gdk_pixbuf_loader_set_size (loader, size, size);
2502   
2503   if (!gdk_pixbuf_loader_write (loader, contents, length, error))
2504     {
2505       gdk_pixbuf_loader_close (loader, NULL);
2506       goto bail;
2507     }
2508   
2509   if (!gdk_pixbuf_loader_close (loader, error))
2510     goto bail;
2511   
2512   pixbuf = g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader));
2513   
2514  bail:
2515   if (loader)
2516     g_object_unref (loader);
2517   if (contents)
2518     g_free (contents);
2519   
2520   return pixbuf;
2521 }
2522
2523 /* This function contains the complicatd logic for deciding
2524  * on the size at which to load the icon and loading it at
2525  * that size.
2526  */
2527 static gboolean
2528 icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info,
2529                                    gboolean     scale_only)
2530 {
2531   int image_width, image_height;
2532   GdkPixbuf *source_pixbuf;
2533
2534   /* First check if we already succeeded have the necessary
2535    * information (or failed earlier)
2536    */
2537   if (scale_only && icon_info->scale >= 0)
2538     return TRUE;
2539
2540   if (icon_info->pixbuf)
2541     return TRUE;
2542
2543   if (icon_info->load_error)
2544     return FALSE;
2545
2546   /* SVG icons are a special case - we just immediately scale them
2547    * to the desired size
2548    */
2549   if (icon_info->filename && g_str_has_suffix (icon_info->filename, ".svg"))
2550     {
2551       icon_info->scale = icon_info->desired_size / 1000.;
2552
2553       if (scale_only)
2554         return TRUE;
2555       
2556       icon_info->pixbuf = load_svg_at_size (icon_info->filename,
2557                                             icon_info->desired_size,
2558                                             &icon_info->load_error);
2559
2560       return icon_info->pixbuf != NULL;
2561     }
2562
2563   /* In many cases, the scale can be determined without actual access
2564    * to the icon file. This is generally true when we have a size
2565    * for the directory where the icon is; the image size doesn't
2566    * matter in that case.
2567    */
2568   if (icon_info->dir_type == ICON_THEME_DIR_FIXED)
2569     icon_info->scale = 1.0;
2570   else if (icon_info->dir_type == ICON_THEME_DIR_THRESHOLD)
2571     {
2572       if (icon_info->desired_size >= icon_info->dir_size - icon_info->threshold &&
2573           icon_info->desired_size <= icon_info->dir_size + icon_info->threshold)
2574         icon_info->scale = 1.0;
2575       else if (icon_info->dir_size > 0)
2576         icon_info->scale =(gdouble) icon_info->desired_size / icon_info->dir_size;
2577     }
2578   else if (icon_info->dir_type == ICON_THEME_DIR_SCALABLE)
2579     {
2580       if (icon_info->dir_size > 0)
2581         icon_info->scale = (gdouble) icon_info->desired_size / icon_info->dir_size;
2582     }
2583
2584   if (icon_info->scale >= 0. && scale_only)
2585     return TRUE;
2586
2587   /* At this point, we need to actually get the icon; either from the
2588    * builting image or by loading the file
2589    */
2590   if (icon_info->cache_pixbuf)
2591     source_pixbuf = g_object_ref (icon_info->cache_pixbuf);
2592   else
2593     {
2594
2595       source_pixbuf = gdk_pixbuf_new_from_file (icon_info->filename,
2596                                                 &icon_info->load_error);
2597       if (!source_pixbuf)
2598         return FALSE;
2599     }
2600
2601   /* Do scale calculations that depend on the image size
2602    */
2603   image_width = gdk_pixbuf_get_width (source_pixbuf);
2604   image_height = gdk_pixbuf_get_height (source_pixbuf);
2605
2606   if (icon_info->scale < 0.0)
2607     {
2608       gint image_size = MAX (image_width, image_height);
2609       if (image_size > 0)
2610         icon_info->scale = icon_info->desired_size / (gdouble)image_size;
2611       else
2612         icon_info->scale = 1.0;
2613       
2614       if (icon_info->dir_type == ICON_THEME_DIR_UNTHEMED)
2615         icon_info->scale = MIN (icon_info->scale, 1.0);
2616     }
2617
2618   /* We don't short-circuit out here for scale_only, since, now
2619    * we've loaded the icon, we might as well go ahead and finish
2620    * the job. This is a bit of a waste when we scale here
2621    * and never get the final pixbuf; at the cost of a bit of
2622    * extra complexity, we could keep the source pixbuf around
2623    * but not actually scale it until neede.
2624    */
2625     
2626   if (icon_info->scale == 1.0)
2627     icon_info->pixbuf = source_pixbuf;
2628   else
2629     {
2630       icon_info->pixbuf = gdk_pixbuf_scale_simple (source_pixbuf,
2631                                                    0.5 + image_width * icon_info->scale,
2632                                                    0.5 + image_height * icon_info->scale,
2633                                                    GDK_INTERP_BILINEAR);
2634       g_object_unref (source_pixbuf);
2635     }
2636
2637   return TRUE;
2638 }
2639
2640 /**
2641  * gtk_icon_info_load_icon:
2642  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
2643  * @error: location to store error information on failure, or %NULL.
2644  * 
2645  * Renders an icon previously looked up in an icon theme using
2646  * gtk_icon_theme_lookup_icon(); the size will be based on the size
2647  * passed to gtk_icon_theme_lookup_icon(). Note that the resulting
2648  * pixbuf may not be exactly this size; an icon theme may have icons
2649  * that differ slightly from their nominal sizes, and in addition GTK+
2650  * will avoid scaling icons that it considers sufficiently close to the
2651  * requested size or for which the source image would have to be scaled
2652  * up too far. (This maintains sharpness.) 
2653  * 
2654  * Return value: the rendered icon; this may be a newly created icon
2655  *  or a new reference to an internal icon, so you must not modify
2656  *  the icon. Use g_object_unref() to release your reference to the
2657  *  icon.
2658  *
2659  * Since: 2.4
2660  **/
2661 GdkPixbuf *
2662 gtk_icon_info_load_icon (GtkIconInfo *icon_info,
2663                          GError     **error)
2664 {
2665   g_return_val_if_fail (icon_info != NULL, NULL);
2666
2667   g_return_val_if_fail (icon_info != NULL, NULL);
2668   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2669
2670   icon_info_ensure_scale_and_pixbuf (icon_info, FALSE);
2671
2672   if (icon_info->load_error)
2673     {
2674       g_propagate_error (error, icon_info->load_error);
2675       return NULL;
2676     }
2677
2678   return g_object_ref (icon_info->pixbuf);
2679 }
2680
2681 /**
2682  * gtk_icon_info_set_raw_coordinates:
2683  * @icon_info: a #GtkIconInfo
2684  * @raw_coordinates: whether the coordinates of embedded rectangles
2685  *   and attached points should be returned in their original
2686  *   (unscaled) form.
2687  * 
2688  * Sets whether the coordinates returned by gtk_icon_info_get_embedded_rect()
2689  * and gtk_icon_info_get_attach_points() should be returned in their
2690  * original form as specified in the icon theme, instead of scaled
2691  * appropriately for the pixbuf returned by gtk_icon_info_load_icon().
2692  *
2693  * Raw coordinates are somewhat strange; they are specified to be with
2694  * respect to the unscaled pixmap for PNG and XPM icons, but for SVG
2695  * icons, they are in a 1000x1000 coordinate space that is scaled
2696  * to the final size of the icon.  You can determine if the icon is an SVG
2697  * icon by using gtk_icon_info_get_filename(), and seeing if it is non-%NULL
2698  * and ends in '.svg'.
2699  *
2700  * This function is provided primarily to allow compatibility wrappers
2701  * for older API's, and is not expected to be useful for applications.
2702  *
2703  * Since: 2.4
2704  **/
2705 void
2706 gtk_icon_info_set_raw_coordinates (GtkIconInfo *icon_info,
2707                                    gboolean     raw_coordinates)
2708 {
2709   g_return_if_fail (icon_info != NULL);
2710   
2711   icon_info->raw_coordinates = raw_coordinates != FALSE;
2712 }
2713
2714 /* Scale coordinates from the icon data prior to returning
2715  * them to the user.
2716  */
2717 static gboolean
2718 icon_info_scale_point (GtkIconInfo  *icon_info,
2719                        gint          x,
2720                        gint          y,
2721                        gint         *x_out,
2722                        gint         *y_out)
2723 {
2724   if (icon_info->raw_coordinates)
2725     {
2726       *x_out = x;
2727       *y_out = y;
2728     }
2729   else
2730     {
2731       if (!icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
2732         return FALSE;
2733
2734       *x_out = 0.5 + x * icon_info->scale;
2735       *y_out = 0.5 + y * icon_info->scale;
2736     }
2737
2738   return TRUE;
2739 }
2740
2741 /**
2742  * gtk_icon_info_get_embedded_rect:
2743  * @icon_info: a #GtkIconInfo
2744  * @rectangle: #GdkRectangle in which to store embedded
2745  *   rectangle coordinates; coordinates are only stored
2746  *   when this function returns %TRUE.
2747  *
2748  * Gets the coordinates of a rectangle within the icon
2749  * that can be used for display of information such
2750  * as a preview of the contents of a text file.
2751  * See gtk_icon_info_set_raw_coordinates() for further
2752  * information about the coordinate system.
2753  * 
2754  * Return value: %TRUE if the icon has an embedded rectangle
2755  *
2756  * Since: 2.4
2757  **/
2758 gboolean
2759 gtk_icon_info_get_embedded_rect (GtkIconInfo  *icon_info,
2760                                  GdkRectangle *rectangle)
2761 {
2762   g_return_val_if_fail (icon_info != NULL, FALSE);
2763
2764   if (icon_info->data && icon_info->data->has_embedded_rect &&
2765       icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
2766     {
2767       gint scaled_x0, scaled_y0;
2768       gint scaled_x1, scaled_y1;
2769       
2770       if (rectangle)
2771         {
2772           icon_info_scale_point (icon_info,
2773                                  icon_info->data->x0, icon_info->data->y0,
2774                                  &scaled_x0, &scaled_y0);
2775           icon_info_scale_point (icon_info,
2776                                  icon_info->data->x1, icon_info->data->y1,
2777                                  &scaled_x1, &scaled_y1);
2778           
2779           rectangle->x = scaled_x0;
2780           rectangle->y = scaled_y0;
2781           rectangle->width = scaled_x1 - rectangle->x;
2782           rectangle->height = scaled_y1 - rectangle->y;
2783         }
2784
2785       return TRUE;
2786     }
2787   else
2788     return FALSE;
2789 }
2790
2791 /**
2792  * gtk_icon_info_get_attach_points:
2793  * @icon_info: a #GtkIconInfo
2794  * @points: location to store pointer to an array of points, or %NULL
2795  *          free the array of points with g_free().
2796  * @n_points: location to store the number of points in @points, or %NULL
2797  * 
2798  * Fetches the set of attach points for an icon. An attach point
2799  * is a location in the icon that can be used as anchor points for attaching
2800  * emblems or overlays to the icon.
2801  * 
2802  * Return value: %TRUE if there are any attach points for the icon.
2803  *
2804  * Since: 2.4
2805  **/
2806 gboolean
2807 gtk_icon_info_get_attach_points (GtkIconInfo *icon_info,
2808                                  GdkPoint   **points,
2809                                  gint        *n_points)
2810 {
2811   g_return_val_if_fail (icon_info != NULL, FALSE);
2812   
2813   if (icon_info->data && icon_info->data->n_attach_points &&
2814       icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
2815     {
2816       if (points)
2817         {
2818           gint i;
2819           
2820           *points = g_new (GdkPoint, icon_info->data->n_attach_points);
2821           for (i = 0; i < icon_info->data->n_attach_points; i++)
2822             icon_info_scale_point (icon_info,
2823                                    icon_info->data->attach_points[i].x,
2824                                    icon_info->data->attach_points[i].y,
2825                                    &(*points)[i].x,
2826                                    &(*points)[i].y);
2827         }
2828           
2829       if (n_points)
2830         *n_points = icon_info->data->n_attach_points;
2831
2832       return TRUE;
2833     }
2834   else
2835     {
2836       if (points)
2837         *points = NULL;
2838       if (n_points)
2839         *n_points = 0;
2840       
2841       return FALSE;
2842     }
2843 }
2844
2845 /**
2846  * gtk_icon_info_get_display_name:
2847  * @icon_info: a #GtkIconInfo
2848  * 
2849  * Gets the display name for an icon. A display name is a
2850  * string to be used in place of the icon name in a user
2851  * visible context like a list of icons.
2852  * 
2853  * Return value: the display name for the icon or %NULL, if
2854  *  the icon doesn't have a specified display name. This value
2855  *  is owned @icon_info and must not be modified or free.
2856  *
2857  * Since: 2.4
2858  **/
2859 G_CONST_RETURN gchar *
2860 gtk_icon_info_get_display_name  (GtkIconInfo *icon_info)
2861 {
2862   g_return_val_if_fail (icon_info != NULL, NULL);
2863
2864   if (icon_info->data)
2865     return icon_info->data->display_name;
2866   else
2867     return NULL;
2868 }
2869
2870 /*
2871  * Builtin icons
2872  */
2873
2874
2875 /**
2876  * gtk_icon_theme_add_builtin_icon:
2877  * @icon_name: the name of the icon to register
2878  * @size: the size at which to register the icon (different
2879  *        images can be registered for the same icon name
2880  *        at different sizes.)
2881  * @pixbuf: #GdkPixbuf that contains the image to use
2882  *          for @icon_name.
2883  * 
2884  * Registers a built-in icon for icon theme lookups. The idea
2885  * of built-in icons is to allow an application or library
2886  * that uses themed icons to function requiring files to
2887  * be present in the file system. For instance, the default
2888  * images for all of GTK+'s stock icons are registered
2889  * as built-icons.
2890  *
2891  * In general, if you use gtk_icon_theme_add_builtin_icon()
2892  * you should also install the icon in the icon theme, so
2893  * that the icon is generally available.
2894  *
2895  * This function will generally be used with pixbufs loaded
2896  * via gdk_pixbuf_new_from_inline().
2897  *
2898  * Since: 2.4
2899  **/
2900 void
2901 gtk_icon_theme_add_builtin_icon (const gchar *icon_name,
2902                                  gint         size,
2903                                  GdkPixbuf   *pixbuf)
2904 {
2905   BuiltinIcon *default_icon;
2906   GSList *icons;
2907   gpointer key;
2908
2909   g_return_if_fail (icon_name != NULL);
2910   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2911   
2912   if (!icon_theme_builtin_icons)
2913     icon_theme_builtin_icons = g_hash_table_new (g_str_hash, g_str_equal);
2914
2915   icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
2916   if (!icons)
2917     key = g_strdup (icon_name);
2918   else
2919     key = (gpointer)icon_name;  /* Won't get stored */
2920
2921   default_icon = g_new (BuiltinIcon, 1);
2922   default_icon->size = size;
2923   default_icon->pixbuf = g_object_ref (pixbuf);
2924   icons = g_slist_prepend (icons, default_icon);
2925
2926   /* Replaces value, leaves key untouched
2927    */
2928   g_hash_table_insert (icon_theme_builtin_icons, key, icons);
2929 }
2930
2931 /* Look up a builtin icon; the min_difference_p and
2932  * has_larger_p out parameters allow us to combine
2933  * this lookup with searching through the actual directories
2934  * of the "hicolor" icon theme. See theme_lookup_icon()
2935  * for how they are used.
2936  */
2937 static BuiltinIcon *
2938 find_builtin_icon (const gchar *icon_name,
2939                    gint         size,
2940                    gint        *min_difference_p,
2941                    gboolean    *has_larger_p)
2942 {
2943   GSList *icons = NULL;
2944   gint min_difference = G_MAXINT;
2945   gboolean has_larger = FALSE;
2946   BuiltinIcon *min_icon = NULL;
2947   
2948   _gtk_icon_factory_ensure_default_icons ();
2949   
2950   if (!icon_theme_builtin_icons)
2951     return NULL;
2952
2953   icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
2954
2955   while (icons)
2956     {
2957       BuiltinIcon *default_icon = icons->data;
2958       int min, max, difference;
2959       gboolean smaller;
2960       
2961       min = default_icon->size - 2;
2962       max = default_icon->size + 2;
2963       smaller = size < min;
2964       if (size < min)
2965         difference = min - size;
2966       else if (size > max)
2967         difference = size - max;
2968       else
2969         difference = 0;
2970       
2971       if (difference == 0)
2972         {
2973           min_icon = default_icon;
2974           break;
2975         }
2976       
2977       if (!has_larger)
2978         {
2979           if (difference < min_difference || smaller)
2980             {
2981               min_difference = difference;
2982               min_icon = default_icon;
2983               has_larger = smaller;
2984             }
2985         }
2986       else
2987         {
2988           if (difference < min_difference && smaller)
2989             {
2990               min_difference = difference;
2991               min_icon = default_icon;
2992             }
2993         }
2994       
2995       icons = icons->next;
2996     }
2997
2998   if (min_difference_p)
2999     *min_difference_p = min_difference;
3000   if (has_larger_p)
3001     *has_larger_p = has_larger;
3002
3003   return min_icon;
3004 }
3005
3006 void
3007 _gtk_icon_theme_check_reload (GdkDisplay *display)
3008 {
3009   gint n_screens, i;
3010   GdkScreen *screen;
3011   GtkIconTheme *icon_theme;
3012
3013   n_screens = gdk_display_get_n_screens (display);
3014   
3015   for (i = 0; i < n_screens; i++)
3016     {
3017       screen = gdk_display_get_screen (display, i);
3018
3019       icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
3020       if (icon_theme)
3021         {
3022           icon_theme->priv->check_reload = TRUE;
3023           ensure_valid_themes (icon_theme);
3024           icon_theme->priv->check_reload = FALSE;
3025         }
3026     }
3027 }
3028
3029 #ifdef G_OS_WIN32
3030
3031 /* DLL ABI stability backward compatibility versions */
3032
3033 #undef gtk_icon_theme_set_search_path
3034
3035 void
3036 gtk_icon_theme_set_search_path (GtkIconTheme *icon_theme,
3037                                 const gchar  *path[],
3038                                 gint          n_elements)
3039 {
3040   const gchar **utf8_path;
3041   gint i;
3042
3043   utf8_path = g_new (const gchar *, n_elements);
3044
3045   for (i = 0; i < n_elements; i++)
3046     utf8_path[i] = g_locale_to_utf8 (path[i], -1, NULL, NULL, NULL);
3047
3048   gtk_icon_theme_set_search_path_utf8 (icon_theme, utf8_path, n_elements);
3049
3050   for (i = 0; i < n_elements; i++)
3051     g_free ((gchar *) utf8_path[i]);
3052
3053   g_free (utf8_path);
3054 }
3055
3056 #undef gtk_icon_theme_get_search_path
3057
3058 void
3059 gtk_icon_theme_get_search_path (GtkIconTheme      *icon_theme,
3060                                 gchar            **path[],
3061                                 gint              *n_elements)
3062 {
3063   gint i, n;
3064
3065   gtk_icon_theme_get_search_path_utf8 (icon_theme, path, &n);
3066
3067   if (n_elements)
3068     *n_elements = n;
3069
3070   if (path)
3071     {
3072       for (i = 0; i < n; i++)
3073         {
3074           gchar *tem = (*path)[i];
3075
3076           (*path)[i] = g_locale_from_utf8 ((*path)[i], -1, NULL, NULL, NULL);
3077           g_free (tem);
3078         }
3079     }
3080 }
3081
3082 #undef gtk_icon_theme_append_search_path
3083
3084 void
3085 gtk_icon_theme_append_search_path (GtkIconTheme *icon_theme,
3086                                    const gchar  *path)
3087 {
3088   gchar *utf8_path = g_locale_from_utf8 (path, -1, NULL, NULL, NULL);
3089
3090   gtk_icon_theme_append_search_path_utf8 (icon_theme, utf8_path);
3091
3092   g_free (utf8_path);
3093 }
3094
3095 #undef gtk_icon_theme_prepend_search_path
3096
3097 void
3098 gtk_icon_theme_prepend_search_path (GtkIconTheme *icon_theme,
3099                                     const gchar  *path)
3100 {
3101   gchar *utf8_path = g_locale_from_utf8 (path, -1, NULL, NULL, NULL);
3102
3103   gtk_icon_theme_prepend_search_path_utf8 (icon_theme, utf8_path);
3104
3105   g_free (utf8_path);
3106 }
3107
3108 #undef gtk_icon_info_get_filename
3109
3110 G_CONST_RETURN gchar *
3111 gtk_icon_info_get_filename (GtkIconInfo *icon_info)
3112 {
3113   g_return_val_if_fail (icon_info != NULL, NULL);
3114
3115   return icon_info->cp_filename;
3116 }
3117
3118 #endif
3119
3120 #define __GTK_ICON_THEME_C__
3121 #include "gtkaliasdef.c"