]> Pileus Git - ~andy/gtk/blob - gtk/gtkicontheme.c
GtkIconTheme Don't leak pixbuf in symbolic icon cache
[~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, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "config.h"
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #ifdef HAVE_UNISTD_H
23 #include <unistd.h>
24 #endif
25 #include <string.h>
26 #include <stdlib.h>
27 #include <math.h>
28 #include <glib.h>
29 #include <glib/gstdio.h>
30
31 #ifdef G_OS_WIN32
32 #ifndef S_ISDIR
33 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
34 #endif
35 #define WIN32_MEAN_AND_LEAN
36 #include <windows.h>
37 #include "win32/gdkwin32.h"
38 #endif /* G_OS_WIN32 */
39
40 #include "gtkicontheme.h"
41 #include "gtkdebug.h"
42 #include "gtkiconfactory.h"
43 #include "gtkiconcache.h"
44 #include "gtkbuiltincache.h"
45 #include "gtkintl.h"
46 #include "gtkmain.h"
47 #include "gtknumerableiconprivate.h"
48 #include "gtksettings.h"
49 #include "gtkprivate.h"
50
51 #undef GDK_DEPRECATED
52 #undef GDK_DEPRECATED_FOR
53 #define GDK_DEPRECATED
54 #define GDK_DEPRECATED_FOR(f)
55
56 #include "deprecated/gtkstyle.h"
57
58
59 /**
60  * SECTION:gtkicontheme
61  * @Short_description: Looking up icons by name
62  * @Title: GtkIconTheme
63  *
64  * #GtkIconTheme provides a facility for looking up icons by name
65  * and size. The main reason for using a name rather than simply
66  * providing a filename is to allow different icons to be used
67  * depending on what <firstterm>icon theme</firstterm> is selected
68  * by the user. The operation of icon themes on Linux and Unix
69  * follows the <ulink
70  * url="http://www.freedesktop.org/Standards/icon-theme-spec">Icon
71  * Theme Specification</ulink>. There is a default icon theme,
72  * named <literal>hicolor</literal> where applications should install
73  * their icons, but more additional application themes can be
74  * installed as operating system vendors and users choose.
75  *
76  * Named icons are similar to the <xref linkend="gtk3-Themeable-Stock-Images"/>
77  * facility, and the distinction between the two may be a bit confusing.
78  * A few things to keep in mind:
79  * <itemizedlist>
80  * <listitem>
81  * Stock images usually are used in conjunction with
82  * <xref linkend="gtk3-Stock-Items"/>, such as %GTK_STOCK_OK or
83  * %GTK_STOCK_OPEN. Named icons are easier to set up and therefore
84  * are more useful for new icons that an application wants to
85  * add, such as application icons or window icons.
86  * </listitem>
87  * <listitem>
88  * Stock images can only be loaded at the symbolic sizes defined
89  * by the #GtkIconSize enumeration, or by custom sizes defined
90  * by gtk_icon_size_register(), while named icons are more flexible
91  * and any pixel size can be specified.
92  * </listitem>
93  * <listitem>
94  * Because stock images are closely tied to stock items, and thus
95  * to actions in the user interface, stock images may come in
96  * multiple variants for different widget states or writing
97  * directions.
98  * </listitem>
99  * </itemizedlist>
100  * A good rule of thumb is that if there is a stock image for what
101  * you want to use, use it, otherwise use a named icon. It turns
102  * out that internally stock images are generally defined in
103  * terms of one or more named icons. (An example of the
104  * more than one case is icons that depend on writing direction;
105  * %GTK_STOCK_GO_FORWARD uses the two themed icons
106  * "gtk-stock-go-forward-ltr" and "gtk-stock-go-forward-rtl".)
107  *
108  * In many cases, named themes are used indirectly, via #GtkImage
109  * or stock items, rather than directly, but looking up icons
110  * directly is also simple. The #GtkIconTheme object acts
111  * as a database of all the icons in the current theme. You
112  * can create new #GtkIconTheme objects, but its much more
113  * efficient to use the standard icon theme for the #GdkScreen
114  * so that the icon information is shared with other people
115  * looking up icons. In the case where the default screen is
116  * being used, looking up an icon can be as simple as:
117  * <informalexample>
118  * <programlisting>
119  * GError *error = NULL;
120  * GtkIconTheme *icon_theme;
121  * GdkPixbuf *pixbuf;
122  *
123  * icon_theme = gtk_icon_theme_get_default ();
124  * pixbuf = gtk_icon_theme_load_icon (icon_theme,
125  *                                    "my-icon-name", // icon name
126  *                                    48, // size
127  *                                    0,  // flags
128  *                                    &error);
129  * if (!pixbuf)
130  *   {
131  *     g_warning ("Couldn't load icon: &percnt;s", error->message);
132  *     g_error_free (error);
133  *   }
134  * else
135  *   {
136  *     // Use the pixbuf
137  *     g_object_unref (pixbuf);
138  *   }
139  * </programlisting>
140  * </informalexample>
141  */
142
143
144 #define DEFAULT_THEME_NAME "hicolor"
145
146 typedef enum
147 {
148   ICON_THEME_DIR_FIXED,  
149   ICON_THEME_DIR_SCALABLE,  
150   ICON_THEME_DIR_THRESHOLD,
151   ICON_THEME_DIR_UNTHEMED
152 } IconThemeDirType;
153
154 /* In reverse search order: */
155 typedef enum
156 {
157   ICON_SUFFIX_NONE = 0,
158   ICON_SUFFIX_XPM = 1 << 0,
159   ICON_SUFFIX_SVG = 1 << 1,
160   ICON_SUFFIX_PNG = 1 << 2,
161   HAS_ICON_FILE = 1 << 3
162 } IconSuffix;
163
164 #define INFO_CACHE_LRU_SIZE 32
165 #if 0
166 #define DEBUG_CACHE(args) g_print args
167 #else
168 #define DEBUG_CACHE(args)
169 #endif
170
171 struct _GtkIconThemePrivate
172 {
173   GHashTable *info_cache;
174   GList *info_cache_lru;
175
176   gchar *current_theme;
177   gchar *fallback_theme;
178   gchar **search_path;
179   gint search_path_len;
180
181   guint custom_theme        : 1;
182   guint is_screen_singleton : 1;
183   guint pixbuf_supports_svg : 1;
184   guint themes_valid        : 1;
185   guint check_reload        : 1;
186   guint loading_themes      : 1;
187
188   /* A list of all the themes needed to look up icons.
189    * In search order, without duplicates
190    */
191   GList *themes;
192   GHashTable *unthemed_icons;
193
194   /* Note: The keys of this hashtable are owned by the
195    * themedir and unthemed hashtables.
196    */
197   GHashTable *all_icons;
198
199   /* GdkScreen for the icon theme (may be NULL)
200    */
201   GdkScreen *screen;
202
203   /* time when we last stat:ed for theme changes */
204   glong last_stat_time;
205   GList *dir_mtimes;
206
207   gulong reset_styles_idle;
208 };
209
210 typedef struct {
211   gchar **icon_names;
212   gint size;
213   GtkIconLookupFlags flags;
214 } IconInfoKey;
215
216 typedef struct _SymbolicPixbufCache SymbolicPixbufCache;
217
218 struct _SymbolicPixbufCache {
219   GdkPixbuf *pixbuf;
220   GdkPixbuf *proxy_pixbuf;
221   GdkRGBA  fg;
222   GdkRGBA  success_color;
223   GdkRGBA  warning_color;
224   GdkRGBA  error_color;
225   SymbolicPixbufCache *next;
226 };
227
228 struct _GtkIconInfoClass
229 {
230   GObjectClass parent_class;
231 };
232
233 struct _GtkIconInfo
234 {
235   GObject parent_instance;
236
237   /* Information about the source
238    */
239   IconInfoKey key;
240   GtkIconTheme *in_cache;
241
242   gchar *filename;
243   GFile *icon_file;
244   GLoadableIcon *loadable;
245   GSList *emblem_infos;
246
247   /* Cache pixbuf (if there is any) */
248   GdkPixbuf *cache_pixbuf;
249
250   GtkIconData *data;
251
252   /* Information about the directory where
253    * the source was found
254    */
255   IconThemeDirType dir_type;
256   gint dir_size;
257   gint threshold;
258
259   /* Parameters influencing the scaled icon
260    */
261   gint desired_size;
262   guint raw_coordinates : 1;
263   guint forced_size     : 1;
264   guint emblems_applied : 1;
265
266   /* Cached information if we go ahead and try to load
267    * the icon.
268    */
269   GdkPixbuf *pixbuf;
270   GdkPixbuf *proxy_pixbuf;
271   GError *load_error;
272   gdouble scale;
273
274   SymbolicPixbufCache *symbolic_pixbuf_cache;
275
276   GtkRequisition *symbolic_pixbuf_size;
277 };
278
279 typedef struct
280 {
281   char *name;
282   char *display_name;
283   char *comment;
284   char *example;
285
286   /* In search order */
287   GList *dirs;
288 } IconTheme;
289
290 typedef struct
291 {
292   IconThemeDirType type;
293   GQuark context;
294
295   int size;
296   int min_size;
297   int max_size;
298   int threshold;
299
300   char *dir;
301   char *subdir;
302   int subdir_index;
303   
304   GtkIconCache *cache;
305   
306   GHashTable *icons;
307   GHashTable *icon_data;
308 } IconThemeDir;
309
310 typedef struct
311 {
312   char *svg_filename;
313   char *no_svg_filename;
314 } UnthemedIcon;
315
316 typedef struct
317 {
318   gint size;
319   GdkPixbuf *pixbuf;
320 } BuiltinIcon;
321
322 typedef struct 
323 {
324   char *dir;
325   time_t mtime; /* 0 == not existing or not a dir */
326
327   GtkIconCache *cache;
328 } IconThemeDirMtime;
329
330 static void  gtk_icon_theme_finalize   (GObject              *object);
331 static void  theme_dir_destroy         (IconThemeDir         *dir);
332
333 static void         theme_destroy     (IconTheme        *theme);
334 static GtkIconInfo *theme_lookup_icon (IconTheme        *theme,
335                                        const char       *icon_name,
336                                        int               size,
337                                        gboolean          allow_svg,
338                                        gboolean          use_default_icons);
339 static void         theme_list_icons  (IconTheme        *theme,
340                                        GHashTable       *icons,
341                                        GQuark            context);
342 static void         theme_list_contexts  (IconTheme        *theme,
343                                           GHashTable       *contexts);
344 static void         theme_subdir_load (GtkIconTheme     *icon_theme,
345                                        IconTheme        *theme,
346                                        GKeyFile         *theme_file,
347                                        char             *subdir);
348 static void         do_theme_change   (GtkIconTheme     *icon_theme);
349
350 static void     blow_themes               (GtkIconTheme    *icon_themes);
351 static gboolean rescan_themes             (GtkIconTheme    *icon_themes);
352
353 static GtkIconData *icon_data_dup      (GtkIconData     *icon_data);
354 static void  icon_data_free            (GtkIconData     *icon_data);
355 static void load_icon_data             (IconThemeDir    *dir,
356                                         const char      *path,
357                                         const char      *name);
358
359 static IconSuffix theme_dir_get_icon_suffix (IconThemeDir *dir,
360                                              const gchar  *icon_name,
361                                              gboolean     *has_icon_file);
362
363
364 static GtkIconInfo *icon_info_new             (void);
365 static GtkIconInfo *icon_info_new_builtin     (BuiltinIcon *icon);
366
367 static IconSuffix suffix_from_name (const char *name);
368
369 static BuiltinIcon *find_builtin_icon (const gchar *icon_name,
370                                        gint        size,
371                                        gint        *min_difference_p,
372                                        gboolean    *has_larger_p);
373 static void remove_from_lru_cache (GtkIconTheme *icon_theme,
374                                    GtkIconInfo *icon_info);
375
376 static guint signal_changed = 0;
377
378 static GHashTable *icon_theme_builtin_icons;
379
380 /* also used in gtkiconfactory.c */
381 GtkIconCache *_builtin_cache = NULL;
382 static GList *builtin_dirs = NULL;
383
384 static guint
385 icon_info_key_hash (gconstpointer _key)
386 {
387   const IconInfoKey *key = _key;
388   guint h = 0;
389   int i;
390   for (i = 0; key->icon_names[i] != NULL; i++)
391     h ^= g_str_hash (key->icon_names[i]);
392
393   h ^= key->size * 0x10001;
394   h ^= key->flags * 0x1000010;
395
396   return h;
397 }
398
399 static gboolean
400 icon_info_key_equal (gconstpointer  _a,
401                      gconstpointer  _b)
402 {
403   const IconInfoKey *a = _a;
404   const IconInfoKey *b = _b;
405   int i;
406
407   if (a->size != b->size)
408     return FALSE;
409
410   if (a->flags != b->flags)
411     return FALSE;
412
413   for (i = 0;
414        a->icon_names[i] != NULL &&
415        b->icon_names[i] != NULL; i++)
416     {
417       if (strcmp (a->icon_names[i], b->icon_names[i]) != 0)
418         return FALSE;
419     }
420
421   return a->icon_names[i] == NULL && b->icon_names[i] == NULL;
422 }
423
424 G_DEFINE_TYPE (GtkIconTheme, gtk_icon_theme, G_TYPE_OBJECT)
425
426 /**
427  * gtk_icon_theme_new:
428  * 
429  * Creates a new icon theme object. Icon theme objects are used
430  * to lookup up an icon by name in a particular icon theme.
431  * Usually, you'll want to use gtk_icon_theme_get_default()
432  * or gtk_icon_theme_get_for_screen() rather than creating
433  * a new icon theme object for scratch.
434  * 
435  * Return value: the newly created #GtkIconTheme object.
436  *
437  * Since: 2.4
438  **/
439 GtkIconTheme *
440 gtk_icon_theme_new (void)
441 {
442   return g_object_new (GTK_TYPE_ICON_THEME, NULL);
443 }
444
445 /**
446  * gtk_icon_theme_get_default:
447  * 
448  * Gets the icon theme for the default screen. See
449  * gtk_icon_theme_get_for_screen().
450  *
451  * Return value: (transfer none): A unique #GtkIconTheme associated with
452  *  the default screen. This icon theme is associated with
453  *  the screen and can be used as long as the screen
454  *  is open. Do not ref or unref it.
455  *
456  * Since: 2.4
457  **/
458 GtkIconTheme *
459 gtk_icon_theme_get_default (void)
460 {
461   return gtk_icon_theme_get_for_screen (gdk_screen_get_default ());
462 }
463
464 /**
465  * gtk_icon_theme_get_for_screen:
466  * @screen: a #GdkScreen
467  * 
468  * Gets the icon theme object associated with @screen; if this
469  * function has not previously been called for the given
470  * screen, a new icon theme object will be created and
471  * associated with the screen. Icon theme objects are
472  * fairly expensive to create, so using this function
473  * is usually a better choice than calling than gtk_icon_theme_new()
474  * and setting the screen yourself; by using this function
475  * a single icon theme object will be shared between users.
476  *
477  * Return value: (transfer none): A unique #GtkIconTheme associated with
478  *  the given screen. This icon theme is associated with
479  *  the screen and can be used as long as the screen
480  *  is open. Do not ref or unref it.
481  *
482  * Since: 2.4
483  **/
484 GtkIconTheme *
485 gtk_icon_theme_get_for_screen (GdkScreen *screen)
486 {
487   GtkIconTheme *icon_theme;
488
489   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
490
491   icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
492   if (!icon_theme)
493     {
494       GtkIconThemePrivate *priv;
495
496       icon_theme = gtk_icon_theme_new ();
497       gtk_icon_theme_set_screen (icon_theme, screen);
498
499       priv = icon_theme->priv;
500       priv->is_screen_singleton = TRUE;
501
502       g_object_set_data (G_OBJECT (screen), I_("gtk-icon-theme"), icon_theme);
503     }
504
505   return icon_theme;
506 }
507
508 static void
509 gtk_icon_theme_class_init (GtkIconThemeClass *klass)
510 {
511   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
512
513   gobject_class->finalize = gtk_icon_theme_finalize;
514
515 /**
516  * GtkIconTheme::changed:
517  * @icon_theme: the icon theme
518  * 
519  * Emitted when the current icon theme is switched or GTK+ detects
520  * that a change has occurred in the contents of the current
521  * icon theme.
522  **/
523   signal_changed = g_signal_new (I_("changed"),
524                                  G_TYPE_FROM_CLASS (klass),
525                                  G_SIGNAL_RUN_LAST,
526                                  G_STRUCT_OFFSET (GtkIconThemeClass, changed),
527                                  NULL, NULL,
528                                  g_cclosure_marshal_VOID__VOID,
529                                  G_TYPE_NONE, 0);
530
531   g_type_class_add_private (klass, sizeof (GtkIconThemePrivate));
532 }
533
534
535 /* Callback when the display that the icon theme is attached to
536  * is closed; unset the screen, and if it's the unique theme
537  * for the screen, drop the reference
538  */
539 static void
540 display_closed (GdkDisplay   *display,
541                 gboolean      is_error,
542                 GtkIconTheme *icon_theme)
543 {
544   GtkIconThemePrivate *priv = icon_theme->priv;
545   GdkScreen *screen = priv->screen;
546   gboolean was_screen_singleton = priv->is_screen_singleton;
547
548   if (was_screen_singleton)
549     {
550       g_object_set_data (G_OBJECT (screen), I_("gtk-icon-theme"), NULL);
551       priv->is_screen_singleton = FALSE;
552     }
553
554   gtk_icon_theme_set_screen (icon_theme, NULL);
555
556   if (was_screen_singleton)
557     {
558       g_object_unref (icon_theme);
559     }
560 }
561
562 static void
563 update_current_theme (GtkIconTheme *icon_theme)
564 {
565 #define theme_changed(_old, _new) \
566   ((_old && !_new) || (!_old && _new) || \
567    (_old && _new && strcmp (_old, _new) != 0))
568   GtkIconThemePrivate *priv = icon_theme->priv;
569
570   if (!priv->custom_theme)
571     {
572       gchar *theme = NULL;
573       gchar *fallback_theme = NULL;
574       gboolean changed = FALSE;
575
576       if (priv->screen)
577         {
578           GtkSettings *settings = gtk_settings_get_for_screen (priv->screen);
579           g_object_get (settings, 
580                         "gtk-icon-theme-name", &theme, 
581                         "gtk-fallback-icon-theme", &fallback_theme, NULL);
582         }
583
584       /* ensure that the current theme (even when just the default)
585        * is searched before any fallback theme
586        */
587       if (!theme && fallback_theme)
588         theme = g_strdup (DEFAULT_THEME_NAME);
589
590       if (theme_changed (priv->current_theme, theme))
591         {
592           g_free (priv->current_theme);
593           priv->current_theme = theme;
594           changed = TRUE;
595         }
596       else
597         g_free (theme);
598
599       if (theme_changed (priv->fallback_theme, fallback_theme))
600         {
601           g_free (priv->fallback_theme);
602           priv->fallback_theme = fallback_theme;
603           changed = TRUE;
604         }
605       else
606         g_free (fallback_theme);
607
608       if (changed)
609         do_theme_change (icon_theme);
610     }
611 #undef theme_changed
612 }
613
614 /* Callback when the icon theme GtkSetting changes
615  */
616 static void
617 theme_changed (GtkSettings  *settings,
618                GParamSpec   *pspec,
619                GtkIconTheme *icon_theme)
620 {
621   update_current_theme (icon_theme);
622 }
623
624 static void
625 unset_screen (GtkIconTheme *icon_theme)
626 {
627   GtkIconThemePrivate *priv = icon_theme->priv;
628   GtkSettings *settings;
629   GdkDisplay *display;
630   
631   if (priv->screen)
632     {
633       settings = gtk_settings_get_for_screen (priv->screen);
634       display = gdk_screen_get_display (priv->screen);
635       
636       g_signal_handlers_disconnect_by_func (display,
637                                             (gpointer) display_closed,
638                                             icon_theme);
639       g_signal_handlers_disconnect_by_func (settings,
640                                             (gpointer) theme_changed,
641                                             icon_theme);
642
643       priv->screen = NULL;
644     }
645 }
646
647 /**
648  * gtk_icon_theme_set_screen:
649  * @icon_theme: a #GtkIconTheme
650  * @screen: a #GdkScreen
651  * 
652  * Sets the screen for an icon theme; the screen is used
653  * to track the user's currently configured icon theme,
654  * which might be different for different screens.
655  *
656  * Since: 2.4
657  **/
658 void
659 gtk_icon_theme_set_screen (GtkIconTheme *icon_theme,
660                            GdkScreen    *screen)
661 {
662   GtkIconThemePrivate *priv;
663   GtkSettings *settings;
664   GdkDisplay *display;
665
666   g_return_if_fail (GTK_ICON_THEME (icon_theme));
667   g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
668
669   priv = icon_theme->priv;
670
671   unset_screen (icon_theme);
672   
673   if (screen)
674     {
675       display = gdk_screen_get_display (screen);
676       settings = gtk_settings_get_for_screen (screen);
677       
678       priv->screen = screen;
679       
680       g_signal_connect (display, "closed",
681                         G_CALLBACK (display_closed), icon_theme);
682       g_signal_connect (settings, "notify::gtk-icon-theme-name",
683                         G_CALLBACK (theme_changed), icon_theme);
684       g_signal_connect (settings, "notify::gtk-fallback-icon-theme-name",
685                         G_CALLBACK (theme_changed), icon_theme);
686     }
687
688   update_current_theme (icon_theme);
689 }
690
691 /* Checks whether a loader for SVG files has been registered
692  * with GdkPixbuf.
693  */
694 static gboolean
695 pixbuf_supports_svg (void)
696 {
697   GSList *formats;
698   GSList *tmp_list;
699   static gint found_svg = -1;
700
701   if (found_svg != -1)
702     return found_svg;
703
704   formats = gdk_pixbuf_get_formats ();
705
706   found_svg = FALSE; 
707   for (tmp_list = formats; tmp_list && !found_svg; tmp_list = tmp_list->next)
708     {
709       gchar **mime_types = gdk_pixbuf_format_get_mime_types (tmp_list->data);
710       gchar **mime_type;
711       
712       for (mime_type = mime_types; *mime_type && !found_svg; mime_type++)
713         {
714           if (strcmp (*mime_type, "image/svg") == 0)
715             found_svg = TRUE;
716         }
717
718       g_strfreev (mime_types);
719     }
720
721   g_slist_free (formats);
722   
723   return found_svg;
724 }
725
726 /* The icon info was removed from the icon_info_hash hash table */
727 static void
728 icon_info_uncached (GtkIconInfo *icon_info)
729 {
730   GtkIconTheme *icon_theme = icon_info->in_cache;
731
732   DEBUG_CACHE (("removing %p (%s %d 0x%x) from cache (icon_them: %p)  (cache size %d)\n",
733                 icon_info,
734                 g_strjoinv (",", icon_info->key.icon_names),
735                 icon_info->key.size, icon_info->key.flags,
736                 icon_theme,
737                 icon_theme != NULL ? g_hash_table_size (icon_theme->priv->info_cache) : 0));
738
739   icon_info->in_cache = NULL;
740
741   if (icon_theme != NULL)
742     remove_from_lru_cache (icon_theme, icon_info);
743 }
744
745 static void
746 gtk_icon_theme_init (GtkIconTheme *icon_theme)
747 {
748   GtkIconThemePrivate *priv;
749   const gchar * const *xdg_data_dirs;
750   int i, j;
751
752   priv = G_TYPE_INSTANCE_GET_PRIVATE (icon_theme,
753                                       GTK_TYPE_ICON_THEME,
754                                       GtkIconThemePrivate);
755   icon_theme->priv = priv;
756
757   priv->info_cache = g_hash_table_new_full (icon_info_key_hash, icon_info_key_equal, NULL,
758                                             (GDestroyNotify)icon_info_uncached);
759
760   priv->custom_theme = FALSE;
761
762   xdg_data_dirs = g_get_system_data_dirs ();
763   for (i = 0; xdg_data_dirs[i]; i++) ;
764
765   priv->search_path_len = 2 * i + 2;
766   
767   priv->search_path = g_new (char *, priv->search_path_len);
768   
769   i = 0;
770   priv->search_path[i++] = g_build_filename (g_get_user_data_dir (), "icons", NULL);
771   priv->search_path[i++] = g_build_filename (g_get_home_dir (), ".icons", NULL);
772   
773   for (j = 0; xdg_data_dirs[j]; j++) 
774     priv->search_path[i++] = g_build_filename (xdg_data_dirs[j], "icons", NULL);
775
776   for (j = 0; xdg_data_dirs[j]; j++) 
777     priv->search_path[i++] = g_build_filename (xdg_data_dirs[j], "pixmaps", NULL);
778
779   priv->themes_valid = FALSE;
780   priv->themes = NULL;
781   priv->unthemed_icons = NULL;
782   
783   priv->pixbuf_supports_svg = pixbuf_supports_svg ();
784 }
785
786 static void
787 free_dir_mtime (IconThemeDirMtime *dir_mtime)
788 {
789   if (dir_mtime->cache)
790     _gtk_icon_cache_unref (dir_mtime->cache);
791
792   g_free (dir_mtime->dir);
793   g_slice_free (IconThemeDirMtime, dir_mtime);
794
795 }
796
797 static gboolean
798 reset_styles_idle (gpointer user_data)
799 {
800   GtkIconTheme *icon_theme;
801   GtkIconThemePrivate *priv;
802
803   icon_theme = GTK_ICON_THEME (user_data);
804   priv = icon_theme->priv;
805
806   if (priv->screen && priv->is_screen_singleton)
807     gtk_style_context_reset_widgets (priv->screen);
808
809   priv->reset_styles_idle = 0;
810
811   return FALSE;
812 }
813
814 static void
815 do_theme_change (GtkIconTheme *icon_theme)
816 {
817   GtkIconThemePrivate *priv = icon_theme->priv;
818
819   g_hash_table_remove_all (priv->info_cache);
820
821   if (!priv->themes_valid)
822     return;
823   
824   GTK_NOTE (ICONTHEME, 
825             g_print ("change to icon theme \"%s\"\n", priv->current_theme));
826   blow_themes (icon_theme);
827   g_signal_emit (icon_theme, signal_changed, 0);
828
829   if (!priv->reset_styles_idle)
830     priv->reset_styles_idle = 
831       gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, 
832                        reset_styles_idle, icon_theme, NULL);
833 }
834
835 static void
836 blow_themes (GtkIconTheme *icon_theme)
837 {
838   GtkIconThemePrivate *priv = icon_theme->priv;
839   
840   if (priv->themes_valid)
841     {
842       g_hash_table_destroy (priv->all_icons);
843       g_list_free_full (priv->themes, (GDestroyNotify) theme_destroy);
844       g_list_free_full (priv->dir_mtimes, (GDestroyNotify) free_dir_mtime);
845       g_hash_table_destroy (priv->unthemed_icons);
846     }
847   priv->themes = NULL;
848   priv->unthemed_icons = NULL;
849   priv->dir_mtimes = NULL;
850   priv->all_icons = NULL;
851   priv->themes_valid = FALSE;
852 }
853
854 static void
855 gtk_icon_theme_finalize (GObject *object)
856 {
857   GtkIconTheme *icon_theme;
858   GtkIconThemePrivate *priv;
859   int i;
860
861   icon_theme = GTK_ICON_THEME (object);
862   priv = icon_theme->priv;
863
864   g_hash_table_destroy (priv->info_cache);
865   g_assert (priv->info_cache_lru == NULL);
866
867   if (priv->reset_styles_idle)
868     {
869       g_source_remove (priv->reset_styles_idle);
870       priv->reset_styles_idle = 0;
871     }
872
873   unset_screen (icon_theme);
874
875   g_free (priv->current_theme);
876   priv->current_theme = NULL;
877
878   for (i = 0; i < priv->search_path_len; i++)
879     g_free (priv->search_path[i]);
880
881   g_free (priv->search_path);
882   priv->search_path = NULL;
883
884   blow_themes (icon_theme);
885
886   G_OBJECT_CLASS (gtk_icon_theme_parent_class)->finalize (object);  
887 }
888
889 /**
890  * gtk_icon_theme_set_search_path:
891  * @icon_theme: a #GtkIconTheme
892  * @path: (array length=n_elements) (element-type filename): array of
893  *     directories that are searched for icon themes
894  * @n_elements: number of elements in @path.
895  * 
896  * Sets the search path for the icon theme object. When looking
897  * for an icon theme, GTK+ will search for a subdirectory of
898  * one or more of the directories in @path with the same name
899  * as the icon theme. (Themes from multiple of the path elements
900  * are combined to allow themes to be extended by adding icons
901  * in the user's home directory.)
902  *
903  * In addition if an icon found isn't found either in the current
904  * icon theme or the default icon theme, and an image file with
905  * the right name is found directly in one of the elements of
906  * @path, then that image will be used for the icon name.
907  * (This is legacy feature, and new icons should be put
908  * into the default icon theme, which is called DEFAULT_THEME_NAME,
909  * rather than directly on the icon path.)
910  *
911  * Since: 2.4
912  **/
913 void
914 gtk_icon_theme_set_search_path (GtkIconTheme *icon_theme,
915                                 const gchar  *path[],
916                                 gint          n_elements)
917 {
918   GtkIconThemePrivate *priv;
919   gint i;
920
921   g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
922
923   priv = icon_theme->priv;
924   for (i = 0; i < priv->search_path_len; i++)
925     g_free (priv->search_path[i]);
926
927   g_free (priv->search_path);
928
929   priv->search_path = g_new (gchar *, n_elements);
930   priv->search_path_len = n_elements;
931
932   for (i = 0; i < priv->search_path_len; i++)
933     priv->search_path[i] = g_strdup (path[i]);
934
935   do_theme_change (icon_theme);
936 }
937
938
939 /**
940  * gtk_icon_theme_get_search_path:
941  * @icon_theme: a #GtkIconTheme
942  * @path: (allow-none) (array length=n_elements) (element-type filename) (out):
943  *     location to store a list of icon theme path directories or %NULL.
944  *     The stored value should be freed with g_strfreev().
945  * @n_elements: location to store number of elements in @path, or %NULL
946  *
947  * Gets the current search path. See gtk_icon_theme_set_search_path().
948  *
949  * Since: 2.4
950  */
951 void
952 gtk_icon_theme_get_search_path (GtkIconTheme  *icon_theme,
953                                 gchar        **path[],
954                                 gint          *n_elements)
955 {
956   GtkIconThemePrivate *priv;
957   int i;
958
959   g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
960
961   priv = icon_theme->priv;
962
963   if (n_elements)
964     *n_elements = priv->search_path_len;
965   
966   if (path)
967     {
968       *path = g_new (gchar *, priv->search_path_len + 1);
969       for (i = 0; i < priv->search_path_len; i++)
970         (*path)[i] = g_strdup (priv->search_path[i]);
971       (*path)[i] = NULL;
972     }
973 }
974
975 /**
976  * gtk_icon_theme_append_search_path:
977  * @icon_theme: a #GtkIconTheme
978  * @path: (type filename): directory name to append to the icon path
979  * 
980  * Appends a directory to the search path. 
981  * See gtk_icon_theme_set_search_path(). 
982  *
983  * Since: 2.4
984  **/
985 void
986 gtk_icon_theme_append_search_path (GtkIconTheme *icon_theme,
987                                    const gchar  *path)
988 {
989   GtkIconThemePrivate *priv;
990
991   g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
992   g_return_if_fail (path != NULL);
993
994   priv = icon_theme->priv;
995   
996   priv->search_path_len++;
997
998   priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
999   priv->search_path[priv->search_path_len-1] = g_strdup (path);
1000
1001   do_theme_change (icon_theme);
1002 }
1003
1004 /**
1005  * gtk_icon_theme_prepend_search_path:
1006  * @icon_theme: a #GtkIconTheme
1007  * @path: (type filename): directory name to prepend to the icon path
1008  * 
1009  * Prepends a directory to the search path. 
1010  * See gtk_icon_theme_set_search_path().
1011  *
1012  * Since: 2.4
1013  **/
1014 void
1015 gtk_icon_theme_prepend_search_path (GtkIconTheme *icon_theme,
1016                                     const gchar  *path)
1017 {
1018   GtkIconThemePrivate *priv;
1019   int i;
1020
1021   g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
1022   g_return_if_fail (path != NULL);
1023
1024   priv = icon_theme->priv;
1025   
1026   priv->search_path_len++;
1027   priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
1028
1029   for (i = priv->search_path_len - 1; i > 0; i--)
1030     priv->search_path[i] = priv->search_path[i - 1];
1031   
1032   priv->search_path[0] = g_strdup (path);
1033
1034   do_theme_change (icon_theme);
1035 }
1036
1037 /**
1038  * gtk_icon_theme_set_custom_theme:
1039  * @icon_theme: a #GtkIconTheme
1040  * @theme_name: (allow-none): name of icon theme to use instead of
1041  *   configured theme, or %NULL to unset a previously set custom theme
1042  * 
1043  * Sets the name of the icon theme that the #GtkIconTheme object uses
1044  * overriding system configuration. This function cannot be called
1045  * on the icon theme objects returned from gtk_icon_theme_get_default()
1046  * and gtk_icon_theme_get_for_screen().
1047  *
1048  * Since: 2.4
1049  **/
1050 void
1051 gtk_icon_theme_set_custom_theme (GtkIconTheme *icon_theme,
1052                                  const gchar  *theme_name)
1053 {
1054   GtkIconThemePrivate *priv;
1055
1056   g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
1057
1058   priv = icon_theme->priv;
1059
1060   g_return_if_fail (!priv->is_screen_singleton);
1061   
1062   if (theme_name != NULL)
1063     {
1064       priv->custom_theme = TRUE;
1065       if (!priv->current_theme || strcmp (theme_name, priv->current_theme) != 0)
1066         {
1067           g_free (priv->current_theme);
1068           priv->current_theme = g_strdup (theme_name);
1069
1070           do_theme_change (icon_theme);
1071         }
1072     }
1073   else
1074     {
1075       if (priv->custom_theme)
1076         {
1077           priv->custom_theme = FALSE;
1078
1079           update_current_theme (icon_theme);
1080         }
1081     }
1082 }
1083
1084 static void
1085 insert_theme (GtkIconTheme *icon_theme, const char *theme_name)
1086 {
1087   int i;
1088   GList *l;
1089   char **dirs;
1090   char **themes;
1091   GtkIconThemePrivate *priv;
1092   IconTheme *theme = NULL;
1093   char *path;
1094   GKeyFile *theme_file;
1095   GError *error = NULL;
1096   IconThemeDirMtime *dir_mtime;
1097   GStatBuf stat_buf;
1098   
1099   priv = icon_theme->priv;
1100
1101   for (l = priv->themes; l != NULL; l = l->next)
1102     {
1103       theme = l->data;
1104       if (strcmp (theme->name, theme_name) == 0)
1105         return;
1106     }
1107   
1108   for (i = 0; i < priv->search_path_len; i++)
1109     {
1110       path = g_build_filename (priv->search_path[i],
1111                                theme_name,
1112                                NULL);
1113       dir_mtime = g_slice_new (IconThemeDirMtime);
1114       dir_mtime->cache = NULL;
1115       dir_mtime->dir = path;
1116       if (g_stat (path, &stat_buf) == 0 && S_ISDIR (stat_buf.st_mode))
1117         dir_mtime->mtime = stat_buf.st_mtime;
1118       else
1119         dir_mtime->mtime = 0;
1120
1121       priv->dir_mtimes = g_list_prepend (priv->dir_mtimes, dir_mtime);
1122     }
1123   priv->dir_mtimes = g_list_reverse (priv->dir_mtimes);
1124
1125   theme_file = NULL;
1126   for (i = 0; i < priv->search_path_len && !theme_file; i++)
1127     {
1128       path = g_build_filename (priv->search_path[i],
1129                                theme_name,
1130                                "index.theme",
1131                                NULL);
1132       if (g_file_test (path, G_FILE_TEST_IS_REGULAR)) 
1133         {
1134           theme_file = g_key_file_new ();
1135           g_key_file_set_list_separator (theme_file, ',');
1136           g_key_file_load_from_file (theme_file, path, 0, &error);
1137           if (error)
1138             {
1139               g_key_file_free (theme_file);
1140               theme_file = NULL;
1141               g_error_free (error);
1142               error = NULL;
1143             }
1144         }
1145       g_free (path);
1146     }
1147
1148   if (theme_file || strcmp (theme_name, DEFAULT_THEME_NAME) == 0)
1149     {
1150       theme = g_new0 (IconTheme, 1);
1151       theme->name = g_strdup (theme_name);
1152       priv->themes = g_list_prepend (priv->themes, theme);
1153     }
1154
1155   if (theme_file == NULL)
1156     return;
1157
1158   theme->display_name = 
1159     g_key_file_get_locale_string (theme_file, "Icon Theme", "Name", NULL, NULL);
1160   if (!theme->display_name)
1161     g_warning ("Theme file for %s has no name\n", theme_name);
1162
1163   dirs = g_key_file_get_string_list (theme_file, "Icon Theme", "Directories", NULL, NULL);
1164   if (!dirs)
1165     {
1166       g_warning ("Theme file for %s has no directories\n", theme_name);
1167       priv->themes = g_list_remove (priv->themes, theme);
1168       g_free (theme->name);
1169       g_free (theme->display_name);
1170       g_free (theme);
1171       g_key_file_free (theme_file);
1172       return;
1173     }
1174   
1175   theme->comment = 
1176     g_key_file_get_locale_string (theme_file, 
1177                                   "Icon Theme", "Comment",
1178                                   NULL, NULL);
1179   theme->example = 
1180     g_key_file_get_string (theme_file, 
1181                            "Icon Theme", "Example",
1182                            NULL);
1183
1184   theme->dirs = NULL;
1185   for (i = 0; dirs[i] != NULL; i++)
1186     theme_subdir_load (icon_theme, theme, theme_file, dirs[i]);
1187
1188   g_strfreev (dirs);
1189
1190   theme->dirs = g_list_reverse (theme->dirs);
1191
1192   themes = g_key_file_get_string_list (theme_file,
1193                                        "Icon Theme",
1194                                        "Inherits",
1195                                        NULL,
1196                                        NULL);
1197   if (themes)
1198     {
1199       for (i = 0; themes[i] != NULL; i++)
1200         insert_theme (icon_theme, themes[i]);
1201       
1202       g_strfreev (themes);
1203     }
1204
1205   g_key_file_free (theme_file);
1206 }
1207
1208 static void
1209 free_unthemed_icon (UnthemedIcon *unthemed_icon)
1210 {
1211   g_free (unthemed_icon->svg_filename);
1212   g_free (unthemed_icon->no_svg_filename);
1213   g_slice_free (UnthemedIcon, unthemed_icon);
1214 }
1215
1216 static char *
1217 strip_suffix (const char *filename)
1218 {
1219   const char *dot;
1220
1221   dot = strrchr (filename, '.');
1222
1223   if (dot == NULL)
1224     return g_strdup (filename);
1225
1226   return g_strndup (filename, dot - filename);
1227 }
1228
1229 static void
1230 load_themes (GtkIconTheme *icon_theme)
1231 {
1232   GtkIconThemePrivate *priv;
1233   GDir *gdir;
1234   int base;
1235   char *dir;
1236   const char *file;
1237   UnthemedIcon *unthemed_icon;
1238   IconSuffix old_suffix, new_suffix;
1239   GTimeVal tv;
1240   IconThemeDirMtime *dir_mtime;
1241   GStatBuf stat_buf;
1242   
1243   priv = icon_theme->priv;
1244
1245   priv->all_icons = g_hash_table_new (g_str_hash, g_str_equal);
1246   
1247   if (priv->current_theme)
1248     insert_theme (icon_theme, priv->current_theme);
1249
1250   /* Always look in the "default" icon theme, and in a fallback theme */
1251   if (priv->fallback_theme)
1252     insert_theme (icon_theme, priv->fallback_theme);
1253   insert_theme (icon_theme, DEFAULT_THEME_NAME);
1254   priv->themes = g_list_reverse (priv->themes);
1255
1256
1257   priv->unthemed_icons = g_hash_table_new_full (g_str_hash, g_str_equal,
1258                                                 g_free, (GDestroyNotify)free_unthemed_icon);
1259
1260   for (base = 0; base < icon_theme->priv->search_path_len; base++)
1261     {
1262       dir = icon_theme->priv->search_path[base];
1263
1264       dir_mtime = g_slice_new (IconThemeDirMtime);
1265       priv->dir_mtimes = g_list_append (priv->dir_mtimes, dir_mtime);
1266       
1267       dir_mtime->dir = g_strdup (dir);
1268       dir_mtime->mtime = 0;
1269       dir_mtime->cache = NULL;
1270
1271       if (g_stat (dir, &stat_buf) != 0 || !S_ISDIR (stat_buf.st_mode))
1272         continue;
1273       dir_mtime->mtime = stat_buf.st_mtime;
1274
1275       dir_mtime->cache = _gtk_icon_cache_new_for_path (dir);
1276       if (dir_mtime->cache != NULL)
1277         continue;
1278
1279       gdir = g_dir_open (dir, 0, NULL);
1280       if (gdir == NULL)
1281         continue;
1282
1283       while ((file = g_dir_read_name (gdir)))
1284         {
1285           new_suffix = suffix_from_name (file);
1286           
1287           if (new_suffix != ICON_SUFFIX_NONE)
1288             {
1289               char *abs_file;
1290               char *base_name;
1291
1292               abs_file = g_build_filename (dir, file, NULL);
1293               base_name = strip_suffix (file);
1294
1295               if ((unthemed_icon = g_hash_table_lookup (priv->unthemed_icons,
1296                                                         base_name)))
1297                 {
1298                   if (new_suffix == ICON_SUFFIX_SVG)
1299                     {
1300                       if (unthemed_icon->svg_filename)
1301                         g_free (abs_file);
1302                       else
1303                         unthemed_icon->svg_filename = abs_file;
1304                     }
1305                   else
1306                     {
1307                       if (unthemed_icon->no_svg_filename)
1308                         {
1309                           old_suffix = suffix_from_name (unthemed_icon->no_svg_filename);
1310                           if (new_suffix > old_suffix)
1311                             {
1312                               g_free (unthemed_icon->no_svg_filename);
1313                               unthemed_icon->no_svg_filename = abs_file;                              
1314                             }
1315                           else
1316                             g_free (abs_file);
1317                         }
1318                       else
1319                         unthemed_icon->no_svg_filename = abs_file;                            
1320                     }
1321
1322                   g_free (base_name);
1323                 }
1324               else
1325                 {
1326                   unthemed_icon = g_slice_new0 (UnthemedIcon);
1327                   
1328                   if (new_suffix == ICON_SUFFIX_SVG)
1329                     unthemed_icon->svg_filename = abs_file;
1330                   else
1331                     unthemed_icon->no_svg_filename = abs_file;
1332
1333                   /* takes ownership of base_name */
1334                   g_hash_table_replace (priv->unthemed_icons,
1335                                         base_name,
1336                                         unthemed_icon);
1337                   g_hash_table_insert (priv->all_icons,
1338                                        base_name, NULL);
1339                 }
1340             }
1341         }
1342       g_dir_close (gdir);
1343     }
1344
1345   priv->themes_valid = TRUE;
1346   
1347   g_get_current_time(&tv);
1348   priv->last_stat_time = tv.tv_sec;
1349 }
1350
1351 void
1352 _gtk_icon_theme_ensure_builtin_cache (void)
1353 {
1354   static gboolean initialized = FALSE;
1355   IconThemeDir *dir;
1356   static IconThemeDir dirs[5] = 
1357     {
1358       { ICON_THEME_DIR_THRESHOLD, 0, 16, 16, 16, 2, NULL, "16", -1, NULL, NULL, NULL },
1359       { ICON_THEME_DIR_THRESHOLD, 0, 20, 20, 20, 2, NULL, "20", -1,  NULL, NULL, NULL },
1360       { ICON_THEME_DIR_THRESHOLD, 0, 24, 24, 24, 2, NULL, "24", -1, NULL, NULL, NULL },
1361       { ICON_THEME_DIR_THRESHOLD, 0, 32, 32, 32, 2, NULL, "32", -1, NULL, NULL, NULL },
1362       { ICON_THEME_DIR_THRESHOLD, 0, 48, 48, 48, 2, NULL, "48", -1, NULL, NULL, NULL }
1363     };
1364   gint i;
1365
1366   if (!initialized)
1367     {
1368       initialized = TRUE;
1369
1370       _builtin_cache = _gtk_icon_cache_new ((gchar *)builtin_icons);
1371
1372       for (i = 0; i < G_N_ELEMENTS (dirs); i++)
1373         {
1374           dir = &(dirs[i]);
1375           dir->cache = _gtk_icon_cache_ref (_builtin_cache);
1376           dir->subdir_index = _gtk_icon_cache_get_directory_index (dir->cache, dir->subdir);
1377
1378           builtin_dirs = g_list_append (builtin_dirs, dir);
1379         }
1380     }
1381 }
1382
1383 static void
1384 ensure_valid_themes (GtkIconTheme *icon_theme)
1385 {
1386   GtkIconThemePrivate *priv = icon_theme->priv;
1387   GTimeVal tv;
1388   gboolean was_valid = priv->themes_valid;
1389
1390   if (priv->loading_themes)
1391     return;
1392   priv->loading_themes = TRUE;
1393
1394   _gtk_icon_theme_ensure_builtin_cache ();
1395
1396   if (priv->themes_valid)
1397     {
1398       g_get_current_time (&tv);
1399
1400       if (ABS (tv.tv_sec - priv->last_stat_time) > 5 &&
1401           rescan_themes (icon_theme))
1402         blow_themes (icon_theme);
1403     }
1404   
1405   if (!priv->themes_valid)
1406     {
1407       load_themes (icon_theme);
1408
1409       if (was_valid)
1410         {
1411           g_signal_emit (icon_theme, signal_changed, 0);
1412         }
1413     }
1414
1415   priv->loading_themes = FALSE;
1416 }
1417
1418 /* The LRU cache is a short list of IconInfos that are kept
1419    alive even though their IconInfo would otherwise have
1420    been freed, so that we can avoid reloading these
1421    constantly.
1422    We put infos on the lru list when nothing otherwise
1423    references the info. So, when we get a cache hit
1424    we remove it from the list, and when the proxy
1425    pixmap is released we put it on the list.
1426 */
1427
1428 static void
1429 ensure_lru_cache_space (GtkIconTheme *icon_theme)
1430 {
1431   GtkIconThemePrivate *priv = icon_theme->priv;
1432   GList *l;
1433
1434   /* Remove last item if LRU full */
1435   l = g_list_nth (priv->info_cache_lru, INFO_CACHE_LRU_SIZE - 1);
1436   if (l)
1437     {
1438       GtkIconInfo *icon_info = l->data;
1439
1440       DEBUG_CACHE (("removing (due to out of space) %p (%s %d 0x%x) from LRU cache (cache size %d)\n",
1441                     icon_info,
1442                     g_strjoinv (",", icon_info->key.icon_names),
1443                     icon_info->key.size, icon_info->key.flags,
1444                     g_list_length (priv->info_cache_lru)));
1445
1446       priv->info_cache_lru = g_list_delete_link (priv->info_cache_lru, l);
1447       gtk_icon_info_free (icon_info);
1448     }
1449 }
1450
1451 static void
1452 add_to_lru_cache (GtkIconTheme *icon_theme,
1453                   GtkIconInfo *icon_info)
1454 {
1455   GtkIconThemePrivate *priv = icon_theme->priv;
1456
1457   DEBUG_CACHE (("adding  %p (%s %d 0x%x) to LRU cache (cache size %d)\n",
1458                 icon_info,
1459                 g_strjoinv (",", icon_info->key.icon_names),
1460                 icon_info->key.size, icon_info->key.flags,
1461                 g_list_length (priv->info_cache_lru)));
1462
1463   g_assert (g_list_find (priv->info_cache_lru, icon_info) == NULL);
1464
1465   ensure_lru_cache_space (icon_theme);
1466   /* prepend new info to LRU */
1467   priv->info_cache_lru = g_list_prepend (priv->info_cache_lru,
1468                                          gtk_icon_info_copy (icon_info));
1469 }
1470
1471 static void
1472 ensure_in_lru_cache (GtkIconTheme *icon_theme,
1473                      GtkIconInfo *icon_info)
1474 {
1475   GtkIconThemePrivate *priv = icon_theme->priv;
1476   GList *l;
1477
1478   l = g_list_find (priv->info_cache_lru, icon_info);
1479   if (l)
1480     {
1481       /* Move to front of LRU if already in it */
1482       priv->info_cache_lru = g_list_remove_link (priv->info_cache_lru, l);
1483       priv->info_cache_lru = g_list_concat (l, priv->info_cache_lru);
1484     }
1485   else
1486     add_to_lru_cache (icon_theme, icon_info);
1487 }
1488
1489 static void
1490 remove_from_lru_cache (GtkIconTheme *icon_theme,
1491                        GtkIconInfo *icon_info)
1492 {
1493   GtkIconThemePrivate *priv = icon_theme->priv;
1494   if (g_list_find (priv->info_cache_lru, icon_info))
1495     {
1496       DEBUG_CACHE (("removing %p (%s %d 0x%x) from LRU cache (cache size %d)\n",
1497                     icon_info,
1498                     g_strjoinv (",", icon_info->key.icon_names),
1499                     icon_info->key.size, icon_info->key.flags,
1500                     g_list_length (priv->info_cache_lru)));
1501
1502       priv->info_cache_lru = g_list_remove (priv->info_cache_lru, icon_info);
1503       gtk_icon_info_free (icon_info);
1504     }
1505 }
1506
1507 static SymbolicPixbufCache *
1508 symbolic_pixbuf_cache_new (GdkPixbuf *pixbuf,
1509                            const GdkRGBA  *fg,
1510                            const GdkRGBA  *success_color,
1511                            const GdkRGBA  *warning_color,
1512                            const GdkRGBA  *error_color,
1513                            SymbolicPixbufCache *next)
1514 {
1515   SymbolicPixbufCache *cache;
1516
1517   cache = g_new0 (SymbolicPixbufCache, 1);
1518   cache->pixbuf = g_object_ref (pixbuf);
1519   if (fg)
1520     cache->fg = *fg;
1521   if (success_color)
1522     cache->success_color = *success_color;
1523   if (warning_color)
1524     cache->warning_color = *warning_color;
1525   if (error_color)
1526     cache->error_color = *error_color;
1527   cache->next = next;
1528   return cache;
1529 }
1530
1531 static gboolean
1532 rgba_matches (const GdkRGBA  *a, const GdkRGBA  *b)
1533 {
1534   GdkRGBA transparent = { 0 };
1535
1536   /* For matching we treat unset colors as transparent rather
1537      than default, which works as well, because transparent
1538      will never be used for real symbolic icon colors */
1539   if (a == NULL)
1540     a = &transparent;
1541
1542   return
1543     fabs(a->red - b->red) < 0.0001 &&
1544     fabs(a->green - b->green) < 0.0001 &&
1545     fabs(a->blue - b->blue) < 0.0001 &&
1546     fabs(a->alpha - b->alpha) < 0.0001;
1547 }
1548
1549 static SymbolicPixbufCache *
1550 symbolic_pixbuf_cache_matches (SymbolicPixbufCache *cache,
1551                                const GdkRGBA  *fg,
1552                                const GdkRGBA  *success_color,
1553                                const GdkRGBA  *warning_color,
1554                                const GdkRGBA  *error_color)
1555 {
1556   while (cache != NULL)
1557     {
1558       if (rgba_matches (fg, &cache->fg) &&
1559           rgba_matches (success_color, &cache->success_color) &&
1560           rgba_matches (warning_color, &cache->warning_color) &&
1561           rgba_matches (error_color, &cache->error_color))
1562         return cache;
1563
1564       cache = cache->next;
1565     }
1566
1567   return NULL;
1568 }
1569
1570 static void
1571 symbolic_pixbuf_cache_free (SymbolicPixbufCache *cache)
1572 {
1573   SymbolicPixbufCache *next;
1574
1575   while (cache != NULL)
1576     {
1577       next = cache->next;
1578       g_object_unref (cache->pixbuf);
1579       g_free (cache);
1580
1581       cache = next;
1582     }
1583 }
1584
1585 static GtkIconInfo *
1586 choose_icon (GtkIconTheme       *icon_theme,
1587              const gchar        *icon_names[],
1588              gint                size,
1589              GtkIconLookupFlags  flags)
1590 {
1591   GtkIconThemePrivate *priv;
1592   GList *l;
1593   GtkIconInfo *icon_info = NULL;
1594   UnthemedIcon *unthemed_icon = NULL;
1595   gboolean allow_svg;
1596   gboolean use_builtin;
1597   gint i;
1598   IconInfoKey key;
1599
1600   priv = icon_theme->priv;
1601
1602   ensure_valid_themes (icon_theme);
1603
1604   key.icon_names = (char **)icon_names;
1605   key.size = size;
1606   key.flags = flags;
1607
1608   icon_info = g_hash_table_lookup (priv->info_cache, &key);
1609   if (icon_info != NULL)
1610     {
1611       DEBUG_CACHE (("cache hit %p (%s %d 0x%x) (cache size %d)\n",
1612                     icon_info,
1613                     g_strjoinv (",", icon_info->key.icon_names),
1614                     icon_info->key.size, icon_info->key.flags,
1615                     g_hash_table_size (priv->info_cache)));
1616
1617       icon_info = gtk_icon_info_copy (icon_info);
1618       remove_from_lru_cache (icon_theme, icon_info);
1619
1620       return icon_info;
1621     }
1622
1623   if (flags & GTK_ICON_LOOKUP_NO_SVG)
1624     allow_svg = FALSE;
1625   else if (flags & GTK_ICON_LOOKUP_FORCE_SVG)
1626     allow_svg = TRUE;
1627   else
1628     allow_svg = priv->pixbuf_supports_svg;
1629
1630   use_builtin = flags & GTK_ICON_LOOKUP_USE_BUILTIN;
1631
1632   /* for symbolic icons, do a search in all registered themes first;
1633    * a theme that inherits them from a parent theme might provide
1634    * an alternative highcolor version, but still expect the symbolic icon
1635    * to show up instead.
1636    */
1637   if (icon_names[0] &&
1638       g_str_has_suffix (icon_names[0], "-symbolic"))
1639     {
1640       for (l = priv->themes; l; l = l->next)
1641         {
1642           IconTheme *theme = l->data;
1643           icon_info = theme_lookup_icon (theme, icon_names[0], size, allow_svg, use_builtin);
1644           if (icon_info)
1645             goto out;
1646         }
1647     }
1648
1649   for (l = priv->themes; l; l = l->next)
1650     {
1651       IconTheme *theme = l->data;
1652       
1653       for (i = 0; icon_names[i]; i++)
1654         {
1655           icon_info = theme_lookup_icon (theme, icon_names[i], size, allow_svg, use_builtin);
1656           if (icon_info)
1657             goto out;
1658         }
1659     }
1660
1661   for (i = 0; icon_names[i]; i++)
1662     {
1663       unthemed_icon = g_hash_table_lookup (priv->unthemed_icons, icon_names[i]);
1664       if (unthemed_icon)
1665         break;
1666     }
1667 #ifdef G_OS_WIN32
1668   /* Still not found an icon, check if reference to a Win32 resource */
1669   if (!unthemed_icon)
1670     {
1671       gchar **resources;
1672       HICON hIcon = NULL;
1673       
1674       resources = g_strsplit (icon_names[0], ",", 0);
1675       if (resources[0])
1676         {
1677           wchar_t *wfile = g_utf8_to_utf16 (resources[0], -1, NULL, NULL, NULL);
1678           ExtractIconExW (wfile, resources[1] ? atoi (resources[1]) : 0, &hIcon, NULL, 1);
1679           g_free (wfile);
1680         }
1681       
1682       if (hIcon)
1683         {
1684           icon_info = icon_info_new ();
1685           icon_info->cache_pixbuf = gdk_win32_icon_to_pixbuf_libgtk_only (hIcon);
1686           DestroyIcon (hIcon);
1687           icon_info->dir_type = ICON_THEME_DIR_UNTHEMED;
1688           icon_info->dir_size = size;
1689         }
1690       g_strfreev (resources);
1691     }
1692 #endif
1693
1694   if (unthemed_icon)
1695     {
1696       icon_info = icon_info_new ();
1697
1698       /* A SVG icon, when allowed, beats out a XPM icon, but not
1699        * a PNG icon
1700        */
1701       if (allow_svg &&
1702           unthemed_icon->svg_filename &&
1703           (!unthemed_icon->no_svg_filename ||
1704            suffix_from_name (unthemed_icon->no_svg_filename) != ICON_SUFFIX_PNG))
1705         icon_info->filename = g_strdup (unthemed_icon->svg_filename);
1706       else if (unthemed_icon->no_svg_filename)
1707         icon_info->filename = g_strdup (unthemed_icon->no_svg_filename);
1708
1709       icon_info->icon_file = g_file_new_for_path (icon_info->filename);
1710
1711       icon_info->dir_type = ICON_THEME_DIR_UNTHEMED;
1712       icon_info->dir_size = size;
1713     }
1714
1715  out:
1716   if (icon_info)
1717     {
1718       icon_info->desired_size = size;
1719       icon_info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
1720
1721       icon_info->key.icon_names = g_strdupv ((char **)icon_names);
1722       icon_info->key.size = size;
1723       icon_info->key.flags = flags;
1724       icon_info->in_cache = icon_theme;
1725       DEBUG_CACHE (("adding %p (%s %d 0x%x) to cache (cache size %d)\n",
1726                     icon_info,
1727                     g_strjoinv (",", icon_info->key.icon_names),
1728                     icon_info->key.size, icon_info->key.flags,
1729                     g_hash_table_size (priv->info_cache)));
1730      g_hash_table_insert (priv->info_cache, &icon_info->key, icon_info);
1731     }
1732   else
1733     {
1734       static gboolean check_for_default_theme = TRUE;
1735       char *default_theme_path;
1736       gboolean found = FALSE;
1737       unsigned i;
1738
1739       if (check_for_default_theme)
1740         {
1741           check_for_default_theme = FALSE;
1742
1743           for (i = 0; !found && i < priv->search_path_len; i++)
1744             {
1745               default_theme_path = g_build_filename (priv->search_path[i],
1746                                                      DEFAULT_THEME_NAME,
1747                                                      "index.theme",
1748                                                      NULL);
1749               found = g_file_test (default_theme_path, G_FILE_TEST_IS_REGULAR);
1750               g_free (default_theme_path);
1751             }
1752
1753           if (!found)
1754             {
1755               g_warning ("Could not find the icon '%s'. The '%s' theme\n"
1756                          "was not found either, perhaps you need to install it.\n"
1757                          "You can get a copy from:\n"
1758                          "\t%s",
1759                          icon_names[0], DEFAULT_THEME_NAME, "http://icon-theme.freedesktop.org/releases");
1760             }
1761         }
1762     }
1763
1764   return icon_info;
1765 }
1766
1767
1768 /**
1769  * gtk_icon_theme_lookup_icon:
1770  * @icon_theme: a #GtkIconTheme
1771  * @icon_name: the name of the icon to lookup
1772  * @size: desired icon size
1773  * @flags: flags modifying the behavior of the icon lookup
1774  * 
1775  * Looks up a named icon and returns a structure containing
1776  * information such as the filename of the icon. The icon
1777  * can then be rendered into a pixbuf using
1778  * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
1779  * combines these two steps if all you need is the pixbuf.)
1780  * 
1781  * Return value: a #GtkIconInfo structure containing information
1782  * about the icon, or %NULL if the icon wasn't found. Free with
1783  * gtk_icon_info_free()
1784  *
1785  * Since: 2.4
1786  */
1787 GtkIconInfo *
1788 gtk_icon_theme_lookup_icon (GtkIconTheme       *icon_theme,
1789                             const gchar        *icon_name,
1790                             gint                size,
1791                             GtkIconLookupFlags  flags)
1792 {
1793   GtkIconInfo *info;
1794
1795   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1796   g_return_val_if_fail (icon_name != NULL, NULL);
1797   g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1798                         (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1799
1800   GTK_NOTE (ICONTHEME, 
1801             g_print ("gtk_icon_theme_lookup_icon %s\n", icon_name));
1802
1803   if (flags & GTK_ICON_LOOKUP_GENERIC_FALLBACK)
1804     {
1805       gchar **names;
1806       gint dashes, i;
1807       gchar *p;
1808  
1809       dashes = 0;
1810       for (p = (gchar *) icon_name; *p; p++)
1811         if (*p == '-')
1812           dashes++;
1813
1814       names = g_new (gchar *, dashes + 2);
1815       names[0] = g_strdup (icon_name);
1816       for (i = 1; i <= dashes; i++)
1817         {
1818           names[i] = g_strdup (names[i - 1]);
1819           p = strrchr (names[i], '-');
1820           *p = '\0';
1821         }
1822       names[dashes + 1] = NULL;
1823    
1824       info = choose_icon (icon_theme, (const gchar **) names, size, flags);
1825       
1826       g_strfreev (names);
1827     }
1828   else 
1829     {
1830       const gchar *names[2];
1831       
1832       names[0] = icon_name;
1833       names[1] = NULL;
1834
1835       info = choose_icon (icon_theme, names, size, flags);
1836     }
1837
1838   return info;
1839 }
1840
1841 /**
1842  * gtk_icon_theme_choose_icon:
1843  * @icon_theme: a #GtkIconTheme
1844  * @icon_names: (array zero-terminated=1): %NULL-terminated array of
1845  *     icon names to lookup
1846  * @size: desired icon size
1847  * @flags: flags modifying the behavior of the icon lookup
1848  * 
1849  * Looks up a named icon and returns a structure containing
1850  * information such as the filename of the icon. The icon
1851  * can then be rendered into a pixbuf using
1852  * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
1853  * combines these two steps if all you need is the pixbuf.)
1854  *
1855  * If @icon_names contains more than one name, this function 
1856  * tries them all in the given order before falling back to 
1857  * inherited icon themes.
1858  * 
1859  * Return value: a #GtkIconInfo structure containing information
1860  * about the icon, or %NULL if the icon wasn't found. Free with
1861  * gtk_icon_info_free()
1862  *
1863  * Since: 2.12
1864  */
1865 GtkIconInfo *
1866 gtk_icon_theme_choose_icon (GtkIconTheme       *icon_theme,
1867                             const gchar        *icon_names[],
1868                             gint                size,
1869                             GtkIconLookupFlags  flags)
1870 {
1871   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1872   g_return_val_if_fail (icon_names != NULL, NULL);
1873   g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1874                         (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1875
1876   return choose_icon (icon_theme, icon_names, size, flags);
1877 }
1878
1879 /* Error quark */
1880 GQuark
1881 gtk_icon_theme_error_quark (void)
1882 {
1883   return g_quark_from_static_string ("gtk-icon-theme-error-quark");
1884 }
1885
1886 /**
1887  * gtk_icon_theme_load_icon:
1888  * @icon_theme: a #GtkIconTheme
1889  * @icon_name: the name of the icon to lookup
1890  * @size: the desired icon size. The resulting icon may not be
1891  *     exactly this size; see gtk_icon_info_load_icon().
1892  * @flags: flags modifying the behavior of the icon lookup
1893  * @error: (allow-none): Location to store error information on failure,
1894  *     or %NULL.
1895  *
1896  * Looks up an icon in an icon theme, scales it to the given size
1897  * and renders it into a pixbuf. This is a convenience function;
1898  * if more details about the icon are needed, use
1899  * gtk_icon_theme_lookup_icon() followed by gtk_icon_info_load_icon().
1900  *
1901  * Note that you probably want to listen for icon theme changes and
1902  * update the icon. This is usually done by connecting to the
1903  * GtkWidget::style-set signal. If for some reason you do not want to
1904  * update the icon when the icon theme changes, you should consider
1905  * using gdk_pixbuf_copy() to make a private copy of the pixbuf
1906  * returned by this function. Otherwise GTK+ may need to keep the old
1907  * icon theme loaded, which would be a waste of memory.
1908  *
1909  * Return value: (transfer full): the rendered icon; this may be a
1910  *     newly created icon or a new reference to an internal icon, so
1911  *     you must not modify the icon. Use g_object_unref() to release
1912  *     your reference to the icon. %NULL if the icon isn't found.
1913  *
1914  * Since: 2.4
1915  **/
1916 GdkPixbuf *
1917 gtk_icon_theme_load_icon (GtkIconTheme         *icon_theme,
1918                           const gchar          *icon_name,
1919                           gint                  size,
1920                           GtkIconLookupFlags    flags,
1921                           GError              **error)
1922 {
1923   GtkIconInfo *icon_info;
1924   GdkPixbuf *pixbuf = NULL;
1925   
1926   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
1927   g_return_val_if_fail (icon_name != NULL, NULL);
1928   g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
1929                         (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
1930   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1931   
1932   icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size,
1933                                           flags | GTK_ICON_LOOKUP_USE_BUILTIN);
1934   if (!icon_info)
1935     {
1936       g_set_error (error, GTK_ICON_THEME_ERROR,  GTK_ICON_THEME_NOT_FOUND,
1937                    _("Icon '%s' not present in theme"), icon_name);
1938       return NULL;
1939     }
1940
1941   pixbuf = gtk_icon_info_load_icon (icon_info, error);
1942   gtk_icon_info_free (icon_info);
1943
1944   return pixbuf;
1945 }
1946
1947 /**
1948  * gtk_icon_theme_has_icon:
1949  * @icon_theme: a #GtkIconTheme
1950  * @icon_name: the name of an icon
1951  * 
1952  * Checks whether an icon theme includes an icon
1953  * for a particular name.
1954  * 
1955  * Return value: %TRUE if @icon_theme includes an
1956  *  icon for @icon_name.
1957  *
1958  * Since: 2.4
1959  **/
1960 gboolean 
1961 gtk_icon_theme_has_icon (GtkIconTheme *icon_theme,
1962                          const char   *icon_name)
1963 {
1964   GtkIconThemePrivate *priv;
1965   GList *l;
1966
1967   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
1968   g_return_val_if_fail (icon_name != NULL, FALSE);
1969
1970   priv = icon_theme->priv;
1971   
1972   ensure_valid_themes (icon_theme);
1973
1974   for (l = priv->dir_mtimes; l; l = l->next)
1975     {
1976       IconThemeDirMtime *dir_mtime = l->data;
1977       GtkIconCache *cache = dir_mtime->cache;
1978       
1979       if (cache && _gtk_icon_cache_has_icon (cache, icon_name))
1980         return TRUE;
1981     }
1982
1983   if (g_hash_table_lookup_extended (priv->all_icons,
1984                                     icon_name, NULL, NULL))
1985     return TRUE;
1986
1987   if (_builtin_cache &&
1988       _gtk_icon_cache_has_icon (_builtin_cache, icon_name))
1989     return TRUE;
1990
1991   if (icon_theme_builtin_icons &&
1992       g_hash_table_lookup_extended (icon_theme_builtin_icons,
1993                                     icon_name, NULL, NULL))
1994     return TRUE;
1995
1996   return FALSE;
1997 }
1998
1999 static void
2000 add_size (gpointer  key,
2001           gpointer  value,
2002           gpointer  user_data)
2003 {
2004   gint **res_p = user_data;
2005
2006   **res_p = GPOINTER_TO_INT (key);
2007
2008   (*res_p)++;
2009 }
2010
2011 /**
2012  * gtk_icon_theme_get_icon_sizes:
2013  * @icon_theme: a #GtkIconTheme
2014  * @icon_name: the name of an icon
2015  * 
2016  * Returns an array of integers describing the sizes at which
2017  * the icon is available without scaling. A size of -1 means 
2018  * that the icon is available in a scalable format. The array 
2019  * is zero-terminated.
2020  * 
2021  * Return value: (array zero-terminated=1): An newly allocated array
2022  * describing the sizes at which the icon is available. The array
2023  * should be freed with g_free() when it is no longer needed.
2024  *
2025  * Since: 2.6
2026  **/
2027 gint *
2028 gtk_icon_theme_get_icon_sizes (GtkIconTheme *icon_theme,
2029                                const char   *icon_name)
2030 {
2031   GList *l, *d, *icons;
2032   GHashTable *sizes;
2033   gint *result, *r;
2034   guint suffix;  
2035   GtkIconThemePrivate *priv;
2036
2037   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2038   
2039   priv = icon_theme->priv;
2040
2041   ensure_valid_themes (icon_theme);
2042
2043   sizes = g_hash_table_new (g_direct_hash, g_direct_equal);
2044
2045   for (l = priv->themes; l; l = l->next)
2046     {
2047       IconTheme *theme = l->data;
2048       for (d = theme->dirs; d; d = d->next)
2049         {
2050           IconThemeDir *dir = d->data;
2051
2052           if (dir->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir->size), NULL, NULL))
2053             continue;
2054
2055           suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);      
2056           if (suffix != ICON_SUFFIX_NONE)
2057             {
2058               if (suffix == ICON_SUFFIX_SVG)
2059                 g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL);
2060               else
2061                 g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL);
2062             }
2063         }
2064     }
2065
2066   for (d = builtin_dirs; d; d = d->next)
2067     {
2068       IconThemeDir *dir = d->data;
2069       
2070       if (dir->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir->size), NULL, NULL))
2071         continue;
2072
2073       suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);          
2074       if (suffix != ICON_SUFFIX_NONE)
2075         {
2076           if (suffix == ICON_SUFFIX_SVG)
2077             g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL);
2078           else
2079             g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL);
2080         }
2081     }
2082
2083   if (icon_theme_builtin_icons)
2084     {
2085       icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
2086       
2087       while (icons)
2088         {
2089           BuiltinIcon *icon = icons->data;
2090         
2091           g_hash_table_insert (sizes, GINT_TO_POINTER (icon->size), NULL);
2092           icons = icons->next;
2093         }      
2094     }
2095
2096   r = result = g_new0 (gint, g_hash_table_size (sizes) + 1);
2097
2098   g_hash_table_foreach (sizes, add_size, &r);
2099   g_hash_table_destroy (sizes);
2100   
2101   return result;
2102 }
2103
2104 static void
2105 add_key_to_hash (gpointer  key,
2106                  gpointer  value,
2107                  gpointer  user_data)
2108 {
2109   GHashTable *hash = user_data;
2110
2111   g_hash_table_insert (hash, key, NULL);
2112 }
2113
2114 static void
2115 add_key_to_list (gpointer  key,
2116                  gpointer  value,
2117                  gpointer  user_data)
2118 {
2119   GList **list = user_data;
2120
2121   *list = g_list_prepend (*list, g_strdup (key));
2122 }
2123
2124 /**
2125  * gtk_icon_theme_list_icons:
2126  * @icon_theme: a #GtkIconTheme
2127  * @context: (allow-none): a string identifying a particular type of
2128  *           icon, or %NULL to list all icons.
2129  * 
2130  * Lists the icons in the current icon theme. Only a subset
2131  * of the icons can be listed by providing a context string.
2132  * The set of values for the context string is system dependent,
2133  * but will typically include such values as "Applications" and
2134  * "MimeTypes".
2135  *
2136  * Return value: (element-type utf8) (transfer full): a #GList list
2137  *  holding the names of all the icons in the theme. You must first
2138  *  free each element in the list with g_free(), then free the list
2139  *  itself with g_list_free().
2140  *
2141  * Since: 2.4
2142  **/
2143 GList *
2144 gtk_icon_theme_list_icons (GtkIconTheme *icon_theme,
2145                            const char   *context)
2146 {
2147   GtkIconThemePrivate *priv;
2148   GHashTable *icons;
2149   GList *list, *l;
2150   GQuark context_quark;
2151   
2152   priv = icon_theme->priv;
2153   
2154   ensure_valid_themes (icon_theme);
2155
2156   if (context)
2157     {
2158       context_quark = g_quark_try_string (context);
2159
2160       if (!context_quark)
2161         return NULL;
2162     }
2163   else
2164     context_quark = 0;
2165
2166   icons = g_hash_table_new (g_str_hash, g_str_equal);
2167   
2168   l = priv->themes;
2169   while (l != NULL)
2170     {
2171       theme_list_icons (l->data, icons, context_quark);
2172       l = l->next;
2173     }
2174
2175   if (context_quark == 0)
2176     g_hash_table_foreach (priv->unthemed_icons,
2177                           add_key_to_hash,
2178                           icons);
2179
2180   list = NULL;
2181   
2182   g_hash_table_foreach (icons,
2183                         add_key_to_list,
2184                         &list);
2185
2186   g_hash_table_destroy (icons);
2187   
2188   return list;
2189 }
2190
2191 /**
2192  * gtk_icon_theme_list_contexts:
2193  * @icon_theme: a #GtkIconTheme
2194  *
2195  * Gets the list of contexts available within the current
2196  * hierarchy of icon themes
2197  *
2198  * Return value: (element-type utf8) (transfer full): a #GList list
2199  *  holding the names of all the contexts in the theme. You must first
2200  *  free each element in the list with g_free(), then free the list
2201  *  itself with g_list_free().
2202  *
2203  * Since: 2.12
2204  **/
2205 GList *
2206 gtk_icon_theme_list_contexts (GtkIconTheme *icon_theme)
2207 {
2208   GtkIconThemePrivate *priv;
2209   GHashTable *contexts;
2210   GList *list, *l;
2211
2212   priv = icon_theme->priv;
2213   
2214   ensure_valid_themes (icon_theme);
2215
2216   contexts = g_hash_table_new (g_str_hash, g_str_equal);
2217
2218   l = priv->themes;
2219   while (l != NULL)
2220     {
2221       theme_list_contexts (l->data, contexts);
2222       l = l->next;
2223     }
2224
2225   list = NULL;
2226
2227   g_hash_table_foreach (contexts,
2228                         add_key_to_list,
2229                         &list);
2230
2231   g_hash_table_destroy (contexts);
2232
2233   return list;
2234 }
2235
2236 /**
2237  * gtk_icon_theme_get_example_icon_name:
2238  * @icon_theme: a #GtkIconTheme
2239  * 
2240  * Gets the name of an icon that is representative of the
2241  * current theme (for instance, to use when presenting
2242  * a list of themes to the user.)
2243  * 
2244  * Return value: the name of an example icon or %NULL.
2245  *  Free with g_free().
2246  *
2247  * Since: 2.4
2248  **/
2249 char *
2250 gtk_icon_theme_get_example_icon_name (GtkIconTheme *icon_theme)
2251 {
2252   GtkIconThemePrivate *priv;
2253   GList *l;
2254   IconTheme *theme;
2255
2256   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2257   
2258   priv = icon_theme->priv;
2259   
2260   ensure_valid_themes (icon_theme);
2261
2262   l = priv->themes;
2263   while (l != NULL)
2264     {
2265       theme = l->data;
2266       if (theme->example)
2267         return g_strdup (theme->example);
2268       
2269       l = l->next;
2270     }
2271   
2272   return NULL;
2273 }
2274
2275
2276 static gboolean
2277 rescan_themes (GtkIconTheme *icon_theme)
2278 {
2279   GtkIconThemePrivate *priv;
2280   IconThemeDirMtime *dir_mtime;
2281   GList *d;
2282   int stat_res;
2283   GStatBuf stat_buf;
2284   GTimeVal tv;
2285
2286   priv = icon_theme->priv;
2287
2288   for (d = priv->dir_mtimes; d != NULL; d = d->next)
2289     {
2290       dir_mtime = d->data;
2291
2292       stat_res = g_stat (dir_mtime->dir, &stat_buf);
2293
2294       /* dir mtime didn't change */
2295       if (stat_res == 0 &&
2296           S_ISDIR (stat_buf.st_mode) &&
2297           dir_mtime->mtime == stat_buf.st_mtime)
2298         continue;
2299       /* didn't exist before, and still doesn't */
2300       if (dir_mtime->mtime == 0 &&
2301           (stat_res != 0 || !S_ISDIR (stat_buf.st_mode)))
2302         continue;
2303
2304       return TRUE;
2305     }
2306
2307   g_get_current_time (&tv);
2308   priv->last_stat_time = tv.tv_sec;
2309
2310   return FALSE;
2311 }
2312
2313 /**
2314  * gtk_icon_theme_rescan_if_needed:
2315  * @icon_theme: a #GtkIconTheme
2316  * 
2317  * Checks to see if the icon theme has changed; if it has, any
2318  * currently cached information is discarded and will be reloaded
2319  * next time @icon_theme is accessed.
2320  * 
2321  * Return value: %TRUE if the icon theme has changed and needed
2322  *   to be reloaded.
2323  *
2324  * Since: 2.4
2325  **/
2326 gboolean
2327 gtk_icon_theme_rescan_if_needed (GtkIconTheme *icon_theme)
2328 {
2329   gboolean retval;
2330
2331   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
2332
2333   retval = rescan_themes (icon_theme);
2334   if (retval)
2335       do_theme_change (icon_theme);
2336
2337   return retval;
2338 }
2339
2340 static void
2341 theme_destroy (IconTheme *theme)
2342 {
2343   g_free (theme->display_name);
2344   g_free (theme->comment);
2345   g_free (theme->name);
2346   g_free (theme->example);
2347
2348   g_list_free_full (theme->dirs, (GDestroyNotify) theme_dir_destroy);
2349   
2350   g_free (theme);
2351 }
2352
2353 static void
2354 theme_dir_destroy (IconThemeDir *dir)
2355 {
2356   if (dir->cache)
2357       _gtk_icon_cache_unref (dir->cache);
2358   else
2359     g_hash_table_destroy (dir->icons);
2360   
2361   if (dir->icon_data)
2362     g_hash_table_destroy (dir->icon_data);
2363   g_free (dir->dir);
2364   g_free (dir->subdir);
2365   g_free (dir);
2366 }
2367
2368 static int
2369 theme_dir_size_difference (IconThemeDir *dir, int size, gboolean *smaller)
2370 {
2371   int min, max;
2372   switch (dir->type)
2373     {
2374     case ICON_THEME_DIR_FIXED:
2375       *smaller = size < dir->size;
2376       return abs (size - dir->size);
2377       break;
2378     case ICON_THEME_DIR_SCALABLE:
2379       *smaller = size < dir->min_size;
2380       if (size < dir->min_size)
2381         return dir->min_size - size;
2382       if (size > dir->max_size)
2383         return size - dir->max_size;
2384       return 0;
2385       break;
2386     case ICON_THEME_DIR_THRESHOLD:
2387       min = dir->size - dir->threshold;
2388       max = dir->size + dir->threshold;
2389       *smaller = size < min;
2390       if (size < min)
2391         return min - size;
2392       if (size > max)
2393         return size - max;
2394       return 0;
2395       break;
2396     case ICON_THEME_DIR_UNTHEMED:
2397       g_assert_not_reached ();
2398       break;
2399     }
2400   g_assert_not_reached ();
2401   return 1000;
2402 }
2403
2404 static const char *
2405 string_from_suffix (IconSuffix suffix)
2406 {
2407   switch (suffix)
2408     {
2409     case ICON_SUFFIX_XPM:
2410       return ".xpm";
2411     case ICON_SUFFIX_SVG:
2412       return ".svg";
2413     case ICON_SUFFIX_PNG:
2414       return ".png";
2415     default:
2416       g_assert_not_reached();
2417     }
2418   return NULL;
2419 }
2420
2421 static IconSuffix
2422 suffix_from_name (const char *name)
2423 {
2424   IconSuffix retval;
2425
2426   if (g_str_has_suffix (name, ".png"))
2427     retval = ICON_SUFFIX_PNG;
2428   else if (g_str_has_suffix (name, ".svg"))
2429     retval = ICON_SUFFIX_SVG;
2430   else if (g_str_has_suffix (name, ".xpm"))
2431     retval = ICON_SUFFIX_XPM;
2432   else
2433     retval = ICON_SUFFIX_NONE;
2434
2435   return retval;
2436 }
2437
2438 static IconSuffix
2439 best_suffix (IconSuffix suffix,
2440              gboolean   allow_svg)
2441 {
2442   if ((suffix & ICON_SUFFIX_PNG) != 0)
2443     return ICON_SUFFIX_PNG;
2444   else if (allow_svg && ((suffix & ICON_SUFFIX_SVG) != 0))
2445     return ICON_SUFFIX_SVG;
2446   else if ((suffix & ICON_SUFFIX_XPM) != 0)
2447     return ICON_SUFFIX_XPM;
2448   else
2449     return ICON_SUFFIX_NONE;
2450 }
2451
2452
2453 static IconSuffix
2454 theme_dir_get_icon_suffix (IconThemeDir *dir,
2455                            const gchar  *icon_name,
2456                            gboolean     *has_icon_file)
2457 {
2458   IconSuffix suffix;
2459
2460   if (dir->cache)
2461     {
2462       suffix = (IconSuffix)_gtk_icon_cache_get_icon_flags (dir->cache,
2463                                                            icon_name,
2464                                                            dir->subdir_index);
2465
2466       if (has_icon_file)
2467         *has_icon_file = suffix & HAS_ICON_FILE;
2468
2469       suffix = suffix & ~HAS_ICON_FILE;
2470     }
2471   else
2472     suffix = GPOINTER_TO_UINT (g_hash_table_lookup (dir->icons, icon_name));
2473
2474   GTK_NOTE (ICONTHEME, 
2475             g_print ("get_icon_suffix%s %u\n", dir->cache ? " (cached)" : "", suffix));
2476
2477   return suffix;
2478 }
2479
2480 static GtkIconInfo *
2481 theme_lookup_icon (IconTheme          *theme,
2482                    const char         *icon_name,
2483                    int                 size,
2484                    gboolean            allow_svg,
2485                    gboolean            use_builtin)
2486 {
2487   GList *dirs, *l;
2488   IconThemeDir *dir, *min_dir;
2489   char *file;
2490   int min_difference, difference;
2491   BuiltinIcon *closest_builtin = NULL;
2492   gboolean smaller, has_larger, match;
2493   IconSuffix suffix;
2494
2495   min_difference = G_MAXINT;
2496   min_dir = NULL;
2497   has_larger = FALSE;
2498   match = FALSE;
2499
2500   /* Builtin icons are logically part of the default theme and
2501    * are searched before other subdirectories of the default theme.
2502    */
2503   if (use_builtin && strcmp (theme->name, DEFAULT_THEME_NAME) == 0)
2504     {
2505       closest_builtin = find_builtin_icon (icon_name, 
2506                                            size,
2507                                            &min_difference,
2508                                            &has_larger);
2509
2510       if (min_difference == 0)
2511         return icon_info_new_builtin (closest_builtin);
2512
2513       dirs = builtin_dirs;
2514     }
2515   else
2516     dirs = theme->dirs;
2517
2518   l = dirs;
2519   while (l != NULL)
2520     {
2521       dir = l->data;
2522
2523       GTK_NOTE (ICONTHEME,
2524                 g_print ("theme_lookup_icon dir %s\n", dir->dir));
2525       suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
2526       if (best_suffix (suffix, allow_svg) != ICON_SUFFIX_NONE)
2527         {
2528           difference = theme_dir_size_difference (dir, size, &smaller);
2529
2530           if (difference == 0)
2531             {
2532               if (dir->type == ICON_THEME_DIR_SCALABLE)
2533                 {
2534                   /* don't pick scalable if we already found
2535                    * a matching non-scalable dir
2536                    */
2537                   if (!match)
2538                     {
2539                       min_dir = dir;
2540                       break;
2541                     }
2542                 }
2543               else
2544                 {
2545                   /* for a matching non-scalable dir keep
2546                    * going and look for a closer match
2547                    */             
2548                   difference = abs (size - dir->size);
2549                   if (!match || difference < min_difference)
2550                     {
2551                       match = TRUE;
2552                       min_difference = difference;
2553                       min_dir = dir;
2554                     }
2555                   if (difference == 0)
2556                     break;
2557                 }
2558             } 
2559   
2560           if (!match)
2561             {
2562               if (!has_larger)
2563                 {
2564                   if (difference < min_difference || smaller)
2565                     {
2566                       min_difference = difference;
2567                       min_dir = dir;
2568                       has_larger = smaller;
2569                     }
2570                 }
2571               else
2572                 {
2573                   if (difference < min_difference && smaller)
2574                     {
2575                       min_difference = difference;
2576                       min_dir = dir;
2577                     }
2578                 }
2579             }
2580         }
2581
2582       l = l->next;
2583
2584       if (l == NULL && dirs == builtin_dirs)
2585         {
2586           dirs = theme->dirs;
2587           l = dirs;
2588         }
2589     }
2590
2591   if (min_dir)
2592     {
2593       GtkIconInfo *icon_info = icon_info_new ();
2594       gboolean has_icon_file = FALSE;
2595       
2596       suffix = theme_dir_get_icon_suffix (min_dir, icon_name, &has_icon_file);
2597       suffix = best_suffix (suffix, allow_svg);
2598       g_assert (suffix != ICON_SUFFIX_NONE);
2599       
2600       if (min_dir->dir)
2601         {
2602           file = g_strconcat (icon_name, string_from_suffix (suffix), NULL);
2603           icon_info->filename = g_build_filename (min_dir->dir, file, NULL);
2604           icon_info->icon_file = g_file_new_for_path (icon_info->filename);
2605           g_free (file);
2606         }
2607       else
2608         {
2609           icon_info->filename = NULL;
2610           icon_info->icon_file = NULL;
2611         }
2612       
2613       if (min_dir->icon_data != NULL)
2614         icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
2615
2616       if (icon_info->data == NULL && min_dir->cache != NULL)
2617         {
2618           icon_info->data = _gtk_icon_cache_get_icon_data (min_dir->cache, icon_name, min_dir->subdir_index);
2619           if (icon_info->data)
2620             {
2621               if (min_dir->icon_data == NULL)
2622                 min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2623                                                             g_free, (GDestroyNotify)icon_data_free);
2624
2625               g_hash_table_replace (min_dir->icon_data, g_strdup (icon_name), icon_info->data);
2626             }
2627         }
2628
2629       if (icon_info->data == NULL && has_icon_file)
2630         {
2631           gchar *icon_file_name, *icon_file_path;
2632
2633           icon_file_name = g_strconcat (icon_name, ".icon", NULL);
2634           icon_file_path = g_build_filename (min_dir->dir, icon_file_name, NULL);
2635
2636           if (g_file_test (icon_file_path, G_FILE_TEST_IS_REGULAR))
2637             {
2638               if (min_dir->icon_data == NULL)   
2639                 min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2640                                                             g_free, (GDestroyNotify)icon_data_free);
2641               load_icon_data (min_dir, icon_file_path, icon_file_name);
2642               
2643               icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
2644             }
2645           g_free (icon_file_name);
2646           g_free (icon_file_path);
2647         }
2648
2649       if (min_dir->cache)
2650         {
2651           icon_info->cache_pixbuf = _gtk_icon_cache_get_icon (min_dir->cache, icon_name,
2652                                                               min_dir->subdir_index);
2653         }
2654
2655       icon_info->dir_type = min_dir->type;
2656       icon_info->dir_size = min_dir->size;
2657       icon_info->threshold = min_dir->threshold;
2658       
2659       return icon_info;
2660     }
2661
2662   if (closest_builtin)
2663     return icon_info_new_builtin (closest_builtin);
2664   
2665   return NULL;
2666 }
2667
2668 static void
2669 theme_list_icons (IconTheme  *theme, 
2670                   GHashTable *icons,
2671                   GQuark      context)
2672 {
2673   GList *l = theme->dirs;
2674   IconThemeDir *dir;
2675   
2676   while (l != NULL)
2677     {
2678       dir = l->data;
2679
2680       if (context == dir->context ||
2681           context == 0)
2682         {
2683           if (dir->cache)
2684             {
2685               _gtk_icon_cache_add_icons (dir->cache,
2686                                          dir->subdir,
2687                                          icons);
2688                                          
2689             }
2690           else
2691             {
2692               g_hash_table_foreach (dir->icons,
2693                                     add_key_to_hash,
2694                                     icons);
2695             }
2696         }
2697       l = l->next;
2698     }
2699 }
2700
2701 static void
2702 theme_list_contexts (IconTheme  *theme, 
2703                      GHashTable *contexts)
2704 {
2705   GList *l = theme->dirs;
2706   IconThemeDir *dir;
2707   const char *context;
2708
2709   while (l != NULL)
2710     {
2711       dir = l->data;
2712
2713       context = g_quark_to_string (dir->context);
2714       g_hash_table_replace (contexts, (gpointer) context, NULL);
2715
2716       l = l->next;
2717     }
2718 }
2719
2720 static void
2721 load_icon_data (IconThemeDir *dir, const char *path, const char *name)
2722 {
2723   GKeyFile *icon_file;
2724   char *base_name;
2725   char **split;
2726   gsize length;
2727   char *str;
2728   char *split_point;
2729   int i;
2730   gint *ivalues;
2731   GError *error = NULL;
2732   
2733   GtkIconData *data;
2734
2735   icon_file = g_key_file_new ();
2736   g_key_file_set_list_separator (icon_file, ',');
2737   g_key_file_load_from_file (icon_file, path, 0, &error);
2738   if (error)
2739     {
2740       g_error_free (error);
2741       g_key_file_free (icon_file);      
2742       return;
2743     }
2744   else
2745     {
2746       base_name = strip_suffix (name);
2747       
2748       data = g_slice_new0 (GtkIconData);
2749       /* takes ownership of base_name */
2750       g_hash_table_replace (dir->icon_data, base_name, data);
2751       
2752       ivalues = g_key_file_get_integer_list (icon_file, 
2753                                              "Icon Data", "EmbeddedTextRectangle",
2754                                               &length, NULL);
2755       if (ivalues)
2756         {
2757           if (length == 4)
2758             {
2759               data->has_embedded_rect = TRUE;
2760               data->x0 = ivalues[0];
2761               data->y0 = ivalues[1];
2762               data->x1 = ivalues[2];
2763               data->y1 = ivalues[3];
2764             }
2765           
2766           g_free (ivalues);
2767         }
2768       
2769       str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
2770       if (str)
2771         {
2772           split = g_strsplit (str, "|", -1);
2773           
2774           data->n_attach_points = g_strv_length (split);
2775           data->attach_points = g_new (GdkPoint, data->n_attach_points);
2776
2777           i = 0;
2778           while (split[i] != NULL && i < data->n_attach_points)
2779             {
2780               split_point = strchr (split[i], ',');
2781               if (split_point)
2782                 {
2783                   *split_point = 0;
2784                   split_point++;
2785                   data->attach_points[i].x = atoi (split[i]);
2786                   data->attach_points[i].y = atoi (split_point);
2787                 }
2788               i++;
2789             }
2790           
2791           g_strfreev (split);
2792           g_free (str);
2793         }
2794       
2795       data->display_name = g_key_file_get_locale_string (icon_file, 
2796                                                          "Icon Data", "DisplayName",
2797                                                          NULL, NULL);
2798       g_key_file_free (icon_file);
2799     }
2800 }
2801
2802 static void
2803 scan_directory (GtkIconThemePrivate *icon_theme,
2804                 IconThemeDir *dir, char *full_dir)
2805 {
2806   GDir *gdir;
2807   const char *name;
2808
2809   GTK_NOTE (ICONTHEME, 
2810             g_print ("scanning directory %s\n", full_dir));
2811   dir->icons = g_hash_table_new_full (g_str_hash, g_str_equal,
2812                                       g_free, NULL);
2813   
2814   gdir = g_dir_open (full_dir, 0, NULL);
2815
2816   if (gdir == NULL)
2817     return;
2818
2819   while ((name = g_dir_read_name (gdir)))
2820     {
2821       char *path;
2822       char *base_name;
2823       IconSuffix suffix, hash_suffix;
2824
2825       if (g_str_has_suffix (name, ".icon"))
2826         {
2827           if (dir->icon_data == NULL)
2828             dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
2829                                                     g_free, (GDestroyNotify)icon_data_free);
2830           
2831           path = g_build_filename (full_dir, name, NULL);
2832           if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
2833             load_icon_data (dir, path, name);
2834           
2835           g_free (path);
2836           
2837           continue;
2838         }
2839
2840       suffix = suffix_from_name (name);
2841       if (suffix == ICON_SUFFIX_NONE)
2842         continue;
2843
2844       base_name = strip_suffix (name);
2845
2846       hash_suffix = GPOINTER_TO_INT (g_hash_table_lookup (dir->icons, base_name));
2847       g_hash_table_replace (icon_theme->all_icons, base_name, NULL);
2848       /* takes ownership of base_name */
2849       g_hash_table_replace (dir->icons, base_name, GUINT_TO_POINTER (hash_suffix| suffix));
2850     }
2851   
2852   g_dir_close (gdir);
2853 }
2854
2855 static void
2856 theme_subdir_load (GtkIconTheme *icon_theme,
2857                    IconTheme    *theme,
2858                    GKeyFile     *theme_file,
2859                    char         *subdir)
2860 {
2861   GList *d;
2862   char *type_string;
2863   IconThemeDir *dir;
2864   IconThemeDirType type;
2865   char *context_string;
2866   GQuark context;
2867   int size;
2868   int min_size;
2869   int max_size;
2870   int threshold;
2871   char *full_dir;
2872   GError *error = NULL;
2873   IconThemeDirMtime *dir_mtime;
2874
2875   size = g_key_file_get_integer (theme_file, subdir, "Size", &error);
2876   if (error)
2877     {
2878       g_error_free (error);
2879       g_warning ("Theme directory %s of theme %s has no size field\n", 
2880                  subdir, theme->name);
2881       return;
2882     }
2883   
2884   type = ICON_THEME_DIR_THRESHOLD;
2885   type_string = g_key_file_get_string (theme_file, subdir, "Type", NULL);
2886   if (type_string)
2887     {
2888       if (strcmp (type_string, "Fixed") == 0)
2889         type = ICON_THEME_DIR_FIXED;
2890       else if (strcmp (type_string, "Scalable") == 0)
2891         type = ICON_THEME_DIR_SCALABLE;
2892       else if (strcmp (type_string, "Threshold") == 0)
2893         type = ICON_THEME_DIR_THRESHOLD;
2894
2895       g_free (type_string);
2896     }
2897   
2898   context = 0;
2899   context_string = g_key_file_get_string (theme_file, subdir, "Context", NULL);
2900   if (context_string)
2901     {
2902       context = g_quark_from_string (context_string);
2903       g_free (context_string);
2904     }
2905
2906   if (g_key_file_has_key (theme_file, subdir, "MaxSize", NULL))
2907     max_size = g_key_file_get_integer (theme_file, subdir, "MaxSize", NULL);
2908   else
2909     max_size = size;
2910
2911   if (g_key_file_has_key (theme_file, subdir, "MinSize", NULL))
2912     min_size = g_key_file_get_integer (theme_file, subdir, "MinSize", NULL);
2913   else
2914     min_size = size;
2915
2916   if (g_key_file_has_key (theme_file, subdir, "Threshold", NULL))
2917     threshold = g_key_file_get_integer (theme_file, subdir, "Threshold", NULL);
2918   else
2919     threshold = 2;
2920
2921   for (d = icon_theme->priv->dir_mtimes; d; d = d->next)
2922     {
2923       dir_mtime = (IconThemeDirMtime *)d->data;
2924
2925       if (dir_mtime->mtime == 0)
2926         continue; /* directory doesn't exist */
2927
2928        full_dir = g_build_filename (dir_mtime->dir, subdir, NULL);
2929
2930       /* First, see if we have a cache for the directory */
2931       if (dir_mtime->cache != NULL || g_file_test (full_dir, G_FILE_TEST_IS_DIR))
2932         {
2933           if (dir_mtime->cache == NULL)
2934             {
2935               /* This will return NULL if the cache doesn't exist or is outdated */
2936               dir_mtime->cache = _gtk_icon_cache_new_for_path (dir_mtime->dir);
2937             }
2938           
2939           dir = g_new (IconThemeDir, 1);
2940           dir->type = type;
2941           dir->context = context;
2942           dir->size = size;
2943           dir->min_size = min_size;
2944           dir->max_size = max_size;
2945           dir->threshold = threshold;
2946           dir->dir = full_dir;
2947           dir->icon_data = NULL;
2948           dir->subdir = g_strdup (subdir);
2949           if (dir_mtime->cache != NULL)
2950             {
2951               dir->cache = _gtk_icon_cache_ref (dir_mtime->cache);
2952               dir->subdir_index = _gtk_icon_cache_get_directory_index (dir->cache, dir->subdir);
2953             }
2954           else
2955             {
2956               dir->cache = NULL;
2957               dir->subdir_index = -1;
2958               scan_directory (icon_theme->priv, dir, full_dir);
2959             }
2960
2961           theme->dirs = g_list_prepend (theme->dirs, dir);
2962         }
2963       else
2964         g_free (full_dir);
2965     }
2966 }
2967
2968 static void
2969 icon_data_free (GtkIconData *icon_data)
2970 {
2971   g_free (icon_data->attach_points);
2972   g_free (icon_data->display_name);
2973   g_slice_free (GtkIconData, icon_data);
2974 }
2975
2976 static GtkIconData *
2977 icon_data_dup (GtkIconData *icon_data)
2978 {
2979   GtkIconData *dup = NULL;
2980   if (icon_data)
2981     {
2982       dup = g_slice_new0 (GtkIconData);
2983       *dup = *icon_data;
2984       if (dup->n_attach_points > 0)
2985         {
2986           dup->attach_points = g_memdup (dup->attach_points,
2987                                          sizeof (GdkPoint) * dup->n_attach_points);
2988         }
2989       dup->display_name = g_strdup (dup->display_name);
2990     }
2991
2992   return dup;
2993 }
2994
2995
2996 /*
2997  * GtkIconInfo
2998  */
2999
3000 static void gtk_icon_info_class_init (GtkIconInfoClass *klass);
3001
3002 G_DEFINE_TYPE (GtkIconInfo, gtk_icon_info, G_TYPE_OBJECT)
3003
3004 static void
3005 gtk_icon_info_init (GtkIconInfo *icon_info)
3006 {
3007   icon_info->scale = -1.;
3008 }
3009
3010 static GtkIconInfo *
3011 icon_info_new (void)
3012 {
3013   return g_object_new (GTK_TYPE_ICON_INFO, NULL);
3014 }
3015
3016 /* This only copies whatever is needed to load the pixbuf, so that we can do
3017  * a load in a thread without affecting the original IconInfo from the thread.
3018  */
3019 static GtkIconInfo *
3020 icon_info_dup (GtkIconInfo *icon_info)
3021 {
3022   GtkIconInfo *dup;
3023   GSList *l;
3024
3025   dup = icon_info_new ();
3026
3027   dup->filename = g_strdup (icon_info->filename);
3028   if (icon_info->icon_file)
3029     dup->icon_file = g_object_ref (icon_info->icon_file);
3030   if (icon_info->loadable)
3031     dup->loadable = g_object_ref (icon_info->loadable);
3032
3033   for (l = icon_info->emblem_infos; l != NULL; l = l->next)
3034     {
3035       dup->emblem_infos =
3036         g_slist_append (dup->emblem_infos,
3037                         icon_info_dup (l->data));
3038     }
3039
3040   if (icon_info->cache_pixbuf)
3041     dup->cache_pixbuf = g_object_ref (icon_info->cache_pixbuf);
3042
3043   dup->data = icon_data_dup (icon_info->data);
3044   dup->dir_type = icon_info->dir_type;
3045   dup->dir_size = icon_info->dir_size;
3046   dup->threshold = icon_info->threshold;
3047   dup->desired_size = icon_info->desired_size;
3048   dup->raw_coordinates = icon_info->raw_coordinates;
3049   dup->forced_size = icon_info->forced_size;
3050   dup->emblems_applied = icon_info->emblems_applied;
3051
3052   return dup;
3053 }
3054
3055 static GtkIconInfo *
3056 icon_info_new_builtin (BuiltinIcon *icon)
3057 {
3058   GtkIconInfo *icon_info = icon_info_new ();
3059
3060   icon_info->cache_pixbuf = g_object_ref (icon->pixbuf);
3061   icon_info->dir_type = ICON_THEME_DIR_THRESHOLD;
3062   icon_info->dir_size = icon->size;
3063   icon_info->threshold = 2;
3064
3065   return icon_info;
3066 }
3067
3068 /**
3069  * gtk_icon_info_copy:
3070  * @icon_info: a #GtkIconInfo
3071  * 
3072  * Make a copy of a #GtkIconInfo.
3073  * 
3074  * Return value: the new GtkIconInfo
3075  *
3076  * Since: 2.4
3077  **/
3078 GtkIconInfo *
3079 gtk_icon_info_copy (GtkIconInfo *icon_info)
3080 {
3081   
3082   g_return_val_if_fail (icon_info != NULL, NULL);
3083
3084   return g_object_ref (icon_info);
3085 }
3086
3087 /**
3088  * gtk_icon_info_free:
3089  * @icon_info: a #GtkIconInfo
3090  * 
3091  * Free a #GtkIconInfo and associated information
3092  *
3093  * Since: 2.4
3094  **/
3095 void
3096 gtk_icon_info_free (GtkIconInfo *icon_info)
3097 {
3098   g_return_if_fail (icon_info != NULL);
3099
3100   g_object_unref (icon_info);
3101 }
3102
3103 static void
3104 gtk_icon_info_finalize (GObject *object)
3105 {
3106   GtkIconInfo *icon_info = (GtkIconInfo *) object;
3107
3108   if (icon_info->in_cache)
3109     g_hash_table_remove (icon_info->in_cache->priv->info_cache, &icon_info->key);
3110
3111   g_strfreev (icon_info->key.icon_names);
3112
3113   g_free (icon_info->filename);
3114   g_clear_object (&icon_info->icon_file);
3115
3116   if (icon_info->loadable)
3117     g_object_unref (icon_info->loadable);
3118   g_slist_free_full (icon_info->emblem_infos, (GDestroyNotify) gtk_icon_info_free);
3119   if (icon_info->pixbuf)
3120     g_object_unref (icon_info->pixbuf);
3121   if (icon_info->cache_pixbuf)
3122     g_object_unref (icon_info->cache_pixbuf);
3123   if (icon_info->symbolic_pixbuf_size)
3124     gtk_requisition_free (icon_info->symbolic_pixbuf_size);
3125
3126   symbolic_pixbuf_cache_free (icon_info->symbolic_pixbuf_cache);
3127
3128   G_OBJECT_CLASS (gtk_icon_info_parent_class)->finalize (object);
3129 }
3130
3131 static void
3132 gtk_icon_info_class_init (GtkIconInfoClass *klass)
3133 {
3134   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3135
3136   gobject_class->finalize = gtk_icon_info_finalize;
3137 }
3138
3139 /**
3140  * gtk_icon_info_get_base_size:
3141  * @icon_info: a #GtkIconInfo
3142  * 
3143  * Gets the base size for the icon. The base size
3144  * is a size for the icon that was specified by
3145  * the icon theme creator. This may be different
3146  * than the actual size of image; an example of
3147  * this is small emblem icons that can be attached
3148  * to a larger icon. These icons will be given
3149  * the same base size as the larger icons to which
3150  * they are attached.
3151  * 
3152  * Return value: the base size, or 0, if no base
3153  *  size is known for the icon.
3154  *
3155  * Since: 2.4
3156  **/
3157 gint
3158 gtk_icon_info_get_base_size (GtkIconInfo *icon_info)
3159 {
3160   g_return_val_if_fail (icon_info != NULL, 0);
3161
3162   return icon_info->dir_size;
3163 }
3164
3165 /**
3166  * gtk_icon_info_get_filename:
3167  * @icon_info: a #GtkIconInfo
3168  * 
3169  * Gets the filename for the icon. If the
3170  * %GTK_ICON_LOOKUP_USE_BUILTIN flag was passed
3171  * to gtk_icon_theme_lookup_icon(), there may be
3172  * no filename if a builtin icon is returned; in this
3173  * case, you should use gtk_icon_info_get_builtin_pixbuf().
3174  * 
3175  * Return value: (type filename): the filename for the icon, or %NULL
3176  *  if gtk_icon_info_get_builtin_pixbuf() should be used instead. The
3177  *  return value is owned by GTK+ and should not be modified or freed.
3178  *
3179  * Since: 2.4
3180  **/
3181 const gchar *
3182 gtk_icon_info_get_filename (GtkIconInfo *icon_info)
3183 {
3184   g_return_val_if_fail (icon_info != NULL, NULL);
3185
3186   return icon_info->filename;
3187 }
3188
3189 /**
3190  * gtk_icon_info_get_builtin_pixbuf:
3191  * @icon_info: a #GtkIconInfo structure
3192  * 
3193  * Gets the built-in image for this icon, if any. To allow
3194  * GTK+ to use built in icon images, you must pass the
3195  * %GTK_ICON_LOOKUP_USE_BUILTIN to
3196  * gtk_icon_theme_lookup_icon().
3197  *
3198  * Return value: (transfer none): the built-in image pixbuf, or %NULL. No
3199  *  extra reference is added to the returned pixbuf, so if
3200  *  you want to keep it around, you must use g_object_ref().
3201  *  The returned image must not be modified.
3202  *
3203  * Since: 2.4
3204  **/
3205 GdkPixbuf *
3206 gtk_icon_info_get_builtin_pixbuf (GtkIconInfo *icon_info)
3207 {
3208   g_return_val_if_fail (icon_info != NULL, NULL);
3209
3210   if (icon_info->filename)
3211     return NULL;
3212   
3213   return icon_info->cache_pixbuf;
3214 }
3215
3216 static gboolean icon_info_ensure_scale_and_pixbuf (GtkIconInfo*, gboolean);
3217
3218 /* Combine the icon with all emblems, the first emblem is placed 
3219  * in the southeast corner. Scale emblems to be at most 3/4 of the
3220  * size of the icon itself.
3221  */
3222 static void 
3223 apply_emblems (GtkIconInfo *info)
3224 {
3225   GdkPixbuf *icon = NULL;
3226   gint w, h, pos;
3227   GSList *l;
3228
3229   if (info->emblem_infos == NULL)
3230     return;
3231
3232   if (info->emblems_applied)
3233     return;
3234
3235   w = gdk_pixbuf_get_width (info->pixbuf);
3236   h = gdk_pixbuf_get_height (info->pixbuf);
3237
3238   for (l = info->emblem_infos, pos = 0; l; l = l->next, pos++)
3239     {
3240       GtkIconInfo *emblem_info = l->data;
3241
3242       if (icon_info_ensure_scale_and_pixbuf (emblem_info, FALSE))
3243         {
3244           GdkPixbuf *emblem = emblem_info->pixbuf;
3245           gint ew, eh;
3246           gint x = 0, y = 0; /* silence compiler */
3247           gdouble scale;
3248
3249           ew = gdk_pixbuf_get_width (emblem);
3250           eh = gdk_pixbuf_get_height (emblem);
3251           if (ew >= w)
3252             {
3253               scale = 0.75;
3254               ew = ew * 0.75;
3255               eh = eh * 0.75;
3256             }
3257           else
3258             scale = 1.0;
3259
3260           switch (pos % 4)
3261             {
3262             case 0:
3263               x = w - ew;
3264               y = h - eh;
3265               break;
3266             case 1:
3267               x = w - ew;
3268               y = 0;
3269               break;
3270             case 2:
3271               x = 0;
3272               y = h - eh;
3273               break;
3274             case 3:
3275               x = 0;
3276               y = 0;
3277               break;
3278             }
3279
3280           if (icon == NULL)
3281             {
3282               icon = gdk_pixbuf_copy (info->pixbuf);
3283               if (icon == NULL)
3284                 break;
3285             }
3286
3287           gdk_pixbuf_composite (emblem, icon, x, y, ew, eh, x, y,
3288                                 scale, scale, GDK_INTERP_BILINEAR, 255);
3289        }
3290    }
3291
3292   if (icon)
3293     {
3294       g_object_unref (info->pixbuf);
3295       info->pixbuf = icon;
3296     }
3297
3298   info->emblems_applied = TRUE;
3299 }
3300
3301 /* If this returns TRUE, its safe to call
3302    icon_info_ensure_scale_and_pixbuf without blocking */
3303 static gboolean
3304 icon_info_get_pixbuf_ready (GtkIconInfo *icon_info)
3305 {
3306   if (icon_info->pixbuf &&
3307       (icon_info->emblem_infos == NULL || icon_info->emblems_applied))
3308     return TRUE;
3309
3310   if (icon_info->load_error)
3311     return TRUE;
3312
3313   return FALSE;
3314 }
3315
3316 /* This function contains the complicated logic for deciding
3317  * on the size at which to load the icon and loading it at
3318  * that size.
3319  */
3320 static gboolean
3321 icon_info_ensure_scale_and_pixbuf (GtkIconInfo  *icon_info,
3322                                    gboolean      scale_only)
3323 {
3324   int image_width, image_height;
3325   GdkPixbuf *source_pixbuf;
3326   gboolean is_svg;
3327
3328   /* First check if we already succeeded have the necessary
3329    * information (or failed earlier)
3330    */
3331   if (scale_only && icon_info->scale >= 0)
3332     return TRUE;
3333
3334   if (icon_info->pixbuf)
3335     {
3336       apply_emblems (icon_info);
3337       return TRUE;
3338     }
3339
3340   if (icon_info->load_error)
3341     return FALSE;
3342
3343   /* SVG icons are a special case - we just immediately scale them
3344    * to the desired size
3345    */
3346   if (icon_info->icon_file && !icon_info->loadable)
3347     icon_info->loadable = G_LOADABLE_ICON (g_file_icon_new (icon_info->icon_file));
3348
3349   is_svg = FALSE;
3350   if (G_IS_FILE_ICON (icon_info->loadable))
3351     {
3352       GFile *file;
3353       GFileInfo *file_info;
3354       const gchar *content_type;
3355
3356       file = g_file_icon_get_file (G_FILE_ICON (icon_info->loadable));
3357       file_info = g_file_query_info (file, 
3358                                      G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
3359                                      G_FILE_QUERY_INFO_NONE,
3360                                      NULL, NULL);
3361       if (file_info) 
3362         {
3363           content_type = g_file_info_get_content_type (file_info);
3364
3365           if (content_type && strcmp (content_type, "image/svg+xml") == 0)
3366             is_svg = TRUE;
3367
3368           g_object_unref (file_info);
3369        }
3370     }
3371
3372   if (is_svg)
3373     {
3374       GInputStream *stream;
3375
3376       icon_info->scale = icon_info->desired_size / 1000.;
3377
3378       if (scale_only)
3379         return TRUE;
3380       
3381       stream = g_loadable_icon_load (icon_info->loadable,
3382                                      icon_info->desired_size,
3383                                      NULL, NULL,
3384                                      &icon_info->load_error);
3385       if (stream)
3386         {
3387           icon_info->pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3388                                                                    icon_info->desired_size,
3389                                                                    icon_info->desired_size,
3390                                                                    TRUE,
3391                                                                    NULL,
3392                                                                    &icon_info->load_error);
3393           g_object_unref (stream);
3394         }
3395
3396       if (!icon_info->pixbuf)
3397         return FALSE;
3398
3399       apply_emblems (icon_info);
3400         
3401       return TRUE;
3402     }
3403
3404   /* In many cases, the scale can be determined without actual access
3405    * to the icon file. This is generally true when we have a size
3406    * for the directory where the icon is; the image size doesn't
3407    * matter in that case.
3408    */
3409   if (icon_info->forced_size)
3410     icon_info->scale = -1;
3411   else if (icon_info->dir_type == ICON_THEME_DIR_FIXED)
3412     icon_info->scale = 1.0;
3413   else if (icon_info->dir_type == ICON_THEME_DIR_THRESHOLD)
3414     {
3415       if (icon_info->desired_size >= icon_info->dir_size - icon_info->threshold &&
3416           icon_info->desired_size <= icon_info->dir_size + icon_info->threshold)
3417         icon_info->scale = 1.0;
3418       else if (icon_info->dir_size > 0)
3419         icon_info->scale =(gdouble) icon_info->desired_size / icon_info->dir_size;
3420     }
3421   else if (icon_info->dir_type == ICON_THEME_DIR_SCALABLE)
3422     {
3423       if (icon_info->dir_size > 0)
3424         icon_info->scale = (gdouble) icon_info->desired_size / icon_info->dir_size;
3425     }
3426
3427   if (icon_info->scale >= 0. && scale_only)
3428     return TRUE;
3429
3430   /* At this point, we need to actually get the icon; either from the
3431    * builtin image or by loading the file
3432    */
3433   source_pixbuf = NULL;
3434   if (icon_info->cache_pixbuf)
3435     source_pixbuf = g_object_ref (icon_info->cache_pixbuf);
3436   else
3437     {
3438       GInputStream *stream;
3439
3440       stream = g_loadable_icon_load (icon_info->loadable,
3441                                      icon_info->desired_size,
3442                                      NULL, NULL,
3443                                      &icon_info->load_error);
3444       if (stream)
3445         {
3446           source_pixbuf = gdk_pixbuf_new_from_stream (stream,
3447                                                       NULL,
3448                                                       &icon_info->load_error);
3449           g_object_unref (stream);
3450         }
3451     }
3452
3453   if (!source_pixbuf)
3454     return FALSE;
3455
3456   /* Do scale calculations that depend on the image size
3457    */
3458   image_width = gdk_pixbuf_get_width (source_pixbuf);
3459   image_height = gdk_pixbuf_get_height (source_pixbuf);
3460
3461   if (icon_info->scale < 0.0)
3462     {
3463       gint image_size = MAX (image_width, image_height);
3464       if (image_size > 0)
3465         icon_info->scale = (gdouble)icon_info->desired_size / (gdouble)image_size;
3466       else
3467         icon_info->scale = 1.0;
3468       
3469       if (icon_info->dir_type == ICON_THEME_DIR_UNTHEMED && 
3470           !icon_info->forced_size)
3471         icon_info->scale = MIN (icon_info->scale, 1.0);
3472     }
3473
3474   /* We don't short-circuit out here for scale_only, since, now
3475    * we've loaded the icon, we might as well go ahead and finish
3476    * the job. This is a bit of a waste when we scale here
3477    * and never get the final pixbuf; at the cost of a bit of
3478    * extra complexity, we could keep the source pixbuf around
3479    * but not actually scale it until needed.
3480    */
3481   if (icon_info->scale == 1.0)
3482     icon_info->pixbuf = source_pixbuf;
3483   else
3484     {
3485       icon_info->pixbuf = gdk_pixbuf_scale_simple (source_pixbuf,
3486                                                    0.5 + image_width * icon_info->scale,
3487                                                    0.5 + image_height * icon_info->scale,
3488                                                    GDK_INTERP_BILINEAR);
3489       g_object_unref (source_pixbuf);
3490     }
3491
3492   apply_emblems (icon_info);
3493
3494   return TRUE;
3495 }
3496
3497 static void
3498 proxy_pixbuf_destroy (guchar *pixels, gpointer data)
3499 {
3500   GtkIconInfo *icon_info = data;
3501   GtkIconTheme *icon_theme = icon_info->in_cache;
3502
3503   g_assert (icon_info->proxy_pixbuf != NULL);
3504   icon_info->proxy_pixbuf = NULL;
3505
3506   /* Keep it alive a bit longer */
3507   if (icon_theme != NULL)
3508     ensure_in_lru_cache (icon_theme, icon_info);
3509
3510   gtk_icon_info_free (icon_info);
3511 }
3512
3513 /**
3514  * gtk_icon_info_load_icon:
3515  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3516  * @error: (allow-none): location to store error information on failure,
3517  *     or %NULL.
3518  *
3519  * Renders an icon previously looked up in an icon theme using
3520  * gtk_icon_theme_lookup_icon(); the size will be based on the size
3521  * passed to gtk_icon_theme_lookup_icon(). Note that the resulting
3522  * pixbuf may not be exactly this size; an icon theme may have icons
3523  * that differ slightly from their nominal sizes, and in addition GTK+
3524  * will avoid scaling icons that it considers sufficiently close to the
3525  * requested size or for which the source image would have to be scaled
3526  * up too far. (This maintains sharpness.). This behaviour can be changed
3527  * by passing the %GTK_ICON_LOOKUP_FORCE_SIZE flag when obtaining
3528  * the #GtkIconInfo. If this flag has been specified, the pixbuf
3529  * returned by this function will be scaled to the exact size.
3530  *
3531  * Return value: (transfer full): the rendered icon; this may be a newly
3532  *     created icon or a new reference to an internal icon, so you must
3533  *     not modify the icon. Use g_object_unref() to release your reference
3534  *     to the icon.
3535  *
3536  * Since: 2.4
3537  **/
3538 GdkPixbuf *
3539 gtk_icon_info_load_icon (GtkIconInfo *icon_info,
3540                          GError     **error)
3541 {
3542   g_return_val_if_fail (icon_info != NULL, NULL);
3543   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3544
3545   if (!icon_info_ensure_scale_and_pixbuf (icon_info, FALSE))
3546     {
3547       if (icon_info->load_error)
3548         g_propagate_error (error, icon_info->load_error);
3549       else
3550         g_set_error_literal (error,  
3551                              GTK_ICON_THEME_ERROR,  
3552                              GTK_ICON_THEME_NOT_FOUND,
3553                              _("Failed to load icon"));
3554  
3555       return NULL;
3556     }
3557
3558   /* Instead of returning the pixbuf directly we
3559      return a proxy to it that we don't own (but that
3560      shares the data with the one we own). This way
3561      we can know when it is freed and ensure the
3562      IconInfo is alive (and thus cached) while
3563      the pixbuf is still alive. */
3564
3565   if (icon_info->proxy_pixbuf != NULL)
3566     return g_object_ref (icon_info->proxy_pixbuf);
3567
3568   icon_info->proxy_pixbuf =
3569     gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (icon_info->pixbuf),
3570                               gdk_pixbuf_get_colorspace (icon_info->pixbuf),
3571                               gdk_pixbuf_get_has_alpha (icon_info->pixbuf),
3572                               gdk_pixbuf_get_bits_per_sample (icon_info->pixbuf),
3573                               gdk_pixbuf_get_width (icon_info->pixbuf),
3574                               gdk_pixbuf_get_height (icon_info->pixbuf),
3575                               gdk_pixbuf_get_rowstride (icon_info->pixbuf),
3576                               proxy_pixbuf_destroy,
3577                               gtk_icon_info_copy (icon_info));
3578
3579   return icon_info->proxy_pixbuf;
3580 }
3581
3582 static void
3583 load_icon_thread  (GTask           *task,
3584                    gpointer         source_object,
3585                    gpointer         task_data,
3586                    GCancellable    *cancellable)
3587 {
3588   GtkIconInfo *dup = task_data;
3589
3590   icon_info_ensure_scale_and_pixbuf (dup, FALSE);
3591   g_task_return_pointer (task, NULL, NULL);
3592 }
3593
3594 /**
3595  * gtk_icon_info_load_icon_async:
3596  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3597  * @cancellable: (allow-none): optional #GCancellable object,
3598  *     %NULL to ignore
3599  * @callback: (scope async): a #GAsyncReadyCallback to call when the
3600  *     request is satisfied
3601  * @user_data: (closure): the data to pass to callback function
3602  *
3603  * Asynchronously load, render and scale an icon previously looked up
3604  * from the icon theme using gtk_icon_theme_lookup_icon().
3605  *
3606  * For more details, see gtk_icon_info_load_icon() which is the synchronous
3607  * version of this call.
3608  *
3609  * Since: 3.8
3610  **/
3611 void
3612 gtk_icon_info_load_icon_async (GtkIconInfo          *icon_info,
3613                                GCancellable         *cancellable,
3614                                GAsyncReadyCallback   callback,
3615                                gpointer              user_data)
3616 {
3617   GTask *task;
3618   GdkPixbuf *pixbuf;
3619   GtkIconInfo *dup;
3620   GError *error = NULL;
3621
3622   task = g_task_new (icon_info, cancellable, callback, user_data);
3623
3624   if (icon_info_get_pixbuf_ready (icon_info))
3625     {
3626       pixbuf = gtk_icon_info_load_icon (icon_info, &error);
3627       if (pixbuf == NULL)
3628         g_task_return_error (task, error);
3629       else
3630         g_task_return_pointer (task, pixbuf, g_object_unref);
3631       g_object_unref (task);
3632     }
3633   else
3634     {
3635       dup = icon_info_dup (icon_info);
3636       g_task_set_task_data (task, dup, g_object_unref);
3637       g_task_run_in_thread (task, load_icon_thread);
3638       g_object_unref (task);
3639     }
3640 }
3641
3642 /**
3643  * gtk_icon_info_load_icon_finish:
3644  * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
3645  * @res: a #GAsyncResult
3646  * @error: (allow-none): location to store error information on failure,
3647  *     or %NULL.
3648  *
3649  * Finishes an async icon load, see gtk_icon_info_load_icon_async().
3650  *
3651  * Return value: (transfer full): the rendered icon; this may be a newly
3652  *     created icon or a new reference to an internal icon, so you must
3653  *     not modify the icon. Use g_object_unref() to release your reference
3654  *     to the icon.
3655  *
3656  * Since: 3.8
3657  **/
3658 GdkPixbuf *
3659 gtk_icon_info_load_icon_finish (GtkIconInfo          *icon_info,
3660                                 GAsyncResult         *result,
3661                                 GError              **error)
3662 {
3663   GTask *task = G_TASK (result);
3664   GtkIconInfo *dup;
3665
3666   g_return_val_if_fail (g_task_is_valid (result, icon_info), NULL);
3667
3668   dup = g_task_get_task_data (task);
3669   if (dup == NULL || g_task_had_error (task))
3670     return g_task_propagate_pointer (task, error);
3671
3672   /* We ran the thread and it was not cancelled */
3673
3674   /* Check if someone else updated the icon_info in between */
3675   if (!icon_info_get_pixbuf_ready (icon_info))
3676     {
3677       /* If not, copy results from dup back to icon_info */
3678
3679       icon_info->emblems_applied = dup->emblems_applied;
3680       icon_info->scale = dup->scale;
3681       g_clear_object (&icon_info->pixbuf);
3682       if (dup->pixbuf)
3683         icon_info->pixbuf = g_object_ref (dup->pixbuf);
3684       g_clear_error (&icon_info->load_error);
3685       if (dup->load_error)
3686         icon_info->load_error = g_error_copy (dup->load_error);
3687     }
3688
3689   g_assert (icon_info_get_pixbuf_ready (icon_info));
3690
3691   /* This is now guaranteed to not block */
3692   return gtk_icon_info_load_icon (icon_info, error);
3693 }
3694
3695 static gchar *
3696 gdk_color_to_css (GdkColor *color)
3697 {
3698   return g_strdup_printf ("rgb(%d,%d,%d)",
3699                           color->red >> 8,
3700                           color->green >> 8,
3701                           color->blue >> 8);
3702 }
3703
3704 static gchar *
3705 gdk_rgba_to_css (const GdkRGBA *color)
3706 {
3707   /* drop alpha for now, since librsvg does not understand rgba() */
3708   return g_strdup_printf ("rgb(%d,%d,%d)",
3709                           (gint)(color->red * 255),
3710                           (gint)(color->green * 255),
3711                           (gint)(color->blue * 255));
3712 }
3713
3714 static void
3715 proxy_symbolic_pixbuf_destroy (guchar *pixels, gpointer data)
3716 {
3717   GtkIconInfo *icon_info = data;
3718   GtkIconTheme *icon_theme = icon_info->in_cache;
3719   SymbolicPixbufCache *symbolic_cache;
3720
3721   for (symbolic_cache = icon_info->symbolic_pixbuf_cache;
3722        symbolic_cache != NULL;
3723        symbolic_cache = symbolic_cache->next)
3724     {
3725       if (symbolic_cache->proxy_pixbuf != NULL &&
3726           gdk_pixbuf_get_pixels (symbolic_cache->proxy_pixbuf) == pixels)
3727         break;
3728     }
3729
3730   g_assert (symbolic_cache != NULL);
3731   g_assert (symbolic_cache->proxy_pixbuf != NULL);
3732
3733   symbolic_cache->proxy_pixbuf = NULL;
3734
3735   /* Keep it alive a bit longer */
3736   if (icon_theme != NULL)
3737     ensure_in_lru_cache (icon_theme, icon_info);
3738
3739   gtk_icon_info_free (icon_info);
3740 }
3741
3742 static GdkPixbuf *
3743 symbolic_cache_get_proxy (SymbolicPixbufCache *symbolic_cache,
3744                           GtkIconInfo  *icon_info)
3745 {
3746   if (symbolic_cache->proxy_pixbuf)
3747     return g_object_ref (symbolic_cache->proxy_pixbuf);
3748
3749   symbolic_cache->proxy_pixbuf =
3750     gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (symbolic_cache->pixbuf),
3751                               gdk_pixbuf_get_colorspace (symbolic_cache->pixbuf),
3752                               gdk_pixbuf_get_has_alpha (symbolic_cache->pixbuf),
3753                               gdk_pixbuf_get_bits_per_sample (symbolic_cache->pixbuf),
3754                               gdk_pixbuf_get_width (symbolic_cache->pixbuf),
3755                               gdk_pixbuf_get_height (symbolic_cache->pixbuf),
3756                               gdk_pixbuf_get_rowstride (symbolic_cache->pixbuf),
3757                               proxy_symbolic_pixbuf_destroy,
3758                               gtk_icon_info_copy (icon_info));
3759
3760   return symbolic_cache->proxy_pixbuf;
3761 }
3762
3763 static GdkPixbuf *
3764 _gtk_icon_info_load_symbolic_internal (GtkIconInfo  *icon_info,
3765                                        const GdkRGBA  *fg,
3766                                        const GdkRGBA  *success_color,
3767                                        const GdkRGBA  *warning_color,
3768                                        const GdkRGBA  *error_color,
3769                                        GError      **error)
3770 {
3771   GInputStream *stream;
3772   GdkPixbuf *pixbuf;
3773   gchar *css_fg;
3774   gchar *css_success;
3775   gchar *css_warning;
3776   gchar *css_error;
3777   gchar *data;
3778   gchar *width, *height, *uri;
3779   SymbolicPixbufCache *symbolic_cache;
3780
3781   symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
3782                                                   fg, success_color, warning_color, error_color);
3783   if (symbolic_cache)
3784     return symbolic_cache_get_proxy (symbolic_cache, icon_info);
3785
3786   /* css_fg can't possibly have failed, otherwise
3787    * that would mean we have a broken style */
3788   g_return_val_if_fail (fg != NULL, NULL);
3789
3790   css_fg = gdk_rgba_to_css (fg);
3791
3792   css_success = css_warning = css_error = NULL;
3793
3794   if (warning_color)
3795     css_warning = gdk_rgba_to_css (warning_color);
3796
3797   if (error_color)
3798     css_error = gdk_rgba_to_css (error_color);
3799
3800   if (success_color)
3801     css_success = gdk_rgba_to_css (success_color);
3802
3803   if (!css_success)
3804     {
3805       GdkColor success_default_color = { 0, 0x4e00, 0x9a00, 0x0600 };
3806       css_success = gdk_color_to_css (&success_default_color);
3807     }
3808   if (!css_warning)
3809     {
3810       GdkColor warning_default_color = { 0, 0xf500, 0x7900, 0x3e00 };
3811       css_warning = gdk_color_to_css (&warning_default_color);
3812     }
3813   if (!css_error)
3814     {
3815       GdkColor error_default_color = { 0, 0xcc00, 0x0000, 0x0000 };
3816       css_error = gdk_color_to_css (&error_default_color);
3817     }
3818
3819   if (!icon_info->symbolic_pixbuf_size)
3820     {
3821       stream = G_INPUT_STREAM (g_file_read (icon_info->icon_file, NULL, error));
3822
3823       if (!stream)
3824         return NULL;
3825
3826       /* Fetch size from the original icon */
3827       pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
3828       g_object_unref (stream);
3829
3830       if (!pixbuf)
3831         return NULL;
3832
3833       icon_info->symbolic_pixbuf_size = gtk_requisition_new ();
3834       icon_info->symbolic_pixbuf_size->width = gdk_pixbuf_get_width (pixbuf);
3835       icon_info->symbolic_pixbuf_size->height = gdk_pixbuf_get_height (pixbuf);
3836       g_object_unref (pixbuf);
3837     }
3838
3839   width = g_strdup_printf ("%d", icon_info->symbolic_pixbuf_size->width);
3840   height = g_strdup_printf ("%d", icon_info->symbolic_pixbuf_size->height);
3841   uri = g_file_get_uri (icon_info->icon_file);
3842
3843   data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
3844                       "<svg version=\"1.1\"\n"
3845                       "     xmlns=\"http://www.w3.org/2000/svg\"\n"
3846                       "     xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n"
3847                       "     width=\"", width, "\"\n"
3848                       "     height=\"", height, "\">\n"
3849                       "  <style type=\"text/css\">\n"
3850                       "    rect,path {\n"
3851                       "      fill: ", css_fg," !important;\n"
3852                       "    }\n"
3853                       "    .warning {\n"
3854                       "      fill: ", css_warning, " !important;\n"
3855                       "    }\n"
3856                       "    .error {\n"
3857                       "      fill: ", css_error ," !important;\n"
3858                       "    }\n"
3859                       "    .success {\n"
3860                       "      fill: ", css_success, " !important;\n"
3861                       "    }\n"
3862                       "  </style>\n"
3863                       "  <xi:include href=\"", uri, "\"/>\n"
3864                       "</svg>",
3865                       NULL);
3866   g_free (css_fg);
3867   g_free (css_warning);
3868   g_free (css_error);
3869   g_free (css_success);
3870   g_free (width);
3871   g_free (height);
3872   g_free (uri);
3873
3874   stream = g_memory_input_stream_new_from_data (data, -1, g_free);
3875   pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3876                                                 icon_info->desired_size,
3877                                                 icon_info->desired_size,
3878                                                 TRUE,
3879                                                 NULL,
3880                                                 error);
3881   g_object_unref (stream);
3882
3883   if (pixbuf != NULL)
3884     {
3885       icon_info->symbolic_pixbuf_cache =
3886         symbolic_pixbuf_cache_new (pixbuf, fg, success_color, warning_color, error_color,
3887                                    icon_info->symbolic_pixbuf_cache);
3888       g_object_unref (pixbuf);
3889       return symbolic_cache_get_proxy (icon_info->symbolic_pixbuf_cache, icon_info);
3890     }
3891
3892   return NULL;
3893 }
3894
3895 /**
3896  * gtk_icon_info_load_symbolic:
3897  * @icon_info: a #GtkIconInfo
3898  * @fg: a #GdkRGBA representing the foreground color of the icon
3899  * @success_color: (allow-none): a #GdkRGBA representing the warning color
3900  *     of the icon or %NULL to use the default color
3901  * @warning_color: (allow-none): a #GdkRGBA representing the warning color
3902  *     of the icon or %NULL to use the default color
3903  * @error_color: (allow-none): a #GdkRGBA representing the error color
3904  *     of the icon or %NULL to use the default color (allow-none)
3905  * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3906  *     loaded icon was a symbolic one and whether the @fg color was
3907  *     applied to it.
3908  * @error: (allow-none): location to store error information on failure,
3909  *     or %NULL.
3910  *
3911  * Loads an icon, modifying it to match the system colours for the foreground,
3912  * success, warning and error colors provided. If the icon is not a symbolic
3913  * one, the function will return the result from gtk_icon_info_load_icon().
3914  *
3915  * This allows loading symbolic icons that will match the system theme.
3916  *
3917  * Unless you are implementing a widget, you will want to use
3918  * g_themed_icon_new_with_default_fallbacks() to load the icon.
3919  *
3920  * As implementation details, the icon loaded needs to be of SVG type,
3921  * contain the "symbolic" term as the last component of the icon name,
3922  * and use the 'fg', 'success', 'warning' and 'error' CSS styles in the
3923  * SVG file itself.
3924  *
3925  * See the <ulink url="http://www.freedesktop.org/wiki/SymbolicIcons">Symbolic Icons spec</ulink>
3926  * for more information about symbolic icons.
3927  *
3928  * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
3929  *
3930  * Since: 3.0
3931  **/
3932 GdkPixbuf *
3933 gtk_icon_info_load_symbolic (GtkIconInfo    *icon_info,
3934                              const GdkRGBA  *fg,
3935                              const GdkRGBA  *success_color,
3936                              const GdkRGBA  *warning_color,
3937                              const GdkRGBA  *error_color,
3938                              gboolean       *was_symbolic,
3939                              GError        **error)
3940 {
3941   gchar *icon_uri;
3942   gboolean is_symbolic;
3943
3944   g_return_val_if_fail (icon_info != NULL, NULL);
3945   g_return_val_if_fail (fg != NULL, NULL);
3946
3947   icon_uri = NULL;
3948   if (icon_info->icon_file)
3949     icon_uri = g_file_get_uri (icon_info->icon_file);
3950
3951   is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
3952   g_free (icon_uri);
3953
3954   if (was_symbolic)
3955     *was_symbolic = is_symbolic;
3956
3957   if (!is_symbolic)
3958     return gtk_icon_info_load_icon (icon_info, error);
3959
3960   return _gtk_icon_info_load_symbolic_internal (icon_info,
3961                                                 fg, success_color,
3962                                                 warning_color, error_color,
3963                                                 error);
3964 }
3965
3966 /**
3967  * gtk_icon_info_load_symbolic_for_context:
3968  * @icon_info: a #GtkIconInfo
3969  * @context: a #GtkStyleContext
3970  * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
3971  *     loaded icon was a symbolic one and whether the @fg color was
3972  *     applied to it.
3973  * @error: (allow-none): location to store error information on failure,
3974  *     or %NULL.
3975  *
3976  * Loads an icon, modifying it to match the system colors for the foreground,
3977  * success, warning and error colors provided. If the icon is not a symbolic
3978  * one, the function will return the result from gtk_icon_info_load_icon().
3979  * This function uses the regular foreground color and the symbolic colors
3980  * with the names "success_color", "warning_color" and "error_color" from
3981  * the context.
3982  *
3983  * This allows loading symbolic icons that will match the system theme.
3984  *
3985  * See gtk_icon_info_load_symbolic() for more details.
3986  *
3987  * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
3988  *
3989  * Since: 3.0
3990  **/
3991 GdkPixbuf *
3992 gtk_icon_info_load_symbolic_for_context (GtkIconInfo      *icon_info,
3993                                          GtkStyleContext  *context,
3994                                          gboolean         *was_symbolic,
3995                                          GError          **error)
3996 {
3997   GdkRGBA *color = NULL;
3998   GdkRGBA fg;
3999   GdkRGBA *fgp;
4000   GdkRGBA success_color;
4001   GdkRGBA *success_colorp;
4002   GdkRGBA warning_color;
4003   GdkRGBA *warning_colorp;
4004   GdkRGBA error_color;
4005   GdkRGBA *error_colorp;
4006   GtkStateFlags state;
4007   gchar *icon_uri;
4008   gboolean is_symbolic;
4009
4010   g_return_val_if_fail (icon_info != NULL, NULL);
4011   g_return_val_if_fail (context != NULL, NULL);
4012
4013   icon_uri = NULL;
4014   if (icon_info->icon_file)
4015     icon_uri = g_file_get_uri (icon_info->icon_file);
4016
4017   is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
4018   g_free (icon_uri);
4019
4020   if (was_symbolic)
4021     *was_symbolic = is_symbolic;
4022
4023   if (!is_symbolic)
4024     return gtk_icon_info_load_icon (icon_info, error);
4025
4026   fgp = success_colorp = warning_colorp = error_colorp = NULL;
4027
4028   state = gtk_style_context_get_state (context);
4029   gtk_style_context_get (context, state, "color", &color, NULL);
4030   if (color)
4031     {
4032       fg = *color;
4033       fgp = &fg;
4034       gdk_rgba_free (color);
4035     }
4036
4037   if (gtk_style_context_lookup_color (context, "success_color", &success_color))
4038     success_colorp = &success_color;
4039
4040   if (gtk_style_context_lookup_color (context, "warning_color", &warning_color))
4041     warning_colorp = &warning_color;
4042
4043   if (gtk_style_context_lookup_color (context, "error_color", &error_color))
4044     error_colorp = &error_color;
4045
4046   return _gtk_icon_info_load_symbolic_internal (icon_info,
4047                                                 fgp, success_colorp,
4048                                                 warning_colorp, error_colorp,
4049                                                 error);
4050 }
4051
4052 static GdkRGBA *
4053 color_to_rgba (GdkColor *color, GdkRGBA *rgba)
4054 {
4055   rgba->red = color->red / 65535.0;
4056   rgba->green = color->green / 65535.0;
4057   rgba->blue = color->blue / 65535.0;
4058   rgba->alpha = 1.0;
4059   return rgba;
4060 }
4061
4062 /**
4063  * gtk_icon_info_load_symbolic_for_style:
4064  * @icon_info: a #GtkIconInfo
4065  * @style: a #GtkStyle to take the colors from
4066  * @state: the widget state to use for colors
4067  * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
4068  *     loaded icon was a symbolic one and whether the @fg color was
4069  *     applied to it.
4070  * @error: (allow-none): location to store error information on failure,
4071  *     or %NULL.
4072  *
4073  * Loads an icon, modifying it to match the system colours for the foreground,
4074  * success, warning and error colors provided. If the icon is not a symbolic
4075  * one, the function will return the result from gtk_icon_info_load_icon().
4076  *
4077  * This allows loading symbolic icons that will match the system theme.
4078  *
4079  * See gtk_icon_info_load_symbolic() for more details.
4080  *
4081  * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
4082  *
4083  * Since: 3.0
4084  *
4085  * Deprecated: 3.0: Use gtk_icon_info_load_symbolic_for_context() instead
4086  **/
4087 GdkPixbuf *
4088 gtk_icon_info_load_symbolic_for_style (GtkIconInfo   *icon_info,
4089                                        GtkStyle      *style,
4090                                        GtkStateType   state,
4091                                        gboolean      *was_symbolic,
4092                                        GError       **error)
4093 {
4094   GdkColor color;
4095   GdkRGBA fg;
4096   GdkRGBA success_color;
4097   GdkRGBA *success_colorp;
4098   GdkRGBA warning_color;
4099   GdkRGBA *warning_colorp;
4100   GdkRGBA error_color;
4101   GdkRGBA *error_colorp;
4102   gchar *icon_uri;
4103   gboolean is_symbolic;
4104
4105   g_return_val_if_fail (icon_info != NULL, NULL);
4106   g_return_val_if_fail (style != NULL, NULL);
4107
4108   icon_uri = NULL;
4109   if (icon_info->icon_file)
4110     icon_uri = g_file_get_uri (icon_info->icon_file);
4111
4112   is_symbolic = (icon_uri != NULL) && (g_str_has_suffix (icon_uri, "-symbolic.svg"));
4113   g_free (icon_uri);
4114
4115   if (was_symbolic)
4116     *was_symbolic = is_symbolic;
4117
4118   if (!is_symbolic)
4119     return gtk_icon_info_load_icon (icon_info, error);
4120
4121   color_to_rgba (&style->fg[state], &fg);
4122
4123   success_colorp = warning_colorp = error_colorp = NULL;
4124
4125   if (gtk_style_lookup_color (style, "success_color", &color))
4126     success_colorp = color_to_rgba (&color, &success_color);
4127
4128   if (gtk_style_lookup_color (style, "warning_color", &color))
4129     warning_colorp = color_to_rgba (&color, &warning_color);
4130
4131   if (gtk_style_lookup_color (style, "error_color", &color))
4132     error_colorp = color_to_rgba (&color, &error_color);
4133
4134   return _gtk_icon_info_load_symbolic_internal (icon_info,
4135                                                 &fg, success_colorp,
4136                                                 warning_colorp, error_colorp,
4137                                                 error);
4138 }
4139
4140 /**
4141  * gtk_icon_info_set_raw_coordinates:
4142  * @icon_info: a #GtkIconInfo
4143  * @raw_coordinates: whether the coordinates of embedded rectangles
4144  *   and attached points should be returned in their original
4145  *   (unscaled) form.
4146  * 
4147  * Sets whether the coordinates returned by gtk_icon_info_get_embedded_rect()
4148  * and gtk_icon_info_get_attach_points() should be returned in their
4149  * original form as specified in the icon theme, instead of scaled
4150  * appropriately for the pixbuf returned by gtk_icon_info_load_icon().
4151  *
4152  * Raw coordinates are somewhat strange; they are specified to be with
4153  * respect to the unscaled pixmap for PNG and XPM icons, but for SVG
4154  * icons, they are in a 1000x1000 coordinate space that is scaled
4155  * to the final size of the icon.  You can determine if the icon is an SVG
4156  * icon by using gtk_icon_info_get_filename(), and seeing if it is non-%NULL
4157  * and ends in '.svg'.
4158  *
4159  * This function is provided primarily to allow compatibility wrappers
4160  * for older API's, and is not expected to be useful for applications.
4161  *
4162  * Since: 2.4
4163  **/
4164 void
4165 gtk_icon_info_set_raw_coordinates (GtkIconInfo *icon_info,
4166                                    gboolean     raw_coordinates)
4167 {
4168   g_return_if_fail (icon_info != NULL);
4169   
4170   icon_info->raw_coordinates = raw_coordinates != FALSE;
4171 }
4172
4173 /* Scale coordinates from the icon data prior to returning
4174  * them to the user.
4175  */
4176 static gboolean
4177 icon_info_scale_point (GtkIconInfo  *icon_info,
4178                        gint          x,
4179                        gint          y,
4180                        gint         *x_out,
4181                        gint         *y_out)
4182 {
4183   if (icon_info->raw_coordinates)
4184     {
4185       *x_out = x;
4186       *y_out = y;
4187     }
4188   else
4189     {
4190       if (!icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4191         return FALSE;
4192
4193       *x_out = 0.5 + x * icon_info->scale;
4194       *y_out = 0.5 + y * icon_info->scale;
4195     }
4196
4197   return TRUE;
4198 }
4199
4200 /**
4201  * gtk_icon_info_get_embedded_rect:
4202  * @icon_info: a #GtkIconInfo
4203  * @rectangle: (out): #GdkRectangle in which to store embedded
4204  *   rectangle coordinates; coordinates are only stored
4205  *   when this function returns %TRUE.
4206  *
4207  * Gets the coordinates of a rectangle within the icon
4208  * that can be used for display of information such
4209  * as a preview of the contents of a text file.
4210  * See gtk_icon_info_set_raw_coordinates() for further
4211  * information about the coordinate system.
4212  * 
4213  * Return value: %TRUE if the icon has an embedded rectangle
4214  *
4215  * Since: 2.4
4216  **/
4217 gboolean
4218 gtk_icon_info_get_embedded_rect (GtkIconInfo  *icon_info,
4219                                  GdkRectangle *rectangle)
4220 {
4221   g_return_val_if_fail (icon_info != NULL, FALSE);
4222
4223   if (icon_info->data && icon_info->data->has_embedded_rect &&
4224       icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4225     {
4226       gint scaled_x0, scaled_y0;
4227       gint scaled_x1, scaled_y1;
4228       
4229       if (rectangle)
4230         {
4231           icon_info_scale_point (icon_info,
4232                                  icon_info->data->x0, icon_info->data->y0,
4233                                  &scaled_x0, &scaled_y0);
4234           icon_info_scale_point (icon_info,
4235                                  icon_info->data->x1, icon_info->data->y1,
4236                                  &scaled_x1, &scaled_y1);
4237           
4238           rectangle->x = scaled_x0;
4239           rectangle->y = scaled_y0;
4240           rectangle->width = scaled_x1 - rectangle->x;
4241           rectangle->height = scaled_y1 - rectangle->y;
4242         }
4243
4244       return TRUE;
4245     }
4246   else
4247     return FALSE;
4248 }
4249
4250 /**
4251  * gtk_icon_info_get_attach_points:
4252  * @icon_info: a #GtkIconInfo
4253  * @points: (allow-none) (array length=n_points) (out): location to store pointer to an array of points, or %NULL
4254  *          free the array of points with g_free().
4255  * @n_points: (allow-none): location to store the number of points in @points, or %NULL
4256  * 
4257  * Fetches the set of attach points for an icon. An attach point
4258  * is a location in the icon that can be used as anchor points for attaching
4259  * emblems or overlays to the icon.
4260  * 
4261  * Return value: %TRUE if there are any attach points for the icon.
4262  *
4263  * Since: 2.4
4264  **/
4265 gboolean
4266 gtk_icon_info_get_attach_points (GtkIconInfo *icon_info,
4267                                  GdkPoint   **points,
4268                                  gint        *n_points)
4269 {
4270   g_return_val_if_fail (icon_info != NULL, FALSE);
4271   
4272   if (icon_info->data && icon_info->data->n_attach_points &&
4273       icon_info_ensure_scale_and_pixbuf (icon_info, TRUE))
4274     {
4275       if (points)
4276         {
4277           gint i;
4278           
4279           *points = g_new (GdkPoint, icon_info->data->n_attach_points);
4280           for (i = 0; i < icon_info->data->n_attach_points; i++)
4281             icon_info_scale_point (icon_info,
4282                                    icon_info->data->attach_points[i].x,
4283                                    icon_info->data->attach_points[i].y,
4284                                    &(*points)[i].x,
4285                                    &(*points)[i].y);
4286         }
4287           
4288       if (n_points)
4289         *n_points = icon_info->data->n_attach_points;
4290
4291       return TRUE;
4292     }
4293   else
4294     {
4295       if (points)
4296         *points = NULL;
4297       if (n_points)
4298         *n_points = 0;
4299       
4300       return FALSE;
4301     }
4302 }
4303
4304 /**
4305  * gtk_icon_info_get_display_name:
4306  * @icon_info: a #GtkIconInfo
4307  * 
4308  * Gets the display name for an icon. A display name is a
4309  * string to be used in place of the icon name in a user
4310  * visible context like a list of icons.
4311  * 
4312  * Return value: the display name for the icon or %NULL, if
4313  *  the icon doesn't have a specified display name. This value
4314  *  is owned @icon_info and must not be modified or free.
4315  *
4316  * Since: 2.4
4317  **/
4318 const gchar *
4319 gtk_icon_info_get_display_name (GtkIconInfo *icon_info)
4320 {
4321   g_return_val_if_fail (icon_info != NULL, NULL);
4322
4323   if (icon_info->data)
4324     return icon_info->data->display_name;
4325   else
4326     return NULL;
4327 }
4328
4329 /*
4330  * Builtin icons
4331  */
4332
4333
4334 /**
4335  * gtk_icon_theme_add_builtin_icon:
4336  * @icon_name: the name of the icon to register
4337  * @size: the size at which to register the icon (different
4338  *        images can be registered for the same icon name
4339  *        at different sizes.)
4340  * @pixbuf: #GdkPixbuf that contains the image to use
4341  *          for @icon_name.
4342  * 
4343  * Registers a built-in icon for icon theme lookups. The idea
4344  * of built-in icons is to allow an application or library
4345  * that uses themed icons to function requiring files to
4346  * be present in the file system. For instance, the default
4347  * images for all of GTK+'s stock icons are registered
4348  * as built-icons.
4349  *
4350  * In general, if you use gtk_icon_theme_add_builtin_icon()
4351  * you should also install the icon in the icon theme, so
4352  * that the icon is generally available.
4353  *
4354  * This function will generally be used with pixbufs loaded
4355  * via gdk_pixbuf_new_from_inline().
4356  *
4357  * Since: 2.4
4358  **/
4359 void
4360 gtk_icon_theme_add_builtin_icon (const gchar *icon_name,
4361                                  gint         size,
4362                                  GdkPixbuf   *pixbuf)
4363 {
4364   BuiltinIcon *default_icon;
4365   GSList *icons;
4366   gpointer key;
4367
4368   g_return_if_fail (icon_name != NULL);
4369   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
4370   
4371   if (!icon_theme_builtin_icons)
4372     icon_theme_builtin_icons = g_hash_table_new (g_str_hash, g_str_equal);
4373
4374   icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
4375   if (!icons)
4376     key = g_strdup (icon_name);
4377   else
4378     key = (gpointer)icon_name;  /* Won't get stored */
4379
4380   default_icon = g_new (BuiltinIcon, 1);
4381   default_icon->size = size;
4382   default_icon->pixbuf = g_object_ref (pixbuf);
4383   icons = g_slist_prepend (icons, default_icon);
4384
4385   /* Replaces value, leaves key untouched
4386    */
4387   g_hash_table_insert (icon_theme_builtin_icons, key, icons);
4388 }
4389
4390 /* Look up a builtin icon; the min_difference_p and
4391  * has_larger_p out parameters allow us to combine
4392  * this lookup with searching through the actual directories
4393  * of the "hicolor" icon theme. See theme_lookup_icon()
4394  * for how they are used.
4395  */
4396 static BuiltinIcon *
4397 find_builtin_icon (const gchar *icon_name,
4398                    gint         size,
4399                    gint        *min_difference_p,
4400                    gboolean    *has_larger_p)
4401 {
4402   GSList *icons = NULL;
4403   gint min_difference = G_MAXINT;
4404   gboolean has_larger = FALSE;
4405   BuiltinIcon *min_icon = NULL;
4406   
4407   if (!icon_theme_builtin_icons)
4408     return NULL;
4409
4410   icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
4411
4412   while (icons)
4413     {
4414       BuiltinIcon *default_icon = icons->data;
4415       int min, max, difference;
4416       gboolean smaller;
4417       
4418       min = default_icon->size - 2;
4419       max = default_icon->size + 2;
4420       smaller = size < min;
4421       if (size < min)
4422         difference = min - size;
4423       else if (size > max)
4424         difference = size - max;
4425       else
4426         difference = 0;
4427       
4428       if (difference == 0)
4429         {
4430           min_icon = default_icon;
4431           break;
4432         }
4433       
4434       if (!has_larger)
4435         {
4436           if (difference < min_difference || smaller)
4437             {
4438               min_difference = difference;
4439               min_icon = default_icon;
4440               has_larger = smaller;
4441             }
4442         }
4443       else
4444         {
4445           if (difference < min_difference && smaller)
4446             {
4447               min_difference = difference;
4448               min_icon = default_icon;
4449             }
4450         }
4451       
4452       icons = icons->next;
4453     }
4454
4455   if (min_difference_p)
4456     *min_difference_p = min_difference;
4457   if (has_larger_p)
4458     *has_larger_p = has_larger;
4459
4460   return min_icon;
4461 }
4462
4463 void
4464 _gtk_icon_theme_check_reload (GdkDisplay *display)
4465 {
4466   gint n_screens, i;
4467   GdkScreen *screen;
4468   GtkIconTheme *icon_theme;
4469
4470   n_screens = gdk_display_get_n_screens (display);
4471   
4472   for (i = 0; i < n_screens; i++)
4473     {
4474       screen = gdk_display_get_screen (display, i);
4475
4476       icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
4477       if (icon_theme)
4478         {
4479           icon_theme->priv->check_reload = TRUE;
4480           ensure_valid_themes (icon_theme);
4481           icon_theme->priv->check_reload = FALSE;
4482         }
4483     }
4484 }
4485
4486
4487 /**
4488  * gtk_icon_theme_lookup_by_gicon:
4489  * @icon_theme: a #GtkIconTheme
4490  * @icon: the #GIcon to look up
4491  * @size: desired icon size
4492  * @flags: flags modifying the behavior of the icon lookup
4493  * 
4494  * Looks up an icon and returns a structure containing
4495  * information such as the filename of the icon. 
4496  * The icon can then be rendered into a pixbuf using
4497  * gtk_icon_info_load_icon().
4498  *
4499  * Return value: a #GtkIconInfo structure containing 
4500  *     information about the icon, or %NULL if the icon 
4501  *     wasn't found. Free with gtk_icon_info_free()
4502  *
4503  * Since: 2.14
4504  */
4505 GtkIconInfo *
4506 gtk_icon_theme_lookup_by_gicon (GtkIconTheme       *icon_theme,
4507                                 GIcon              *icon,
4508                                 gint                size,
4509                                 GtkIconLookupFlags  flags)
4510 {
4511   GtkIconInfo *info;
4512
4513   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
4514   g_return_val_if_fail (G_IS_ICON (icon), NULL);
4515
4516   if (G_IS_LOADABLE_ICON (icon))
4517     {
4518       info = icon_info_new ();
4519       info->loadable = G_LOADABLE_ICON (g_object_ref (icon));
4520
4521       if (G_IS_FILE_ICON (icon))
4522         {
4523           GFile *file = g_file_icon_get_file (G_FILE_ICON (icon));
4524           if (file != NULL)
4525             {
4526               info->icon_file = g_object_ref (file);
4527               info->filename = g_file_get_path (file);
4528             }
4529         }
4530
4531       info->dir_type = ICON_THEME_DIR_UNTHEMED;
4532       info->dir_size = size;
4533       info->desired_size = size;
4534       info->threshold = 2;
4535       info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
4536
4537       return info;
4538     }
4539   else if (G_IS_THEMED_ICON (icon))
4540     {
4541       const gchar **names;
4542
4543       names = (const gchar **)g_themed_icon_get_names (G_THEMED_ICON (icon));
4544       info = gtk_icon_theme_choose_icon (icon_theme, names, size, flags);
4545
4546       return info;
4547     }
4548   else if (G_IS_EMBLEMED_ICON (icon))
4549     {
4550       GIcon *base, *emblem;
4551       GList *list, *l;
4552       GtkIconInfo *emblem_info;
4553
4554       if (GTK_IS_NUMERABLE_ICON (icon))
4555         _gtk_numerable_icon_set_background_icon_size (GTK_NUMERABLE_ICON (icon), size / 2);
4556
4557       base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
4558       info = gtk_icon_theme_lookup_by_gicon (icon_theme, base, size, flags);
4559       if (info)
4560         {
4561           list = g_emblemed_icon_get_emblems (G_EMBLEMED_ICON (icon));
4562           for (l = list; l; l = l->next)
4563             {
4564               emblem = g_emblem_get_icon (G_EMBLEM (l->data));
4565               /* always force size for emblems */
4566               emblem_info = gtk_icon_theme_lookup_by_gicon (icon_theme, emblem, size / 2, flags | GTK_ICON_LOOKUP_FORCE_SIZE);
4567               if (emblem_info)
4568                 info->emblem_infos = g_slist_prepend (info->emblem_infos, emblem_info);
4569             }
4570         }
4571
4572       return info;
4573     }
4574   else if (GDK_IS_PIXBUF (icon))
4575     {
4576       GdkPixbuf *pixbuf;
4577
4578       pixbuf = GDK_PIXBUF (icon);
4579
4580       if ((flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0)
4581         {
4582           gint width, height, max;
4583           gdouble scale;
4584           GdkPixbuf *scaled;
4585
4586           width = gdk_pixbuf_get_width (pixbuf);
4587           height = gdk_pixbuf_get_height (pixbuf);
4588           max = MAX (width, height);
4589           scale = (gdouble) size / (gdouble) max;
4590
4591           scaled = gdk_pixbuf_scale_simple (pixbuf,
4592                                             0.5 + width * scale,
4593                                             0.5 + height * scale,
4594                                             GDK_INTERP_BILINEAR);
4595
4596           info = gtk_icon_info_new_for_pixbuf (icon_theme, scaled);
4597
4598           g_object_unref (scaled);
4599         }
4600       else
4601         {
4602           info = gtk_icon_info_new_for_pixbuf (icon_theme, pixbuf);
4603         }
4604
4605       return info;
4606     }
4607
4608   return NULL;
4609 }
4610
4611 /**
4612  * gtk_icon_info_new_for_pixbuf:
4613  * @icon_theme: a #GtkIconTheme
4614  * @pixbuf: the pixbuf to wrap in a #GtkIconInfo
4615  *
4616  * Creates a #GtkIconInfo for a #GdkPixbuf.
4617  *
4618  * Returns: a #GtkIconInfo
4619  *
4620  * Since: 2.14
4621  */
4622 GtkIconInfo *
4623 gtk_icon_info_new_for_pixbuf (GtkIconTheme *icon_theme,
4624                               GdkPixbuf    *pixbuf)
4625 {
4626   GtkIconInfo *info;
4627
4628   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
4629   g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
4630
4631   info = icon_info_new ();
4632   info->pixbuf = g_object_ref (pixbuf);
4633   info->scale = 1.0;
4634   info->dir_type = ICON_THEME_DIR_UNTHEMED;
4635
4636   return info;
4637 }