]> Pileus Git - ~andy/gtk/blob - gtk/gtkicontheme.c
Make builtin icons work in gtk_window_set_icon_name() (#321046, Maxim
[~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 || strcmp (theme_name, DEFAULT_THEME_NAME) == 0)
941     {
942       theme = g_new0 (IconTheme, 1);
943       theme->name = g_strdup (theme_name);
944       priv->themes = g_list_prepend (priv->themes, theme);
945     }
946
947   if (theme_file == NULL)
948     return;
949
950   theme->display_name = 
951     g_key_file_get_locale_string (theme_file, "Icon Theme", "Name", NULL, NULL);
952   if (!theme->display_name)
953     g_warning ("Theme file for %s has no name\n", theme_name);
954
955   dirs = g_key_file_get_string_list (theme_file, "Icon Theme", "Directories", NULL, NULL);
956   if (!dirs)
957     g_warning ("Theme file for %s has no directories\n", theme_name);
958   
959   theme->comment = 
960     g_key_file_get_locale_string (theme_file, 
961                                   "Icon Theme", "Comment",
962                                   NULL, NULL);
963   theme->example = 
964     g_key_file_get_string (theme_file, 
965                            "Icon Theme", "Example",
966                            NULL);
967
968   theme->dirs = NULL;
969   for (i = 0; dirs[i] != NULL; i++)
970     theme_subdir_load (icon_theme, theme, theme_file, dirs[i]);
971   
972   g_strfreev (dirs);
973   
974   theme->dirs = g_list_reverse (theme->dirs);
975
976   themes = g_key_file_get_string_list (theme_file,
977                                        "Icon Theme",
978                                        "Inherits",
979                                        NULL,
980                                        NULL);
981   if (themes)
982     {
983       for (i = 0; themes[i] != NULL; i++)
984         insert_theme (icon_theme, themes[i]);
985       
986       g_strfreev (themes);
987     }
988
989   g_key_file_free (theme_file);
990 }
991
992 static void
993 free_unthemed_icon (UnthemedIcon *unthemed_icon)
994 {
995   if (unthemed_icon->svg_filename)
996     g_free (unthemed_icon->svg_filename);
997   if (unthemed_icon->no_svg_filename)
998     g_free (unthemed_icon->no_svg_filename);
999   g_free (unthemed_icon);
1000 }
1001
1002 static void
1003 load_themes (GtkIconTheme *icon_theme)
1004 {
1005   GtkIconThemePrivate *priv;
1006   GDir *gdir;
1007   int base;
1008   char *dir, *base_name, *dot;
1009   const char *file;
1010   char *abs_file;
1011   UnthemedIcon *unthemed_icon;
1012   IconSuffix old_suffix, new_suffix;
1013   GTimeVal tv;
1014   IconThemeDirMtime *dir_mtime;
1015   struct stat stat_buf;
1016   
1017   priv = icon_theme->priv;
1018
1019   priv->all_icons = g_hash_table_new (g_str_hash, g_str_equal);
1020   
1021   insert_theme (icon_theme, priv->current_theme);
1022   
1023   /* Always look in the "default" icon theme */
1024   insert_theme (icon_theme, DEFAULT_THEME_NAME);
1025   priv->themes = g_list_reverse (priv->themes);
1026
1027
1028   priv->unthemed_icons = g_hash_table_new_full (g_str_hash, g_str_equal,
1029                                                 g_free, (GDestroyNotify)free_unthemed_icon);
1030
1031   for (base = 0; base < icon_theme->priv->search_path_len; base++)
1032     {
1033       dir = icon_theme->priv->search_path[base];
1034
1035       dir_mtime = g_new (IconThemeDirMtime, 1);
1036       dir_mtime->cache = _gtk_icon_cache_new_for_path (dir);
1037       dir_mtime->dir = g_strdup (dir);
1038       if (g_stat (dir, &stat_buf) == 0 && S_ISDIR (stat_buf.st_mode))
1039         dir_mtime->mtime = stat_buf.st_mtime;
1040       else
1041         dir_mtime->mtime = 0;
1042
1043       priv->dir_mtimes = g_list_append (priv->dir_mtimes, dir_mtime);
1044
1045       if (dir_mtime->cache != NULL)
1046         continue;
1047
1048       gdir = g_dir_open (dir, 0, NULL);
1049
1050       if (gdir == NULL)
1051         continue;
1052       
1053       while ((file = g_dir_read_name (gdir)))
1054         {
1055           new_suffix = suffix_from_name (file);
1056
1057           if (new_suffix != ICON_SUFFIX_NONE)
1058             {
1059               abs_file = g_build_filename (dir, file, NULL);
1060
1061               base_name = g_strdup (file);
1062                   
1063               dot = strrchr (base_name, '.');
1064               if (dot)
1065                 *dot = 0;
1066
1067               if ((unthemed_icon = g_hash_table_lookup (priv->unthemed_icons,
1068                                                         base_name)))
1069                 {
1070                   if (new_suffix == ICON_SUFFIX_SVG)
1071                     {
1072                       if (unthemed_icon->svg_filename)
1073                         g_free (abs_file);
1074                       else
1075                         unthemed_icon->svg_filename = abs_file;
1076                     }
1077                   else
1078                     {
1079                       if (unthemed_icon->no_svg_filename)
1080                         {
1081                           old_suffix = suffix_from_name (unthemed_icon->no_svg_filename);
1082                           if (new_suffix > old_suffix)
1083                             {
1084                               g_free (unthemed_icon->no_svg_filename);
1085                               unthemed_icon->no_svg_filename = abs_file;                              
1086                             }
1087                           else
1088                             g_free (abs_file);
1089                         }
1090                       else
1091                         unthemed_icon->no_svg_filename = abs_file;                            
1092                     }
1093
1094                   g_free (base_name);
1095                 }
1096               else
1097                 {
1098                   unthemed_icon = g_new0 (UnthemedIcon, 1);
1099                   
1100                   if (new_suffix == ICON_SUFFIX_SVG)
1101                     unthemed_icon->svg_filename = abs_file;
1102                   else
1103                     unthemed_icon->no_svg_filename = abs_file;
1104
1105                   g_hash_table_insert (priv->unthemed_icons,
1106                                        base_name,
1107                                        unthemed_icon);
1108                   g_hash_table_insert (priv->all_icons,
1109                                        base_name, NULL);
1110                 }
1111             }
1112         }
1113       g_dir_close (gdir);
1114     }
1115
1116   priv->themes_valid = TRUE;
1117   
1118   g_get_current_time(&tv);
1119   priv->last_stat_time = tv.tv_sec;
1120 }
1121
1122 static void
1123 _gtk_icon_theme_ensure_builtin_cache (void)
1124 {
1125   static gboolean initialized = FALSE;
1126   IconThemeDir *dir;
1127   gint sizes[5] = { 16, 20, 24, 32, 48 };
1128   gint n_sizes = G_N_ELEMENTS (sizes);
1129   gint i;
1130
1131   if (!initialized)
1132     {
1133       initialized = TRUE;
1134
1135       _builtin_cache = _gtk_icon_cache_new ((gchar *)builtin_icons);
1136
1137       for (i = 0; i < n_sizes; i++)
1138         {
1139           dir = g_new (IconThemeDir, 1);
1140           dir->type = ICON_THEME_DIR_THRESHOLD;
1141           dir->context = 0;
1142           dir->size = sizes[i];
1143           dir->min_size = sizes[i];
1144           dir->max_size = sizes[i];
1145           dir->threshold = 2;
1146           dir->dir = NULL;
1147           dir->icon_data = NULL;
1148           dir->subdir = g_strdup_printf ("%d", sizes[i]);
1149           dir->cache = _gtk_icon_cache_ref (_builtin_cache);
1150
1151           builtin_dirs = g_list_append (builtin_dirs, dir);
1152         }
1153     }
1154 }
1155
1156 static void
1157 ensure_valid_themes (GtkIconTheme *icon_theme)
1158 {
1159   GtkIconThemePrivate *priv = icon_theme->priv;
1160   GTimeVal tv;
1161
1162   _gtk_icon_theme_ensure_builtin_cache ();
1163
1164   if (priv->themes_valid)
1165     {
1166       g_get_current_time (&tv);
1167
1168       if (ABS (tv.tv_sec - priv->last_stat_time) > 5)
1169         gtk_icon_theme_rescan_if_needed (icon_theme);
1170     }
1171   
1172   if (!priv->themes_valid)
1173     {
1174       load_themes (icon_theme);
1175       
1176       if (!priv->check_reload && priv->screen)
1177         {         
1178           static GdkAtom atom_iconthemes = GDK_NONE;
1179           GdkEvent *event = gdk_event_new (GDK_CLIENT_EVENT);
1180           int i;
1181
1182           if (!atom_iconthemes)
1183             atom_iconthemes = gdk_atom_intern_static_string ("_GTK_LOAD_ICONTHEMES");
1184
1185           for (i = 0; i < 5; i++)
1186             event->client.data.l[i] = 0;
1187           event->client.data_format = 32;
1188           event->client.message_type = atom_iconthemes;
1189
1190           gdk_screen_broadcast_client_message (priv->screen, event);
1191         }
1192     }
1193 }
1194
1195 /**
1196  * gtk_icon_theme_lookup_icon:
1197  * @icon_theme: a #GtkIconTheme
1198  * @icon_name: the name of the icon to lookup
1199  * @size: desired icon size
1200  * @flags: flags modifying the behavior of the icon lookup
1201  * 
1202  * Looks up a named icon and returns a structure containing
1203  * information such as the filename of the icon. The icon
1204  * can then be rendered into a pixbuf using
1205  * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
1206  * combines these two steps if all you need is the pixbuf.)
1207  * 
1208  * Return value: a #GtkIconInfo structure containing information
1209  * about the icon, or %NULL if the icon wasn't found. Free with
1210  * gtk_icon_info_free()
1211  *
1212  * Since: 2.4
1213  **/
1214 GtkIconInfo *
1215 gtk_icon_theme_lookup_icon (GtkIconTheme       *icon_theme,
1216                             const gchar        *icon_name,
1217                             gint                size,
1218                             GtkIconLookupFlags  flags)
1219 {
1220   GtkIconThemePrivate *priv;
1221   GList *l;
1222   GtkIconInfo *icon_info = NULL;
1223   UnthemedIcon *unthemed_icon;
1224   gboolean allow_svg;
1225   gboolean use_builtin;
1226
1227   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1228   g_return_val_if_fail (icon_name != NULL, NULL);
1229   g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1230                         (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1231
1232   priv = icon_theme->priv;
1233
1234   GTK_NOTE (ICONTHEME, 
1235             g_print ("gtk_icon_theme_lookup_icon %s\n", icon_name));
1236   if (flags & GTK_ICON_LOOKUP_NO_SVG)
1237     allow_svg = FALSE;
1238   else if (flags & GTK_ICON_LOOKUP_FORCE_SVG)
1239     allow_svg = TRUE;
1240   else
1241     allow_svg = priv->pixbuf_supports_svg;
1242
1243   use_builtin = (flags & GTK_ICON_LOOKUP_USE_BUILTIN);
1244
1245   ensure_valid_themes (icon_theme);
1246
1247   for (l = priv->themes; l; l = l->next)
1248     {
1249       IconTheme *theme = l->data;
1250       
1251       icon_info = theme_lookup_icon (theme, icon_name, size, allow_svg, use_builtin);
1252       if (icon_info)
1253         goto out;
1254     }
1255
1256   unthemed_icon = g_hash_table_lookup (priv->unthemed_icons, icon_name);
1257   if (unthemed_icon)
1258     {
1259       icon_info = icon_info_new ();
1260
1261       /* A SVG icon, when allowed, beats out a XPM icon, but not
1262        * a PNG icon
1263        */
1264       if (allow_svg &&
1265           unthemed_icon->svg_filename &&
1266           (!unthemed_icon->no_svg_filename ||
1267            suffix_from_name (unthemed_icon->no_svg_filename) != ICON_SUFFIX_PNG))
1268         icon_info->filename = g_strdup (unthemed_icon->svg_filename);
1269       else if (unthemed_icon->no_svg_filename)
1270         icon_info->filename = g_strdup (unthemed_icon->no_svg_filename);
1271 #ifdef G_OS_WIN32
1272       icon_info->cp_filename = g_locale_from_utf8 (icon_info->filename,
1273                                                    -1, NULL, NULL, NULL);
1274 #endif
1275
1276       icon_info->dir_type = ICON_THEME_DIR_UNTHEMED;
1277     }
1278
1279  out:
1280   if (icon_info)
1281     icon_info->desired_size = size;
1282   else
1283     {
1284       static gboolean check_for_default_theme = TRUE;
1285       char *default_theme_path;
1286       gboolean found = FALSE;
1287       unsigned i;
1288
1289       if (check_for_default_theme)
1290         {
1291           check_for_default_theme = FALSE;
1292
1293           for (i = 0; !found && i < priv->search_path_len; i++)
1294             {
1295               default_theme_path = g_build_filename (priv->search_path[i],
1296                                                      DEFAULT_THEME_NAME,
1297                                                      "index.theme",
1298                                                      NULL);
1299               found = g_file_test (default_theme_path, G_FILE_TEST_IS_REGULAR);
1300               g_free (default_theme_path);
1301             }
1302           if (!found)
1303             {
1304               g_warning (_("Could not find the icon '%s'. The '%s' theme\n"
1305                            "was not found either, perhaps you need to install it.\n"
1306                            "You can get a copy from:\n"
1307                            "\t%s"),
1308                          icon_name, DEFAULT_THEME_NAME, "http://icon-theme.freedesktop.org/releases");
1309             }
1310         }
1311     }
1312
1313   return icon_info;
1314 }
1315
1316 /* Error quark */
1317 GQuark
1318 gtk_icon_theme_error_quark (void)
1319 {
1320   static GQuark q = 0;
1321   if (q == 0)
1322     q = g_quark_from_static_string ("gtk-icon-theme-error-quark");
1323
1324   return q;
1325 }
1326
1327 /**
1328  * gtk_icon_theme_load_icon:
1329  * @icon_theme: a #GtkIconTheme
1330  * @icon_name: the name of the icon to lookup
1331  * @size: the desired icon size. The resulting icon may not be
1332  *        exactly this size; see gtk_icon_info_load_icon().
1333  * @flags: flags modifying the behavior of the icon lookup
1334  * @error: Location to store error information on failure, or %NULL.
1335  * 
1336  * Looks up an icon in an icon theme, scales it to the given size
1337  * and renders it into a pixbuf. This is a convenience function;
1338  * if more details about the icon are needed, use
1339  * gtk_icon_theme_lookup_icon() followed by gtk_icon_info_load_icon().
1340  *
1341  * Note that you probably want to listen for icon theme changes and
1342  * update the icon. This is usually done by connecting to the 
1343  * GtkWidget::style-set signal. If for some reason you do not want to
1344  * update the icon when the icon theme changes, you should consider
1345  * using gdk_pixbuf_copy() to make a private copy of the pixbuf
1346  * returned by this function. Otherwise GTK+ may need to keep the old 
1347  * icon theme loaded, which would be a waste of memory.
1348  * 
1349  * Return value: the rendered icon; this may be a newly created icon
1350  *  or a new reference to an internal icon, so you must not modify
1351  *  the icon. Use g_object_unref() to release your reference to the
1352  *  icon. %NULL if the icon isn't found.
1353  *
1354  * Since: 2.4
1355  **/
1356 GdkPixbuf *
1357 gtk_icon_theme_load_icon (GtkIconTheme         *icon_theme,
1358                           const gchar          *icon_name,
1359                           gint                  size,
1360                           GtkIconLookupFlags    flags,
1361                           GError              **error)
1362 {
1363   GtkIconInfo *icon_info;
1364   GdkPixbuf *pixbuf = NULL;
1365   
1366   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1367   g_return_val_if_fail (icon_name != NULL, NULL);
1368   g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1369                         (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1370   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1371   
1372   icon_info  = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size,
1373                                            flags | GTK_ICON_LOOKUP_USE_BUILTIN);
1374   if (!icon_info)
1375     {
1376       g_set_error (error, GTK_ICON_THEME_ERROR,  GTK_ICON_THEME_NOT_FOUND,
1377                    _("Icon '%s' not present in theme"), icon_name);
1378       return NULL;
1379     }
1380
1381   pixbuf = gtk_icon_info_load_icon (icon_info, error);
1382   gtk_icon_info_free (icon_info);
1383
1384   return pixbuf;
1385 }
1386
1387 /**
1388  * gtk_icon_theme_has_icon:
1389  * @icon_theme: a #GtkIconTheme
1390  * @icon_name: the name of an icon
1391  * 
1392  * Checks whether an icon theme includes an icon
1393  * for a particular name.
1394  * 
1395  * Return value: %TRUE if @icon_theme includes an
1396  *  icon for @icon_name.
1397  *
1398  * Since: 2.4
1399  **/
1400 gboolean 
1401 gtk_icon_theme_has_icon (GtkIconTheme *icon_theme,
1402                          const char   *icon_name)
1403 {
1404   GtkIconThemePrivate *priv;
1405   GList *l;
1406
1407   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
1408   
1409   priv = icon_theme->priv;
1410   
1411   ensure_valid_themes (icon_theme);
1412
1413   for (l = priv->dir_mtimes; l; l = l->next)
1414     {
1415       IconThemeDirMtime *dir_mtime = l->data;
1416       GtkIconCache *cache = dir_mtime->cache;
1417       
1418       if (cache && _gtk_icon_cache_has_icon (cache, icon_name))
1419         return TRUE;
1420     }
1421
1422   if (g_hash_table_lookup_extended (priv->all_icons,
1423                                     icon_name, NULL, NULL))
1424     return TRUE;
1425
1426   if (_builtin_cache &&
1427       _gtk_icon_cache_has_icon (_builtin_cache, icon_name))
1428     return TRUE;
1429
1430   if (icon_theme_builtin_icons &&
1431       g_hash_table_lookup_extended (icon_theme_builtin_icons,
1432                                     icon_name, NULL, NULL))
1433     return TRUE;
1434
1435   return FALSE;
1436 }
1437
1438 static void
1439 add_size (gpointer  key,
1440           gpointer  value,
1441           gpointer  user_data)
1442 {
1443   gint **res_p = user_data;
1444
1445   **res_p = GPOINTER_TO_INT (key);
1446
1447   (*res_p)++;
1448 }
1449
1450 /**
1451  * gtk_icon_theme_get_icon_sizes:
1452  * @icon_theme: a #GtkIconTheme
1453  * @icon_name: the name of an icon
1454  * 
1455  * Returns an array of integers describing the sizes at which
1456  * the icon is available without scaling. A size of -1 means 
1457  * that the icon is available in a scalable format. The array 
1458  * is zero-terminated.
1459  * 
1460  * Return value: An newly allocated array describing the sizes at
1461  * which the icon is available. The array should be freed with g_free()
1462  * when it is no longer needed.
1463  *
1464  * Since: 2.6
1465  **/
1466 gint *
1467 gtk_icon_theme_get_icon_sizes (GtkIconTheme *icon_theme,
1468                                const char   *icon_name)
1469 {
1470   GList *l, *d, *icons;
1471   GHashTable *sizes;
1472   gint *result, *r;
1473   guint suffix;  
1474   GtkIconThemePrivate *priv;
1475
1476   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1477   
1478   priv = icon_theme->priv;
1479
1480   ensure_valid_themes (icon_theme);
1481
1482   sizes = g_hash_table_new (g_direct_hash, g_direct_equal);
1483
1484   for (l = priv->themes; l; l = l->next)
1485     {
1486       IconTheme *theme = l->data;
1487       for (d = theme->dirs; d; d = d->next)
1488         {
1489           IconThemeDir *dir = d->data;
1490
1491           suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);      
1492           if (suffix != ICON_SUFFIX_NONE)
1493             {
1494               if (suffix == ICON_SUFFIX_SVG)
1495                 g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL);
1496               else
1497                 g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL);
1498             }
1499         }
1500     }
1501
1502   for (d = builtin_dirs; d; d = d->next)
1503     {
1504       IconThemeDir *dir = d->data;
1505       
1506       suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);          
1507       if (suffix != ICON_SUFFIX_NONE)
1508         {
1509           if (suffix == ICON_SUFFIX_SVG)
1510             g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL);
1511           else
1512             g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL);
1513         }
1514     }
1515
1516   if (icon_theme_builtin_icons)
1517     {
1518       icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
1519       
1520       for ( ; icons; icons = icons->next);
1521       {
1522         BuiltinIcon *icon = icons->data;
1523         
1524         g_hash_table_insert (sizes, GINT_TO_POINTER (icon->size), NULL);
1525       }      
1526     }
1527
1528   r = result = g_new0 (gint, g_hash_table_size (sizes) + 1);
1529
1530   g_hash_table_foreach (sizes, add_size, &r);
1531   g_hash_table_destroy (sizes);
1532   
1533   return result;
1534 }
1535
1536 static void
1537 add_key_to_hash (gpointer  key,
1538                  gpointer  value,
1539                  gpointer  user_data)
1540 {
1541   GHashTable *hash = user_data;
1542
1543   g_hash_table_insert (hash, key, NULL);
1544 }
1545
1546 static void
1547 add_key_to_list (gpointer  key,
1548                  gpointer  value,
1549                  gpointer  user_data)
1550 {
1551   GList **list = user_data;
1552
1553   *list = g_list_prepend (*list, g_strdup (key));
1554 }
1555
1556 /**
1557  * gtk_icon_theme_list_icons:
1558  * @icon_theme: a #GtkIconTheme
1559  * @context: a string identifying a particular type of icon,
1560  *           or %NULL to list all icons.
1561  * 
1562  * Lists the icons in the current icon theme. Only a subset
1563  * of the icons can be listed by providing a context string.
1564  * The set of values for the context string is system dependent,
1565  * but will typically include such values as 'apps' and
1566  * 'mimetypes'.
1567  * 
1568  * Return value: a #GList list holding the names of all the
1569  *  icons in the theme. You must first free each element
1570  *  in the list with g_free(), then free the list itself
1571  *  with g_list_free().
1572  *
1573  * Since: 2.4
1574  **/
1575 GList *
1576 gtk_icon_theme_list_icons (GtkIconTheme *icon_theme,
1577                            const char   *context)
1578 {
1579   GtkIconThemePrivate *priv;
1580   GHashTable *icons;
1581   GList *list, *l;
1582   GQuark context_quark;
1583   
1584   priv = icon_theme->priv;
1585   
1586   ensure_valid_themes (icon_theme);
1587
1588   if (context)
1589     {
1590       context_quark = g_quark_try_string (context);
1591
1592       if (!context_quark)
1593         return NULL;
1594     }
1595   else
1596     context_quark = 0;
1597
1598   icons = g_hash_table_new (g_str_hash, g_str_equal);
1599   
1600   l = priv->themes;
1601   while (l != NULL)
1602     {
1603       theme_list_icons (l->data, icons, context_quark);
1604       l = l->next;
1605     }
1606
1607   if (context_quark == 0)
1608     g_hash_table_foreach (priv->unthemed_icons,
1609                           add_key_to_hash,
1610                           icons);
1611
1612   list = NULL;
1613   
1614   g_hash_table_foreach (icons,
1615                         add_key_to_list,
1616                         &list);
1617
1618   g_hash_table_destroy (icons);
1619   
1620   return list;
1621 }
1622
1623 /**
1624  * gtk_icon_theme_get_example_icon_name:
1625  * @icon_theme: a #GtkIconTheme
1626  * 
1627  * Gets the name of an icon that is representative of the
1628  * current theme (for instance, to use when presenting
1629  * a list of themes to the user.)
1630  * 
1631  * Return value: the name of an example icon or %NULL.
1632  *  Free with g_free().
1633  *
1634  * Since: 2.4
1635  **/
1636 char *
1637 gtk_icon_theme_get_example_icon_name (GtkIconTheme *icon_theme)
1638 {
1639   GtkIconThemePrivate *priv;
1640   GList *l;
1641   IconTheme *theme;
1642
1643   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1644   
1645   priv = icon_theme->priv;
1646   
1647   ensure_valid_themes (icon_theme);
1648
1649   l = priv->themes;
1650   while (l != NULL)
1651     {
1652       theme = l->data;
1653       if (theme->example)
1654         return g_strdup (theme->example);
1655       
1656       l = l->next;
1657     }
1658   
1659   return NULL;
1660 }
1661
1662 /**
1663  * gtk_icon_theme_rescan_if_needed:
1664  * @icon_theme: a #GtkIconTheme
1665  * 
1666  * Checks to see if the icon theme has changed; if it has, any
1667  * currently cached information is discarded and will be reloaded
1668  * next time @icon_theme is accessed.
1669  * 
1670  * Return value: %TRUE if the icon theme has changed and needed
1671  *   to be reloaded.
1672  *
1673  * Since: 2.4
1674  **/
1675 gboolean
1676 gtk_icon_theme_rescan_if_needed (GtkIconTheme *icon_theme)
1677 {
1678   GtkIconThemePrivate *priv;
1679   IconThemeDirMtime *dir_mtime;
1680   GList *d;
1681   int stat_res;
1682   struct stat stat_buf;
1683   GTimeVal tv;
1684
1685   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
1686
1687   priv = icon_theme->priv;
1688   
1689   for (d = priv->dir_mtimes; d != NULL; d = d->next)
1690     {
1691       dir_mtime = d->data;
1692
1693       stat_res = g_stat (dir_mtime->dir, &stat_buf);
1694
1695       /* dir mtime didn't change */
1696       if (stat_res == 0 && 
1697           S_ISDIR (stat_buf.st_mode) &&
1698           dir_mtime->mtime == stat_buf.st_mtime)
1699         continue;
1700       /* didn't exist before, and still doesn't */
1701       if (dir_mtime->mtime == 0 &&
1702           (stat_res != 0 || !S_ISDIR (stat_buf.st_mode)))
1703         continue;
1704           
1705       do_theme_change (icon_theme);
1706       return TRUE;
1707     }
1708   
1709   g_get_current_time (&tv);
1710   priv->last_stat_time = tv.tv_sec;
1711
1712   return FALSE;
1713 }
1714
1715 static void
1716 theme_destroy (IconTheme *theme)
1717 {
1718   g_free (theme->display_name);
1719   g_free (theme->comment);
1720   g_free (theme->name);
1721   g_free (theme->example);
1722
1723   g_list_foreach (theme->dirs, (GFunc)theme_dir_destroy, NULL);
1724   g_list_free (theme->dirs);
1725   
1726   g_free (theme);
1727 }
1728
1729 static void
1730 theme_dir_destroy (IconThemeDir *dir)
1731 {
1732   if (dir->cache)
1733       _gtk_icon_cache_unref (dir->cache);
1734   else
1735     g_hash_table_destroy (dir->icons);
1736   
1737   if (dir->icon_data)
1738     g_hash_table_destroy (dir->icon_data);
1739   g_free (dir->dir);
1740   g_free (dir->subdir);
1741   g_free (dir);
1742 }
1743
1744 static int
1745 theme_dir_size_difference (IconThemeDir *dir, int size, gboolean *smaller)
1746 {
1747   int min, max;
1748   switch (dir->type)
1749     {
1750     case ICON_THEME_DIR_FIXED:
1751       *smaller = size < dir->size;
1752       return abs (size - dir->size);
1753       break;
1754     case ICON_THEME_DIR_SCALABLE:
1755       *smaller = size < dir->min_size;
1756       if (size < dir->min_size)
1757         return dir->min_size - size;
1758       if (size > dir->max_size)
1759         return size - dir->max_size;
1760       return 0;
1761       break;
1762     case ICON_THEME_DIR_THRESHOLD:
1763       min = dir->size - dir->threshold;
1764       max = dir->size + dir->threshold;
1765       *smaller = size < min;
1766       if (size < min)
1767         return min - size;
1768       if (size > max)
1769         return size - max;
1770       return 0;
1771       break;
1772     case ICON_THEME_DIR_UNTHEMED:
1773       g_assert_not_reached ();
1774       break;
1775     }
1776   g_assert_not_reached ();
1777   return 1000;
1778 }
1779
1780 static const char *
1781 string_from_suffix (IconSuffix suffix)
1782 {
1783   switch (suffix)
1784     {
1785     case ICON_SUFFIX_XPM:
1786       return ".xpm";
1787     case ICON_SUFFIX_SVG:
1788       return ".svg";
1789     case ICON_SUFFIX_PNG:
1790       return ".png";
1791     default:
1792       g_assert_not_reached();
1793     }
1794   return NULL;
1795 }
1796
1797 static IconSuffix
1798 suffix_from_name (const char *name)
1799 {
1800   IconSuffix retval;
1801
1802   if (g_str_has_suffix (name, ".png"))
1803     retval = ICON_SUFFIX_PNG;
1804   else if (g_str_has_suffix (name, ".svg"))
1805     retval = ICON_SUFFIX_SVG;
1806   else if (g_str_has_suffix (name, ".xpm"))
1807     retval = ICON_SUFFIX_XPM;
1808   else
1809     retval = ICON_SUFFIX_NONE;
1810
1811   return retval;
1812 }
1813
1814 static IconSuffix
1815 best_suffix (IconSuffix suffix,
1816              gboolean   allow_svg)
1817 {
1818   if ((suffix & ICON_SUFFIX_PNG) != 0)
1819     return ICON_SUFFIX_PNG;
1820   else if (allow_svg && ((suffix & ICON_SUFFIX_SVG) != 0))
1821     return ICON_SUFFIX_SVG;
1822   else if ((suffix & ICON_SUFFIX_XPM) != 0)
1823     return ICON_SUFFIX_XPM;
1824   else
1825     return ICON_SUFFIX_NONE;
1826 }
1827
1828
1829 static IconSuffix
1830 theme_dir_get_icon_suffix (IconThemeDir *dir,
1831                            const gchar  *icon_name,
1832                            gboolean     *has_icon_file)
1833 {
1834   IconSuffix suffix;
1835
1836   if (dir->cache)
1837     {
1838       suffix = (IconSuffix)_gtk_icon_cache_get_icon_flags (dir->cache,
1839                                                            icon_name,
1840                                                            dir->subdir);
1841
1842       if (has_icon_file)
1843         *has_icon_file = suffix & HAS_ICON_FILE;
1844
1845       suffix = suffix & ~HAS_ICON_FILE;
1846     }
1847   else
1848     suffix = GPOINTER_TO_UINT (g_hash_table_lookup (dir->icons, icon_name));
1849
1850   GTK_NOTE (ICONTHEME, 
1851             g_print ("get_icon_suffix%s %d\n", dir->cache ? " (cached)" : "", suffix));
1852
1853   return suffix;
1854 }
1855
1856 static GtkIconInfo *
1857 theme_lookup_icon (IconTheme          *theme,
1858                    const char         *icon_name,
1859                    int                 size,
1860                    gboolean            allow_svg,
1861                    gboolean            use_builtin)
1862 {
1863   GList *dirs, *l;
1864   IconThemeDir *dir, *min_dir;
1865   char *file;
1866   int min_difference, difference;
1867   BuiltinIcon *closest_builtin = NULL;
1868   gboolean smaller, has_larger;
1869   IconSuffix suffix;
1870
1871   min_difference = G_MAXINT;
1872   min_dir = NULL;
1873   has_larger = FALSE;
1874
1875   /* Builtin icons are logically part of the default theme and
1876    * are searched before other subdirectories of the default theme.
1877    */
1878   if (strcmp (theme->name, DEFAULT_THEME_NAME) == 0 && use_builtin)
1879     {
1880       closest_builtin = find_builtin_icon (icon_name, 
1881                                            size,
1882                                            &min_difference,
1883                                            &has_larger);
1884
1885       if (min_difference == 0)
1886         return icon_info_new_builtin (closest_builtin);
1887
1888       dirs = builtin_dirs;
1889     }
1890   else
1891     dirs = theme->dirs;
1892
1893   l = dirs;
1894   while (l != NULL)
1895     {
1896       dir = l->data;
1897
1898       GTK_NOTE (ICONTHEME, 
1899                 g_print ("theme_lookup_icon dir %s\n", dir->dir));
1900       suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
1901       if (best_suffix (suffix, allow_svg) != ICON_SUFFIX_NONE)
1902         {
1903           difference = theme_dir_size_difference (dir, size, &smaller);
1904
1905           if (difference == 0)
1906             {
1907               min_dir = dir;
1908               break;
1909             }
1910
1911           if (!has_larger)
1912             {
1913               if (difference < min_difference || smaller)
1914                 {
1915                   min_difference = difference;
1916                   min_dir = dir;
1917                   closest_builtin = NULL;
1918                   has_larger = smaller;
1919                 }
1920             }
1921           else
1922             {
1923               if (difference < min_difference && smaller)
1924                 {
1925                   min_difference = difference;
1926                   min_dir = dir;
1927                   closest_builtin = NULL;
1928                 }
1929             }
1930
1931         }
1932
1933       l = l->next;
1934
1935       if (l == NULL && dirs == builtin_dirs)
1936         {
1937           dirs = theme->dirs;
1938           l = dirs;
1939         }
1940     }
1941
1942   if (closest_builtin)
1943     return icon_info_new_builtin (closest_builtin);
1944   
1945   if (min_dir)
1946     {
1947       GtkIconInfo *icon_info = icon_info_new ();
1948       gboolean has_icon_file = FALSE;
1949       
1950       suffix = theme_dir_get_icon_suffix (min_dir, icon_name, &has_icon_file);
1951       suffix = best_suffix (suffix, allow_svg);
1952       g_assert (suffix != ICON_SUFFIX_NONE);
1953       
1954       file = g_strconcat (icon_name, string_from_suffix (suffix), NULL);
1955       icon_info->filename = g_build_filename (min_dir->dir, file, NULL);
1956       g_free (file);
1957 #ifdef G_OS_WIN32
1958       icon_info->cp_filename = g_locale_from_utf8 (icon_info->filename,
1959                                                    -1, NULL, NULL, NULL);
1960 #endif
1961       
1962       if (min_dir->icon_data != NULL)
1963         icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
1964
1965       if (icon_info->data == NULL && min_dir->cache != NULL)
1966         {
1967           icon_info->data = _gtk_icon_cache_get_icon_data (min_dir->cache, icon_name, min_dir->subdir);
1968           if (icon_info->data)
1969             {
1970               if (min_dir->icon_data == NULL)
1971                 min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
1972                                                             g_free, (GDestroyNotify)icon_data_free);
1973
1974               g_hash_table_replace (min_dir->icon_data, g_strdup (icon_name), icon_info->data);
1975             }
1976         }
1977
1978       if (icon_info->data == NULL && has_icon_file)
1979         {
1980           gchar *icon_file_name, *icon_file_path;
1981
1982           icon_file_name = g_strconcat (icon_name, ".icon", NULL);
1983           icon_file_path = g_build_filename (min_dir->dir, icon_file_name, NULL);
1984
1985           if (g_file_test (icon_file_path, G_FILE_TEST_IS_REGULAR))
1986             {
1987               if (min_dir->icon_data == NULL)   
1988                 min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
1989                                                             g_free, (GDestroyNotify)icon_data_free);
1990               load_icon_data (min_dir, icon_file_path, icon_file_name);
1991               
1992               icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
1993             }
1994           g_free (icon_file_name);
1995           g_free (icon_file_path);
1996         }
1997
1998       if (min_dir->cache)
1999         {
2000           icon_info->cache_pixbuf = _gtk_icon_cache_get_icon (min_dir->cache, icon_name,
2001                                                               min_dir->subdir);
2002         }
2003
2004       icon_info->dir_type = min_dir->type;
2005       icon_info->dir_size = min_dir->size;
2006       icon_info->threshold = min_dir->threshold;
2007       
2008       return icon_info;
2009     }
2010  
2011   return NULL;
2012 }
2013
2014 static void
2015 theme_list_icons (IconTheme *theme, GHashTable *icons,
2016                   GQuark context)
2017 {
2018   GList *l = theme->dirs;
2019   IconThemeDir *dir;
2020   
2021   while (l != NULL)
2022     {
2023       dir = l->data;
2024
2025       if (context == dir->context ||
2026           context == 0)
2027         {
2028           if (dir->cache)
2029             {
2030               _gtk_icon_cache_add_icons (dir->cache,
2031                                          dir->subdir,
2032                                          icons);
2033                                          
2034             }
2035           else
2036             {
2037               g_hash_table_foreach (dir->icons,
2038                                     add_key_to_hash,
2039                                     icons);
2040             }
2041         }
2042       l = l->next;
2043     }
2044 }
2045
2046 static void
2047 load_icon_data (IconThemeDir *dir, const char *path, const char *name)
2048 {
2049   GKeyFile *icon_file;
2050   char *base_name;
2051   char **split;
2052   gsize length;
2053   char *dot;
2054   char *str;
2055   char *split_point;
2056   int i;
2057   gint *ivalues;
2058   GError *error = NULL;
2059   
2060   GtkIconData *data;
2061
2062   icon_file = g_key_file_new ();
2063   g_key_file_set_list_separator (icon_file, ',');
2064   g_key_file_load_from_file (icon_file, path, 0, &error);
2065   if (error)
2066     {
2067       g_error_free (error);
2068       return;
2069     }
2070   else
2071     {
2072       base_name = g_strdup (name);
2073       dot = strrchr (base_name, '.');
2074       *dot = 0;
2075       
2076       data = g_new0 (GtkIconData, 1);
2077       g_hash_table_replace (dir->icon_data, base_name, data);
2078       
2079       ivalues = g_key_file_get_integer_list (icon_file, 
2080                                              "Icon Data", "EmbeddedTextRectangle",
2081                                               &length, NULL);
2082       if (ivalues)
2083         {
2084           if (length == 4)
2085             {
2086               data->has_embedded_rect = TRUE;
2087               data->x0 = ivalues[0];
2088               data->y0 = ivalues[1];
2089               data->x1 = ivalues[2];
2090               data->y1 = ivalues[3];
2091             }
2092           
2093           g_free (ivalues);
2094         }
2095       
2096       str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
2097       if (str)
2098         {
2099           split = g_strsplit (str, "|", -1);
2100           
2101           data->n_attach_points = g_strv_length (split);
2102           data->attach_points = g_malloc (sizeof (GdkPoint) * data->n_attach_points);
2103
2104           i = 0;
2105           while (split[i] != NULL && i < data->n_attach_points)
2106             {
2107               split_point = strchr (split[i], ',');
2108               if (split_point)
2109                 {
2110                   *split_point = 0;
2111                   split_point++;
2112                   data->attach_points[i].x = atoi (split[i]);
2113                   data->attach_points[i].y = atoi (split_point);
2114                 }
2115               i++;
2116             }
2117           
2118           g_strfreev (split);
2119           g_free (str);
2120         }
2121       
2122       data->display_name = g_key_file_get_locale_string (icon_file, 
2123                                                          "Icon Data", "DisplayName",
2124                                                          NULL, NULL);
2125       g_key_file_free (icon_file);
2126     }
2127 }
2128
2129 static void
2130 scan_directory (GtkIconThemePrivate *icon_theme,
2131                 IconThemeDir *dir, char *full_dir)
2132 {
2133   GDir *gdir;
2134   const char *name;
2135   char *base_name, *dot;
2136   char *path;
2137   IconSuffix suffix, hash_suffix;
2138
2139   GTK_NOTE (ICONTHEME, 
2140             g_print ("scanning directory %s\n", full_dir));
2141   dir->icons = g_hash_table_new_full (g_str_hash, g_str_equal,
2142                                       g_free, NULL);
2143   
2144   gdir = g_dir_open (full_dir, 0, NULL);
2145
2146   if (gdir == NULL)
2147     return;
2148
2149   while ((name = g_dir_read_name (gdir)))
2150     {
2151       if (g_str_has_suffix (name, ".icon"))
2152         {
2153           if (dir->icon_data == NULL)
2154             dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2155                                                     g_free, (GDestroyNotify)icon_data_free);
2156           
2157           path = g_build_filename (full_dir, name, NULL);
2158           if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
2159             load_icon_data (dir, path, name);
2160           
2161           g_free (path);
2162           
2163           continue;
2164         }
2165
2166       suffix = suffix_from_name (name);
2167       if (suffix == ICON_SUFFIX_NONE)
2168         continue;
2169       
2170       base_name = g_strdup (name);
2171       dot = strrchr (base_name, '.');
2172       *dot = 0;
2173       
2174       hash_suffix = GPOINTER_TO_INT (g_hash_table_lookup (dir->icons, base_name));
2175       g_hash_table_replace (dir->icons, base_name, GUINT_TO_POINTER (hash_suffix| suffix));
2176       g_hash_table_insert (icon_theme->all_icons, base_name, NULL);
2177     }
2178   
2179   g_dir_close (gdir);
2180 }
2181
2182 static void
2183 theme_subdir_load (GtkIconTheme *icon_theme,
2184                    IconTheme    *theme,
2185                    GKeyFile     *theme_file,
2186                    char         *subdir)
2187 {
2188   GList *d;
2189   char *type_string;
2190   IconThemeDir *dir;
2191   IconThemeDirType type;
2192   char *context_string;
2193   GQuark context;
2194   int size;
2195   int min_size;
2196   int max_size;
2197   int threshold;
2198   char *full_dir;
2199   GError *error = NULL;
2200   IconThemeDirMtime *dir_mtime;
2201
2202   size = g_key_file_get_integer (theme_file, subdir, "Size", &error);
2203   if (error)
2204     {
2205       g_error_free (error);
2206       g_warning ("Theme directory %s of theme %s has no size field\n", 
2207                  subdir, theme->name);
2208       return;
2209     }
2210   
2211   type = ICON_THEME_DIR_THRESHOLD;
2212   type_string = g_key_file_get_string (theme_file, subdir, "Type", NULL);
2213   if (type_string)
2214     {
2215       if (strcmp (type_string, "Fixed") == 0)
2216         type = ICON_THEME_DIR_FIXED;
2217       else if (strcmp (type_string, "Scalable") == 0)
2218         type = ICON_THEME_DIR_SCALABLE;
2219       else if (strcmp (type_string, "Threshold") == 0)
2220         type = ICON_THEME_DIR_THRESHOLD;
2221
2222       g_free (type_string);
2223     }
2224   
2225   context = 0;
2226   context_string = g_key_file_get_string (theme_file, subdir, "Context", NULL);
2227   if (context_string)
2228     {
2229       context = g_quark_from_string (context_string);
2230       g_free (context_string);
2231     }
2232
2233   max_size = g_key_file_get_integer (theme_file, subdir, "MaxSize", &error);
2234   if (error)
2235     {
2236       max_size = size;
2237
2238       g_error_free (error);
2239       error = NULL;
2240     }
2241
2242   min_size = g_key_file_get_integer (theme_file, subdir, "MinSize", &error);
2243   if (error)
2244     {
2245       min_size = size;
2246
2247       g_error_free (error);
2248       error = NULL;
2249     }
2250   
2251   threshold = g_key_file_get_integer (theme_file, subdir, "Threshold", &error);
2252   if (error)
2253     {
2254       threshold = 2;
2255
2256       g_error_free (error);
2257       error = NULL;
2258     }
2259
2260   for (d = icon_theme->priv->dir_mtimes; d; d = d->next)
2261     {
2262       dir_mtime = (IconThemeDirMtime *)d->data;
2263
2264       if (dir_mtime->mtime == 0)
2265         continue; /* directory doesn't exist */
2266
2267        full_dir = g_build_filename (dir_mtime->dir, subdir, NULL);
2268
2269       /* First, see if we have a cache for the directory */
2270       if (dir_mtime->cache != NULL || g_file_test (full_dir, G_FILE_TEST_IS_DIR))
2271         {
2272           if (dir_mtime->cache == NULL)
2273             {
2274               /* This will return NULL if the cache doesn't exist or is outdated */
2275               dir_mtime->cache = _gtk_icon_cache_new_for_path (dir_mtime->dir);
2276             }
2277           
2278           dir = g_new (IconThemeDir, 1);
2279           dir->type = type;
2280           dir->context = context;
2281           dir->size = size;
2282           dir->min_size = min_size;
2283           dir->max_size = max_size;
2284           dir->threshold = threshold;
2285           dir->dir = full_dir;
2286           dir->icon_data = NULL;
2287           dir->subdir = g_strdup (subdir);
2288           if (dir_mtime->cache != NULL)
2289             dir->cache = _gtk_icon_cache_ref (dir_mtime->cache);
2290           else
2291             {
2292               dir->cache = NULL;
2293               scan_directory (icon_theme->priv, dir, full_dir);
2294             }
2295
2296           theme->dirs = g_list_prepend (theme->dirs, dir);
2297         }
2298       else
2299         g_free (full_dir);
2300     }
2301 }
2302
2303 static void
2304 icon_data_free (GtkIconData *icon_data)
2305 {
2306   g_free (icon_data->attach_points);
2307   g_free (icon_data->display_name);
2308   g_free (icon_data);
2309 }
2310
2311 /*
2312  * GtkIconInfo
2313  */
2314 GType
2315 gtk_icon_info_get_type (void)
2316 {
2317   static GType our_type = 0;
2318   
2319   if (our_type == 0)
2320     our_type = g_boxed_type_register_static (I_("GtkIconInfo"),
2321                                              (GBoxedCopyFunc) gtk_icon_info_copy,
2322                                              (GBoxedFreeFunc) gtk_icon_info_free);
2323
2324   return our_type;
2325 }
2326
2327 static GtkIconInfo *
2328 icon_info_new (void)
2329 {
2330   GtkIconInfo *icon_info = g_new0 (GtkIconInfo, 1);
2331
2332   icon_info->scale = -1.;
2333
2334   return icon_info;
2335 }
2336
2337 static GtkIconInfo *
2338 icon_info_new_builtin (BuiltinIcon *icon)
2339 {
2340   GtkIconInfo *icon_info = icon_info_new ();
2341
2342   icon_info->cache_pixbuf = g_object_ref (icon->pixbuf);
2343   icon_info->dir_type = ICON_THEME_DIR_THRESHOLD;
2344   icon_info->dir_size = icon->size;
2345   icon_info->threshold = 2;
2346   
2347   return icon_info;
2348 }
2349
2350 /**
2351  * gtk_icon_info_copy:
2352  * @icon_info: a #GtkIconInfo
2353  * 
2354  * Make a copy of a #GtkIconInfo.
2355  * 
2356  * Return value: the new GtkIconInfo
2357  *
2358  * Since: 2.4
2359  **/
2360 GtkIconInfo *
2361 gtk_icon_info_copy (GtkIconInfo *icon_info)
2362 {
2363   GtkIconInfo *copy;
2364   
2365   g_return_val_if_fail (icon_info != NULL, NULL);
2366
2367   copy = g_memdup (icon_info, sizeof (GtkIconInfo));
2368   if (copy->cache_pixbuf)
2369     g_object_ref (copy->cache_pixbuf);
2370   if (copy->pixbuf)
2371     g_object_ref (copy->pixbuf);
2372   if (copy->load_error)
2373     copy->load_error = g_error_copy (copy->load_error);
2374   if (copy->filename)
2375     copy->filename = g_strdup (copy->filename);
2376 #ifdef G_OS_WIN32
2377   if (copy->cp_filename)
2378     copy->cp_filename = g_strdup (copy->cp_filename);
2379 #endif
2380
2381   return copy;
2382 }
2383
2384 /**
2385  * gtk_icon_info_free:
2386  * @icon_info: a #GtkIconInfo
2387  * 
2388  * Free a #GtkIconInfo and associated information
2389  *
2390  * Since: 2.4
2391  **/
2392 void
2393 gtk_icon_info_free (GtkIconInfo *icon_info)
2394 {
2395   g_return_if_fail (icon_info != NULL);
2396
2397   if (icon_info->filename)
2398     g_free (icon_info->filename);
2399 #ifdef G_OS_WIN32
2400   if (icon_info->cp_filename)
2401     g_free (icon_info->cp_filename);
2402 #endif
2403   if (icon_info->pixbuf)
2404     g_object_unref (icon_info->pixbuf);
2405   if (icon_info->cache_pixbuf)
2406     g_object_unref (icon_info->cache_pixbuf);
2407
2408   g_free (icon_info);
2409 }
2410
2411 /**
2412  * gtk_icon_info_get_base_size:
2413  * @icon_info: a #GtkIconInfo
2414  * 
2415  * Gets the base size for the icon. The base size
2416  * is a size for the icon that was specified by
2417  * the icon theme creator. This may be different
2418  * than the actual size of image; an example of
2419  * this is small emblem icons that can be attached
2420  * to a larger icon. These icons will be given
2421  * the same base size as the larger icons to which
2422  * they are attached.
2423  * 
2424  * Return value: the base size, or 0, if no base
2425  *  size is known for the icon.
2426  *
2427  * Since: 2.4
2428  **/
2429 gint
2430 gtk_icon_info_get_base_size (GtkIconInfo *icon_info)
2431 {
2432   g_return_val_if_fail (icon_info != NULL, 0);
2433
2434   return icon_info->dir_size;
2435 }
2436
2437 /**
2438  * gtk_icon_info_get_filename:
2439  * @icon_info: a #GtkIconInfo
2440  * 
2441  * Gets the filename for the icon. If the
2442  * %GTK_ICON_LOOKUP_USE_BUILTIN flag was passed
2443  * to gtk_icon_theme_lookup_icon(), there may be
2444  * no filename if a builtin icon is returned; in this
2445  * case, you should use gtk_icon_info_get_builtin_pixbuf().
2446  * 
2447  * Return value: the filename for the icon, or %NULL
2448  *  if gtk_icon_info_get_builtin_pixbuf() should
2449  *  be used instead. The return value is owned by
2450  *  GTK+ and should not be modified or freed.
2451  *
2452  * Since: 2.4
2453  **/
2454 G_CONST_RETURN gchar *
2455 gtk_icon_info_get_filename (GtkIconInfo *icon_info)
2456 {
2457   g_return_val_if_fail (icon_info != NULL, NULL);
2458
2459   return icon_info->filename;
2460 }
2461
2462 /**
2463  * gtk_icon_info_get_builtin_pixbuf:
2464  * @icon_info: a #GtkIconInfo structure
2465  * 
2466  * Gets the built-in image for this icon, if any. To allow
2467  * GTK+ to use built in icon images, you must pass the
2468  * %GTK_ICON_LOOKUP_USE_BUILTIN to
2469  * gtk_icon_theme_lookup_icon().
2470  * 
2471  * Return value: the built-in image pixbuf, or %NULL. No
2472  *  extra reference is added to the returned pixbuf, so if
2473  *  you want to keep it around, you must use g_object_ref().
2474  *  The returned image must not be modified.
2475  *
2476  * Since: 2.4
2477  **/
2478 GdkPixbuf *
2479 gtk_icon_info_get_builtin_pixbuf (GtkIconInfo *icon_info)
2480 {
2481   g_return_val_if_fail (icon_info != NULL, NULL);
2482
2483   if (icon_info->filename)
2484     return NULL;
2485   
2486   return icon_info->cache_pixbuf;
2487 }
2488
2489 static GdkPixbuf *
2490 load_svg_at_size (const gchar *filename,
2491                   gint         size,
2492                   GError      **error)
2493 {
2494   GdkPixbuf *pixbuf = NULL;
2495   GdkPixbufLoader *loader = NULL;
2496   gchar *contents = NULL;
2497   gsize length;
2498   
2499   if (!g_file_get_contents (filename,
2500                             &contents, &length, error))
2501     goto bail;
2502   
2503   loader = gdk_pixbuf_loader_new_with_type ("svg", error);
2504   if (loader == NULL)
2505     goto bail;
2506
2507   gdk_pixbuf_loader_set_size (loader, size, size);
2508   
2509   if (!gdk_pixbuf_loader_write (loader, contents, length, error))
2510     {
2511       gdk_pixbuf_loader_close (loader, NULL);
2512       goto bail;
2513     }
2514   
2515   if (!gdk_pixbuf_loader_close (loader, error))
2516     goto bail;
2517   
2518   pixbuf = g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader));
2519   
2520  bail:
2521   if (loader)
2522     g_object_unref (loader);
2523   if (contents)
2524     g_free (contents);
2525   
2526   return pixbuf;
2527 }
2528
2529 /* This function contains the complicatd logic for deciding
2530  * on the size at which to load the icon and loading it at
2531  * that size.
2532  */
2533 static gboolean
2534 icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info,
2535                                    gboolean     scale_only)
2536 {
2537   int image_width, image_height;
2538   GdkPixbuf *source_pixbuf;
2539
2540   /* First check if we already succeeded have the necessary
2541    * information (or failed earlier)
2542    */
2543   if (scale_only && icon_info->scale >= 0)
2544     return TRUE;
2545
2546   if (icon_info->pixbuf)
2547     return TRUE;
2548
2549   if (icon_info->load_error)
2550     return FALSE;
2551
2552   /* SVG icons are a special case - we just immediately scale them
2553    * to the desired size
2554    */
2555   if (icon_info->filename && g_str_has_suffix (icon_info->filename, ".svg"))
2556     {
2557       icon_info->scale = icon_info->desired_size / 1000.;
2558
2559       if (scale_only)
2560         return TRUE;
2561       
2562       icon_info->pixbuf = load_svg_at_size (icon_info->filename,
2563                                             icon_info->desired_size,
2564                                             &icon_info->load_error);
2565
2566       return icon_info->pixbuf != NULL;
2567     }
2568
2569   /* In many cases, the scale can be determined without actual access
2570    * to the icon file. This is generally true when we have a size
2571    * for the directory where the icon is; the image size doesn't
2572    * matter in that case.
2573    */
2574   if (icon_info->dir_type == ICON_THEME_DIR_FIXED)
2575     icon_info->scale = 1.0;
2576   else if (icon_info->dir_type == ICON_THEME_DIR_THRESHOLD)
2577     {
2578       if (icon_info->desired_size >= icon_info->dir_size - icon_info->threshold &&
2579           icon_info->desired_size <= icon_info->dir_size + icon_info->threshold)
2580         icon_info->scale = 1.0;
2581       else if (icon_info->dir_size > 0)
2582         icon_info->scale =(gdouble) icon_info->desired_size / icon_info->dir_size;
2583     }
2584   else if (icon_info->dir_type == ICON_THEME_DIR_SCALABLE)
2585     {
2586       if (icon_info->dir_size > 0)
2587         icon_info->scale = (gdouble) icon_info->desired_size / icon_info->dir_size;
2588     }
2589
2590   if (icon_info->scale >= 0. && scale_only)
2591     return TRUE;
2592
2593   /* At this point, we need to actually get the icon; either from the
2594    * builting image or by loading the file
2595    */
2596   if (icon_info->cache_pixbuf)
2597     source_pixbuf = g_object_ref (icon_info->cache_pixbuf);
2598   else
2599     {
2600
2601       source_pixbuf = gdk_pixbuf_new_from_file (icon_info->filename,
2602                                                 &icon_info->load_error);
2603       if (!source_pixbuf)
2604         return FALSE;
2605     }
2606
2607   /* Do scale calculations that depend on the image size
2608    */
2609   image_width = gdk_pixbuf_get_width (source_pixbuf);
2610   image_height = gdk_pixbuf_get_height (source_pixbuf);
2611
2612   if (icon_info->scale < 0.0)
2613     {
2614       gint image_size = MAX (image_width, image_height);
2615       if (image_size > 0)
2616         icon_info->scale = icon_info->desired_size / (gdouble)image_size;
2617       else
2618         icon_info->scale = 1.0;
2619       
2620       if (icon_info->dir_type == ICON_THEME_DIR_UNTHEMED)
2621         icon_info->scale = MIN (icon_info->scale, 1.0);
2622     }
2623
2624   /* We don't short-circuit out here for scale_only, since, now
2625    * we've loaded the icon, we might as well go ahead and finish
2626    * the job. This is a bit of a waste when we scale here
2627    * and never get the final pixbuf; at the cost of a bit of
2628    * extra complexity, we could keep the source pixbuf around
2629    * but not actually scale it until neede.
2630    */
2631     
2632   if (icon_info->scale == 1.0)
2633     icon_info->pixbuf = source_pixbuf;
2634   else
2635     {
2636       icon_info->pixbuf = gdk_pixbuf_scale_simple (source_pixbuf,
2637                                                    0.5 + image_width * icon_info->scale,
2638                                                    0.5 + image_height * icon_info->scale,
2639                                                    GDK_INTERP_BILINEAR);
2640       g_object_unref (source_pixbuf);
2641     }
2642
2643   return TRUE;
2644 }
2645
2646 /**
2647  * gtk_icon_info_load_icon:
2648  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
2649  * @error: location to store error information on failure, or %NULL.
2650  * 
2651  * Renders an icon previously looked up in an icon theme using
2652  * gtk_icon_theme_lookup_icon(); the size will be based on the size
2653  * passed to gtk_icon_theme_lookup_icon(). Note that the resulting
2654  * pixbuf may not be exactly this size; an icon theme may have icons
2655  * that differ slightly from their nominal sizes, and in addition GTK+
2656  * will avoid scaling icons that it considers sufficiently close to the
2657  * requested size or for which the source image would have to be scaled
2658  * up too far. (This maintains sharpness.) 
2659  * 
2660  * Return value: the rendered icon; this may be a newly created icon
2661  *  or a new reference to an internal icon, so you must not modify
2662  *  the icon. Use g_object_unref() to release your reference to the
2663  *  icon.
2664  *
2665  * Since: 2.4
2666  **/
2667 GdkPixbuf *
2668 gtk_icon_info_load_icon (GtkIconInfo *icon_info,
2669                          GError     **error)
2670 {
2671   g_return_val_if_fail (icon_info != NULL, NULL);
2672
2673   g_return_val_if_fail (icon_info != NULL, NULL);
2674   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2675
2676   icon_info_ensure_scale_and_pixbuf (icon_info, FALSE);
2677
2678   if (icon_info->load_error)
2679     {
2680       g_propagate_error (error, icon_info->load_error);
2681       return NULL;
2682     }
2683
2684   return g_object_ref (icon_info->pixbuf);
2685 }
2686
2687 /**
2688  * gtk_icon_info_set_raw_coordinates:
2689  * @icon_info: a #GtkIconInfo
2690  * @raw_coordinates: whether the coordinates of embedded rectangles
2691  *   and attached points should be returned in their original
2692  *   (unscaled) form.
2693  * 
2694  * Sets whether the coordinates returned by gtk_icon_info_get_embedded_rect()
2695  * and gtk_icon_info_get_attach_points() should be returned in their
2696  * original form as specified in the icon theme, instead of scaled
2697  * appropriately for the pixbuf returned by gtk_icon_info_load_icon().
2698  *
2699  * Raw coordinates are somewhat strange; they are specified to be with
2700  * respect to the unscaled pixmap for PNG and XPM icons, but for SVG
2701  * icons, they are in a 1000x1000 coordinate space that is scaled
2702  * to the final size of the icon.  You can determine if the icon is an SVG
2703  * icon by using gtk_icon_info_get_filename(), and seeing if it is non-%NULL
2704  * and ends in '.svg'.
2705  *
2706  * This function is provided primarily to allow compatibility wrappers
2707  * for older API's, and is not expected to be useful for applications.
2708  *
2709  * Since: 2.4
2710  **/
2711 void
2712 gtk_icon_info_set_raw_coordinates (GtkIconInfo *icon_info,
2713                                    gboolean     raw_coordinates)
2714 {
2715   g_return_if_fail (icon_info != NULL);
2716   
2717   icon_info->raw_coordinates = raw_coordinates != FALSE;
2718 }
2719
2720 /* Scale coordinates from the icon data prior to returning
2721  * them to the user.
2722  */
2723 static gboolean
2724 icon_info_scale_point (GtkIconInfo  *icon_info,
2725                        gint          x,
2726                        gint          y,
2727                        gint         *x_out,
2728                        gint         *y_out)
2729 {
2730   if (icon_info->raw_coordinates)
2731     {
2732       *x_out = x;
2733       *y_out = y;
2734     }
2735   else
2736     {
2737       if (!icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
2738         return FALSE;
2739
2740       *x_out = 0.5 + x * icon_info->scale;
2741       *y_out = 0.5 + y * icon_info->scale;
2742     }
2743
2744   return TRUE;
2745 }
2746
2747 /**
2748  * gtk_icon_info_get_embedded_rect:
2749  * @icon_info: a #GtkIconInfo
2750  * @rectangle: #GdkRectangle in which to store embedded
2751  *   rectangle coordinates; coordinates are only stored
2752  *   when this function returns %TRUE.
2753  *
2754  * Gets the coordinates of a rectangle within the icon
2755  * that can be used for display of information such
2756  * as a preview of the contents of a text file.
2757  * See gtk_icon_info_set_raw_coordinates() for further
2758  * information about the coordinate system.
2759  * 
2760  * Return value: %TRUE if the icon has an embedded rectangle
2761  *
2762  * Since: 2.4
2763  **/
2764 gboolean
2765 gtk_icon_info_get_embedded_rect (GtkIconInfo  *icon_info,
2766                                  GdkRectangle *rectangle)
2767 {
2768   g_return_val_if_fail (icon_info != NULL, FALSE);
2769
2770   if (icon_info->data && icon_info->data->has_embedded_rect &&
2771       icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
2772     {
2773       gint scaled_x0, scaled_y0;
2774       gint scaled_x1, scaled_y1;
2775       
2776       if (rectangle)
2777         {
2778           icon_info_scale_point (icon_info,
2779                                  icon_info->data->x0, icon_info->data->y0,
2780                                  &scaled_x0, &scaled_y0);
2781           icon_info_scale_point (icon_info,
2782                                  icon_info->data->x1, icon_info->data->y1,
2783                                  &scaled_x1, &scaled_y1);
2784           
2785           rectangle->x = scaled_x0;
2786           rectangle->y = scaled_y0;
2787           rectangle->width = scaled_x1 - rectangle->x;
2788           rectangle->height = scaled_y1 - rectangle->y;
2789         }
2790
2791       return TRUE;
2792     }
2793   else
2794     return FALSE;
2795 }
2796
2797 /**
2798  * gtk_icon_info_get_attach_points:
2799  * @icon_info: a #GtkIconInfo
2800  * @points: location to store pointer to an array of points, or %NULL
2801  *          free the array of points with g_free().
2802  * @n_points: location to store the number of points in @points, or %NULL
2803  * 
2804  * Fetches the set of attach points for an icon. An attach point
2805  * is a location in the icon that can be used as anchor points for attaching
2806  * emblems or overlays to the icon.
2807  * 
2808  * Return value: %TRUE if there are any attach points for the icon.
2809  *
2810  * Since: 2.4
2811  **/
2812 gboolean
2813 gtk_icon_info_get_attach_points (GtkIconInfo *icon_info,
2814                                  GdkPoint   **points,
2815                                  gint        *n_points)
2816 {
2817   g_return_val_if_fail (icon_info != NULL, FALSE);
2818   
2819   if (icon_info->data && icon_info->data->n_attach_points &&
2820       icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
2821     {
2822       if (points)
2823         {
2824           gint i;
2825           
2826           *points = g_new (GdkPoint, icon_info->data->n_attach_points);
2827           for (i = 0; i < icon_info->data->n_attach_points; i++)
2828             icon_info_scale_point (icon_info,
2829                                    icon_info->data->attach_points[i].x,
2830                                    icon_info->data->attach_points[i].y,
2831                                    &(*points)[i].x,
2832                                    &(*points)[i].y);
2833         }
2834           
2835       if (n_points)
2836         *n_points = icon_info->data->n_attach_points;
2837
2838       return TRUE;
2839     }
2840   else
2841     {
2842       if (points)
2843         *points = NULL;
2844       if (n_points)
2845         *n_points = 0;
2846       
2847       return FALSE;
2848     }
2849 }
2850
2851 /**
2852  * gtk_icon_info_get_display_name:
2853  * @icon_info: a #GtkIconInfo
2854  * 
2855  * Gets the display name for an icon. A display name is a
2856  * string to be used in place of the icon name in a user
2857  * visible context like a list of icons.
2858  * 
2859  * Return value: the display name for the icon or %NULL, if
2860  *  the icon doesn't have a specified display name. This value
2861  *  is owned @icon_info and must not be modified or free.
2862  *
2863  * Since: 2.4
2864  **/
2865 G_CONST_RETURN gchar *
2866 gtk_icon_info_get_display_name  (GtkIconInfo *icon_info)
2867 {
2868   g_return_val_if_fail (icon_info != NULL, NULL);
2869
2870   if (icon_info->data)
2871     return icon_info->data->display_name;
2872   else
2873     return NULL;
2874 }
2875
2876 /*
2877  * Builtin icons
2878  */
2879
2880
2881 /**
2882  * gtk_icon_theme_add_builtin_icon:
2883  * @icon_name: the name of the icon to register
2884  * @size: the size at which to register the icon (different
2885  *        images can be registered for the same icon name
2886  *        at different sizes.)
2887  * @pixbuf: #GdkPixbuf that contains the image to use
2888  *          for @icon_name.
2889  * 
2890  * Registers a built-in icon for icon theme lookups. The idea
2891  * of built-in icons is to allow an application or library
2892  * that uses themed icons to function requiring files to
2893  * be present in the file system. For instance, the default
2894  * images for all of GTK+'s stock icons are registered
2895  * as built-icons.
2896  *
2897  * In general, if you use gtk_icon_theme_add_builtin_icon()
2898  * you should also install the icon in the icon theme, so
2899  * that the icon is generally available.
2900  *
2901  * This function will generally be used with pixbufs loaded
2902  * via gdk_pixbuf_new_from_inline().
2903  *
2904  * Since: 2.4
2905  **/
2906 void
2907 gtk_icon_theme_add_builtin_icon (const gchar *icon_name,
2908                                  gint         size,
2909                                  GdkPixbuf   *pixbuf)
2910 {
2911   BuiltinIcon *default_icon;
2912   GSList *icons;
2913   gpointer key;
2914
2915   g_return_if_fail (icon_name != NULL);
2916   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2917   
2918   if (!icon_theme_builtin_icons)
2919     icon_theme_builtin_icons = g_hash_table_new (g_str_hash, g_str_equal);
2920
2921   icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
2922   if (!icons)
2923     key = g_strdup (icon_name);
2924   else
2925     key = (gpointer)icon_name;  /* Won't get stored */
2926
2927   default_icon = g_new (BuiltinIcon, 1);
2928   default_icon->size = size;
2929   default_icon->pixbuf = g_object_ref (pixbuf);
2930   icons = g_slist_prepend (icons, default_icon);
2931
2932   /* Replaces value, leaves key untouched
2933    */
2934   g_hash_table_insert (icon_theme_builtin_icons, key, icons);
2935 }
2936
2937 /* Look up a builtin icon; the min_difference_p and
2938  * has_larger_p out parameters allow us to combine
2939  * this lookup with searching through the actual directories
2940  * of the "hicolor" icon theme. See theme_lookup_icon()
2941  * for how they are used.
2942  */
2943 static BuiltinIcon *
2944 find_builtin_icon (const gchar *icon_name,
2945                    gint         size,
2946                    gint        *min_difference_p,
2947                    gboolean    *has_larger_p)
2948 {
2949   GSList *icons = NULL;
2950   gint min_difference = G_MAXINT;
2951   gboolean has_larger = FALSE;
2952   BuiltinIcon *min_icon = NULL;
2953   
2954   if (!icon_theme_builtin_icons)
2955     return NULL;
2956
2957   icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
2958
2959   while (icons)
2960     {
2961       BuiltinIcon *default_icon = icons->data;
2962       int min, max, difference;
2963       gboolean smaller;
2964       
2965       min = default_icon->size - 2;
2966       max = default_icon->size + 2;
2967       smaller = size < min;
2968       if (size < min)
2969         difference = min - size;
2970       else if (size > max)
2971         difference = size - max;
2972       else
2973         difference = 0;
2974       
2975       if (difference == 0)
2976         {
2977           min_icon = default_icon;
2978           break;
2979         }
2980       
2981       if (!has_larger)
2982         {
2983           if (difference < min_difference || smaller)
2984             {
2985               min_difference = difference;
2986               min_icon = default_icon;
2987               has_larger = smaller;
2988             }
2989         }
2990       else
2991         {
2992           if (difference < min_difference && smaller)
2993             {
2994               min_difference = difference;
2995               min_icon = default_icon;
2996             }
2997         }
2998       
2999       icons = icons->next;
3000     }
3001
3002   if (min_difference_p)
3003     *min_difference_p = min_difference;
3004   if (has_larger_p)
3005     *has_larger_p = has_larger;
3006
3007   return min_icon;
3008 }
3009
3010 void
3011 _gtk_icon_theme_check_reload (GdkDisplay *display)
3012 {
3013   gint n_screens, i;
3014   GdkScreen *screen;
3015   GtkIconTheme *icon_theme;
3016
3017   n_screens = gdk_display_get_n_screens (display);
3018   
3019   for (i = 0; i < n_screens; i++)
3020     {
3021       screen = gdk_display_get_screen (display, i);
3022
3023       icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
3024       if (icon_theme)
3025         {
3026           icon_theme->priv->check_reload = TRUE;
3027           ensure_valid_themes (icon_theme);
3028           icon_theme->priv->check_reload = FALSE;
3029         }
3030     }
3031 }
3032
3033 #ifdef G_OS_WIN32
3034
3035 /* DLL ABI stability backward compatibility versions */
3036
3037 #undef gtk_icon_theme_set_search_path
3038
3039 void
3040 gtk_icon_theme_set_search_path (GtkIconTheme *icon_theme,
3041                                 const gchar  *path[],
3042                                 gint          n_elements)
3043 {
3044   const gchar **utf8_path;
3045   gint i;
3046
3047   utf8_path = g_new (const gchar *, n_elements);
3048
3049   for (i = 0; i < n_elements; i++)
3050     utf8_path[i] = g_locale_to_utf8 (path[i], -1, NULL, NULL, NULL);
3051
3052   gtk_icon_theme_set_search_path_utf8 (icon_theme, utf8_path, n_elements);
3053
3054   for (i = 0; i < n_elements; i++)
3055     g_free ((gchar *) utf8_path[i]);
3056
3057   g_free (utf8_path);
3058 }
3059
3060 #undef gtk_icon_theme_get_search_path
3061
3062 void
3063 gtk_icon_theme_get_search_path (GtkIconTheme      *icon_theme,
3064                                 gchar            **path[],
3065                                 gint              *n_elements)
3066 {
3067   gint i, n;
3068
3069   gtk_icon_theme_get_search_path_utf8 (icon_theme, path, &n);
3070
3071   if (n_elements)
3072     *n_elements = n;
3073
3074   if (path)
3075     {
3076       for (i = 0; i < n; i++)
3077         {
3078           gchar *tem = (*path)[i];
3079
3080           (*path)[i] = g_locale_from_utf8 ((*path)[i], -1, NULL, NULL, NULL);
3081           g_free (tem);
3082         }
3083     }
3084 }
3085
3086 #undef gtk_icon_theme_append_search_path
3087
3088 void
3089 gtk_icon_theme_append_search_path (GtkIconTheme *icon_theme,
3090                                    const gchar  *path)
3091 {
3092   gchar *utf8_path = g_locale_from_utf8 (path, -1, NULL, NULL, NULL);
3093
3094   gtk_icon_theme_append_search_path_utf8 (icon_theme, utf8_path);
3095
3096   g_free (utf8_path);
3097 }
3098
3099 #undef gtk_icon_theme_prepend_search_path
3100
3101 void
3102 gtk_icon_theme_prepend_search_path (GtkIconTheme *icon_theme,
3103                                     const gchar  *path)
3104 {
3105   gchar *utf8_path = g_locale_from_utf8 (path, -1, NULL, NULL, NULL);
3106
3107   gtk_icon_theme_prepend_search_path_utf8 (icon_theme, utf8_path);
3108
3109   g_free (utf8_path);
3110 }
3111
3112 #undef gtk_icon_info_get_filename
3113
3114 G_CONST_RETURN gchar *
3115 gtk_icon_info_get_filename (GtkIconInfo *icon_info)
3116 {
3117   g_return_val_if_fail (icon_info != NULL, NULL);
3118
3119   return icon_info->cp_filename;
3120 }
3121
3122 #endif
3123
3124 #define __GTK_ICON_THEME_C__
3125 #include "gtkaliasdef.c"