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