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