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