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