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