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