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