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