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