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