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