]> Pileus Git - ~andy/gtk/blob - gtk/gtkiconfactory.c
bf418eb49814aa255c3360c80fc55a4726911de6
[~andy/gtk] / gtk / gtkiconfactory.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2000 Red Hat, Inc.
3  *               2008 Johan Dahlin
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 /*
19  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20  * file for a list of people on the GTK+ Team.  See the ChangeLog
21  * files for a list of changes.  These files are distributed with
22  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23  */
24
25 #include "config.h"
26
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <string.h>
30
31 #include "gtkiconfactory.h"
32 #include "gtkiconcache.h"
33 #include "gtkdebug.h"
34 #include "gtkicontheme.h"
35 #include "gtksettingsprivate.h"
36 #include "gtkstock.h"
37 #include "gtkwidget.h"
38 #include "gtkintl.h"
39 #include "gtkbuildable.h"
40 #include "gtkbuilderprivate.h"
41 #include "gtktypebuiltins.h"
42 #include "deprecated/gtkstyle.h"
43
44
45 /**
46  * SECTION:gtkiconfactory
47  * @Short_description: Manipulating stock icons
48  * @Title: Themeable Stock Images
49  *
50  * Browse the available stock icons in the list of stock IDs found <link
51  * linkend="gtk-Stock-Items">here</link>. You can also use
52  * the <application>gtk-demo</application> application for this purpose.
53  *
54  * An icon factory manages a collection of #GtkIconSet; a #GtkIconSet manages a
55  * set of variants of a particular icon (i.e. a #GtkIconSet contains variants for
56  * different sizes and widget states). Icons in an icon factory are named by a
57  * stock ID, which is a simple string identifying the icon. Each #GtkStyle has a
58  * list of #GtkIconFactory derived from the current theme; those icon factories
59  * are consulted first when searching for an icon. If the theme doesn't set a
60  * particular icon, GTK+ looks for the icon in a list of default icon factories,
61  * maintained by gtk_icon_factory_add_default() and
62  * gtk_icon_factory_remove_default(). Applications with icons should add a default
63  * icon factory with their icons, which will allow themes to override the icons
64  * for the application.
65  *
66  * To display an icon, always use gtk_style_lookup_icon_set() on the widget that
67  * will display the icon, or the convenience function
68  * gtk_widget_render_icon(). These functions take the theme into account when
69  * looking up the icon to use for a given stock ID.
70  *
71  * <refsect2 id="GtkIconFactory-BUILDER-UI">
72  * <title>GtkIconFactory as GtkBuildable</title>
73  * <para>
74  * GtkIconFactory supports a custom &lt;sources&gt; element, which can contain
75  * multiple &lt;source&gt; elements.
76  * The following attributes are allowed:
77  * <variablelist>
78  * <varlistentry>
79  * <term>stock-id</term>
80  * <listitem><para>
81  * The stock id of the source, a string.
82  * This attribute is mandatory
83  * </para></listitem>
84  * </varlistentry>
85  * <varlistentry>
86  * <term>filename</term>
87  * <listitem><para>
88  * The filename of the source, a string.
89  * This attribute is optional
90  * </para></listitem>
91  * </varlistentry>
92  * <varlistentry>
93  * <term>icon-name</term>
94  * <listitem><para>
95  * The icon name for the source, a string.
96  * This attribute is optional.
97  * </para></listitem>
98  * </varlistentry>
99  * <varlistentry>
100  * <term>size</term>
101  * <listitem><para>
102  * Size of the icon, a #GtkIconSize enum value.
103  * This attribute is optional.
104  * </para></listitem>
105  * </varlistentry>
106  * <varlistentry>
107  * <term>direction</term>
108  * <listitem><para>
109  * Direction of the source, a #GtkTextDirection enum value.
110  * This attribute is optional.
111  * </para></listitem>
112  * </varlistentry>
113  * <varlistentry>
114  * <term>state</term>
115  * <listitem><para>
116  * State of the source, a #GtkStateType enum value.
117  * This attribute is optional.
118  * </para></listitem>
119  * </varlistentry>
120  * </variablelist>
121  * <example>
122  * <title>A #GtkIconFactory UI definition fragment.</title>
123  * <programlisting><![CDATA[
124  * <object class="GtkIconFactory" id="iconfactory1">
125  *   <sources>
126  *     <source stock-id="apple-red" filename="apple-red.png"/>
127  *   </sources>
128  * </object>
129  * <object class="GtkWindow" id="window1">
130  *   <child>
131  *     <object class="GtkButton" id="apple_button">
132  *       <property name="label">apple-red</property>
133  *       <property name="use-stock">True</property>
134  *     </object>
135  *   </child>
136  * </object>
137  * ]]>
138  * </programlisting>
139  * </example>
140  * </para>
141  * </refsect2>
142  */
143
144
145 static GSList *all_icon_factories = NULL;
146
147 struct _GtkIconFactoryPrivate
148 {
149   GHashTable *icons;
150 };
151
152 typedef enum {
153   GTK_ICON_SOURCE_EMPTY,
154   GTK_ICON_SOURCE_ICON_NAME,
155   GTK_ICON_SOURCE_STATIC_ICON_NAME,
156   GTK_ICON_SOURCE_FILENAME,
157   GTK_ICON_SOURCE_PIXBUF
158 } GtkIconSourceType;
159
160 struct _GtkIconSource
161 {
162   GtkIconSourceType type;
163
164   union {
165     gchar *icon_name;
166     gchar *filename;
167     GdkPixbuf *pixbuf;
168   } source;
169
170   GdkPixbuf *filename_pixbuf;
171
172   GtkTextDirection direction;
173   GtkStateType state;
174   GtkIconSize size;
175
176   /* If TRUE, then the parameter is wildcarded, and the above
177    * fields should be ignored. If FALSE, the parameter is
178    * specified, and the above fields should be valid.
179    */
180   guint any_direction : 1;
181   guint any_state : 1;
182   guint any_size : 1;
183 };
184
185
186 static void
187 gtk_icon_factory_buildable_init  (GtkBuildableIface      *iface);
188
189 static gboolean gtk_icon_factory_buildable_custom_tag_start (GtkBuildable     *buildable,
190                                                              GtkBuilder       *builder,
191                                                              GObject          *child,
192                                                              const gchar      *tagname,
193                                                              GMarkupParser    *parser,
194                                                              gpointer         *data);
195 static void gtk_icon_factory_buildable_custom_tag_end (GtkBuildable *buildable,
196                                                        GtkBuilder   *builder,
197                                                        GObject      *child,
198                                                        const gchar  *tagname,
199                                                        gpointer     *user_data);
200 static void gtk_icon_factory_finalize   (GObject             *object);
201 static void get_default_icons           (GtkIconFactory      *icon_factory);
202 static void icon_source_clear           (GtkIconSource       *source);
203
204 static GtkIconSize icon_size_register_intern (const gchar *name,
205                                               gint         width,
206                                               gint         height);
207
208 #define GTK_ICON_SOURCE_INIT(any_direction, any_state, any_size)        \
209   { GTK_ICON_SOURCE_EMPTY, { NULL }, NULL,                              \
210    0, 0, 0,                                                             \
211    any_direction, any_state, any_size }
212
213 G_DEFINE_TYPE_WITH_CODE (GtkIconFactory, gtk_icon_factory, G_TYPE_OBJECT,
214                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
215                                                 gtk_icon_factory_buildable_init))
216
217 static void
218 gtk_icon_factory_init (GtkIconFactory *factory)
219 {
220   GtkIconFactoryPrivate *priv;
221
222   factory->priv = G_TYPE_INSTANCE_GET_PRIVATE (factory,
223                                                GTK_TYPE_ICON_FACTORY,
224                                                GtkIconFactoryPrivate);
225   priv = factory->priv;
226
227   priv->icons = g_hash_table_new (g_str_hash, g_str_equal);
228   all_icon_factories = g_slist_prepend (all_icon_factories, factory);
229 }
230
231 static void
232 gtk_icon_factory_class_init (GtkIconFactoryClass *klass)
233 {
234   GObjectClass *object_class = G_OBJECT_CLASS (klass);
235
236   object_class->finalize = gtk_icon_factory_finalize;
237
238   g_type_class_add_private (klass, sizeof (GtkIconFactoryPrivate));
239 }
240
241 static void
242 gtk_icon_factory_buildable_init (GtkBuildableIface *iface)
243 {
244   iface->custom_tag_start = gtk_icon_factory_buildable_custom_tag_start;
245   iface->custom_tag_end = gtk_icon_factory_buildable_custom_tag_end;
246 }
247
248 static void
249 free_icon_set (gpointer key, gpointer value, gpointer data)
250 {
251   g_free (key);
252   gtk_icon_set_unref (value);
253 }
254
255 static void
256 gtk_icon_factory_finalize (GObject *object)
257 {
258   GtkIconFactory *factory = GTK_ICON_FACTORY (object);
259   GtkIconFactoryPrivate *priv = factory->priv;
260
261   all_icon_factories = g_slist_remove (all_icon_factories, factory);
262
263   g_hash_table_foreach (priv->icons, free_icon_set, NULL);
264
265   g_hash_table_destroy (priv->icons);
266
267   G_OBJECT_CLASS (gtk_icon_factory_parent_class)->finalize (object);
268 }
269
270 /**
271  * gtk_icon_factory_new:
272  *
273  * Creates a new #GtkIconFactory. An icon factory manages a collection
274  * of #GtkIconSet<!-- -->s; a #GtkIconSet manages a set of variants of a
275  * particular icon (i.e. a #GtkIconSet contains variants for different
276  * sizes and widget states). Icons in an icon factory are named by a
277  * stock ID, which is a simple string identifying the icon. Each
278  * #GtkStyle has a list of #GtkIconFactory<!-- -->s derived from the current
279  * theme; those icon factories are consulted first when searching for
280  * an icon. If the theme doesn't set a particular icon, GTK+ looks for
281  * the icon in a list of default icon factories, maintained by
282  * gtk_icon_factory_add_default() and
283  * gtk_icon_factory_remove_default(). Applications with icons should
284  * add a default icon factory with their icons, which will allow
285  * themes to override the icons for the application.
286  *
287  * Return value: a new #GtkIconFactory
288  */
289 GtkIconFactory*
290 gtk_icon_factory_new (void)
291 {
292   return g_object_new (GTK_TYPE_ICON_FACTORY, NULL);
293 }
294
295 /**
296  * gtk_icon_factory_add:
297  * @factory: a #GtkIconFactory
298  * @stock_id: icon name
299  * @icon_set: icon set
300  *
301  * Adds the given @icon_set to the icon factory, under the name
302  * @stock_id.  @stock_id should be namespaced for your application,
303  * e.g. "myapp-whatever-icon".  Normally applications create a
304  * #GtkIconFactory, then add it to the list of default factories with
305  * gtk_icon_factory_add_default(). Then they pass the @stock_id to
306  * widgets such as #GtkImage to display the icon. Themes can provide
307  * an icon with the same name (such as "myapp-whatever-icon") to
308  * override your application's default icons. If an icon already
309  * existed in @factory for @stock_id, it is unreferenced and replaced
310  * with the new @icon_set.
311  */
312 void
313 gtk_icon_factory_add (GtkIconFactory *factory,
314                       const gchar    *stock_id,
315                       GtkIconSet     *icon_set)
316 {
317   GtkIconFactoryPrivate *priv = factory->priv;
318   gpointer old_key = NULL;
319   gpointer old_value = NULL;
320
321   g_return_if_fail (GTK_IS_ICON_FACTORY (factory));
322   g_return_if_fail (stock_id != NULL);
323   g_return_if_fail (icon_set != NULL);
324
325   g_hash_table_lookup_extended (priv->icons, stock_id,
326                                 &old_key, &old_value);
327
328   if (old_value == icon_set)
329     return;
330
331   gtk_icon_set_ref (icon_set);
332
333   /* GHashTable key memory management is so fantastically broken. */
334   if (old_key)
335     g_hash_table_insert (priv->icons, old_key, icon_set);
336   else
337     g_hash_table_insert (priv->icons, g_strdup (stock_id), icon_set);
338
339   if (old_value)
340     gtk_icon_set_unref (old_value);
341 }
342
343 /**
344  * gtk_icon_factory_lookup:
345  * @factory: a #GtkIconFactory
346  * @stock_id: an icon name
347  *
348  * Looks up @stock_id in the icon factory, returning an icon set
349  * if found, otherwise %NULL. For display to the user, you should
350  * use gtk_style_lookup_icon_set() on the #GtkStyle for the
351  * widget that will display the icon, instead of using this
352  * function directly, so that themes are taken into account.
353  *
354  * Return value: (transfer none): icon set of @stock_id.
355  */
356 GtkIconSet *
357 gtk_icon_factory_lookup (GtkIconFactory *factory,
358                          const gchar    *stock_id)
359 {
360   GtkIconFactoryPrivate *priv;
361
362   g_return_val_if_fail (GTK_IS_ICON_FACTORY (factory), NULL);
363   g_return_val_if_fail (stock_id != NULL, NULL);
364
365   priv = factory->priv;
366
367   return g_hash_table_lookup (priv->icons, stock_id);
368 }
369
370 static GSList *default_factories = NULL;
371
372 /**
373  * gtk_icon_factory_add_default:
374  * @factory: a #GtkIconFactory
375  *
376  * Adds an icon factory to the list of icon factories searched by
377  * gtk_style_lookup_icon_set(). This means that, for example,
378  * gtk_image_new_from_stock() will be able to find icons in @factory.
379  * There will normally be an icon factory added for each library or
380  * application that comes with icons. The default icon factories
381  * can be overridden by themes.
382  */
383 void
384 gtk_icon_factory_add_default (GtkIconFactory *factory)
385 {
386   g_return_if_fail (GTK_IS_ICON_FACTORY (factory));
387
388   g_object_ref (factory);
389
390   default_factories = g_slist_prepend (default_factories, factory);
391 }
392
393 /**
394  * gtk_icon_factory_remove_default:
395  * @factory: a #GtkIconFactory previously added with gtk_icon_factory_add_default()
396  *
397  * Removes an icon factory from the list of default icon
398  * factories. Not normally used; you might use it for a library that
399  * can be unloaded or shut down.
400  */
401 void
402 gtk_icon_factory_remove_default (GtkIconFactory  *factory)
403 {
404   g_return_if_fail (GTK_IS_ICON_FACTORY (factory));
405
406   default_factories = g_slist_remove (default_factories, factory);
407
408   g_object_unref (factory);
409 }
410
411 static GtkIconFactory *
412 _gtk_icon_factory_get_default_icons (void)
413 {
414   static GtkIconFactory *default_icons = NULL;
415   GtkIconFactory *icons = NULL;
416   GdkScreen *screen = gdk_screen_get_default ();
417
418   if (screen)
419     icons = g_object_get_data (G_OBJECT (screen), "gtk-default-icons");
420
421   if (icons == NULL)
422     {
423       if (default_icons == NULL)
424         {
425           default_icons = gtk_icon_factory_new ();
426           get_default_icons (default_icons);
427         }
428       if (screen)
429         g_object_set_data_full (G_OBJECT (screen),
430                                 I_("gtk-default-icons"),
431                                 default_icons,
432                                 g_object_unref);
433       icons = default_icons;
434     }
435
436   return icons;
437 }
438
439 /**
440  * gtk_icon_factory_lookup_default:
441  * @stock_id: an icon name
442  *
443  * Looks for an icon in the list of default icon factories.  For
444  * display to the user, you should use gtk_style_lookup_icon_set() on
445  * the #GtkStyle for the widget that will display the icon, instead of
446  * using this function directly, so that themes are taken into
447  * account.
448  *
449  * Return value: (transfer none): a #GtkIconSet, or %NULL
450  */
451 GtkIconSet *
452 gtk_icon_factory_lookup_default (const gchar *stock_id)
453 {
454   GSList *tmp_list;
455   GtkIconFactory *default_icons;
456
457   g_return_val_if_fail (stock_id != NULL, NULL);
458
459   tmp_list = default_factories;
460   while (tmp_list != NULL)
461     {
462       GtkIconSet *icon_set =
463         gtk_icon_factory_lookup (GTK_ICON_FACTORY (tmp_list->data),
464                                  stock_id);
465
466       if (icon_set)
467         return icon_set;
468
469       tmp_list = g_slist_next (tmp_list);
470     }
471
472   default_icons = _gtk_icon_factory_get_default_icons ();
473   if (default_icons)
474     return gtk_icon_factory_lookup (default_icons, stock_id);
475   else
476     return NULL;
477 }
478
479 static void
480 register_stock_icon (GtkIconFactory *factory,
481                      const gchar    *stock_id,
482                      const gchar    *icon_name)
483 {
484   GtkIconSet *set = gtk_icon_set_new ();
485   GtkIconSource source = GTK_ICON_SOURCE_INIT (TRUE, TRUE, TRUE);
486
487   source.type = GTK_ICON_SOURCE_STATIC_ICON_NAME;
488   source.source.icon_name = (gchar *)icon_name;
489   source.direction = GTK_TEXT_DIR_NONE;
490   gtk_icon_set_add_source (set, &source);
491
492   gtk_icon_factory_add (factory, stock_id, set);
493   gtk_icon_set_unref (set);
494 }
495
496 static void
497 register_bidi_stock_icon (GtkIconFactory *factory,
498                           const gchar    *stock_id,
499                           const gchar    *icon_name)
500 {
501   GtkIconSet *set = gtk_icon_set_new ();
502   GtkIconSource source = GTK_ICON_SOURCE_INIT (FALSE, TRUE, TRUE);
503
504   source.type = GTK_ICON_SOURCE_STATIC_ICON_NAME;
505   source.source.icon_name = (gchar *)icon_name;
506   source.direction = GTK_TEXT_DIR_LTR;
507   gtk_icon_set_add_source (set, &source);
508
509   source.type = GTK_ICON_SOURCE_STATIC_ICON_NAME;
510   source.source.icon_name = (gchar *)icon_name;
511   source.direction = GTK_TEXT_DIR_RTL;
512   gtk_icon_set_add_source (set, &source);
513
514   gtk_icon_factory_add (factory, stock_id, set);
515   gtk_icon_set_unref (set);
516 }
517
518 static void
519 get_default_icons (GtkIconFactory *factory)
520 {
521   /* KEEP IN SYNC with gtkstock.c */
522
523   register_stock_icon (factory, GTK_STOCK_DIALOG_AUTHENTICATION, "dialog-password");
524   register_stock_icon (factory, GTK_STOCK_DIALOG_ERROR, "dialog-error");
525   register_stock_icon (factory, GTK_STOCK_DIALOG_INFO, "dialog-information");
526   register_stock_icon (factory, GTK_STOCK_DIALOG_QUESTION, "dialog-question");
527   register_stock_icon (factory, GTK_STOCK_DIALOG_WARNING, "dialog-warning");
528   register_stock_icon (factory, GTK_STOCK_DND, GTK_STOCK_DND);
529   register_stock_icon (factory, GTK_STOCK_DND_MULTIPLE, GTK_STOCK_DND_MULTIPLE);
530   register_stock_icon (factory, GTK_STOCK_APPLY, GTK_STOCK_APPLY);
531   register_stock_icon (factory, GTK_STOCK_CANCEL, GTK_STOCK_CANCEL);
532   register_stock_icon (factory, GTK_STOCK_NO, GTK_STOCK_NO);
533   register_stock_icon (factory, GTK_STOCK_OK, GTK_STOCK_OK);
534   register_stock_icon (factory, GTK_STOCK_YES, GTK_STOCK_YES);
535   register_stock_icon (factory, GTK_STOCK_CLOSE, "window-close");
536   register_stock_icon (factory, GTK_STOCK_ADD, "list-add");
537   register_stock_icon (factory, GTK_STOCK_JUSTIFY_CENTER, "format-justify-center");
538   register_stock_icon (factory, GTK_STOCK_JUSTIFY_FILL, "format-justify-fill");
539   register_stock_icon (factory, GTK_STOCK_JUSTIFY_LEFT, "format-justify-left");
540   register_stock_icon (factory, GTK_STOCK_JUSTIFY_RIGHT, "format-justify-right");
541   register_stock_icon (factory, GTK_STOCK_GOTO_BOTTOM, "go-bottom");
542   register_stock_icon (factory, GTK_STOCK_CDROM, "media-optical");
543   register_stock_icon (factory, GTK_STOCK_CONVERT, GTK_STOCK_CONVERT);
544   register_stock_icon (factory, GTK_STOCK_COPY, "edit-copy");
545   register_stock_icon (factory, GTK_STOCK_CUT, "edit-cut");
546   register_stock_icon (factory, GTK_STOCK_GO_DOWN, "go-down");
547   register_stock_icon (factory, GTK_STOCK_EXECUTE, "system-run");
548   register_stock_icon (factory, GTK_STOCK_QUIT, "application-exit");
549   register_bidi_stock_icon (factory, GTK_STOCK_GOTO_FIRST, "go-first");
550   register_stock_icon (factory, GTK_STOCK_SELECT_FONT, GTK_STOCK_SELECT_FONT);
551   register_stock_icon (factory, GTK_STOCK_FULLSCREEN, "view-fullscreen");
552   register_stock_icon (factory, GTK_STOCK_LEAVE_FULLSCREEN, "view-restore");
553   register_stock_icon (factory, GTK_STOCK_HARDDISK, "drive-harddisk");
554   register_stock_icon (factory, GTK_STOCK_HELP, "help-contents");
555   register_stock_icon (factory, GTK_STOCK_HOME, "go-home");
556   register_stock_icon (factory, GTK_STOCK_INFO, "dialog-information");
557   register_bidi_stock_icon (factory, GTK_STOCK_JUMP_TO, "go-jump");
558   register_bidi_stock_icon (factory, GTK_STOCK_GOTO_LAST, "go-last");
559   register_bidi_stock_icon (factory, GTK_STOCK_GO_BACK, "go-previous");
560   register_stock_icon (factory, GTK_STOCK_MISSING_IMAGE, "image-missing");
561   register_stock_icon (factory, GTK_STOCK_NETWORK, "network-idle");
562   register_stock_icon (factory, GTK_STOCK_NEW, "document-new");
563   register_stock_icon (factory, GTK_STOCK_OPEN, "document-open");
564   register_stock_icon (factory, GTK_STOCK_ORIENTATION_PORTRAIT, GTK_STOCK_ORIENTATION_PORTRAIT);
565   register_stock_icon (factory, GTK_STOCK_ORIENTATION_LANDSCAPE, GTK_STOCK_ORIENTATION_LANDSCAPE);
566   register_stock_icon (factory, GTK_STOCK_ORIENTATION_REVERSE_PORTRAIT, GTK_STOCK_ORIENTATION_REVERSE_PORTRAIT);
567   register_stock_icon (factory, GTK_STOCK_ORIENTATION_REVERSE_LANDSCAPE, GTK_STOCK_ORIENTATION_REVERSE_LANDSCAPE);
568   register_stock_icon (factory, GTK_STOCK_PAGE_SETUP, GTK_STOCK_PAGE_SETUP);
569   register_stock_icon (factory, GTK_STOCK_PASTE, "edit-paste");
570   register_stock_icon (factory, GTK_STOCK_PREFERENCES, GTK_STOCK_PREFERENCES);
571   register_stock_icon (factory, GTK_STOCK_PRINT, "document-print");
572   register_stock_icon (factory, GTK_STOCK_PRINT_ERROR, "printer-error");
573   register_stock_icon (factory, GTK_STOCK_PRINT_PAUSED, "printer-paused");
574   register_stock_icon (factory, GTK_STOCK_PRINT_PREVIEW, "document-print-preview");
575   register_stock_icon (factory, GTK_STOCK_PRINT_REPORT, "printer-info");
576   register_stock_icon (factory, GTK_STOCK_PRINT_WARNING, "printer-warning");
577   register_stock_icon (factory, GTK_STOCK_PROPERTIES, "document-properties");
578   register_bidi_stock_icon (factory, GTK_STOCK_REDO, "edit-redo");
579   register_stock_icon (factory, GTK_STOCK_REMOVE, "list-remove");
580   register_stock_icon (factory, GTK_STOCK_REFRESH, "view-refresh");
581   register_bidi_stock_icon (factory, GTK_STOCK_REVERT_TO_SAVED, "document-revert");
582   register_bidi_stock_icon (factory, GTK_STOCK_GO_FORWARD, "go-next");
583   register_stock_icon (factory, GTK_STOCK_SAVE, "document-save");
584   register_stock_icon (factory, GTK_STOCK_FLOPPY, "media-floppy");
585   register_stock_icon (factory, GTK_STOCK_SAVE_AS, "document-save-as");
586   register_stock_icon (factory, GTK_STOCK_FIND, "edit-find");
587   register_stock_icon (factory, GTK_STOCK_FIND_AND_REPLACE, "edit-find-replace");
588   register_stock_icon (factory, GTK_STOCK_SORT_DESCENDING, "view-sort-descending");
589   register_stock_icon (factory, GTK_STOCK_SORT_ASCENDING, "view-sort-ascending");
590   register_stock_icon (factory, GTK_STOCK_SPELL_CHECK, "tools-check-spelling");
591   register_stock_icon (factory, GTK_STOCK_STOP, "process-stop");
592   register_stock_icon (factory, GTK_STOCK_BOLD, "format-text-bold");
593   register_stock_icon (factory, GTK_STOCK_ITALIC, "format-text-italic");
594   register_stock_icon (factory, GTK_STOCK_STRIKETHROUGH, "format-text-strikethrough");
595   register_stock_icon (factory, GTK_STOCK_UNDERLINE, "format-text-underline");
596   register_bidi_stock_icon (factory, GTK_STOCK_INDENT, "format-indent-more");
597   register_bidi_stock_icon (factory, GTK_STOCK_UNINDENT, "format-indent-less");
598   register_stock_icon (factory, GTK_STOCK_GOTO_TOP, "go-top");
599   register_stock_icon (factory, GTK_STOCK_DELETE, "edit-delete");
600   register_bidi_stock_icon (factory, GTK_STOCK_UNDELETE, GTK_STOCK_UNDELETE);
601   register_bidi_stock_icon (factory, GTK_STOCK_UNDO, "edit-undo");
602   register_stock_icon (factory, GTK_STOCK_GO_UP, "go-up");
603   register_stock_icon (factory, GTK_STOCK_FILE, "text-x-generic");
604   register_stock_icon (factory, GTK_STOCK_DIRECTORY, "folder");
605   register_stock_icon (factory, GTK_STOCK_ABOUT, "help-about");
606   register_stock_icon (factory, GTK_STOCK_CONNECT, GTK_STOCK_CONNECT);
607   register_stock_icon (factory, GTK_STOCK_DISCONNECT, GTK_STOCK_DISCONNECT);
608   register_stock_icon (factory, GTK_STOCK_EDIT, GTK_STOCK_EDIT);
609   register_stock_icon (factory, GTK_STOCK_CAPS_LOCK_WARNING, GTK_STOCK_CAPS_LOCK_WARNING);
610   register_bidi_stock_icon (factory, GTK_STOCK_MEDIA_FORWARD, "media-seek-forward");
611   register_bidi_stock_icon (factory, GTK_STOCK_MEDIA_NEXT, "media-skip-forward");
612   register_stock_icon (factory, GTK_STOCK_MEDIA_PAUSE, "media-playback-pause");
613   register_bidi_stock_icon (factory, GTK_STOCK_MEDIA_PLAY, "media-playback-start");
614   register_bidi_stock_icon (factory, GTK_STOCK_MEDIA_PREVIOUS, "media-skip-backward");
615   register_stock_icon (factory, GTK_STOCK_MEDIA_RECORD, "media-record");
616   register_bidi_stock_icon (factory, GTK_STOCK_MEDIA_REWIND, "media-seek-backward");
617   register_stock_icon (factory, GTK_STOCK_MEDIA_STOP, "media-playback-stop");
618   register_stock_icon (factory, GTK_STOCK_INDEX, GTK_STOCK_INDEX);
619   register_stock_icon (factory, GTK_STOCK_ZOOM_100, "zoom-original");
620   register_stock_icon (factory, GTK_STOCK_ZOOM_IN, "zoom-in");
621   register_stock_icon (factory, GTK_STOCK_ZOOM_OUT, "zoom-out");
622   register_stock_icon (factory, GTK_STOCK_ZOOM_FIT, "zoom-fit-best");
623   register_stock_icon (factory, GTK_STOCK_SELECT_ALL, "edit-select-all");
624   register_stock_icon (factory, GTK_STOCK_CLEAR, "edit-clear");
625   register_stock_icon (factory, GTK_STOCK_SELECT_COLOR, GTK_STOCK_SELECT_COLOR);
626   register_stock_icon (factory, GTK_STOCK_COLOR_PICKER, GTK_STOCK_COLOR_PICKER);
627 }
628
629 /************************************************************
630  *                    Icon size handling                    *
631  ************************************************************/
632
633 typedef struct _IconSize IconSize;
634
635 struct _IconSize
636 {
637   gint size;
638   gchar *name;
639
640   gint width;
641   gint height;
642 };
643
644 typedef struct _IconAlias IconAlias;
645
646 struct _IconAlias
647 {
648   gchar *name;
649   gint   target;
650 };
651
652 typedef struct _SettingsIconSize SettingsIconSize;
653
654 struct _SettingsIconSize
655 {
656   gint width;
657   gint height;
658 };
659
660 static GHashTable *icon_aliases = NULL;
661 static IconSize *icon_sizes = NULL;
662 static gint      icon_sizes_allocated = 0;
663 static gint      icon_sizes_used = 0;
664
665 static void
666 init_icon_sizes (void)
667 {
668   if (icon_sizes == NULL)
669     {
670 #define NUM_BUILTIN_SIZES 7
671       gint i;
672
673       icon_aliases = g_hash_table_new (g_str_hash, g_str_equal);
674
675       icon_sizes = g_new (IconSize, NUM_BUILTIN_SIZES);
676       icon_sizes_allocated = NUM_BUILTIN_SIZES;
677       icon_sizes_used = NUM_BUILTIN_SIZES;
678
679       icon_sizes[GTK_ICON_SIZE_INVALID].size = 0;
680       icon_sizes[GTK_ICON_SIZE_INVALID].name = NULL;
681       icon_sizes[GTK_ICON_SIZE_INVALID].width = 0;
682       icon_sizes[GTK_ICON_SIZE_INVALID].height = 0;
683
684       /* the name strings aren't copied since we don't ever remove
685        * icon sizes, so we don't need to know whether they're static.
686        * Even if we did I suppose removing the builtin sizes would be
687        * disallowed.
688        */
689
690       icon_sizes[GTK_ICON_SIZE_MENU].size = GTK_ICON_SIZE_MENU;
691       icon_sizes[GTK_ICON_SIZE_MENU].name = "gtk-menu";
692       icon_sizes[GTK_ICON_SIZE_MENU].width = 16;
693       icon_sizes[GTK_ICON_SIZE_MENU].height = 16;
694
695       icon_sizes[GTK_ICON_SIZE_BUTTON].size = GTK_ICON_SIZE_BUTTON;
696       icon_sizes[GTK_ICON_SIZE_BUTTON].name = "gtk-button";
697       icon_sizes[GTK_ICON_SIZE_BUTTON].width = 20;
698       icon_sizes[GTK_ICON_SIZE_BUTTON].height = 20;
699
700       icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].size = GTK_ICON_SIZE_SMALL_TOOLBAR;
701       icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].name = "gtk-small-toolbar";
702       icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].width = 18;
703       icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].height = 18;
704
705       icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].size = GTK_ICON_SIZE_LARGE_TOOLBAR;
706       icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].name = "gtk-large-toolbar";
707       icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].width = 24;
708       icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].height = 24;
709
710       icon_sizes[GTK_ICON_SIZE_DND].size = GTK_ICON_SIZE_DND;
711       icon_sizes[GTK_ICON_SIZE_DND].name = "gtk-dnd";
712       icon_sizes[GTK_ICON_SIZE_DND].width = 32;
713       icon_sizes[GTK_ICON_SIZE_DND].height = 32;
714
715       icon_sizes[GTK_ICON_SIZE_DIALOG].size = GTK_ICON_SIZE_DIALOG;
716       icon_sizes[GTK_ICON_SIZE_DIALOG].name = "gtk-dialog";
717       icon_sizes[GTK_ICON_SIZE_DIALOG].width = 48;
718       icon_sizes[GTK_ICON_SIZE_DIALOG].height = 48;
719
720       g_assert ((GTK_ICON_SIZE_DIALOG + 1) == NUM_BUILTIN_SIZES);
721
722       /* Alias everything to itself. */
723       i = 1; /* skip invalid size */
724       while (i < NUM_BUILTIN_SIZES)
725         {
726           gtk_icon_size_register_alias (icon_sizes[i].name, icon_sizes[i].size);
727
728           ++i;
729         }
730
731 #undef NUM_BUILTIN_SIZES
732     }
733 }
734
735 static void
736 free_settings_sizes (gpointer data)
737 {
738   g_array_free (data, TRUE);
739 }
740
741 static GArray *
742 get_settings_sizes (GtkSettings *settings,
743                     gboolean    *created)
744 {
745   GArray *settings_sizes;
746   static GQuark sizes_quark = 0;
747
748   if (!sizes_quark)
749     sizes_quark = g_quark_from_static_string ("gtk-icon-sizes");
750
751   settings_sizes = g_object_get_qdata (G_OBJECT (settings), sizes_quark);
752   if (!settings_sizes)
753     {
754       settings_sizes = g_array_new (FALSE, FALSE, sizeof (SettingsIconSize));
755       g_object_set_qdata_full (G_OBJECT (settings), sizes_quark,
756                                settings_sizes, free_settings_sizes);
757       if (created)
758         *created = TRUE;
759     }
760
761   return settings_sizes;
762 }
763
764 static void
765 icon_size_set_for_settings (GtkSettings *settings,
766                             const gchar *size_name,
767                             gint         width,
768                             gint         height)
769 {
770   GtkIconSize size;
771   GArray *settings_sizes;
772   SettingsIconSize *settings_size;
773
774   g_return_if_fail (size_name != NULL);
775
776   size = gtk_icon_size_from_name (size_name);
777   if (size == GTK_ICON_SIZE_INVALID)
778     /* Reserve a place */
779     size = icon_size_register_intern (size_name, -1, -1);
780
781   settings_sizes = get_settings_sizes (settings, NULL);
782   if (size >= settings_sizes->len)
783     {
784       SettingsIconSize unset = { -1, -1 };
785       gint i;
786
787       for (i = settings_sizes->len; i <= size; i++)
788         g_array_append_val (settings_sizes, unset);
789     }
790
791   settings_size = &g_array_index (settings_sizes, SettingsIconSize, size);
792
793   settings_size->width = width;
794   settings_size->height = height;
795 }
796
797 /* Like pango_parse_word, but accept - as well
798  */
799 static gboolean
800 scan_icon_size_name (const char **pos, GString *out)
801 {
802   const char *p = *pos;
803
804   while (g_ascii_isspace (*p))
805     p++;
806
807   if (!((*p >= 'A' && *p <= 'Z') ||
808         (*p >= 'a' && *p <= 'z') ||
809         *p == '_' || *p == '-'))
810     return FALSE;
811
812   g_string_truncate (out, 0);
813   g_string_append_c (out, *p);
814   p++;
815
816   while ((*p >= 'A' && *p <= 'Z') ||
817          (*p >= 'a' && *p <= 'z') ||
818          (*p >= '0' && *p <= '9') ||
819          *p == '_' || *p == '-')
820     {
821       g_string_append_c (out, *p);
822       p++;
823     }
824
825   *pos = p;
826
827   return TRUE;
828 }
829
830 static void
831 icon_size_setting_parse (GtkSettings *settings,
832                          const gchar *icon_size_string)
833 {
834   GString *name_buf = g_string_new (NULL);
835   const gchar *p = icon_size_string;
836
837   while (pango_skip_space (&p))
838     {
839       gint width, height;
840
841       if (!scan_icon_size_name (&p, name_buf))
842         goto err;
843
844       if (!pango_skip_space (&p))
845         goto err;
846
847       if (*p != '=')
848         goto err;
849
850       p++;
851
852       if (!pango_scan_int (&p, &width))
853         goto err;
854
855       if (!pango_skip_space (&p))
856         goto err;
857
858       if (*p != ',')
859         goto err;
860
861       p++;
862
863       if (!pango_scan_int (&p, &height))
864         goto err;
865
866       if (width > 0 && height > 0)
867         {
868           icon_size_set_for_settings (settings, name_buf->str,
869                                       width, height);
870         }
871       else
872         {
873           g_warning ("Invalid size in gtk-icon-sizes: %d,%d\n", width, height);
874         }
875
876       pango_skip_space (&p);
877       if (*p == '\0')
878         break;
879       if (*p == ':')
880         p++;
881       else
882         goto err;
883     }
884
885   g_string_free (name_buf, TRUE);
886   return;
887
888  err:
889   g_warning ("Error parsing gtk-icon-sizes string:\n\t'%s'", icon_size_string);
890   g_string_free (name_buf, TRUE);
891 }
892
893 static void
894 icon_size_set_all_from_settings (GtkSettings *settings)
895 {
896   GArray *settings_sizes;
897   gchar *icon_size_string;
898
899   /* Reset old settings */
900   settings_sizes = get_settings_sizes (settings, NULL);
901   g_array_set_size (settings_sizes, 0);
902
903   g_object_get (settings,
904                 "gtk-icon-sizes", &icon_size_string,
905                 NULL);
906
907   if (icon_size_string)
908     {
909       icon_size_setting_parse (settings, icon_size_string);
910       g_free (icon_size_string);
911     }
912 }
913
914 static void
915 icon_size_settings_changed (GtkSettings  *settings,
916                             GParamSpec   *pspec)
917 {
918   icon_size_set_all_from_settings (settings);
919
920   gtk_style_context_reset_widgets (_gtk_settings_get_screen (settings));
921 }
922
923 static void
924 icon_sizes_init_for_settings (GtkSettings *settings)
925 {
926   g_signal_connect (settings,
927                     "notify::gtk-icon-sizes",
928                     G_CALLBACK (icon_size_settings_changed),
929                     NULL);
930
931   icon_size_set_all_from_settings (settings);
932 }
933
934 static gboolean
935 icon_size_lookup_intern (GtkSettings *settings,
936                          GtkIconSize  size,
937                          gint        *widthp,
938                          gint        *heightp)
939 {
940   GArray *settings_sizes;
941   gint width_for_settings = -1;
942   gint height_for_settings = -1;
943
944   init_icon_sizes ();
945
946   if (size == (GtkIconSize)-1)
947     return FALSE;
948
949   if (size >= icon_sizes_used)
950     return FALSE;
951
952   if (size == GTK_ICON_SIZE_INVALID)
953     return FALSE;
954
955   if (settings)
956     {
957       gboolean initial = FALSE;
958
959       settings_sizes = get_settings_sizes (settings, &initial);
960
961       if (initial)
962         icon_sizes_init_for_settings (settings);
963
964       if (size < settings_sizes->len)
965         {
966           SettingsIconSize *settings_size;
967
968           settings_size = &g_array_index (settings_sizes, SettingsIconSize, size);
969
970           width_for_settings = settings_size->width;
971           height_for_settings = settings_size->height;
972         }
973     }
974
975   if (widthp)
976     *widthp = width_for_settings >= 0 ? width_for_settings : icon_sizes[size].width;
977
978   if (heightp)
979     *heightp = height_for_settings >= 0 ? height_for_settings : icon_sizes[size].height;
980
981   return TRUE;
982 }
983
984 /**
985  * gtk_icon_size_lookup_for_settings:
986  * @settings: a #GtkSettings object, used to determine
987  *   which set of user preferences to used.
988  * @size: (type int): an icon size
989  * @width: (out): location to store icon width
990  * @height: (out): location to store icon height
991  *
992  * Obtains the pixel size of a semantic icon size, possibly
993  * modified by user preferences for a particular
994  * #GtkSettings. Normally @size would be
995  * #GTK_ICON_SIZE_MENU, #GTK_ICON_SIZE_BUTTON, etc.  This function
996  * isn't normally needed, gtk_widget_render_icon_pixbuf() is the usual
997  * way to get an icon for rendering, then just look at the size of
998  * the rendered pixbuf. The rendered pixbuf may not even correspond to
999  * the width/height returned by gtk_icon_size_lookup(), because themes
1000  * are free to render the pixbuf however they like, including changing
1001  * the usual size.
1002  *
1003  * Return value: %TRUE if @size was a valid size
1004  *
1005  * Since: 2.2
1006  */
1007 gboolean
1008 gtk_icon_size_lookup_for_settings (GtkSettings *settings,
1009                                    GtkIconSize  size,
1010                                    gint        *width,
1011                                    gint        *height)
1012 {
1013   g_return_val_if_fail (GTK_IS_SETTINGS (settings), FALSE);
1014
1015   return icon_size_lookup_intern (settings, size, width, height);
1016 }
1017
1018 /**
1019  * gtk_icon_size_lookup:
1020  * @size: (type int): an icon size
1021  * @width: (out): location to store icon width
1022  * @height: (out): location to store icon height
1023  *
1024  * Obtains the pixel size of a semantic icon size, possibly
1025  * modified by user preferences for the default #GtkSettings.
1026  * (See gtk_icon_size_lookup_for_settings().)
1027  * Normally @size would be
1028  * #GTK_ICON_SIZE_MENU, #GTK_ICON_SIZE_BUTTON, etc.  This function
1029  * isn't normally needed, gtk_widget_render_icon_pixbuf() is the usual
1030  * way to get an icon for rendering, then just look at the size of
1031  * the rendered pixbuf. The rendered pixbuf may not even correspond to
1032  * the width/height returned by gtk_icon_size_lookup(), because themes
1033  * are free to render the pixbuf however they like, including changing
1034  * the usual size.
1035  *
1036  * Return value: %TRUE if @size was a valid size
1037  */
1038 gboolean
1039 gtk_icon_size_lookup (GtkIconSize  size,
1040                       gint        *widthp,
1041                       gint        *heightp)
1042 {
1043   GTK_NOTE (MULTIHEAD,
1044             g_warning ("gtk_icon_size_lookup ()) is not multihead safe"));
1045
1046   return gtk_icon_size_lookup_for_settings (gtk_settings_get_default (),
1047                                             size, widthp, heightp);
1048 }
1049
1050 static GtkIconSize
1051 icon_size_register_intern (const gchar *name,
1052                            gint         width,
1053                            gint         height)
1054 {
1055   IconAlias *old_alias;
1056   GtkIconSize size;
1057
1058   init_icon_sizes ();
1059
1060   old_alias = g_hash_table_lookup (icon_aliases, name);
1061   if (old_alias && icon_sizes[old_alias->target].width > 0)
1062     {
1063       g_warning ("Icon size name '%s' already exists", name);
1064       return GTK_ICON_SIZE_INVALID;
1065     }
1066
1067   if (old_alias)
1068     {
1069       size = old_alias->target;
1070     }
1071   else
1072     {
1073       if (icon_sizes_used == icon_sizes_allocated)
1074         {
1075           icon_sizes_allocated *= 2;
1076           icon_sizes = g_renew (IconSize, icon_sizes, icon_sizes_allocated);
1077         }
1078
1079       size = icon_sizes_used++;
1080
1081       /* alias to self. */
1082       gtk_icon_size_register_alias (name, size);
1083
1084       icon_sizes[size].size = size;
1085       icon_sizes[size].name = g_strdup (name);
1086     }
1087
1088   icon_sizes[size].width = width;
1089   icon_sizes[size].height = height;
1090
1091   return size;
1092 }
1093
1094 /**
1095  * gtk_icon_size_register:
1096  * @name: name of the icon size
1097  * @width: the icon width
1098  * @height: the icon height
1099  *
1100  * Registers a new icon size, along the same lines as #GTK_ICON_SIZE_MENU,
1101  * etc. Returns the integer value for the size.
1102  *
1103  * Returns: (type int): integer value representing the size
1104  */
1105 GtkIconSize
1106 gtk_icon_size_register (const gchar *name,
1107                         gint         width,
1108                         gint         height)
1109 {
1110   g_return_val_if_fail (name != NULL, 0);
1111   g_return_val_if_fail (width > 0, 0);
1112   g_return_val_if_fail (height > 0, 0);
1113
1114   return icon_size_register_intern (name, width, height);
1115 }
1116
1117 /**
1118  * gtk_icon_size_register_alias:
1119  * @alias: an alias for @target
1120  * @target: (type int): an existing icon size
1121  *
1122  * Registers @alias as another name for @target.
1123  * So calling gtk_icon_size_from_name() with @alias as argument
1124  * will return @target.
1125  */
1126 void
1127 gtk_icon_size_register_alias (const gchar *alias,
1128                               GtkIconSize  target)
1129 {
1130   IconAlias *ia;
1131
1132   g_return_if_fail (alias != NULL);
1133
1134   init_icon_sizes ();
1135
1136   if (!icon_size_lookup_intern (NULL, target, NULL, NULL))
1137     g_warning ("gtk_icon_size_register_alias: Icon size %u does not exist", target);
1138
1139   ia = g_hash_table_lookup (icon_aliases, alias);
1140   if (ia)
1141     {
1142       if (icon_sizes[ia->target].width > 0)
1143         {
1144           g_warning ("gtk_icon_size_register_alias: Icon size name '%s' already exists", alias);
1145           return;
1146         }
1147
1148       ia->target = target;
1149     }
1150
1151   if (!ia)
1152     {
1153       ia = g_new (IconAlias, 1);
1154       ia->name = g_strdup (alias);
1155       ia->target = target;
1156
1157       g_hash_table_insert (icon_aliases, ia->name, ia);
1158     }
1159 }
1160
1161 /**
1162  * gtk_icon_size_from_name:
1163  * @name: the name to look up.
1164  *
1165  * Looks up the icon size associated with @name.
1166  *
1167  * Return value: (type int): the icon size
1168  */
1169 GtkIconSize
1170 gtk_icon_size_from_name (const gchar *name)
1171 {
1172   IconAlias *ia;
1173
1174   init_icon_sizes ();
1175
1176   ia = g_hash_table_lookup (icon_aliases, name);
1177
1178   if (ia && icon_sizes[ia->target].width > 0)
1179     return ia->target;
1180   else
1181     return GTK_ICON_SIZE_INVALID;
1182 }
1183
1184 /**
1185  * gtk_icon_size_get_name:
1186  * @size: (type int): a #GtkIconSize.
1187  *
1188  * Gets the canonical name of the given icon size. The returned string
1189  * is statically allocated and should not be freed.
1190  *
1191  * Returns: the name of the given icon size.
1192  */
1193 const gchar*
1194 gtk_icon_size_get_name (GtkIconSize  size)
1195 {
1196   if (size >= icon_sizes_used)
1197     return NULL;
1198   else
1199     return icon_sizes[size].name;
1200 }
1201
1202 /************************************************************/
1203
1204 /* Icon Set */
1205
1206
1207 static GdkPixbuf *find_in_cache     (GtkIconSet       *icon_set,
1208                                      GtkStyleContext  *style_context,
1209                                      GtkTextDirection  direction,
1210                                      GtkStateType      state,
1211                                      GtkIconSize       size);
1212 static void       add_to_cache      (GtkIconSet       *icon_set,
1213                                      GtkStyleContext  *style_context,
1214                                      GtkTextDirection  direction,
1215                                      GtkStateType      state,
1216                                      GtkIconSize       size,
1217                                      GdkPixbuf        *pixbuf);
1218 /* Clear icon set contents, drop references to all contained
1219  * GdkPixbuf objects and forget all GtkIconSources. Used to
1220  * recycle an icon set.
1221  */
1222 static void       clear_cache       (GtkIconSet       *icon_set,
1223                                      gboolean          style_detach);
1224 static GSList*    copy_cache        (GtkIconSet       *icon_set,
1225                                      GtkIconSet       *copy_recipient);
1226 static void       attach_to_style   (GtkIconSet       *icon_set,
1227                                      GtkStyleContext  *style_context);
1228 static void       detach_from_style (GtkIconSet       *icon_set,
1229                                      GtkStyleContext  *style_context);
1230 static void       style_dnotify     (gpointer          data);
1231
1232 struct _GtkIconSet
1233 {
1234   guint ref_count;
1235
1236   GSList *sources;
1237
1238   /* Cache of the last few rendered versions of the icon. */
1239   GSList *cache;
1240
1241   guint cache_size;
1242
1243   guint cache_serial;
1244 };
1245
1246 static guint cache_serial = 0;
1247
1248 /**
1249  * gtk_icon_set_new:
1250  *
1251  * Creates a new #GtkIconSet. A #GtkIconSet represents a single icon
1252  * in various sizes and widget states. It can provide a #GdkPixbuf
1253  * for a given size and state on request, and automatically caches
1254  * some of the rendered #GdkPixbuf objects.
1255  *
1256  * Normally you would use gtk_widget_render_icon_pixbuf() instead of
1257  * using #GtkIconSet directly. The one case where you'd use
1258  * #GtkIconSet is to create application-specific icon sets to place in
1259  * a #GtkIconFactory.
1260  *
1261  * Return value: a new #GtkIconSet
1262  */
1263 GtkIconSet*
1264 gtk_icon_set_new (void)
1265 {
1266   GtkIconSet *icon_set;
1267
1268   icon_set = g_new (GtkIconSet, 1);
1269
1270   icon_set->ref_count = 1;
1271   icon_set->sources = NULL;
1272   icon_set->cache = NULL;
1273   icon_set->cache_size = 0;
1274   icon_set->cache_serial = cache_serial;
1275
1276   return icon_set;
1277 }
1278
1279 /**
1280  * gtk_icon_set_new_from_pixbuf:
1281  * @pixbuf: a #GdkPixbuf
1282  *
1283  * Creates a new #GtkIconSet with @pixbuf as the default/fallback
1284  * source image. If you don't add any additional #GtkIconSource to the
1285  * icon set, all variants of the icon will be created from @pixbuf,
1286  * using scaling, pixelation, etc. as required to adjust the icon size
1287  * or make the icon look insensitive/prelighted.
1288  *
1289  * Return value: a new #GtkIconSet
1290  */
1291 GtkIconSet *
1292 gtk_icon_set_new_from_pixbuf (GdkPixbuf *pixbuf)
1293 {
1294   GtkIconSet *set;
1295
1296   GtkIconSource source = GTK_ICON_SOURCE_INIT (TRUE, TRUE, TRUE);
1297
1298   g_return_val_if_fail (pixbuf != NULL, NULL);
1299
1300   set = gtk_icon_set_new ();
1301
1302   gtk_icon_source_set_pixbuf (&source, pixbuf);
1303   gtk_icon_set_add_source (set, &source);
1304   gtk_icon_source_set_pixbuf (&source, NULL);
1305
1306   return set;
1307 }
1308
1309
1310 /**
1311  * gtk_icon_set_ref:
1312  * @icon_set: a #GtkIconSet.
1313  *
1314  * Increments the reference count on @icon_set.
1315  *
1316  * Return value: @icon_set.
1317  */
1318 GtkIconSet*
1319 gtk_icon_set_ref (GtkIconSet *icon_set)
1320 {
1321   g_return_val_if_fail (icon_set != NULL, NULL);
1322   g_return_val_if_fail (icon_set->ref_count > 0, NULL);
1323
1324   icon_set->ref_count += 1;
1325
1326   return icon_set;
1327 }
1328
1329 /**
1330  * gtk_icon_set_unref:
1331  * @icon_set: a #GtkIconSet
1332  *
1333  * Decrements the reference count on @icon_set, and frees memory
1334  * if the reference count reaches 0.
1335  */
1336 void
1337 gtk_icon_set_unref (GtkIconSet *icon_set)
1338 {
1339   g_return_if_fail (icon_set != NULL);
1340   g_return_if_fail (icon_set->ref_count > 0);
1341
1342   icon_set->ref_count -= 1;
1343
1344   if (icon_set->ref_count == 0)
1345     {
1346       GSList *tmp_list = icon_set->sources;
1347       while (tmp_list != NULL)
1348         {
1349           gtk_icon_source_free (tmp_list->data);
1350
1351           tmp_list = g_slist_next (tmp_list);
1352         }
1353       g_slist_free (icon_set->sources);
1354
1355       clear_cache (icon_set, TRUE);
1356
1357       g_free (icon_set);
1358     }
1359 }
1360
1361 G_DEFINE_BOXED_TYPE (GtkIconSet, gtk_icon_set,
1362                      gtk_icon_set_ref,
1363                      gtk_icon_set_unref)
1364
1365 /**
1366  * gtk_icon_set_copy:
1367  * @icon_set: a #GtkIconSet
1368  *
1369  * Copies @icon_set by value.
1370  *
1371  * Return value: a new #GtkIconSet identical to the first.
1372  **/
1373 GtkIconSet*
1374 gtk_icon_set_copy (GtkIconSet *icon_set)
1375 {
1376   GtkIconSet *copy;
1377   GSList *tmp_list;
1378
1379   copy = gtk_icon_set_new ();
1380
1381   tmp_list = icon_set->sources;
1382   while (tmp_list != NULL)
1383     {
1384       copy->sources = g_slist_prepend (copy->sources,
1385                                        gtk_icon_source_copy (tmp_list->data));
1386
1387       tmp_list = g_slist_next (tmp_list);
1388     }
1389
1390   copy->sources = g_slist_reverse (copy->sources);
1391
1392   copy->cache = copy_cache (icon_set, copy);
1393   copy->cache_size = icon_set->cache_size;
1394   copy->cache_serial = icon_set->cache_serial;
1395
1396   return copy;
1397 }
1398
1399 static gboolean
1400 sizes_equivalent (GtkIconSize lhs,
1401                   GtkIconSize rhs)
1402 {
1403   /* We used to consider sizes equivalent if they were
1404    * the same pixel size, but we don't have the GtkSettings
1405    * here, so we can't do that. Plus, it's not clear that
1406    * it is right... it was just a workaround for the fact
1407    * that we register icons by logical size, not pixel size.
1408    */
1409 #if 1
1410   return lhs == rhs;
1411 #else
1412
1413   gint r_w, r_h, l_w, l_h;
1414
1415   icon_size_lookup_intern (NULL, rhs, &r_w, &r_h);
1416   icon_size_lookup_intern (NULL, lhs, &l_w, &l_h);
1417
1418   return r_w == l_w && r_h == l_h;
1419 #endif
1420 }
1421
1422 static GtkIconSource *
1423 find_best_matching_source (GtkIconSet       *icon_set,
1424                            GtkTextDirection  direction,
1425                            GtkStateType      state,
1426                            GtkIconSize       size,
1427                            GSList           *failed)
1428 {
1429   GtkIconSource *source;
1430   GSList *tmp_list;
1431
1432   /* We need to find the best icon source.  Direction matters more
1433    * than state, state matters more than size. icon_set->sources
1434    * is sorted according to wildness, so if we take the first
1435    * match we find it will be the least-wild match (if there are
1436    * multiple matches for a given "wildness" then the RC file contained
1437    * dumb stuff, and we end up with an arbitrary matching source)
1438    */
1439
1440   source = NULL;
1441   tmp_list = icon_set->sources;
1442   while (tmp_list != NULL)
1443     {
1444       GtkIconSource *s = tmp_list->data;
1445
1446       if ((s->any_direction || (s->direction == direction)) &&
1447           (s->any_state || (s->state == state)) &&
1448           (s->any_size || size == (GtkIconSize)-1 || (sizes_equivalent (size, s->size))))
1449         {
1450           if (!g_slist_find (failed, s))
1451             {
1452               source = s;
1453               break;
1454             }
1455         }
1456
1457       tmp_list = g_slist_next (tmp_list);
1458     }
1459
1460   return source;
1461 }
1462
1463 static gboolean
1464 ensure_filename_pixbuf (GtkIconSet    *icon_set,
1465                         GtkIconSource *source)
1466 {
1467   if (source->filename_pixbuf == NULL)
1468     {
1469       GError *error = NULL;
1470
1471       source->filename_pixbuf = gdk_pixbuf_new_from_file (source->source.filename, &error);
1472
1473       if (source->filename_pixbuf == NULL)
1474         {
1475           /* Remove this icon source so we don't keep trying to
1476            * load it.
1477            */
1478           g_warning ("Error loading icon: %s", error->message);
1479           g_error_free (error);
1480
1481           icon_set->sources = g_slist_remove (icon_set->sources, source);
1482
1483           gtk_icon_source_free (source);
1484
1485           return FALSE;
1486         }
1487     }
1488
1489   return TRUE;
1490 }
1491
1492 static GdkPixbuf *
1493 render_icon_name_pixbuf (GtkIconSource    *icon_source,
1494                          GtkStyleContext  *context,
1495                          GtkIconSize       size)
1496 {
1497   GdkPixbuf *pixbuf;
1498   GdkPixbuf *tmp_pixbuf;
1499   GtkIconSource tmp_source;
1500   GdkScreen *screen;
1501   GtkIconTheme *icon_theme;
1502   GtkSettings *settings;
1503   gint width, height, pixel_size;
1504   gint *sizes, *s, dist;
1505   GError *error = NULL;
1506
1507   screen = gtk_style_context_get_screen (context);
1508   icon_theme = gtk_icon_theme_get_for_screen (screen);
1509   settings = gtk_settings_get_for_screen (screen);
1510
1511   if (!gtk_icon_size_lookup_for_settings (settings, size, &width, &height))
1512     {
1513       if (size == (GtkIconSize)-1)
1514         {
1515           /* Find an available size close to 48 */
1516           sizes = gtk_icon_theme_get_icon_sizes (icon_theme, icon_source->source.icon_name);
1517           dist = 1000;
1518           width = height = 48;
1519           for (s = sizes; *s; s++)
1520             {
1521               if (*s == -1)
1522                 {
1523                   width = height = 48;
1524                   break;
1525                 }
1526               if (*s < 48)
1527                 {
1528                   if (48 - *s < dist)
1529                     {
1530                       width = height = *s;
1531                       dist = 48 - *s;
1532                     }
1533                 }
1534               else
1535                 {
1536                   if (*s - 48 < dist)
1537                     {
1538                       width = height = *s;
1539                       dist = *s - 48;
1540                     }
1541                 }
1542             }
1543
1544           g_free (sizes);
1545         }
1546       else
1547         {
1548           g_warning ("Invalid icon size %u\n", size);
1549           width = height = 24;
1550         }
1551     }
1552
1553   pixel_size = MIN (width, height);
1554
1555   if (icon_source->direction != GTK_TEXT_DIR_NONE)
1556     {
1557       gchar *suffix[3] = { NULL, "-ltr", "-rtl" };
1558       gchar *names[3];
1559       GtkIconInfo *info;
1560
1561       names[0] = g_strconcat (icon_source->source.icon_name, suffix[icon_source->direction], NULL);
1562       names[1] = icon_source->source.icon_name;
1563       names[2] = NULL;
1564
1565       info = gtk_icon_theme_choose_icon (icon_theme,
1566                                          (const char **) names,
1567                                          pixel_size, GTK_ICON_LOOKUP_USE_BUILTIN);
1568       g_free (names[0]);
1569       if (info)
1570         {
1571           tmp_pixbuf = gtk_icon_info_load_icon (info, &error);
1572           gtk_icon_info_free (info);
1573         }
1574       else
1575         tmp_pixbuf = NULL;
1576     }
1577   else
1578     {
1579       tmp_pixbuf = gtk_icon_theme_load_icon (icon_theme,
1580                                              icon_source->source.icon_name,
1581                                              pixel_size, 0,
1582                                              &error);
1583     }
1584
1585   if (!tmp_pixbuf)
1586     {
1587       g_warning ("Error loading theme icon '%s' for stock: %s",
1588                  icon_source->source.icon_name, error ? error->message : "");
1589       if (error)
1590         g_error_free (error);
1591       return NULL;
1592     }
1593
1594   tmp_source = *icon_source;
1595   tmp_source.type = GTK_ICON_SOURCE_PIXBUF;
1596   tmp_source.source.pixbuf = tmp_pixbuf;
1597
1598   pixbuf = gtk_render_icon_pixbuf (context, &tmp_source, -1);
1599
1600   if (!pixbuf)
1601     g_warning ("Failed to render icon");
1602
1603   g_object_unref (tmp_pixbuf);
1604
1605   return pixbuf;
1606 }
1607
1608 static GdkPixbuf *
1609 find_and_render_icon_source (GtkIconSet       *icon_set,
1610                              GtkStyleContext  *context,
1611                              GtkTextDirection  direction,
1612                              GtkStateType      state,
1613                              GtkIconSize       size)
1614 {
1615   GSList *failed = NULL;
1616   GdkPixbuf *pixbuf = NULL;
1617
1618   /* We treat failure in two different ways:
1619    *
1620    *  A) If loading a source that specifies a filename fails,
1621    *     we treat that as permanent, and remove the source
1622    *     from the GtkIconSet. (in ensure_filename_pixbuf ()
1623    *  B) If loading a themed icon fails, or scaling an icon
1624    *     fails, we treat that as transient and will try
1625    *     again next time the icon falls out of the cache
1626    *     and we need to recreate it.
1627    */
1628   while (pixbuf == NULL)
1629     {
1630       GtkIconSource *source = find_best_matching_source (icon_set, direction, state, size, failed);
1631
1632       if (source == NULL)
1633         break;
1634
1635       switch (source->type)
1636         {
1637         case GTK_ICON_SOURCE_FILENAME:
1638           if (!ensure_filename_pixbuf (icon_set, source))
1639             break;
1640           /* Fall through */
1641         case GTK_ICON_SOURCE_PIXBUF:
1642           pixbuf = gtk_render_icon_pixbuf (context, source, size);
1643           if (!pixbuf)
1644             {
1645               g_warning ("Failed to render icon");
1646               failed = g_slist_prepend (failed, source);
1647             }
1648           break;
1649         case GTK_ICON_SOURCE_ICON_NAME:
1650         case GTK_ICON_SOURCE_STATIC_ICON_NAME:
1651           pixbuf = render_icon_name_pixbuf (source, context, size);
1652           if (!pixbuf)
1653             failed = g_slist_prepend (failed, source);
1654           break;
1655         case GTK_ICON_SOURCE_EMPTY:
1656           g_assert_not_reached ();
1657         }
1658     }
1659
1660   g_slist_free (failed);
1661
1662   return pixbuf;
1663 }
1664
1665 extern GtkIconCache *_builtin_cache;
1666
1667 static GdkPixbuf*
1668 render_fallback_image (GtkStyleContext   *context,
1669                        GtkTextDirection   direction,
1670                        GtkStateType       state,
1671                        GtkIconSize        size)
1672 {
1673   /* This icon can be used for any direction/state/size */
1674   static GtkIconSource fallback_source = GTK_ICON_SOURCE_INIT (TRUE, TRUE, TRUE);
1675
1676   if (fallback_source.type == GTK_ICON_SOURCE_EMPTY)
1677     {
1678       gint index;
1679       GdkPixbuf *pixbuf;
1680
1681       _gtk_icon_theme_ensure_builtin_cache ();
1682
1683       index = _gtk_icon_cache_get_directory_index (_builtin_cache, "24");
1684       pixbuf = _gtk_icon_cache_get_icon (_builtin_cache, "image-missing", index);
1685
1686       g_return_val_if_fail(pixbuf != NULL, NULL);
1687
1688       gtk_icon_source_set_pixbuf (&fallback_source, pixbuf);
1689       g_object_unref (pixbuf);
1690     }
1691
1692   return gtk_render_icon_pixbuf (context, &fallback_source, size);
1693 }
1694
1695 /**
1696  * gtk_icon_set_render_icon_pixbuf:
1697  * @icon_set: a #GtkIconSet
1698  * @context: a #GtkStyleContext
1699  * @size: (type int): icon size. A size of (GtkIconSize)-1
1700  *        means render at the size of the source and don't scale.
1701  *
1702  * Renders an icon using gtk_render_icon_pixbuf(). In most cases,
1703  * gtk_widget_render_icon_pixbuf() is better, since it automatically provides
1704  * most of the arguments from the current widget settings.  This
1705  * function never returns %NULL; if the icon can't be rendered
1706  * (perhaps because an image file fails to load), a default "missing
1707  * image" icon will be returned instead.
1708  *
1709  * Return value: (transfer full): a #GdkPixbuf to be displayed
1710  *
1711  * Since: 3.0
1712  */
1713 GdkPixbuf *
1714 gtk_icon_set_render_icon_pixbuf (GtkIconSet        *icon_set,
1715                                  GtkStyleContext   *context,
1716                                  GtkIconSize        size)
1717 {
1718   GdkPixbuf *icon = NULL;
1719   GtkStateFlags flags = 0;
1720   GtkStateType state;
1721   GtkTextDirection direction;
1722
1723   g_return_val_if_fail (icon_set != NULL, NULL);
1724   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
1725
1726   flags = gtk_style_context_get_state (context);
1727   if (flags & GTK_STATE_FLAG_INSENSITIVE)
1728     state = GTK_STATE_INSENSITIVE;
1729   else if (flags & GTK_STATE_FLAG_PRELIGHT)
1730     state = GTK_STATE_PRELIGHT;
1731   else
1732     state = GTK_STATE_NORMAL;
1733
1734   direction = gtk_style_context_get_direction (context);
1735
1736   if (icon_set->sources)
1737     {
1738       icon = find_in_cache (icon_set, context, direction, state, size);
1739       if (icon)
1740         {
1741           g_object_ref (icon);
1742           return icon;
1743         }
1744     }
1745
1746   if (icon_set->sources)
1747     icon = find_and_render_icon_source (icon_set, context, direction, state, size);
1748
1749   if (icon == NULL)
1750     icon = render_fallback_image (context, direction, state, size);
1751
1752   add_to_cache (icon_set, context, direction, state, size, icon);
1753
1754   return icon;
1755 }
1756
1757 /**
1758  * gtk_icon_set_render_icon:
1759  * @icon_set: a #GtkIconSet
1760  * @style: (allow-none): a #GtkStyle associated with @widget, or %NULL
1761  * @direction: text direction
1762  * @state: widget state
1763  * @size: (type int): icon size. A size of (GtkIconSize)-1
1764  *        means render at the size of the source and don't scale.
1765  * @widget: (allow-none): widget that will display the icon, or %NULL.
1766  *          The only use that is typically made of this
1767  *          is to determine the appropriate #GdkScreen.
1768  * @detail: (allow-none): detail to pass to the theme engine, or %NULL.
1769  *          Note that passing a detail of anything but %NULL
1770  *          will disable caching.
1771  *
1772  * Renders an icon using gtk_style_render_icon(). In most cases,
1773  * gtk_widget_render_icon() is better, since it automatically provides
1774  * most of the arguments from the current widget settings.  This
1775  * function never returns %NULL; if the icon can't be rendered
1776  * (perhaps because an image file fails to load), a default "missing
1777  * image" icon will be returned instead.
1778  *
1779  * Return value: (transfer full): a #GdkPixbuf to be displayed
1780  *
1781  * Deprecated: 3.0: Use gtk_icon_set_render_icon_pixbuf() instead
1782  */
1783 GdkPixbuf*
1784 gtk_icon_set_render_icon (GtkIconSet        *icon_set,
1785                           GtkStyle          *style,
1786                           GtkTextDirection   direction,
1787                           GtkStateType       state,
1788                           GtkIconSize        size,
1789                           GtkWidget         *widget,
1790                           const char        *detail)
1791 {
1792   GdkPixbuf *icon;
1793   GtkStyleContext *context = NULL;
1794   GtkStateFlags flags = 0;
1795
1796   g_return_val_if_fail (icon_set != NULL, NULL);
1797   g_return_val_if_fail (style == NULL || GTK_IS_STYLE (style), NULL);
1798
1799   if (style && gtk_style_has_context (style))
1800     {
1801       g_object_get (style, "context", &context, NULL);
1802       /* g_object_get returns a refed object */
1803       if (context)
1804         g_object_unref (context);
1805     }
1806   else if (widget)
1807     {
1808       context = gtk_widget_get_style_context (widget);
1809     }
1810
1811   if (!context)
1812     return render_fallback_image (context, direction, state, size);
1813
1814   gtk_style_context_save (context);
1815
1816   switch (state)
1817     {
1818     case GTK_STATE_PRELIGHT:
1819       flags |= GTK_STATE_FLAG_PRELIGHT;
1820       break;
1821     case GTK_STATE_INSENSITIVE:
1822       flags |= GTK_STATE_FLAG_INSENSITIVE;
1823       break;
1824     default:
1825       break;
1826     }
1827
1828   gtk_style_context_set_state (context, flags);
1829   gtk_style_context_set_direction (context, direction);
1830
1831   icon = gtk_icon_set_render_icon_pixbuf (icon_set, context, size);
1832
1833   gtk_style_context_restore (context);
1834
1835   return icon;
1836 }
1837
1838 /* Order sources by their "wildness", so that "wilder" sources are
1839  * greater than "specific" sources; for determining ordering,
1840  * direction beats state beats size.
1841  */
1842
1843 static int
1844 icon_source_compare (gconstpointer ap, gconstpointer bp)
1845 {
1846   const GtkIconSource *a = ap;
1847   const GtkIconSource *b = bp;
1848
1849   if (!a->any_direction && b->any_direction)
1850     return -1;
1851   else if (a->any_direction && !b->any_direction)
1852     return 1;
1853   else if (!a->any_state && b->any_state)
1854     return -1;
1855   else if (a->any_state && !b->any_state)
1856     return 1;
1857   else if (!a->any_size && b->any_size)
1858     return -1;
1859   else if (a->any_size && !b->any_size)
1860     return 1;
1861   else
1862     return 0;
1863 }
1864
1865 /**
1866  * gtk_icon_set_add_source:
1867  * @icon_set: a #GtkIconSet
1868  * @source: a #GtkIconSource
1869  *
1870  * Icon sets have a list of #GtkIconSource, which they use as base
1871  * icons for rendering icons in different states and sizes. Icons are
1872  * scaled, made to look insensitive, etc. in
1873  * gtk_icon_set_render_icon(), but #GtkIconSet needs base images to
1874  * work with. The base images and when to use them are described by
1875  * a #GtkIconSource.
1876  *
1877  * This function copies @source, so you can reuse the same source immediately
1878  * without affecting the icon set.
1879  *
1880  * An example of when you'd use this function: a web browser's "Back
1881  * to Previous Page" icon might point in a different direction in
1882  * Hebrew and in English; it might look different when insensitive;
1883  * and it might change size depending on toolbar mode (small/large
1884  * icons). So a single icon set would contain all those variants of
1885  * the icon, and you might add a separate source for each one.
1886  *
1887  * You should nearly always add a "default" icon source with all
1888  * fields wildcarded, which will be used as a fallback if no more
1889  * specific source matches. #GtkIconSet always prefers more specific
1890  * icon sources to more generic icon sources. The order in which you
1891  * add the sources to the icon set does not matter.
1892  *
1893  * gtk_icon_set_new_from_pixbuf() creates a new icon set with a
1894  * default icon source based on the given pixbuf.
1895  */
1896 void
1897 gtk_icon_set_add_source (GtkIconSet          *icon_set,
1898                          const GtkIconSource *source)
1899 {
1900   g_return_if_fail (icon_set != NULL);
1901   g_return_if_fail (source != NULL);
1902
1903   if (source->type == GTK_ICON_SOURCE_EMPTY)
1904     {
1905       g_warning ("Useless empty GtkIconSource");
1906       return;
1907     }
1908
1909   icon_set->sources = g_slist_insert_sorted (icon_set->sources,
1910                                              gtk_icon_source_copy (source),
1911                                              icon_source_compare);
1912 }
1913
1914 /**
1915  * gtk_icon_set_get_sizes:
1916  * @icon_set: a #GtkIconSet
1917  * @sizes: (array length=n_sizes) (out) (type int): return location
1918  *     for array of sizes
1919  * @n_sizes: location to store number of elements in returned array
1920  *
1921  * Obtains a list of icon sizes this icon set can render. The returned
1922  * array must be freed with g_free().
1923  */
1924 void
1925 gtk_icon_set_get_sizes (GtkIconSet   *icon_set,
1926                         GtkIconSize **sizes,
1927                         gint         *n_sizes)
1928 {
1929   GSList *tmp_list;
1930   gboolean all_sizes = FALSE;
1931   GSList *specifics = NULL;
1932
1933   g_return_if_fail (icon_set != NULL);
1934   g_return_if_fail (sizes != NULL);
1935   g_return_if_fail (n_sizes != NULL);
1936
1937   tmp_list = icon_set->sources;
1938   while (tmp_list != NULL)
1939     {
1940       GtkIconSource *source;
1941
1942       source = tmp_list->data;
1943
1944       if (source->any_size)
1945         {
1946           all_sizes = TRUE;
1947           break;
1948         }
1949       else
1950         specifics = g_slist_prepend (specifics, GINT_TO_POINTER (source->size));
1951
1952       tmp_list = g_slist_next (tmp_list);
1953     }
1954
1955   if (all_sizes)
1956     {
1957       /* Need to find out what sizes exist */
1958       gint i;
1959
1960       init_icon_sizes ();
1961
1962       *sizes = g_new (GtkIconSize, icon_sizes_used);
1963       *n_sizes = icon_sizes_used - 1;
1964
1965       i = 1;
1966       while (i < icon_sizes_used)
1967         {
1968           (*sizes)[i - 1] = icon_sizes[i].size;
1969           ++i;
1970         }
1971     }
1972   else
1973     {
1974       gint i;
1975
1976       *n_sizes = g_slist_length (specifics);
1977       *sizes = g_new (GtkIconSize, *n_sizes);
1978
1979       i = 0;
1980       tmp_list = specifics;
1981       while (tmp_list != NULL)
1982         {
1983           (*sizes)[i] = GPOINTER_TO_INT (tmp_list->data);
1984
1985           ++i;
1986           tmp_list = g_slist_next (tmp_list);
1987         }
1988     }
1989
1990   g_slist_free (specifics);
1991 }
1992
1993
1994 /**
1995  * gtk_icon_source_new:
1996  *
1997  * Creates a new #GtkIconSource. A #GtkIconSource contains a #GdkPixbuf (or
1998  * image filename) that serves as the base image for one or more of the
1999  * icons in a #GtkIconSet, along with a specification for which icons in the
2000  * icon set will be based on that pixbuf or image file. An icon set contains
2001  * a set of icons that represent "the same" logical concept in different states,
2002  * different global text directions, and different sizes.
2003  *
2004  * So for example a web browser's "Back to Previous Page" icon might
2005  * point in a different direction in Hebrew and in English; it might
2006  * look different when insensitive; and it might change size depending
2007  * on toolbar mode (small/large icons). So a single icon set would
2008  * contain all those variants of the icon. #GtkIconSet contains a list
2009  * of #GtkIconSource from which it can derive specific icon variants in
2010  * the set.
2011  *
2012  * In the simplest case, #GtkIconSet contains one source pixbuf from
2013  * which it derives all variants. The convenience function
2014  * gtk_icon_set_new_from_pixbuf() handles this case; if you only have
2015  * one source pixbuf, just use that function.
2016  *
2017  * If you want to use a different base pixbuf for different icon
2018  * variants, you create multiple icon sources, mark which variants
2019  * they'll be used to create, and add them to the icon set with
2020  * gtk_icon_set_add_source().
2021  *
2022  * By default, the icon source has all parameters wildcarded. That is,
2023  * the icon source will be used as the base icon for any desired text
2024  * direction, widget state, or icon size.
2025  *
2026  * Return value: a new #GtkIconSource
2027  */
2028 GtkIconSource*
2029 gtk_icon_source_new (void)
2030 {
2031   GtkIconSource *src;
2032
2033   src = g_new0 (GtkIconSource, 1);
2034
2035   src->direction = GTK_TEXT_DIR_NONE;
2036   src->size = GTK_ICON_SIZE_INVALID;
2037   src->state = GTK_STATE_NORMAL;
2038
2039   src->any_direction = TRUE;
2040   src->any_state = TRUE;
2041   src->any_size = TRUE;
2042
2043   return src;
2044 }
2045
2046 /**
2047  * gtk_icon_source_copy:
2048  * @source: a #GtkIconSource
2049  *
2050  * Creates a copy of @source; mostly useful for language bindings.
2051  *
2052  * Return value: a new #GtkIconSource
2053  */
2054 GtkIconSource*
2055 gtk_icon_source_copy (const GtkIconSource *source)
2056 {
2057   GtkIconSource *copy;
2058
2059   g_return_val_if_fail (source != NULL, NULL);
2060
2061   copy = g_new (GtkIconSource, 1);
2062
2063   *copy = *source;
2064
2065   switch (copy->type)
2066     {
2067     case GTK_ICON_SOURCE_EMPTY:
2068     case GTK_ICON_SOURCE_STATIC_ICON_NAME:
2069       break;
2070     case GTK_ICON_SOURCE_ICON_NAME:
2071       copy->source.icon_name = g_strdup (copy->source.icon_name);
2072       break;
2073     case GTK_ICON_SOURCE_FILENAME:
2074       copy->source.filename = g_strdup (copy->source.filename);
2075       if (copy->filename_pixbuf)
2076         g_object_ref (copy->filename_pixbuf);
2077       break;
2078     case GTK_ICON_SOURCE_PIXBUF:
2079       g_object_ref (copy->source.pixbuf);
2080       break;
2081     default:
2082       g_assert_not_reached();
2083     }
2084
2085   return copy;
2086 }
2087
2088 /**
2089  * gtk_icon_source_free:
2090  * @source: a #GtkIconSource
2091  *
2092  * Frees a dynamically-allocated icon source, along with its
2093  * filename, size, and pixbuf fields if those are not %NULL.
2094  */
2095 void
2096 gtk_icon_source_free (GtkIconSource *source)
2097 {
2098   g_return_if_fail (source != NULL);
2099
2100   icon_source_clear (source);
2101   g_free (source);
2102 }
2103
2104 G_DEFINE_BOXED_TYPE (GtkIconSource, gtk_icon_source,
2105                      gtk_icon_source_copy,
2106                      gtk_icon_source_free)
2107
2108 static void
2109 icon_source_clear (GtkIconSource *source)
2110 {
2111   switch (source->type)
2112     {
2113     case GTK_ICON_SOURCE_EMPTY:
2114       break;
2115     case GTK_ICON_SOURCE_ICON_NAME:
2116       g_free (source->source.icon_name);
2117       /* fall thru */
2118     case GTK_ICON_SOURCE_STATIC_ICON_NAME:
2119       source->source.icon_name = NULL;
2120       break;
2121     case GTK_ICON_SOURCE_FILENAME:
2122       g_free (source->source.filename);
2123       source->source.filename = NULL;
2124       if (source->filename_pixbuf) 
2125         g_object_unref (source->filename_pixbuf);
2126       source->filename_pixbuf = NULL;
2127       break;
2128     case GTK_ICON_SOURCE_PIXBUF:
2129       g_object_unref (source->source.pixbuf);
2130       source->source.pixbuf = NULL;
2131       break;
2132     default:
2133       g_assert_not_reached();
2134     }
2135
2136   source->type = GTK_ICON_SOURCE_EMPTY;
2137 }
2138
2139 /**
2140  * gtk_icon_source_set_filename:
2141  * @source: a #GtkIconSource
2142  * @filename: (type filename): image file to use
2143  *
2144  * Sets the name of an image file to use as a base image when creating
2145  * icon variants for #GtkIconSet. The filename must be absolute.
2146  */
2147 void
2148 gtk_icon_source_set_filename (GtkIconSource *source,
2149                               const gchar   *filename)
2150 {
2151   g_return_if_fail (source != NULL);
2152   g_return_if_fail (filename == NULL || g_path_is_absolute (filename));
2153
2154   if (source->type == GTK_ICON_SOURCE_FILENAME &&
2155       source->source.filename == filename)
2156     return;
2157
2158   icon_source_clear (source);
2159
2160   if (filename != NULL)
2161     {
2162       source->type = GTK_ICON_SOURCE_FILENAME;
2163       source->source.filename = g_strdup (filename);
2164     }
2165 }
2166
2167 /**
2168  * gtk_icon_source_set_icon_name:
2169  * @source: a #GtkIconSource
2170  * @icon_name: (allow-none): name of icon to use
2171  *
2172  * Sets the name of an icon to look up in the current icon theme
2173  * to use as a base image when creating icon variants for #GtkIconSet.
2174  */
2175 void
2176 gtk_icon_source_set_icon_name (GtkIconSource *source,
2177                                const gchar   *icon_name)
2178 {
2179   g_return_if_fail (source != NULL);
2180
2181   if (source->type == GTK_ICON_SOURCE_ICON_NAME &&
2182       source->source.icon_name == icon_name)
2183     return;
2184
2185   icon_source_clear (source);
2186
2187   if (icon_name != NULL)
2188     {
2189       source->type = GTK_ICON_SOURCE_ICON_NAME;
2190       source->source.icon_name = g_strdup (icon_name);
2191     }
2192 }
2193
2194 /**
2195  * gtk_icon_source_set_pixbuf:
2196  * @source: a #GtkIconSource
2197  * @pixbuf: pixbuf to use as a source
2198  *
2199  * Sets a pixbuf to use as a base image when creating icon variants
2200  * for #GtkIconSet.
2201  */
2202 void
2203 gtk_icon_source_set_pixbuf (GtkIconSource *source,
2204                             GdkPixbuf     *pixbuf)
2205 {
2206   g_return_if_fail (source != NULL);
2207   g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
2208
2209   if (source->type == GTK_ICON_SOURCE_PIXBUF &&
2210       source->source.pixbuf == pixbuf)
2211     return;
2212
2213   icon_source_clear (source);
2214
2215   if (pixbuf != NULL)
2216     {
2217       source->type = GTK_ICON_SOURCE_PIXBUF;
2218       source->source.pixbuf = g_object_ref (pixbuf);
2219     }
2220 }
2221
2222 /**
2223  * gtk_icon_source_get_filename:
2224  * @source: a #GtkIconSource
2225  *
2226  * Retrieves the source filename, or %NULL if none is set. The
2227  * filename is not a copy, and should not be modified or expected to
2228  * persist beyond the lifetime of the icon source.
2229  *
2230  * Return value: (type filename): image filename. This string must not
2231  * be modified or freed.
2232  */
2233 const gchar*
2234 gtk_icon_source_get_filename (const GtkIconSource *source)
2235 {
2236   g_return_val_if_fail (source != NULL, NULL);
2237
2238   if (source->type == GTK_ICON_SOURCE_FILENAME)
2239     return source->source.filename;
2240   else
2241     return NULL;
2242 }
2243
2244 /**
2245  * gtk_icon_source_get_icon_name:
2246  * @source: a #GtkIconSource
2247  *
2248  * Retrieves the source icon name, or %NULL if none is set. The
2249  * icon_name is not a copy, and should not be modified or expected to
2250  * persist beyond the lifetime of the icon source.
2251  *
2252  * Return value: icon name. This string must not be modified or freed.
2253  */
2254 const gchar*
2255 gtk_icon_source_get_icon_name (const GtkIconSource *source)
2256 {
2257   g_return_val_if_fail (source != NULL, NULL);
2258
2259   if (source->type == GTK_ICON_SOURCE_ICON_NAME ||
2260      source->type == GTK_ICON_SOURCE_STATIC_ICON_NAME)
2261     return source->source.icon_name;
2262   else
2263     return NULL;
2264 }
2265
2266 /**
2267  * gtk_icon_source_get_pixbuf:
2268  * @source: a #GtkIconSource
2269  *
2270  * Retrieves the source pixbuf, or %NULL if none is set.
2271  * In addition, if a filename source is in use, this
2272  * function in some cases will return the pixbuf from
2273  * loaded from the filename. This is, for example, true
2274  * for the GtkIconSource passed to the GtkStyle::render_icon()
2275  * virtual function. The reference count on the pixbuf is
2276  * not incremented.
2277  *
2278  * Return value: (transfer none): source pixbuf
2279  */
2280 GdkPixbuf*
2281 gtk_icon_source_get_pixbuf (const GtkIconSource *source)
2282 {
2283   g_return_val_if_fail (source != NULL, NULL);
2284
2285   if (source->type == GTK_ICON_SOURCE_PIXBUF)
2286     return source->source.pixbuf;
2287   else if (source->type == GTK_ICON_SOURCE_FILENAME)
2288     return source->filename_pixbuf;
2289   else
2290     return NULL;
2291 }
2292
2293 /**
2294  * gtk_icon_source_set_direction_wildcarded:
2295  * @source: a #GtkIconSource
2296  * @setting: %TRUE to wildcard the text direction
2297  *
2298  * If the text direction is wildcarded, this source can be used
2299  * as the base image for an icon in any #GtkTextDirection.
2300  * If the text direction is not wildcarded, then the
2301  * text direction the icon source applies to should be set
2302  * with gtk_icon_source_set_direction(), and the icon source
2303  * will only be used with that text direction.
2304  *
2305  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
2306  * wildcarded sources, and will use an exact match when possible.
2307  */
2308 void
2309 gtk_icon_source_set_direction_wildcarded (GtkIconSource *source,
2310                                           gboolean       setting)
2311 {
2312   g_return_if_fail (source != NULL);
2313
2314   source->any_direction = setting != FALSE;
2315 }
2316
2317 /**
2318  * gtk_icon_source_set_state_wildcarded:
2319  * @source: a #GtkIconSource
2320  * @setting: %TRUE to wildcard the widget state
2321  *
2322  * If the widget state is wildcarded, this source can be used as the
2323  * base image for an icon in any #GtkStateType.  If the widget state
2324  * is not wildcarded, then the state the source applies to should be
2325  * set with gtk_icon_source_set_state() and the icon source will
2326  * only be used with that specific state.
2327  *
2328  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
2329  * wildcarded sources, and will use an exact match when possible.
2330  *
2331  * #GtkIconSet will normally transform wildcarded source images to
2332  * produce an appropriate icon for a given state, for example
2333  * lightening an image on prelight, but will not modify source images
2334  * that match exactly.
2335  */
2336 void
2337 gtk_icon_source_set_state_wildcarded (GtkIconSource *source,
2338                                       gboolean       setting)
2339 {
2340   g_return_if_fail (source != NULL);
2341
2342   source->any_state = setting != FALSE;
2343 }
2344
2345
2346 /**
2347  * gtk_icon_source_set_size_wildcarded:
2348  * @source: a #GtkIconSource
2349  * @setting: %TRUE to wildcard the widget state
2350  *
2351  * If the icon size is wildcarded, this source can be used as the base
2352  * image for an icon of any size.  If the size is not wildcarded, then
2353  * the size the source applies to should be set with
2354  * gtk_icon_source_set_size() and the icon source will only be used
2355  * with that specific size.
2356  *
2357  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
2358  * wildcarded sources, and will use an exact match when possible.
2359  *
2360  * #GtkIconSet will normally scale wildcarded source images to produce
2361  * an appropriate icon at a given size, but will not change the size
2362  * of source images that match exactly.
2363  */
2364 void
2365 gtk_icon_source_set_size_wildcarded (GtkIconSource *source,
2366                                      gboolean       setting)
2367 {
2368   g_return_if_fail (source != NULL);
2369
2370   source->any_size = setting != FALSE;
2371 }
2372
2373 /**
2374  * gtk_icon_source_get_size_wildcarded:
2375  * @source: a #GtkIconSource
2376  *
2377  * Gets the value set by gtk_icon_source_set_size_wildcarded().
2378  *
2379  * Return value: %TRUE if this icon source is a base for any icon size variant
2380  */
2381 gboolean
2382 gtk_icon_source_get_size_wildcarded (const GtkIconSource *source)
2383 {
2384   g_return_val_if_fail (source != NULL, TRUE);
2385
2386   return source->any_size;
2387 }
2388
2389 /**
2390  * gtk_icon_source_get_state_wildcarded:
2391  * @source: a #GtkIconSource
2392  *
2393  * Gets the value set by gtk_icon_source_set_state_wildcarded().
2394  *
2395  * Return value: %TRUE if this icon source is a base for any widget state variant
2396  */
2397 gboolean
2398 gtk_icon_source_get_state_wildcarded (const GtkIconSource *source)
2399 {
2400   g_return_val_if_fail (source != NULL, TRUE);
2401
2402   return source->any_state;
2403 }
2404
2405 /**
2406  * gtk_icon_source_get_direction_wildcarded:
2407  * @source: a #GtkIconSource
2408  *
2409  * Gets the value set by gtk_icon_source_set_direction_wildcarded().
2410  *
2411  * Return value: %TRUE if this icon source is a base for any text direction variant
2412  */
2413 gboolean
2414 gtk_icon_source_get_direction_wildcarded (const GtkIconSource *source)
2415 {
2416   g_return_val_if_fail (source != NULL, TRUE);
2417
2418   return source->any_direction;
2419 }
2420
2421 /**
2422  * gtk_icon_source_set_direction:
2423  * @source: a #GtkIconSource
2424  * @direction: text direction this source applies to
2425  *
2426  * Sets the text direction this icon source is intended to be used
2427  * with.
2428  *
2429  * Setting the text direction on an icon source makes no difference
2430  * if the text direction is wildcarded. Therefore, you should usually
2431  * call gtk_icon_source_set_direction_wildcarded() to un-wildcard it
2432  * in addition to calling this function.
2433  */
2434 void
2435 gtk_icon_source_set_direction (GtkIconSource   *source,
2436                                GtkTextDirection direction)
2437 {
2438   g_return_if_fail (source != NULL);
2439
2440   source->direction = direction;
2441 }
2442
2443 /**
2444  * gtk_icon_source_set_state:
2445  * @source: a #GtkIconSource
2446  * @state: widget state this source applies to
2447  *
2448  * Sets the widget state this icon source is intended to be used
2449  * with.
2450  *
2451  * Setting the widget state on an icon source makes no difference
2452  * if the state is wildcarded. Therefore, you should usually
2453  * call gtk_icon_source_set_state_wildcarded() to un-wildcard it
2454  * in addition to calling this function.
2455  */
2456 void
2457 gtk_icon_source_set_state (GtkIconSource *source,
2458                            GtkStateType   state)
2459 {
2460   g_return_if_fail (source != NULL);
2461
2462   source->state = state;
2463 }
2464
2465 /**
2466  * gtk_icon_source_set_size:
2467  * @source: a #GtkIconSource
2468  * @size: (type int): icon size this source applies to
2469  *
2470  * Sets the icon size this icon source is intended to be used
2471  * with.
2472  *
2473  * Setting the icon size on an icon source makes no difference
2474  * if the size is wildcarded. Therefore, you should usually
2475  * call gtk_icon_source_set_size_wildcarded() to un-wildcard it
2476  * in addition to calling this function.
2477  */
2478 void
2479 gtk_icon_source_set_size (GtkIconSource *source,
2480                           GtkIconSize    size)
2481 {
2482   g_return_if_fail (source != NULL);
2483
2484   source->size = size;
2485 }
2486
2487 /**
2488  * gtk_icon_source_get_direction:
2489  * @source: a #GtkIconSource
2490  *
2491  * Obtains the text direction this icon source applies to. The return
2492  * value is only useful/meaningful if the text direction is <emphasis>not</emphasis>
2493  * wildcarded.
2494  *
2495  * Return value: text direction this source matches
2496  */
2497 GtkTextDirection
2498 gtk_icon_source_get_direction (const GtkIconSource *source)
2499 {
2500   g_return_val_if_fail (source != NULL, 0);
2501
2502   return source->direction;
2503 }
2504
2505 /**
2506  * gtk_icon_source_get_state:
2507  * @source: a #GtkIconSource
2508  *
2509  * Obtains the widget state this icon source applies to. The return
2510  * value is only useful/meaningful if the widget state is <emphasis>not</emphasis>
2511  * wildcarded.
2512  *
2513  * Return value: widget state this source matches
2514  */
2515 GtkStateType
2516 gtk_icon_source_get_state (const GtkIconSource *source)
2517 {
2518   g_return_val_if_fail (source != NULL, 0);
2519
2520   return source->state;
2521 }
2522
2523 /**
2524  * gtk_icon_source_get_size:
2525  * @source: a #GtkIconSource
2526  *
2527  * Obtains the icon size this source applies to. The return value
2528  * is only useful/meaningful if the icon size is <emphasis>not</emphasis> wildcarded.
2529  *
2530  * Return value: (type int): icon size this source matches.
2531  */
2532 GtkIconSize
2533 gtk_icon_source_get_size (const GtkIconSource *source)
2534 {
2535   g_return_val_if_fail (source != NULL, 0);
2536
2537   return source->size;
2538 }
2539
2540 #define NUM_CACHED_ICONS 8
2541
2542 typedef struct _CachedIcon CachedIcon;
2543
2544 struct _CachedIcon
2545 {
2546   /* These must all match to use the cached pixbuf.
2547    * If any don't match, we must re-render the pixbuf.
2548    */
2549   GtkStyleContext *style;
2550   GtkTextDirection direction;
2551   GtkStateType state;
2552   GtkIconSize size;
2553
2554   GdkPixbuf *pixbuf;
2555 };
2556
2557 static void
2558 ensure_cache_up_to_date (GtkIconSet *icon_set)
2559 {
2560   if (icon_set->cache_serial != cache_serial)
2561     {
2562       clear_cache (icon_set, TRUE);
2563       icon_set->cache_serial = cache_serial;
2564     }
2565 }
2566
2567 static void
2568 cached_icon_free (CachedIcon *icon)
2569 {
2570   g_object_unref (icon->pixbuf);
2571   g_object_unref (icon->style);
2572
2573   g_free (icon);
2574 }
2575
2576 static GdkPixbuf *
2577 find_in_cache (GtkIconSet      *icon_set,
2578                GtkStyleContext *style_context,
2579                GtkTextDirection direction,
2580                GtkStateType     state,
2581                GtkIconSize      size)
2582 {
2583   GSList *tmp_list;
2584   GSList *prev;
2585
2586   ensure_cache_up_to_date (icon_set);
2587
2588   prev = NULL;
2589   tmp_list = icon_set->cache;
2590   while (tmp_list != NULL)
2591     {
2592       CachedIcon *icon = tmp_list->data;
2593
2594       if (icon->style == style_context &&
2595           icon->direction == direction &&
2596           icon->state == state &&
2597           (size == (GtkIconSize)-1 || icon->size == size))
2598         {
2599           if (prev)
2600             {
2601               /* Move this icon to the front of the list. */
2602               prev->next = tmp_list->next;
2603               tmp_list->next = icon_set->cache;
2604               icon_set->cache = tmp_list;
2605             }
2606
2607           return icon->pixbuf;
2608         }
2609
2610       prev = tmp_list;
2611       tmp_list = g_slist_next (tmp_list);
2612     }
2613
2614   return NULL;
2615 }
2616
2617 static void
2618 add_to_cache (GtkIconSet      *icon_set,
2619               GtkStyleContext *style_context,
2620               GtkTextDirection direction,
2621               GtkStateType     state,
2622               GtkIconSize      size,
2623               GdkPixbuf       *pixbuf)
2624 {
2625   CachedIcon *icon;
2626
2627   ensure_cache_up_to_date (icon_set);
2628
2629   g_object_ref (pixbuf);
2630
2631   icon = g_new (CachedIcon, 1);
2632   icon_set->cache = g_slist_prepend (icon_set->cache, icon);
2633   icon_set->cache_size++;
2634
2635   icon->style = g_object_ref (style_context);
2636   icon->direction = direction;
2637   icon->state = state;
2638   icon->size = size;
2639   icon->pixbuf = pixbuf;
2640   attach_to_style (icon_set, icon->style);
2641
2642   if (icon_set->cache_size >= NUM_CACHED_ICONS)
2643     {
2644       /* Remove oldest item in the cache */
2645       GSList *tmp_list;
2646
2647       tmp_list = icon_set->cache;
2648
2649       /* Find next-to-last link */
2650       g_assert (NUM_CACHED_ICONS > 2);
2651       while (tmp_list->next->next)
2652         tmp_list = tmp_list->next;
2653
2654       g_assert (tmp_list != NULL);
2655       g_assert (tmp_list->next != NULL);
2656       g_assert (tmp_list->next->next == NULL);
2657
2658       /* Free the last icon */
2659       icon = tmp_list->next->data;
2660
2661       g_slist_free (tmp_list->next);
2662       tmp_list->next = NULL;
2663
2664       cached_icon_free (icon);
2665     }
2666 }
2667
2668 static void
2669 clear_cache (GtkIconSet *icon_set,
2670              gboolean    style_detach)
2671 {
2672   GSList *cache, *tmp_list;
2673   GtkStyleContext *last_style = NULL;
2674
2675   cache = icon_set->cache;
2676   icon_set->cache = NULL;
2677   icon_set->cache_size = 0;
2678   tmp_list = cache;
2679   while (tmp_list != NULL)
2680     {
2681       CachedIcon *icon = tmp_list->data;
2682
2683       if (style_detach)
2684         {
2685           /* simple optimization for the case where the cache
2686            * contains contiguous icons from the same style.
2687            * it's safe to call detach_from_style more than
2688            * once on the same style though.
2689            */
2690           if (last_style != icon->style)
2691             {
2692               detach_from_style (icon_set, icon->style);
2693               last_style = icon->style;
2694             }
2695         }
2696
2697       cached_icon_free (icon);
2698
2699       tmp_list = g_slist_next (tmp_list);
2700     }
2701
2702   g_slist_free (cache);
2703 }
2704
2705 static GSList*
2706 copy_cache (GtkIconSet *icon_set,
2707             GtkIconSet *copy_recipient)
2708 {
2709   GSList *tmp_list;
2710   GSList *copy = NULL;
2711
2712   ensure_cache_up_to_date (icon_set);
2713
2714   tmp_list = icon_set->cache;
2715   while (tmp_list != NULL)
2716     {
2717       CachedIcon *icon = tmp_list->data;
2718       CachedIcon *icon_copy = g_new (CachedIcon, 1);
2719
2720       *icon_copy = *icon;
2721
2722       attach_to_style (copy_recipient, icon_copy->style);
2723       g_object_ref (icon_copy->style);
2724
2725       g_object_ref (icon_copy->pixbuf);
2726
2727       icon_copy->size = icon->size;
2728
2729       copy = g_slist_prepend (copy, icon_copy);
2730
2731       tmp_list = g_slist_next (tmp_list);
2732     }
2733
2734   return g_slist_reverse (copy);
2735 }
2736
2737 static void
2738 attach_to_style (GtkIconSet      *icon_set,
2739                  GtkStyleContext *style_context)
2740 {
2741   GHashTable *table;
2742
2743   table = g_object_get_qdata (G_OBJECT (style_context),
2744                               g_quark_try_string ("gtk-style-icon-sets"));
2745
2746   if (table == NULL)
2747     {
2748       table = g_hash_table_new (NULL, NULL);
2749       g_object_set_qdata_full (G_OBJECT (style_context),
2750                                g_quark_from_static_string ("gtk-style-icon-sets"),
2751                                table,
2752                                style_dnotify);
2753     }
2754
2755   g_hash_table_insert (table, icon_set, icon_set);
2756 }
2757
2758 static void
2759 detach_from_style (GtkIconSet       *icon_set,
2760                    GtkStyleContext  *style_context)
2761 {
2762   GHashTable *table;
2763
2764   table = g_object_get_qdata (G_OBJECT (style_context),
2765                               g_quark_try_string ("gtk-style-icon-sets"));
2766
2767   if (table != NULL)
2768     g_hash_table_remove (table, icon_set);
2769 }
2770
2771 static void
2772 iconsets_foreach (gpointer key,
2773                   gpointer value,
2774                   gpointer user_data)
2775 {
2776   GtkIconSet *icon_set = key;
2777
2778   /* We only need to remove cache entries for the given style;
2779    * but that complicates things because in destroy notify
2780    * we don't know which style got destroyed, and 95% of the
2781    * time all cache entries will have the same style,
2782    * so this is faster anyway.
2783    */
2784
2785   clear_cache (icon_set, FALSE);
2786 }
2787
2788 static void
2789 style_dnotify (gpointer data)
2790 {
2791   GHashTable *table = data;
2792
2793   g_hash_table_foreach (table, iconsets_foreach, NULL);
2794
2795   g_hash_table_destroy (table);
2796 }
2797
2798 /* This allows the icon set to detect that its cache is out of date. */
2799 void
2800 _gtk_icon_set_invalidate_caches (void)
2801 {
2802   ++cache_serial;
2803 }
2804
2805 /**
2806  * _gtk_icon_factory_list_ids:
2807  *
2808  * Gets all known IDs stored in an existing icon factory.
2809  * The strings in the returned list aren't copied.
2810  * The list itself should be freed.
2811  *
2812  * Return value: List of ids in icon factories
2813  */
2814 GList*
2815 _gtk_icon_factory_list_ids (void)
2816 {
2817   GSList *tmp_list;
2818   GList *ids;
2819
2820   ids = NULL;
2821
2822   _gtk_icon_factory_get_default_icons ();
2823
2824   tmp_list = all_icon_factories;
2825   while (tmp_list != NULL)
2826     {
2827       GList *these_ids;
2828       GtkIconFactory *factory = GTK_ICON_FACTORY (tmp_list->data);
2829       GtkIconFactoryPrivate *priv = factory->priv;
2830
2831       these_ids = g_hash_table_get_keys (priv->icons);
2832
2833       ids = g_list_concat (ids, these_ids);
2834
2835       tmp_list = g_slist_next (tmp_list);
2836     }
2837
2838   return ids;
2839 }
2840
2841 typedef struct {
2842   GSList *sources;
2843   gboolean in_source;
2844
2845 } IconFactoryParserData;
2846
2847 typedef struct {
2848   gchar            *stock_id;
2849   gchar            *filename;
2850   gchar            *icon_name;
2851   GtkTextDirection  direction;
2852   GtkIconSize       size;
2853   GtkStateType      state;
2854 } IconSourceParserData;
2855
2856 static void
2857 icon_source_start_element (GMarkupParseContext  *context,
2858                            const gchar          *element_name,
2859                            const gchar         **names,
2860                            const gchar         **values,
2861                            gpointer              user_data,
2862                            GError              **error)
2863 {
2864   gint i;
2865   gchar *stock_id = NULL;
2866   gchar *filename = NULL;
2867   gchar *icon_name = NULL;
2868   gint size = -1;
2869   gint direction = -1;
2870   gint state = -1;
2871   IconFactoryParserData *parser_data;
2872   IconSourceParserData *source_data;
2873   gchar *error_msg;
2874   GQuark error_domain;
2875
2876   parser_data = (IconFactoryParserData*)user_data;
2877
2878   if (!parser_data->in_source)
2879     {
2880       if (strcmp (element_name, "sources") != 0)
2881         {
2882           error_msg = g_strdup_printf ("Unexpected element %s, expected <sources>", element_name);
2883           error_domain = GTK_BUILDER_ERROR_INVALID_TAG;
2884           goto error;
2885         }
2886       parser_data->in_source = TRUE;
2887       return;
2888     }
2889   else
2890     {
2891       if (strcmp (element_name, "source") != 0)
2892         {
2893           error_msg = g_strdup_printf ("Unexpected element %s, expected <source>", element_name);
2894           error_domain = GTK_BUILDER_ERROR_INVALID_TAG;
2895           goto error;
2896         }
2897     }
2898
2899   for (i = 0; names[i]; i++)
2900     {
2901       if (strcmp (names[i], "stock-id") == 0)
2902         stock_id = g_strdup (values[i]);
2903       else if (strcmp (names[i], "filename") == 0)
2904         filename = g_strdup (values[i]);
2905       else if (strcmp (names[i], "icon-name") == 0)
2906         icon_name = g_strdup (values[i]);
2907       else if (strcmp (names[i], "size") == 0)
2908         {
2909           if (!_gtk_builder_enum_from_string (GTK_TYPE_ICON_SIZE,
2910                                               values[i],
2911                                               &size,
2912                                               error))
2913             return;
2914         }
2915       else if (strcmp (names[i], "direction") == 0)
2916         {
2917           if (!_gtk_builder_enum_from_string (GTK_TYPE_TEXT_DIRECTION,
2918                                               values[i],
2919                                               &direction,
2920                                               error))
2921             return;
2922         }
2923       else if (strcmp (names[i], "state") == 0)
2924         {
2925           if (!_gtk_builder_enum_from_string (GTK_TYPE_STATE_TYPE,
2926                                               values[i],
2927                                               &state,
2928                                               error))
2929             return;
2930         }
2931       else
2932         {
2933           error_msg = g_strdup_printf ("'%s' is not a valid attribute of <%s>",
2934                                        names[i], "source");
2935           error_domain = GTK_BUILDER_ERROR_INVALID_ATTRIBUTE;
2936           goto error;
2937         }
2938     }
2939
2940   if (!stock_id)
2941     {
2942       error_msg = g_strdup_printf ("<source> requires a stock_id");
2943       error_domain = GTK_BUILDER_ERROR_MISSING_ATTRIBUTE;
2944       goto error;
2945     }
2946
2947   source_data = g_slice_new (IconSourceParserData);
2948   source_data->stock_id = stock_id;
2949   source_data->filename = filename;
2950   source_data->icon_name = icon_name;
2951   source_data->size = size;
2952   source_data->direction = direction;
2953   source_data->state = state;
2954
2955   parser_data->sources = g_slist_prepend (parser_data->sources, source_data);
2956   return;
2957
2958  error:
2959   {
2960     gchar *tmp;
2961     gint line_number, char_number;
2962
2963     g_markup_parse_context_get_position (context, &line_number, &char_number);
2964
2965     tmp = g_strdup_printf ("%s:%d:%d %s", "input",
2966                            line_number, char_number, error_msg);
2967     g_set_error_literal (error, GTK_BUILDER_ERROR, error_domain, tmp);
2968     g_free (tmp);
2969     g_free (stock_id);
2970     g_free (filename);
2971     g_free (icon_name);
2972     return;
2973   }
2974 }
2975
2976 static const GMarkupParser icon_source_parser =
2977   {
2978     icon_source_start_element,
2979   };
2980
2981 static gboolean
2982 gtk_icon_factory_buildable_custom_tag_start (GtkBuildable     *buildable,
2983                                              GtkBuilder       *builder,
2984                                              GObject          *child,
2985                                              const gchar      *tagname,
2986                                              GMarkupParser    *parser,
2987                                              gpointer         *data)
2988 {
2989   g_assert (buildable);
2990
2991   if (strcmp (tagname, "sources") == 0)
2992     {
2993       IconFactoryParserData *parser_data;
2994
2995       parser_data = g_slice_new0 (IconFactoryParserData);
2996       *parser = icon_source_parser;
2997       *data = parser_data;
2998       return TRUE;
2999     }
3000   return FALSE;
3001 }
3002
3003 static void
3004 gtk_icon_factory_buildable_custom_tag_end (GtkBuildable *buildable,
3005                                            GtkBuilder   *builder,
3006                                            GObject      *child,
3007                                            const gchar  *tagname,
3008                                            gpointer     *user_data)
3009 {
3010   GtkIconFactory *icon_factory;
3011
3012   icon_factory = GTK_ICON_FACTORY (buildable);
3013
3014   if (strcmp (tagname, "sources") == 0)
3015     {
3016       IconFactoryParserData *parser_data;
3017       GtkIconSource *icon_source;
3018       GtkIconSet *icon_set;
3019       GSList *l;
3020
3021       parser_data = (IconFactoryParserData*)user_data;
3022
3023       for (l = parser_data->sources; l; l = l->next)
3024         {
3025           IconSourceParserData *source_data = l->data;
3026
3027           icon_set = gtk_icon_factory_lookup (icon_factory, source_data->stock_id);
3028           if (!icon_set)
3029             {
3030               icon_set = gtk_icon_set_new ();
3031               gtk_icon_factory_add (icon_factory, source_data->stock_id, icon_set);
3032               gtk_icon_set_unref (icon_set);
3033             }
3034
3035           icon_source = gtk_icon_source_new ();
3036
3037           if (source_data->filename)
3038             {
3039               gchar *filename;
3040               filename = _gtk_builder_get_absolute_filename (builder, source_data->filename);
3041               gtk_icon_source_set_filename (icon_source, filename);
3042               g_free (filename);
3043             }
3044           if (source_data->icon_name)
3045             gtk_icon_source_set_icon_name (icon_source, source_data->icon_name);
3046           if (source_data->size != -1)
3047             {
3048               gtk_icon_source_set_size (icon_source, source_data->size);
3049               gtk_icon_source_set_size_wildcarded (icon_source, FALSE);
3050             }
3051           if (source_data->direction != -1)
3052             {
3053               gtk_icon_source_set_direction (icon_source, source_data->direction);
3054               gtk_icon_source_set_direction_wildcarded (icon_source, FALSE);
3055             }
3056           if (source_data->state != -1)
3057             {
3058               gtk_icon_source_set_state (icon_source, source_data->state);
3059               gtk_icon_source_set_state_wildcarded (icon_source, FALSE);
3060             }
3061
3062           /* Inline source_add() to avoid creating a copy */
3063           g_assert (icon_source->type != GTK_ICON_SOURCE_EMPTY);
3064           icon_set->sources = g_slist_insert_sorted (icon_set->sources,
3065                                                      icon_source,
3066                                                      icon_source_compare);
3067
3068           g_free (source_data->stock_id);
3069           g_free (source_data->filename);
3070           g_free (source_data->icon_name);
3071           g_slice_free (IconSourceParserData, source_data);
3072         }
3073       g_slist_free (parser_data->sources);
3074       g_slice_free (IconFactoryParserData, parser_data);
3075
3076       /* TODO: Add an attribute/tag to prevent this.
3077        * Usually it's the right thing to do though.
3078        */
3079       gtk_icon_factory_add_default (icon_factory);
3080     }
3081 }