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