]> Pileus Git - ~andy/gtk/blob - gtk/gtkiconfactory.c
Register 16x16 version of properties stock icon.
[~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 "gtkiconfactory.h"
28 #include "stock-icons/gtkstockpixbufs.h"
29 #include "gtkstock.h"
30 #include "gtkintl.h"
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <string.h>
34
35 static GSList *all_icon_factories = NULL;
36
37 struct _GtkIconSource
38 {
39   /* Either filename or pixbuf can be NULL. If both are non-NULL,
40    * the pixbuf is assumed to be the already-loaded contents of the
41    * file.
42    */
43   gchar *filename;
44   GdkPixbuf *pixbuf;
45
46   GtkTextDirection direction;
47   GtkStateType state;
48   GtkIconSize size;
49
50   /* If TRUE, then the parameter is wildcarded, and the above
51    * fields should be ignored. If FALSE, the parameter is
52    * specified, and the above fields should be valid.
53    */
54   guint any_direction : 1;
55   guint any_state : 1;
56   guint any_size : 1;
57 };
58
59 static gpointer parent_class = NULL;
60
61 static void gtk_icon_factory_init       (GtkIconFactory      *icon_factory);
62 static void gtk_icon_factory_class_init (GtkIconFactoryClass *klass);
63 static void gtk_icon_factory_finalize   (GObject             *object);
64 static void get_default_icons           (GtkIconFactory      *icon_factory);
65
66 GType
67 gtk_icon_factory_get_type (void)
68 {
69   static GType object_type = 0;
70
71   if (!object_type)
72     {
73       static const GTypeInfo object_info =
74       {
75         sizeof (GtkIconFactoryClass),
76         (GBaseInitFunc) NULL,
77         (GBaseFinalizeFunc) NULL,
78         (GClassInitFunc) gtk_icon_factory_class_init,
79         NULL,           /* class_finalize */
80         NULL,           /* class_data */
81         sizeof (GtkIconFactory),
82         0,              /* n_preallocs */
83         (GInstanceInitFunc) gtk_icon_factory_init,
84       };
85       
86       object_type = g_type_register_static (G_TYPE_OBJECT,
87                                             "GtkIconFactory",
88                                             &object_info, 0);
89     }
90   
91   return object_type;
92 }
93
94 static void
95 gtk_icon_factory_init (GtkIconFactory *factory)
96 {
97   factory->icons = g_hash_table_new (g_str_hash, g_str_equal);
98   all_icon_factories = g_slist_prepend (all_icon_factories, factory);
99 }
100
101 static void
102 gtk_icon_factory_class_init (GtkIconFactoryClass *klass)
103 {
104   GObjectClass *object_class = G_OBJECT_CLASS (klass);
105   
106   parent_class = g_type_class_peek_parent (klass);
107
108   object_class->finalize = gtk_icon_factory_finalize;
109 }
110
111 static void
112 free_icon_set (gpointer key, gpointer value, gpointer data)
113 {
114   g_free (key);
115   gtk_icon_set_unref (value);
116 }
117
118 static void
119 gtk_icon_factory_finalize (GObject *object)
120 {
121   GtkIconFactory *factory = GTK_ICON_FACTORY (object);
122
123   all_icon_factories = g_slist_remove (all_icon_factories, factory);
124   
125   g_hash_table_foreach (factory->icons, free_icon_set, NULL);
126   
127   g_hash_table_destroy (factory->icons);
128   
129   G_OBJECT_CLASS (parent_class)->finalize (object);
130 }
131
132 /**
133  * gtk_icon_factory_new:
134  *
135  * Creates a new #GtkIconFactory. An icon factory manages a collection
136  * of #GtkIconSet<!>s; a #GtkIconSet manages a set of variants of a
137  * particular icon (i.e. a #GtkIconSet contains variants for different
138  * sizes and widget states). Icons in an icon factory are named by a
139  * stock ID, which is a simple string identifying the icon. Each
140  * #GtkStyle has a list of #GtkIconFactory<!>s derived from the current
141  * theme; those icon factories are consulted first when searching for
142  * an icon. If the theme doesn't set a particular icon, GTK+ looks for
143  * the icon in a list of default icon factories, maintained by
144  * gtk_icon_factory_add_default() and
145  * gtk_icon_factory_remove_default(). Applications with icons should
146  * add a default icon factory with their icons, which will allow
147  * themes to override the icons for the application.
148  * 
149  * Return value: a new #GtkIconFactory
150  **/
151 GtkIconFactory*
152 gtk_icon_factory_new (void)
153 {
154   return GTK_ICON_FACTORY (g_object_new (GTK_TYPE_ICON_FACTORY, NULL));
155 }
156
157 /**
158  * gtk_icon_factory_add:
159  * @factory: a #GtkIconFactory
160  * @stock_id: icon name
161  * @icon_set: icon set
162  *
163  * Adds the given @icon_set to the icon factory, under the name
164  * @stock_id.  @stock_id should be namespaced for your application,
165  * e.g. "myapp-whatever-icon".  Normally applications create a
166  * #GtkIconFactory, then add it to the list of default factories with
167  * gtk_icon_factory_add_default(). Then they pass the @stock_id to
168  * widgets such as #GtkImage to display the icon. Themes can provide
169  * an icon with the same name (such as "myapp-whatever-icon") to
170  * override your application's default icons. If an icon already
171  * existed in @factory for @stock_id, it is unreferenced and replaced
172  * with the new @icon_set.
173  * 
174  **/
175 void
176 gtk_icon_factory_add (GtkIconFactory *factory,
177                       const gchar    *stock_id,
178                       GtkIconSet     *icon_set)
179 {
180   gpointer old_key = NULL;
181   gpointer old_value = NULL;
182
183   g_return_if_fail (GTK_IS_ICON_FACTORY (factory));
184   g_return_if_fail (stock_id != NULL);
185   g_return_if_fail (icon_set != NULL);  
186
187   g_hash_table_lookup_extended (factory->icons, stock_id,
188                                 &old_key, &old_value);
189
190   if (old_value == icon_set)
191     return;
192   
193   gtk_icon_set_ref (icon_set);
194
195   /* GHashTable key memory management is so fantastically broken. */
196   if (old_key)
197     g_hash_table_insert (factory->icons, old_key, icon_set);
198   else
199     g_hash_table_insert (factory->icons, g_strdup (stock_id), icon_set);
200
201   if (old_value)
202     gtk_icon_set_unref (old_value);
203 }
204
205 /**
206  * gtk_icon_factory_lookup:
207  * @factory: a #GtkIconFactory
208  * @stock_id: an icon name
209  * 
210  * Looks up @stock_id in the icon factory, returning an icon set
211  * if found, otherwise %NULL. For display to the user, you should
212  * use gtk_style_lookup_icon_set() on the #GtkStyle for the
213  * widget that will display the icon, instead of using this
214  * function directly, so that themes are taken into account.
215  * 
216  * Return value: icon set of @stock_id.
217  **/
218 GtkIconSet *
219 gtk_icon_factory_lookup (GtkIconFactory *factory,
220                          const gchar    *stock_id)
221 {
222   g_return_val_if_fail (GTK_IS_ICON_FACTORY (factory), NULL);
223   g_return_val_if_fail (stock_id != NULL, NULL);
224   
225   return g_hash_table_lookup (factory->icons, stock_id);
226 }
227
228 static GtkIconFactory *gtk_default_icons = NULL;
229 static GSList *default_factories = NULL;
230
231 /**
232  * gtk_icon_factory_add_default:
233  * @factory: a #GtkIconFactory
234  * 
235  * Adds an icon factory to the list of icon factories searched by
236  * gtk_style_lookup_icon_set(). This means that, for example,
237  * gtk_image_new_from_stock() will be able to find icons in @factory.
238  * There will normally be an icon factory added for each library or
239  * application that comes with icons. The default icon factories
240  * can be overridden by themes.
241  * 
242  **/
243 void
244 gtk_icon_factory_add_default (GtkIconFactory *factory)
245 {
246   g_return_if_fail (GTK_IS_ICON_FACTORY (factory));
247
248   g_object_ref (G_OBJECT (factory));
249   
250   default_factories = g_slist_prepend (default_factories, factory);
251 }
252
253 /**
254  * gtk_icon_factory_remove_default:
255  * @factory: a #GtkIconFactory previously added with gtk_icon_factory_add_default()
256  *
257  * Removes an icon factory from the list of default icon
258  * factories. Not normally used; you might use it for a library that
259  * can be unloaded or shut down.
260  * 
261  **/
262 void
263 gtk_icon_factory_remove_default (GtkIconFactory  *factory)
264 {
265   g_return_if_fail (GTK_IS_ICON_FACTORY (factory));
266
267   default_factories = g_slist_remove (default_factories, factory);
268
269   g_object_unref (G_OBJECT (factory));
270 }
271
272 static void
273 ensure_default_icons (void)
274 {
275   if (gtk_default_icons == NULL)
276     {
277       gtk_default_icons = gtk_icon_factory_new ();
278
279       get_default_icons (gtk_default_icons);
280     }
281 }
282
283 /**
284  * gtk_icon_factory_lookup_default:
285  * @stock_id: an icon name
286  *
287  * Looks for an icon in the list of default icon factories.  For
288  * display to the user, you should use gtk_style_lookup_icon_set() on
289  * the #GtkStyle for the widget that will display the icon, instead of
290  * using this function directly, so that themes are taken into
291  * account.
292  * 
293  * 
294  * Return value: a #GtkIconSet, or %NULL
295  **/
296 GtkIconSet *
297 gtk_icon_factory_lookup_default (const gchar *stock_id)
298 {
299   GSList *tmp_list;
300
301   g_return_val_if_fail (stock_id != NULL, NULL);
302   
303   tmp_list = default_factories;
304   while (tmp_list != NULL)
305     {
306       GtkIconSet *icon_set =
307         gtk_icon_factory_lookup (GTK_ICON_FACTORY (tmp_list->data),
308                                  stock_id);
309
310       if (icon_set)
311         return icon_set;
312       
313       tmp_list = g_slist_next (tmp_list);
314     }
315
316   ensure_default_icons ();
317   
318   return gtk_icon_factory_lookup (gtk_default_icons, stock_id);
319 }
320
321 #if 0
322 static GtkIconSet *
323 sized_icon_set_from_inline (const guchar *inline_data,
324                             GtkIconSize   size)
325 {
326   GtkIconSet *set;
327
328   GtkIconSource source = { NULL, NULL, 0, 0, 0,
329                            TRUE, TRUE, FALSE };
330
331   source.size = size;
332
333   set = gtk_icon_set_new ();
334
335   source.pixbuf = gdk_pixbuf_new_from_inline (-1, inline_data, FALSE, NULL);
336   
337   g_assert (source.pixbuf);
338
339   gtk_icon_set_add_source (set, &source);
340
341   g_object_unref (G_OBJECT (source.pixbuf));
342   
343   return set;
344 }
345 #endif
346
347 static GtkIconSet *
348 sized_with_fallback_icon_set_from_inline (const guchar *fallback_data,
349                                           const guchar *inline_data,
350                                           GtkIconSize   size)
351 {
352   GtkIconSet *set;
353
354   GtkIconSource source = { NULL, NULL, 0, 0, 0,
355                            TRUE, TRUE, FALSE };
356
357   source.size = size;
358
359   set = gtk_icon_set_new ();
360
361   source.pixbuf = gdk_pixbuf_new_from_inline (-1, inline_data, FALSE, NULL);
362
363   g_assert (source.pixbuf);
364
365   gtk_icon_set_add_source (set, &source);
366
367   g_object_unref (G_OBJECT (source.pixbuf));
368   
369   source.any_size = TRUE;
370
371   source.pixbuf = gdk_pixbuf_new_from_inline (-1, fallback_data, FALSE, NULL);
372
373   g_assert (source.pixbuf);
374
375   gtk_icon_set_add_source (set, &source);
376
377   g_object_unref (G_OBJECT (source.pixbuf));  
378   
379   return set;
380 }
381
382 static GtkIconSet *
383 unsized_icon_set_from_inline (const guchar *inline_data)
384 {
385   GtkIconSet *set;
386
387   /* This icon can be used for any direction/state/size */
388   GtkIconSource source = { NULL, NULL, 0, 0, 0,
389                            TRUE, TRUE, TRUE };
390
391   set = gtk_icon_set_new ();
392
393   source.pixbuf = gdk_pixbuf_new_from_inline (-1, inline_data, FALSE, NULL);
394
395   g_assert (source.pixbuf);
396
397   gtk_icon_set_add_source (set, &source);
398
399   g_object_unref (G_OBJECT (source.pixbuf));
400   
401   return set;
402 }
403
404 #if 0
405 static void
406 add_sized (GtkIconFactory *factory,
407            const guchar   *inline_data,
408            GtkIconSize     size,
409            const gchar    *stock_id)
410 {
411   GtkIconSet *set;
412   
413   set = sized_icon_set_from_inline (inline_data, size);
414   
415   gtk_icon_factory_add (factory, stock_id, set);
416
417   gtk_icon_set_unref (set);
418 }
419 #endif
420
421 static void
422 add_sized_with_fallback (GtkIconFactory *factory,
423                          const guchar   *fallback_data,
424                          const guchar   *inline_data,
425                          GtkIconSize     size,
426                          const gchar    *stock_id)
427 {
428   GtkIconSet *set;
429   
430   set = sized_with_fallback_icon_set_from_inline (fallback_data, inline_data, size);
431   
432   gtk_icon_factory_add (factory, stock_id, set);
433
434   gtk_icon_set_unref (set);
435 }
436
437 static void
438 add_unsized (GtkIconFactory *factory,
439              const guchar   *inline_data,
440              const gchar    *stock_id)
441 {
442   GtkIconSet *set;
443   
444   set = unsized_icon_set_from_inline (inline_data);
445   
446   gtk_icon_factory_add (factory, stock_id, set);
447
448   gtk_icon_set_unref (set);
449 }
450
451 static void
452 get_default_icons (GtkIconFactory *factory)
453 {
454   /* KEEP IN SYNC with gtkstock.c */
455
456   /* We add all stock icons unsized, since it's confusing if icons only
457    * can be loaded at certain sizes.
458    */
459
460   /* Have dialog size */
461   add_unsized (factory, stock_dialog_error_48, GTK_STOCK_DIALOG_ERROR);
462   add_unsized (factory, stock_dialog_info_48, GTK_STOCK_DIALOG_INFO);
463   add_unsized (factory, stock_dialog_question_48, GTK_STOCK_DIALOG_QUESTION);
464   add_unsized (factory, stock_dialog_warning_48,GTK_STOCK_DIALOG_WARNING);
465   
466   /* Have dnd size */
467   add_unsized (factory, stock_dnd_32, GTK_STOCK_DND);
468   add_unsized (factory, stock_dnd_multiple_32, GTK_STOCK_DND_MULTIPLE);
469   
470   /* Have button sizes */
471   add_unsized (factory, stock_apply_20, GTK_STOCK_APPLY);
472   add_unsized (factory, stock_cancel_20, GTK_STOCK_CANCEL);
473   add_unsized (factory, stock_no_20, GTK_STOCK_NO);
474   add_unsized (factory, stock_ok_20, GTK_STOCK_OK);
475   add_unsized (factory, stock_yes_20, GTK_STOCK_YES);
476
477   /* Generic + button sizes */
478   add_sized_with_fallback (factory,
479                            stock_close_24,
480                            stock_close_20,
481                            GTK_ICON_SIZE_BUTTON,
482                            GTK_STOCK_CLOSE);
483
484   /* Generic + menu sizes */  
485   add_sized_with_fallback (factory,
486                            stock_align_center_24,
487                            stock_align_center_16,
488                            GTK_ICON_SIZE_MENU,
489                            GTK_STOCK_JUSTIFY_CENTER);
490
491   add_sized_with_fallback (factory,
492                            stock_align_justify_24,
493                            stock_align_justify_16,
494                            GTK_ICON_SIZE_MENU,
495                            GTK_STOCK_JUSTIFY_FILL);
496
497   add_sized_with_fallback (factory,
498                            stock_align_left_24,
499                            stock_align_left_16,
500                            GTK_ICON_SIZE_MENU,
501                            GTK_STOCK_JUSTIFY_LEFT);
502
503   add_sized_with_fallback (factory,
504                            stock_align_right_24,
505                            stock_align_right_16,
506                            GTK_ICON_SIZE_MENU,
507                            GTK_STOCK_JUSTIFY_RIGHT);
508
509   add_sized_with_fallback (factory,
510                            stock_bottom_24,
511                            stock_bottom_16,
512                            GTK_ICON_SIZE_MENU,
513                            GTK_STOCK_GOTO_BOTTOM);
514
515   add_sized_with_fallback (factory,
516                            stock_cdrom_24,
517                            stock_cdrom_16,
518                            GTK_ICON_SIZE_MENU,
519                            GTK_STOCK_CDROM);
520
521   add_sized_with_fallback (factory,
522                            stock_convert_24,
523                            stock_convert_16,
524                            GTK_ICON_SIZE_MENU,
525                            GTK_STOCK_CONVERT);
526
527   add_sized_with_fallback (factory,
528                            stock_copy_24,
529                            stock_copy_16,
530                            GTK_ICON_SIZE_MENU,
531                            GTK_STOCK_COPY);
532
533   add_sized_with_fallback (factory,
534                            stock_cut_24,
535                            stock_cut_16,
536                            GTK_ICON_SIZE_MENU,
537                            GTK_STOCK_CUT);
538
539   add_sized_with_fallback (factory,
540                            stock_down_arrow_24,
541                            stock_down_arrow_16,
542                            GTK_ICON_SIZE_MENU,
543                            GTK_STOCK_GO_DOWN);
544
545   add_sized_with_fallback (factory,
546                            stock_exec_24,
547                            stock_exec_16,
548                            GTK_ICON_SIZE_MENU,
549                            GTK_STOCK_EXECUTE);
550
551   add_sized_with_fallback (factory,
552                            stock_exit_24,
553                            stock_exit_16,
554                            GTK_ICON_SIZE_MENU,
555                            GTK_STOCK_QUIT);
556
557   add_sized_with_fallback (factory,
558                            stock_first_24,
559                            stock_first_16,
560                            GTK_ICON_SIZE_MENU,
561                            GTK_STOCK_GOTO_FIRST);
562
563   add_sized_with_fallback (factory,
564                            stock_font_24,
565                            stock_font_16,
566                            GTK_ICON_SIZE_MENU,
567                            GTK_STOCK_SELECT_FONT);
568
569   add_sized_with_fallback (factory,
570                            stock_help_24,
571                            stock_help_16,
572                            GTK_ICON_SIZE_MENU,
573                            GTK_STOCK_HELP);
574
575   add_sized_with_fallback (factory,
576                            stock_home_24,
577                            stock_home_16,
578                            GTK_ICON_SIZE_MENU,
579                            GTK_STOCK_HOME);
580
581   add_sized_with_fallback (factory,
582                            stock_jump_to_24,
583                            stock_jump_to_16,
584                            GTK_ICON_SIZE_MENU,
585                            GTK_STOCK_JUMP_TO);
586
587   add_sized_with_fallback (factory,
588                            stock_last_24,
589                            stock_last_16,
590                            GTK_ICON_SIZE_MENU,
591                            GTK_STOCK_GOTO_LAST);
592
593   add_sized_with_fallback (factory,
594                            stock_left_arrow_24,
595                            stock_left_arrow_16,
596                            GTK_ICON_SIZE_MENU,
597                            GTK_STOCK_GO_BACK);
598
599   add_sized_with_fallback (factory,
600                            stock_missing_image_24,
601                            stock_missing_image_16,
602                            GTK_ICON_SIZE_MENU,
603                            GTK_STOCK_MISSING_IMAGE);
604
605   add_sized_with_fallback (factory,
606                            stock_new_24,
607                            stock_new_16,
608                            GTK_ICON_SIZE_MENU,
609                            GTK_STOCK_NEW);
610
611   add_sized_with_fallback (factory,
612                            stock_open_24,
613                            stock_open_16,
614                            GTK_ICON_SIZE_MENU,
615                            GTK_STOCK_OPEN);
616
617   add_sized_with_fallback (factory,
618                            stock_paste_24,
619                            stock_paste_16,
620                            GTK_ICON_SIZE_MENU,
621                            GTK_STOCK_PASTE);
622
623   add_sized_with_fallback (factory,
624                            stock_preferences_24,
625                            stock_preferences_16,
626                            GTK_ICON_SIZE_MENU,
627                            GTK_STOCK_PREFERENCES);
628
629   add_sized_with_fallback (factory,
630                            stock_print_24,
631                            stock_print_16,
632                            GTK_ICON_SIZE_MENU,
633                            GTK_STOCK_PRINT);
634
635   add_sized_with_fallback (factory,
636                            stock_print_preview_24,
637                            stock_print_preview_16,
638                            GTK_ICON_SIZE_MENU,
639                            GTK_STOCK_PRINT_PREVIEW);
640
641   add_sized_with_fallback (factory,
642                            stock_properties_24,
643                            stock_properties_16,
644                            GTK_ICON_SIZE_MENU,
645                            GTK_STOCK_PROPERTIES);
646   
647   add_sized_with_fallback (factory,
648                            stock_redo_24,
649                            stock_redo_16,
650                            GTK_ICON_SIZE_MENU,
651                            GTK_STOCK_REDO);
652
653   add_sized_with_fallback (factory,
654                            stock_refresh_24,
655                            stock_refresh_16,
656                            GTK_ICON_SIZE_MENU,
657                            GTK_STOCK_REFRESH);
658
659   add_sized_with_fallback (factory,
660                            stock_revert_24,
661                            stock_revert_16,
662                            GTK_ICON_SIZE_MENU,
663                            GTK_STOCK_REVERT_TO_SAVED);
664
665   add_sized_with_fallback (factory,
666                            stock_right_arrow_24,
667                            stock_right_arrow_16,
668                            GTK_ICON_SIZE_MENU,
669                            GTK_STOCK_GO_FORWARD);
670
671   add_sized_with_fallback (factory,
672                            stock_save_24,
673                            stock_save_16,
674                            GTK_ICON_SIZE_MENU,
675                            GTK_STOCK_SAVE);
676
677   add_sized_with_fallback (factory,
678                            stock_save_24,
679                            stock_save_16,
680                            GTK_ICON_SIZE_MENU,
681                            GTK_STOCK_FLOPPY);
682
683   add_sized_with_fallback (factory,
684                            stock_save_as_24,
685                            stock_save_as_16,
686                            GTK_ICON_SIZE_MENU,
687                            GTK_STOCK_SAVE_AS);
688
689   add_sized_with_fallback (factory,
690                            stock_search_24,
691                            stock_search_16,
692                            GTK_ICON_SIZE_MENU,
693                            GTK_STOCK_FIND);
694
695   add_sized_with_fallback (factory,
696                            stock_search_replace_24,
697                            stock_search_replace_16,
698                            GTK_ICON_SIZE_MENU,
699                            GTK_STOCK_FIND_AND_REPLACE);
700
701   add_sized_with_fallback (factory,
702                            stock_sort_descending_24,
703                            stock_sort_descending_16,
704                            GTK_ICON_SIZE_MENU,
705                            GTK_STOCK_SORT_DESCENDING);
706
707   add_sized_with_fallback (factory,
708                            stock_sort_ascending_24,
709                            stock_sort_ascending_16,
710                            GTK_ICON_SIZE_MENU,
711                            GTK_STOCK_SORT_ASCENDING);
712
713   add_sized_with_fallback (factory,
714                            stock_spellcheck_24,
715                            stock_spellcheck_16,
716                            GTK_ICON_SIZE_MENU,
717                            GTK_STOCK_SPELL_CHECK);
718
719   add_sized_with_fallback (factory,
720                            stock_stop_24,
721                            stock_stop_16,
722                            GTK_ICON_SIZE_MENU,
723                            GTK_STOCK_STOP);
724
725   add_sized_with_fallback (factory,
726                            stock_text_bold_24,
727                            stock_text_bold_16,
728                            GTK_ICON_SIZE_MENU,
729                            GTK_STOCK_BOLD);
730
731   add_sized_with_fallback (factory,
732                            stock_text_italic_24,
733                            stock_text_italic_16,
734                            GTK_ICON_SIZE_MENU,
735                            GTK_STOCK_ITALIC);
736
737   add_sized_with_fallback (factory,
738                            stock_text_strikethrough_24,
739                            stock_text_strikethrough_16,
740                            GTK_ICON_SIZE_MENU,
741                            GTK_STOCK_STRIKETHROUGH);
742
743   add_sized_with_fallback (factory,
744                            stock_text_underline_24,
745                            stock_text_underline_16,
746                            GTK_ICON_SIZE_MENU,
747                            GTK_STOCK_UNDERLINE);
748
749   add_sized_with_fallback (factory,
750                            stock_top_24,
751                            stock_top_16,
752                            GTK_ICON_SIZE_MENU,
753                            GTK_STOCK_GOTO_TOP);
754
755   add_sized_with_fallback (factory,
756                            stock_trash_24,
757                            stock_trash_16,
758                            GTK_ICON_SIZE_MENU,
759                            GTK_STOCK_DELETE);
760
761   add_sized_with_fallback (factory,
762                            stock_undelete_24,
763                            stock_undelete_16,
764                            GTK_ICON_SIZE_MENU,
765                            GTK_STOCK_UNDELETE);
766
767   add_sized_with_fallback (factory,
768                            stock_undo_24,
769                            stock_undo_16,
770                            GTK_ICON_SIZE_MENU,
771                            GTK_STOCK_UNDO);
772
773   add_sized_with_fallback (factory,
774                            stock_up_arrow_24,
775                            stock_up_arrow_16,
776                            GTK_ICON_SIZE_MENU,
777                            GTK_STOCK_GO_UP);
778
779 /* Generic size only */
780
781   add_unsized (factory, stock_add_24, GTK_STOCK_ADD);
782   add_unsized (factory, stock_clear_24, GTK_STOCK_CLEAR);
783   add_unsized (factory, stock_colorselector_24, GTK_STOCK_SELECT_COLOR);
784   add_unsized (factory, stock_index_24, GTK_STOCK_INDEX);
785   add_unsized (factory, stock_remove_24, GTK_STOCK_REMOVE);
786   add_unsized (factory, stock_zoom_1_24, GTK_STOCK_ZOOM_100);
787   add_unsized (factory, stock_zoom_fit_24, GTK_STOCK_ZOOM_FIT);
788   add_unsized (factory, stock_zoom_in_24, GTK_STOCK_ZOOM_IN);
789   add_unsized (factory, stock_zoom_out_24, GTK_STOCK_ZOOM_OUT);
790 }
791
792 /* Sizes */
793
794 typedef struct _IconSize IconSize;
795
796 struct _IconSize
797 {
798   gint size;
799   gchar *name;
800   
801   gint width;
802   gint height;
803 };
804
805 typedef struct _IconAlias IconAlias;
806
807 struct _IconAlias
808 {
809   gchar *name;
810   gint   target;
811 };
812
813 static GHashTable *icon_aliases = NULL;
814 static IconSize *icon_sizes = NULL;
815 static gint      icon_sizes_allocated = 0;
816 static gint      icon_sizes_used = 0;
817
818 static void
819 init_icon_sizes (void)
820 {
821   if (icon_sizes == NULL)
822     {
823 #define NUM_BUILTIN_SIZES 7
824       gint i;
825
826       icon_aliases = g_hash_table_new (g_str_hash, g_str_equal);
827       
828       icon_sizes = g_new (IconSize, NUM_BUILTIN_SIZES);
829       icon_sizes_allocated = NUM_BUILTIN_SIZES;
830       icon_sizes_used = NUM_BUILTIN_SIZES;
831
832       icon_sizes[GTK_ICON_SIZE_INVALID].size = 0;
833       icon_sizes[GTK_ICON_SIZE_INVALID].name = NULL;
834       icon_sizes[GTK_ICON_SIZE_INVALID].width = 0;
835       icon_sizes[GTK_ICON_SIZE_INVALID].height = 0;
836
837       /* the name strings aren't copied since we don't ever remove
838        * icon sizes, so we don't need to know whether they're static.
839        * Even if we did I suppose removing the builtin sizes would be
840        * disallowed.
841        */
842       
843       icon_sizes[GTK_ICON_SIZE_MENU].size = GTK_ICON_SIZE_MENU;
844       icon_sizes[GTK_ICON_SIZE_MENU].name = "gtk-menu";
845       icon_sizes[GTK_ICON_SIZE_MENU].width = 16;
846       icon_sizes[GTK_ICON_SIZE_MENU].height = 16;
847
848       icon_sizes[GTK_ICON_SIZE_BUTTON].size = GTK_ICON_SIZE_BUTTON;
849       icon_sizes[GTK_ICON_SIZE_BUTTON].name = "gtk-button";
850       icon_sizes[GTK_ICON_SIZE_BUTTON].width = 20;
851       icon_sizes[GTK_ICON_SIZE_BUTTON].height = 20;
852
853       icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].size = GTK_ICON_SIZE_SMALL_TOOLBAR;
854       icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].name = "gtk-small-toolbar";
855       icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].width = 18;
856       icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].height = 18;
857       
858       icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].size = GTK_ICON_SIZE_LARGE_TOOLBAR;
859       icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].name = "gtk-large-toolbar";
860       icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].width = 24;
861       icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].height = 24;
862
863       icon_sizes[GTK_ICON_SIZE_DND].size = GTK_ICON_SIZE_DND;
864       icon_sizes[GTK_ICON_SIZE_DND].name = "gtk-dnd";
865       icon_sizes[GTK_ICON_SIZE_DND].width = 32;
866       icon_sizes[GTK_ICON_SIZE_DND].height = 32;
867
868       icon_sizes[GTK_ICON_SIZE_DIALOG].size = GTK_ICON_SIZE_DIALOG;
869       icon_sizes[GTK_ICON_SIZE_DIALOG].name = "gtk-dialog";
870       icon_sizes[GTK_ICON_SIZE_DIALOG].width = 48;
871       icon_sizes[GTK_ICON_SIZE_DIALOG].height = 48;
872
873       g_assert ((GTK_ICON_SIZE_DIALOG + 1) == NUM_BUILTIN_SIZES);
874
875       /* Alias everything to itself. */
876       i = 1; /* skip invalid size */
877       while (i < NUM_BUILTIN_SIZES)
878         {
879           gtk_icon_size_register_alias (icon_sizes[i].name, icon_sizes[i].size);
880           
881           ++i;
882         }
883       
884 #undef NUM_BUILTIN_SIZES
885     }
886 }
887
888 /**
889  * gtk_icon_size_lookup:
890  * @size: an icon size
891  * @width: location to store icon width
892  * @height: location to store icon height
893  *
894  * Obtains the pixel size of a semantic icon size, normally @size would be
895  * #GTK_ICON_SIZE_MENU, #GTK_ICON_SIZE_BUTTON, etc.  This function
896  * isn't normally needed, gtk_widget_render_icon() is the usual
897  * way to get an icon for rendering, then just look at the size of
898  * the rendered pixbuf. The rendered pixbuf may not even correspond to
899  * the width/height returned by gtk_icon_size_lookup(), because themes
900  * are free to render the pixbuf however they like, including changing
901  * the usual size.
902  * 
903  * Return value: %TRUE if @size was a valid size
904  **/
905 gboolean
906 gtk_icon_size_lookup (GtkIconSize  size,
907                       gint        *widthp,
908                       gint        *heightp)
909 {
910   init_icon_sizes ();
911
912   if (size >= icon_sizes_used)
913     return FALSE;
914
915   if (size == GTK_ICON_SIZE_INVALID)
916     return FALSE;
917   
918   if (widthp)
919     *widthp = icon_sizes[size].width;
920
921   if (heightp)
922     *heightp = icon_sizes[size].height;
923
924   return TRUE;
925 }
926
927 /**
928  * gtk_icon_size_register:
929  * @name: name of the icon size
930  * @width: the icon width
931  * @height: the icon height
932  *
933  * Registers a new icon size, along the same lines as #GTK_ICON_SIZE_MENU,
934  * etc. Returns the integer value for the size.
935  *
936  * Returns: integer value representing the size
937  * 
938  **/
939 GtkIconSize
940 gtk_icon_size_register (const gchar *name,
941                         gint         width,
942                         gint         height)
943 {
944   g_return_val_if_fail (name != NULL, 0);
945   g_return_val_if_fail (width > 0, 0);
946   g_return_val_if_fail (height > 0, 0);
947   
948   init_icon_sizes ();
949
950   if (icon_sizes_used == icon_sizes_allocated)
951     {
952       icon_sizes_allocated *= 2;
953       icon_sizes = g_renew (IconSize, icon_sizes, icon_sizes_allocated);
954     }
955   
956   icon_sizes[icon_sizes_used].size = icon_sizes_used;
957   icon_sizes[icon_sizes_used].name = g_strdup (name);
958   icon_sizes[icon_sizes_used].width = width;
959   icon_sizes[icon_sizes_used].height = height;
960
961   ++icon_sizes_used;
962
963   /* alias to self. */
964   gtk_icon_size_register_alias (name, icon_sizes_used - 1);
965   
966   return icon_sizes_used - 1;
967 }
968
969 /**
970  * gtk_icon_size_register_alias:
971  * @alias: an alias for @target
972  * @target: an existing icon size
973  *
974  * Registers @alias as another name for @target.
975  * So calling gtk_icon_size_from_name() with @alias as argument
976  * will return @target.
977  *
978  **/
979 void
980 gtk_icon_size_register_alias (const gchar *alias,
981                               GtkIconSize  target)
982 {
983   IconAlias *ia;
984   
985   g_return_if_fail (alias != NULL);
986
987   init_icon_sizes ();
988
989   if (g_hash_table_lookup (icon_aliases, alias))
990     g_warning ("gtk_icon_size_register_alias: Icon size name '%s' already exists", alias);
991
992   if (!gtk_icon_size_lookup (target, NULL, NULL))
993     g_warning ("gtk_icon_size_register_alias: Icon size %d does not exist", target);
994   
995   ia = g_new (IconAlias, 1);
996   ia->name = g_strdup (alias);
997   ia->target = target;
998
999   g_hash_table_insert (icon_aliases, ia->name, ia);
1000 }
1001
1002 /** 
1003  * gtk_icon_size_from_name:
1004  * @name: the name to look up.
1005  * @returns: the icon size with the given name.
1006  * 
1007  * Looks up the icon size associated with @name.
1008  **/
1009 GtkIconSize
1010 gtk_icon_size_from_name (const gchar *name)
1011 {
1012   IconAlias *ia;
1013
1014   init_icon_sizes ();
1015   
1016   ia = g_hash_table_lookup (icon_aliases, name);
1017
1018   if (ia)
1019     return ia->target;
1020   else
1021     return GTK_ICON_SIZE_INVALID;
1022 }
1023
1024 /**
1025  * gtk_icon_size_get_name:
1026  * @size: a #GtkIconSize.
1027  * @returns: the name of the given icon size.
1028  * 
1029  * Gets the canonical name of the given icon size. The returned string 
1030  * is statically allocated and should not be freed.
1031  **/
1032 G_CONST_RETURN gchar*
1033 gtk_icon_size_get_name (GtkIconSize  size)
1034 {
1035   if (size >= icon_sizes_used)
1036     return NULL;
1037   else
1038     return icon_sizes[size].name;
1039 }
1040
1041 /* Icon Set */
1042
1043
1044 /* Clear icon set contents, drop references to all contained
1045  * GdkPixbuf objects and forget all GtkIconSources. Used to
1046  * recycle an icon set.
1047  */
1048 static GdkPixbuf *find_in_cache     (GtkIconSet       *icon_set,
1049                                      GtkStyle         *style,
1050                                      GtkTextDirection  direction,
1051                                      GtkStateType      state,
1052                                      GtkIconSize       size);
1053 static void       add_to_cache      (GtkIconSet       *icon_set,
1054                                      GtkStyle         *style,
1055                                      GtkTextDirection  direction,
1056                                      GtkStateType      state,
1057                                      GtkIconSize       size,
1058                                      GdkPixbuf        *pixbuf);
1059 static void       clear_cache       (GtkIconSet       *icon_set,
1060                                      gboolean          style_detach);
1061 static GSList*    copy_cache        (GtkIconSet       *icon_set,
1062                                      GtkIconSet       *copy_recipient);
1063 static void       attach_to_style   (GtkIconSet       *icon_set,
1064                                      GtkStyle         *style);
1065 static void       detach_from_style (GtkIconSet       *icon_set,
1066                                      GtkStyle         *style);
1067 static void       style_dnotify     (gpointer          data);
1068
1069 struct _GtkIconSet
1070 {
1071   guint ref_count;
1072
1073   GSList *sources;
1074
1075   /* Cache of the last few rendered versions of the icon. */
1076   GSList *cache;
1077
1078   guint cache_size;
1079
1080   guint cache_serial;
1081 };
1082
1083 static guint cache_serial = 0;
1084
1085 /**
1086  * gtk_icon_set_new:
1087  * 
1088  * Creates a new #GtkIconSet. A #GtkIconSet represents a single icon
1089  * in various sizes and widget states. It can provide a #GdkPixbuf
1090  * for a given size and state on request, and automatically caches
1091  * some of the rendered #GdkPixbuf objects.
1092  *
1093  * Normally you would use gtk_widget_render_icon() instead of
1094  * using #GtkIconSet directly. The one case where you'd use
1095  * #GtkIconSet is to create application-specific icon sets to place in
1096  * a #GtkIconFactory.
1097  * 
1098  * Return value: a new #GtkIconSet
1099  **/
1100 GtkIconSet*
1101 gtk_icon_set_new (void)
1102 {
1103   GtkIconSet *icon_set;
1104
1105   icon_set = g_new (GtkIconSet, 1);
1106
1107   icon_set->ref_count = 1;
1108   icon_set->sources = NULL;
1109   icon_set->cache = NULL;
1110   icon_set->cache_size = 0;
1111   icon_set->cache_serial = cache_serial;
1112   
1113   return icon_set;
1114 }
1115
1116 /**
1117  * gtk_icon_set_new_from_pixbuf:
1118  * @pixbuf: a #GdkPixbuf
1119  * 
1120  * Creates a new #GtkIconSet with @pixbuf as the default/fallback
1121  * source image. If you don't add any additional #GtkIconSource to the
1122  * icon set, all variants of the icon will be created from @pixbuf,
1123  * using scaling, pixelation, etc. as required to adjust the icon size
1124  * or make the icon look insensitive/prelighted.
1125  * 
1126  * Return value: a new #GtkIconSet
1127  **/
1128 GtkIconSet *
1129 gtk_icon_set_new_from_pixbuf (GdkPixbuf *pixbuf)
1130 {
1131   GtkIconSet *set;
1132
1133   GtkIconSource source = { NULL, NULL, 0, 0, 0,
1134                            TRUE, TRUE, TRUE };
1135
1136   g_return_val_if_fail (pixbuf != NULL, NULL);
1137
1138   set = gtk_icon_set_new ();
1139
1140   source.pixbuf = pixbuf;
1141
1142   gtk_icon_set_add_source (set, &source);
1143   
1144   return set;
1145 }
1146
1147
1148 /**
1149  * gtk_icon_set_ref:
1150  * @icon_set: a #GtkIconSet.
1151  * 
1152  * Increments the reference count on @icon_set.
1153  * 
1154  * Return value: @icon_set.
1155  **/
1156 GtkIconSet*
1157 gtk_icon_set_ref (GtkIconSet *icon_set)
1158 {
1159   g_return_val_if_fail (icon_set != NULL, NULL);
1160   g_return_val_if_fail (icon_set->ref_count > 0, NULL);
1161
1162   icon_set->ref_count += 1;
1163
1164   return icon_set;
1165 }
1166
1167 /**
1168  * gtk_icon_set_unref:
1169  * @icon_set: a #GtkIconSet
1170  * 
1171  * Decrements the reference count on @icon_set, and frees memory
1172  * if the reference count reaches 0.
1173  **/
1174 void
1175 gtk_icon_set_unref (GtkIconSet *icon_set)
1176 {
1177   g_return_if_fail (icon_set != NULL);
1178   g_return_if_fail (icon_set->ref_count > 0);
1179
1180   icon_set->ref_count -= 1;
1181
1182   if (icon_set->ref_count == 0)
1183     {
1184       GSList *tmp_list = icon_set->sources;
1185       while (tmp_list != NULL)
1186         {
1187           gtk_icon_source_free (tmp_list->data);
1188
1189           tmp_list = g_slist_next (tmp_list);
1190         }
1191
1192       clear_cache (icon_set, TRUE);
1193
1194       g_free (icon_set);
1195     }
1196 }
1197
1198 GType
1199 gtk_icon_set_get_type (void)
1200 {
1201   static GType our_type = 0;
1202   
1203   if (our_type == 0)
1204     our_type = g_boxed_type_register_static ("GtkTypeIconSet",
1205                                              (GBoxedCopyFunc) gtk_icon_set_ref,
1206                                              (GBoxedFreeFunc) gtk_icon_set_unref);
1207
1208   return our_type;
1209 }
1210
1211 /**
1212  * gtk_icon_set_copy:
1213  * @icon_set: a #GtkIconSet
1214  * 
1215  * Copies @icon_set by value. 
1216  * 
1217  * Return value: a new #GtkIconSet identical to the first.
1218  **/
1219 GtkIconSet*
1220 gtk_icon_set_copy (GtkIconSet *icon_set)
1221 {
1222   GtkIconSet *copy;
1223   GSList *tmp_list;
1224   
1225   copy = gtk_icon_set_new ();
1226
1227   tmp_list = icon_set->sources;
1228   while (tmp_list != NULL)
1229     {
1230       copy->sources = g_slist_prepend (copy->sources,
1231                                        gtk_icon_source_copy (tmp_list->data));
1232
1233       tmp_list = g_slist_next (tmp_list);
1234     }
1235
1236   copy->sources = g_slist_reverse (copy->sources);
1237
1238   copy->cache = copy_cache (icon_set, copy);
1239   copy->cache_size = icon_set->cache_size;
1240   copy->cache_serial = icon_set->cache_serial;
1241   
1242   return copy;
1243 }
1244
1245
1246 static gboolean
1247 sizes_equivalent (GtkIconSize lhs,
1248                   GtkIconSize rhs)
1249 {
1250   gint r_w, r_h, l_w, l_h;
1251
1252   gtk_icon_size_lookup (rhs, &r_w, &r_h);
1253   gtk_icon_size_lookup (lhs, &l_w, &l_h);
1254
1255   return r_w == l_w && r_h == l_h;
1256 }
1257
1258 static GtkIconSource*
1259 find_and_prep_icon_source (GtkIconSet       *icon_set,
1260                            GtkTextDirection  direction,
1261                            GtkStateType      state,
1262                            GtkIconSize       size)
1263 {
1264   GtkIconSource *source;
1265   GSList *tmp_list;
1266
1267
1268   /* We need to find the best icon source.  Direction matters more
1269    * than state, state matters more than size. icon_set->sources
1270    * is sorted according to wildness, so if we take the first
1271    * match we find it will be the least-wild match (if there are
1272    * multiple matches for a given "wildness" then the RC file contained
1273    * dumb stuff, and we end up with an arbitrary matching source)
1274    */
1275   
1276   source = NULL;
1277   tmp_list = icon_set->sources;
1278   while (tmp_list != NULL)
1279     {
1280       GtkIconSource *s = tmp_list->data;
1281       
1282       if ((s->any_direction || (s->direction == direction)) &&
1283           (s->any_state || (s->state == state)) &&
1284           (s->any_size || (sizes_equivalent (size, s->size))))
1285         {
1286           source = s;
1287           break;
1288         }
1289       
1290       tmp_list = g_slist_next (tmp_list);
1291     }
1292
1293   if (source == NULL)
1294     return NULL;
1295   
1296   if (source->pixbuf == NULL)
1297     {
1298       GError *error = NULL;
1299       
1300       g_assert (source->filename);
1301       source->pixbuf = gdk_pixbuf_new_from_file (source->filename, &error);
1302
1303       if (source->pixbuf == NULL)
1304         {
1305           /* Remove this icon source so we don't keep trying to
1306            * load it.
1307            */
1308           g_warning (_("Error loading icon: %s"), error->message);
1309           g_error_free (error);
1310           
1311           icon_set->sources = g_slist_remove (icon_set->sources, source);
1312
1313           gtk_icon_source_free (source);
1314
1315           /* Try to fall back to other sources */
1316           if (icon_set->sources != NULL)
1317             return find_and_prep_icon_source (icon_set,
1318                                               direction,
1319                                               state,
1320                                               size);
1321           else
1322             return NULL;
1323         }
1324     }
1325
1326   return source;
1327 }
1328
1329 static GdkPixbuf*
1330 render_fallback_image (GtkStyle          *style,
1331                        GtkTextDirection   direction,
1332                        GtkStateType       state,
1333                        GtkIconSize        size,
1334                        GtkWidget         *widget,
1335                        const char        *detail)
1336 {
1337   /* This icon can be used for any direction/state/size */
1338   static GtkIconSource fallback_source = { NULL, NULL, 0, 0, 0, TRUE, TRUE, TRUE };
1339
1340   if (fallback_source.pixbuf == NULL)
1341     fallback_source.pixbuf = gdk_pixbuf_new_from_inline (-1, stock_missing_image_24, FALSE, NULL);
1342   
1343   return gtk_style_render_icon (style,
1344                                 &fallback_source,
1345                                 direction,
1346                                 state,
1347                                 size,
1348                                 widget,
1349                                 detail);
1350 }
1351
1352 /**
1353  * gtk_icon_set_render_icon:
1354  * @icon_set: a #GtkIconSet
1355  * @style: a #GtkStyle associated with @widget, or %NULL
1356  * @direction: text direction
1357  * @state: widget state
1358  * @size: icon size
1359  * @widget: widget that will display the icon, or %NULL
1360  * @detail: detail to pass to the theme engine, or %NULL
1361  * 
1362  * Renders an icon using gtk_style_render_icon(). In most cases,
1363  * gtk_widget_render_icon() is better, since it automatically provides
1364  * most of the arguments from the current widget settings.  This
1365  * function never returns %NULL; if the icon can't be rendered
1366  * (perhaps because an image file fails to load), a default "missing
1367  * image" icon will be returned instead.
1368  * 
1369  * Return value: a #GdkPixbuf to be displayed
1370  **/
1371 GdkPixbuf*
1372 gtk_icon_set_render_icon (GtkIconSet        *icon_set,
1373                           GtkStyle          *style,
1374                           GtkTextDirection   direction,
1375                           GtkStateType       state,
1376                           GtkIconSize        size,
1377                           GtkWidget         *widget,
1378                           const char        *detail)
1379 {
1380   GdkPixbuf *icon;
1381   GtkIconSource *source;
1382   
1383   g_return_val_if_fail (icon_set != NULL, NULL);
1384   g_return_val_if_fail (GTK_IS_STYLE (style), NULL);
1385
1386   if (icon_set->sources == NULL)
1387     return render_fallback_image (style, direction, state, size, widget, detail);
1388   
1389   icon = find_in_cache (icon_set, style, direction,
1390                         state, size);
1391
1392   if (icon)
1393     {
1394       g_object_ref (G_OBJECT (icon));
1395       return icon;
1396     }
1397
1398   
1399   source = find_and_prep_icon_source (icon_set, direction, state, size);
1400
1401   if (source == NULL)
1402     return render_fallback_image (style, direction, state, size, widget, detail);
1403
1404   g_assert (source->pixbuf != NULL);
1405   
1406   icon = gtk_style_render_icon (style,
1407                                 source,
1408                                 direction,
1409                                 state,
1410                                 size,
1411                                 widget,
1412                                 detail);
1413
1414   if (icon == NULL)
1415     {
1416       g_warning ("Theme engine failed to render icon");
1417       return NULL;
1418     }
1419   
1420   add_to_cache (icon_set, style, direction, state, size, icon);
1421   
1422   return icon;
1423 }
1424
1425 /* Order sources by their "wildness", so that "wilder" sources are
1426  * greater than "specific" sources; for determining ordering,
1427  * direction beats state beats size.
1428  */
1429
1430 static int
1431 icon_source_compare (gconstpointer ap, gconstpointer bp)
1432 {
1433   const GtkIconSource *a = ap;
1434   const GtkIconSource *b = bp;
1435
1436   if (!a->any_direction && b->any_direction)
1437     return -1;
1438   else if (a->any_direction && !b->any_direction)
1439     return 1;
1440   else if (!a->any_state && b->any_state)
1441     return -1;
1442   else if (a->any_state && !b->any_state)
1443     return 1;
1444   else if (!a->any_size && b->any_size)
1445     return -1;
1446   else if (a->any_size && !b->any_size)
1447     return 1;
1448   else
1449     return 0;
1450 }
1451
1452 /**
1453  * gtk_icon_set_add_source:
1454  * @icon_set: a #GtkIconSet
1455  * @source: a #GtkIconSource
1456  *
1457  * Icon sets have a list of #GtkIconSource, which they use as base
1458  * icons for rendering icons in different states and sizes. Icons are
1459  * scaled, made to look insensitive, etc. in
1460  * gtk_icon_set_render_icon(), but #GtkIconSet needs base images to
1461  * work with. The base images and when to use them are described by
1462  * a #GtkIconSource.
1463  * 
1464  * This function copies @source, so you can reuse the same source immediately
1465  * without affecting the icon set.
1466  *
1467  * An example of when you'd use this function: a web browser's "Back
1468  * to Previous Page" icon might point in a different direction in
1469  * Hebrew and in English; it might look different when insensitive;
1470  * and it might change size depending on toolbar mode (small/large
1471  * icons). So a single icon set would contain all those variants of
1472  * the icon, and you might add a separate source for each one.
1473  *
1474  * You should nearly always add a "default" icon source with all
1475  * fields wildcarded, which will be used as a fallback if no more
1476  * specific source matches. #GtkIconSet always prefers more specific
1477  * icon sources to more generic icon sources. The order in which you
1478  * add the sources to the icon set does not matter.
1479  *
1480  * gtk_icon_set_new_from_pixbuf() creates a new icon set with a
1481  * default icon source based on the given pixbuf.
1482  * 
1483  **/
1484 void
1485 gtk_icon_set_add_source (GtkIconSet *icon_set,
1486                          const GtkIconSource *source)
1487 {
1488   g_return_if_fail (icon_set != NULL);
1489   g_return_if_fail (source != NULL);
1490
1491   if (source->pixbuf == NULL &&
1492       source->filename == NULL)
1493     {
1494       g_warning ("Useless GtkIconSource contains NULL filename and pixbuf");
1495       return;
1496     }
1497   
1498   icon_set->sources = g_slist_insert_sorted (icon_set->sources,
1499                                              gtk_icon_source_copy (source),
1500                                              icon_source_compare);
1501 }
1502
1503 /**
1504  * gtk_icon_set_get_sizes:
1505  * @icon_set: a #GtkIconSet
1506  * @sizes: return location for array of sizes
1507  * @n_sizes: location to store number of elements in returned array
1508  *
1509  * Obtains a list of icon sizes this icon set can render. The returned
1510  * array must be freed with g_free().
1511  * 
1512  **/
1513 void
1514 gtk_icon_set_get_sizes (GtkIconSet   *icon_set,
1515                         GtkIconSize **sizes,
1516                         gint         *n_sizes)
1517 {
1518   GSList *tmp_list;
1519   gboolean all_sizes = FALSE;
1520   GSList *specifics = NULL;
1521   
1522   g_return_if_fail (icon_set != NULL);
1523   g_return_if_fail (sizes != NULL);
1524   g_return_if_fail (n_sizes != NULL);
1525   
1526   tmp_list = icon_set->sources;
1527   while (tmp_list != NULL)
1528     {
1529       GtkIconSource *source;
1530
1531       source = tmp_list->data;
1532
1533       if (source->any_size)
1534         {
1535           all_sizes = TRUE;
1536           break;
1537         }
1538       else
1539         specifics = g_slist_prepend (specifics, GINT_TO_POINTER (source->size));
1540       
1541       tmp_list = g_slist_next (tmp_list);
1542     }
1543
1544   if (all_sizes)
1545     {
1546       /* Need to find out what sizes exist */
1547       gint i;
1548
1549       init_icon_sizes ();
1550       
1551       *sizes = g_new (GtkIconSize, icon_sizes_used);
1552       *n_sizes = icon_sizes_used - 1;
1553       
1554       i = 1;      
1555       while (i < icon_sizes_used)
1556         {
1557           (*sizes)[i - 1] = icon_sizes[i].size;
1558           ++i;
1559         }
1560     }
1561   else
1562     {
1563       gint i;
1564       
1565       *n_sizes = g_slist_length (specifics);
1566       *sizes = g_new (GtkIconSize, *n_sizes);
1567
1568       i = 0;
1569       tmp_list = specifics;
1570       while (tmp_list != NULL)
1571         {
1572           (*sizes)[i] = GPOINTER_TO_INT (tmp_list->data);
1573
1574           ++i;
1575           tmp_list = g_slist_next (tmp_list);
1576         }
1577     }
1578
1579   g_slist_free (specifics);
1580 }
1581
1582
1583 /**
1584  * gtk_icon_source_new:
1585  * 
1586  * Creates a new #GtkIconSource. A #GtkIconSource contains a #GdkPixbuf (or
1587  * image filename) that serves as the base image for one or more of the
1588  * icons in a #GtkIconSet, along with a specification for which icons in the
1589  * icon set will be based on that pixbuf or image file. An icon set contains
1590  * a set of icons that represent "the same" logical concept in different states,
1591  * different global text directions, and different sizes.
1592  * 
1593  * So for example a web browser's "Back to Previous Page" icon might
1594  * point in a different direction in Hebrew and in English; it might
1595  * look different when insensitive; and it might change size depending
1596  * on toolbar mode (small/large icons). So a single icon set would
1597  * contain all those variants of the icon. #GtkIconSet contains a list
1598  * of #GtkIconSource from which it can derive specific icon variants in
1599  * the set. 
1600  *
1601  * In the simplest case, #GtkIconSet contains one source pixbuf from
1602  * which it derives all variants. The convenience function
1603  * gtk_icon_set_new_from_pixbuf() handles this case; if you only have
1604  * one source pixbuf, just use that function.
1605  *
1606  * If you want to use a different base pixbuf for different icon
1607  * variants, you create multiple icon sources, mark which variants
1608  * they'll be used to create, and add them to the icon set with
1609  * gtk_icon_set_add_source().
1610  *
1611  * By default, the icon source has all parameters wildcarded. That is,
1612  * the icon source will be used as the base icon for any desired text
1613  * direction, widget state, or icon size.
1614  * 
1615  * Return value: a new #GtkIconSource
1616  **/
1617 GtkIconSource*
1618 gtk_icon_source_new (void)
1619 {
1620   GtkIconSource *src;
1621   
1622   src = g_new0 (GtkIconSource, 1);
1623
1624   src->direction = GTK_TEXT_DIR_NONE;
1625   src->size = GTK_ICON_SIZE_INVALID;
1626   src->state = GTK_STATE_NORMAL;
1627   
1628   src->any_direction = TRUE;
1629   src->any_state = TRUE;
1630   src->any_size = TRUE;
1631   
1632   return src;
1633 }
1634
1635 /**
1636  * gtk_icon_source_copy:
1637  * @source: a #GtkIconSource
1638  * 
1639  * Creates a copy of @source; mostly useful for language bindings.
1640  * 
1641  * Return value: a new #GtkIconSource
1642  **/
1643 GtkIconSource*
1644 gtk_icon_source_copy (const GtkIconSource *source)
1645 {
1646   GtkIconSource *copy;
1647   
1648   g_return_val_if_fail (source != NULL, NULL);
1649
1650   copy = g_new (GtkIconSource, 1);
1651
1652   *copy = *source;
1653   
1654   copy->filename = g_strdup (source->filename);
1655   copy->size = source->size;
1656   if (copy->pixbuf)
1657     g_object_ref (G_OBJECT (copy->pixbuf));
1658
1659   return copy;
1660 }
1661
1662 /**
1663  * gtk_icon_source_free:
1664  * @source: a #GtkIconSource
1665  * 
1666  * Frees a dynamically-allocated icon source, along with its
1667  * filename, size, and pixbuf fields if those are not %NULL.
1668  **/
1669 void
1670 gtk_icon_source_free (GtkIconSource *source)
1671 {
1672   g_return_if_fail (source != NULL);
1673
1674   g_free ((char*) source->filename);
1675   if (source->pixbuf)
1676     g_object_unref (G_OBJECT (source->pixbuf));
1677
1678   g_free (source);
1679 }
1680
1681 GType
1682 gtk_icon_source_get_type (void)
1683 {
1684   static GType our_type = 0;
1685   
1686   if (our_type == 0)
1687     our_type = g_boxed_type_register_static ("GtkTypeIconSource",
1688                                              (GBoxedCopyFunc) gtk_icon_source_copy,
1689                                              (GBoxedFreeFunc) gtk_icon_source_free);
1690
1691   return our_type;
1692 }
1693
1694 /**
1695  * gtk_icon_source_set_filename:
1696  * @source: a #GtkIconSource
1697  * @filename: image file to use
1698  *
1699  * Sets the name of an image file to use as a base image when creating
1700  * icon variants for #GtkIconSet. The filename must be absolute. 
1701  **/
1702 void
1703 gtk_icon_source_set_filename (GtkIconSource *source,
1704                               const gchar   *filename)
1705 {
1706   g_return_if_fail (source != NULL);
1707   g_return_if_fail (filename == NULL || g_path_is_absolute (filename));
1708
1709   if (source->filename == filename)
1710     return;
1711   
1712   if (source->filename)
1713     g_free (source->filename);
1714
1715   source->filename = g_strdup (filename);  
1716 }
1717
1718 /**
1719  * gtk_icon_source_set_pixbuf:
1720  * @source: a #GtkIconSource
1721  * @pixbuf: pixbuf to use as a source
1722  *
1723  * Sets a pixbuf to use as a base image when creating icon variants
1724  * for #GtkIconSet. If an icon source has both a filename and a pixbuf
1725  * set, the pixbuf will take priority.
1726  * 
1727  **/
1728 void
1729 gtk_icon_source_set_pixbuf (GtkIconSource *source,
1730                             GdkPixbuf     *pixbuf)
1731 {
1732   g_return_if_fail (source != NULL);
1733
1734   if (pixbuf)
1735     g_object_ref (G_OBJECT (pixbuf));
1736
1737   if (source->pixbuf)
1738     g_object_unref (G_OBJECT (source->pixbuf));
1739
1740   source->pixbuf = pixbuf;
1741 }
1742
1743 /**
1744  * gtk_icon_source_get_filename:
1745  * @source: a #GtkIconSource
1746  * 
1747  * Retrieves the source filename, or %NULL if none is set.  The
1748  * filename is not a copy, and should not be modified or expected to
1749  * persist beyond the lifetime of the icon source.
1750  * 
1751  * Return value: image filename
1752  **/
1753 G_CONST_RETURN gchar*
1754 gtk_icon_source_get_filename (const GtkIconSource *source)
1755 {
1756   g_return_val_if_fail (source != NULL, NULL);
1757   
1758   return source->filename;
1759 }
1760
1761 /**
1762  * gtk_icon_source_get_pixbuf:
1763  * @source: a #GtkIconSource
1764  * 
1765  * Retrieves the source pixbuf, or %NULL if none is set.
1766  * The reference count on the pixbuf is not incremented.
1767  * 
1768  * Return value: source pixbuf
1769  **/
1770 GdkPixbuf*
1771 gtk_icon_source_get_pixbuf (const GtkIconSource *source)
1772 {
1773   g_return_val_if_fail (source != NULL, NULL);
1774   
1775   return source->pixbuf;
1776 }
1777
1778 /**
1779  * gtk_icon_source_set_direction_wildcarded:
1780  * @source: a #GtkIconSource
1781  * @setting: %TRUE to wildcard the text direction
1782  *
1783  * If the text direction is wildcarded, this source can be used
1784  * as the base image for an icon in any #GtkTextDirection.
1785  * If the text direction is not wildcarded, then the
1786  * text direction the icon source applies to should be set
1787  * with gtk_icon_source_set_direction(), and the icon source
1788  * will only be used with that text direction.
1789  *
1790  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
1791  * wildcarded sources, and will use an exact match when possible.
1792  * 
1793  **/
1794 void
1795 gtk_icon_source_set_direction_wildcarded (GtkIconSource *source,
1796                                           gboolean       setting)
1797 {
1798   g_return_if_fail (source != NULL);
1799
1800   source->any_direction = setting != FALSE;
1801 }
1802
1803 /**
1804  * gtk_icon_source_set_state_wildcarded:
1805  * @source: a #GtkIconSource
1806  * @setting: %TRUE to wildcard the widget state
1807  *
1808  * If the widget state is wildcarded, this source can be used as the
1809  * base image for an icon in any #GtkStateType.  If the widget state
1810  * is not wildcarded, then the state the source applies to should be
1811  * set with gtk_icon_source_set_state() and the icon source will
1812  * only be used with that specific state.
1813  *
1814  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
1815  * wildcarded sources, and will use an exact match when possible.
1816  *
1817  * #GtkIconSet will normally transform wildcarded source images to
1818  * produce an appropriate icon for a given state, for example
1819  * lightening an image on prelight, but will not modify source images
1820  * that match exactly.
1821  **/
1822 void
1823 gtk_icon_source_set_state_wildcarded (GtkIconSource *source,
1824                                       gboolean       setting)
1825 {
1826   g_return_if_fail (source != NULL);
1827
1828   source->any_state = setting != FALSE;
1829 }
1830
1831
1832 /**
1833  * gtk_icon_source_set_size_wildcarded:
1834  * @source: a #GtkIconSource
1835  * @setting: %TRUE to wildcard the widget state
1836  *
1837  * If the icon size is wildcarded, this source can be used as the base
1838  * image for an icon of any size.  If the size is not wildcarded, then
1839  * the size the source applies to should be set with
1840  * gtk_icon_source_set_size() and the icon source will only be used
1841  * with that specific size.
1842  *
1843  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
1844  * wildcarded sources, and will use an exact match when possible.
1845  *
1846  * #GtkIconSet will normally scale wildcarded source images to produce
1847  * an appropriate icon at a given size, but will not change the size
1848  * of source images that match exactly.
1849  **/
1850 void
1851 gtk_icon_source_set_size_wildcarded (GtkIconSource *source,
1852                                      gboolean       setting)
1853 {
1854   g_return_if_fail (source != NULL);
1855
1856   source->any_size = setting != FALSE;  
1857 }
1858
1859 /**
1860  * gtk_icon_source_get_size_wildcarded:
1861  * @source: a #GtkIconSource
1862  * 
1863  * Gets the value set by gtk_icon_source_set_size_wildcarded().
1864  * 
1865  * Return value: %TRUE if this icon source is a base for any icon size variant
1866  **/
1867 gboolean
1868 gtk_icon_source_get_size_wildcarded (const GtkIconSource *source)
1869 {
1870   g_return_val_if_fail (source != NULL, TRUE);
1871   
1872   return source->any_size;
1873 }
1874
1875 /**
1876  * gtk_icon_source_get_state_wildcarded:
1877  * @source: a #GtkIconSource
1878  * 
1879  * Gets the value set by gtk_icon_source_set_state_wildcarded().
1880  * 
1881  * Return value: %TRUE if this icon source is a base for any widget state variant
1882  **/
1883 gboolean
1884 gtk_icon_source_get_state_wildcarded (const GtkIconSource *source)
1885 {
1886   g_return_val_if_fail (source != NULL, TRUE);
1887
1888   return source->any_state;
1889 }
1890
1891 /**
1892  * gtk_icon_source_get_direction_wildcarded:
1893  * @source: a #GtkIconSource
1894  * 
1895  * Gets the value set by gtk_icon_source_set_direction_wildcarded().
1896  * 
1897  * Return value: %TRUE if this icon source is a base for any text direction variant
1898  **/
1899 gboolean
1900 gtk_icon_source_get_direction_wildcarded (const GtkIconSource *source)
1901 {
1902   g_return_val_if_fail (source != NULL, TRUE);
1903
1904   return source->any_direction;
1905 }
1906
1907 /**
1908  * gtk_icon_source_set_direction:
1909  * @source: a #GtkIconSource
1910  * @direction: text direction this source applies to
1911  *
1912  * Sets the text direction this icon source is intended to be used
1913  * with.
1914  * 
1915  * Setting the text direction on an icon source makes no difference
1916  * if the text direction is wildcarded. Therefore, you should usually
1917  * call gtk_icon_source_set_direction_wildcarded() to un-wildcard it
1918  * in addition to calling this function.
1919  * 
1920  **/
1921 void
1922 gtk_icon_source_set_direction (GtkIconSource   *source,
1923                                GtkTextDirection direction)
1924 {
1925   g_return_if_fail (source != NULL);
1926
1927   source->direction = direction;
1928 }
1929
1930 /**
1931  * gtk_icon_source_set_state:
1932  * @source: a #GtkIconSource
1933  * @state: widget state this source applies to
1934  *
1935  * Sets the widget state this icon source is intended to be used
1936  * with.
1937  * 
1938  * Setting the widget state on an icon source makes no difference
1939  * if the state is wildcarded. Therefore, you should usually
1940  * call gtk_icon_source_set_state_wildcarded() to un-wildcard it
1941  * in addition to calling this function.
1942  * 
1943  **/
1944 void
1945 gtk_icon_source_set_state (GtkIconSource *source,
1946                            GtkStateType   state)
1947 {
1948   g_return_if_fail (source != NULL);
1949
1950   source->state = state;
1951 }
1952
1953 /**
1954  * gtk_icon_source_set_size:
1955  * @source: a #GtkIconSource
1956  * @size: icon size this source applies to
1957  *
1958  * Sets the icon size this icon source is intended to be used
1959  * with.
1960  * 
1961  * Setting the icon size on an icon source makes no difference
1962  * if the size is wildcarded. Therefore, you should usually
1963  * call gtk_icon_source_set_size_wildcarded() to un-wildcard it
1964  * in addition to calling this function.
1965  * 
1966  **/
1967 void
1968 gtk_icon_source_set_size (GtkIconSource *source,
1969                           GtkIconSize    size)
1970 {
1971   g_return_if_fail (source != NULL);
1972
1973   source->size = size;
1974 }
1975
1976 /**
1977  * gtk_icon_source_get_direction:
1978  * @source: a #GtkIconSource
1979  * 
1980  * Obtains the text direction this icon source applies to. The return
1981  * value is only useful/meaningful if the text direction is <emphasis>not</emphasis> 
1982  * wildcarded.
1983  * 
1984  * Return value: text direction this source matches
1985  **/
1986 GtkTextDirection
1987 gtk_icon_source_get_direction (const GtkIconSource *source)
1988 {
1989   g_return_val_if_fail (source != NULL, 0);
1990
1991   return source->direction;
1992 }
1993
1994 /**
1995  * gtk_icon_source_get_state:
1996  * @source: a #GtkIconSource
1997  * 
1998  * Obtains the widget state this icon source applies to. The return
1999  * value is only useful/meaningful if the widget state is <emphasis>not</emphasis>
2000  * wildcarded.
2001  * 
2002  * Return value: widget state this source matches
2003  **/
2004 GtkStateType
2005 gtk_icon_source_get_state (const GtkIconSource *source)
2006 {
2007   g_return_val_if_fail (source != NULL, 0);
2008
2009   return source->state;
2010 }
2011
2012 /**
2013  * gtk_icon_source_get_size:
2014  * @source: a #GtkIconSource
2015  * 
2016  * Obtains the icon size this source applies to. The return value
2017  * is only useful/meaningful if the icon size is <emphasis>not</emphasis> wildcarded.
2018  * 
2019  * Return value: icon size this source matches.
2020  **/
2021 GtkIconSize
2022 gtk_icon_source_get_size (const GtkIconSource *source)
2023 {
2024   g_return_val_if_fail (source != NULL, 0);
2025
2026   return source->size;
2027 }
2028
2029 /* Note that the logical maximum is 20 per GtkTextDirection, so we could
2030  * eventually set this to >20 to never throw anything out.
2031  */
2032 #define NUM_CACHED_ICONS 8
2033
2034 typedef struct _CachedIcon CachedIcon;
2035
2036 struct _CachedIcon
2037 {
2038   /* These must all match to use the cached pixbuf.
2039    * If any don't match, we must re-render the pixbuf.
2040    */
2041   GtkStyle *style;
2042   GtkTextDirection direction;
2043   GtkStateType state;
2044   GtkIconSize size;
2045
2046   GdkPixbuf *pixbuf;
2047 };
2048
2049 static void
2050 ensure_cache_up_to_date (GtkIconSet *icon_set)
2051 {
2052   if (icon_set->cache_serial != cache_serial)
2053     clear_cache (icon_set, TRUE);
2054 }
2055
2056 static void
2057 cached_icon_free (CachedIcon *icon)
2058 {
2059   g_object_unref (G_OBJECT (icon->pixbuf));
2060
2061   g_free (icon);
2062 }
2063
2064 static GdkPixbuf *
2065 find_in_cache (GtkIconSet      *icon_set,
2066                GtkStyle        *style,
2067                GtkTextDirection direction,
2068                GtkStateType     state,
2069                GtkIconSize      size)
2070 {
2071   GSList *tmp_list;
2072   GSList *prev;
2073
2074   ensure_cache_up_to_date (icon_set);
2075   
2076   prev = NULL;
2077   tmp_list = icon_set->cache;
2078   while (tmp_list != NULL)
2079     {
2080       CachedIcon *icon = tmp_list->data;
2081
2082       if (icon->style == style &&
2083           icon->direction == direction &&
2084           icon->state == state &&
2085           icon->size == size)
2086         {
2087           if (prev)
2088             {
2089               /* Move this icon to the front of the list. */
2090               prev->next = tmp_list->next;
2091               tmp_list->next = icon_set->cache;
2092               icon_set->cache = tmp_list;
2093             }
2094           
2095           return icon->pixbuf;
2096         }
2097           
2098       prev = tmp_list;
2099       tmp_list = g_slist_next (tmp_list);
2100     }
2101
2102   return NULL;
2103 }
2104
2105 static void
2106 add_to_cache (GtkIconSet      *icon_set,
2107               GtkStyle        *style,
2108               GtkTextDirection direction,
2109               GtkStateType     state,
2110               GtkIconSize      size,
2111               GdkPixbuf       *pixbuf)
2112 {
2113   CachedIcon *icon;
2114
2115   ensure_cache_up_to_date (icon_set);
2116   
2117   g_object_ref (G_OBJECT (pixbuf));
2118
2119   /* We have to ref the style, since if the style was finalized
2120    * its address could be reused by another style, creating a
2121    * really weird bug
2122    */
2123   
2124   if (style)
2125     g_object_ref (G_OBJECT (style));
2126   
2127
2128   icon = g_new (CachedIcon, 1);
2129   icon_set->cache = g_slist_prepend (icon_set->cache, icon);
2130
2131   icon->style = style;
2132   icon->direction = direction;
2133   icon->state = state;
2134   icon->size = size;
2135   icon->pixbuf = pixbuf;
2136
2137   if (icon->style)
2138     attach_to_style (icon_set, icon->style);
2139   
2140   if (icon_set->cache_size >= NUM_CACHED_ICONS)
2141     {
2142       /* Remove oldest item in the cache */
2143       
2144       GSList *tmp_list;
2145       
2146       tmp_list = icon_set->cache;
2147
2148       /* Find next-to-last link */
2149       g_assert (NUM_CACHED_ICONS > 2);
2150       while (tmp_list->next->next)
2151         tmp_list = tmp_list->next;
2152
2153       g_assert (tmp_list != NULL);
2154       g_assert (tmp_list->next != NULL);
2155       g_assert (tmp_list->next->next == NULL);
2156
2157       /* Free the last icon */
2158       icon = tmp_list->next->data;
2159
2160       g_slist_free (tmp_list->next);
2161       tmp_list->next = NULL;
2162
2163       cached_icon_free (icon);
2164     }
2165 }
2166
2167 static void
2168 clear_cache (GtkIconSet *icon_set,
2169              gboolean    style_detach)
2170 {
2171   GSList *tmp_list;
2172   GtkStyle *last_style = NULL;
2173
2174   tmp_list = icon_set->cache;
2175   while (tmp_list != NULL)
2176     {
2177       CachedIcon *icon = tmp_list->data;
2178
2179       if (style_detach)
2180         {
2181           /* simple optimization for the case where the cache
2182            * contains contiguous icons from the same style.
2183            * it's safe to call detach_from_style more than
2184            * once on the same style though.
2185            */
2186           if (last_style != icon->style)
2187             {
2188               detach_from_style (icon_set, icon->style);
2189               last_style = icon->style;
2190             }
2191         }
2192       
2193       cached_icon_free (icon);      
2194       
2195       tmp_list = g_slist_next (tmp_list);
2196     }
2197
2198   g_slist_free (icon_set->cache);
2199   icon_set->cache = NULL;
2200   icon_set->cache_size = 0;
2201 }
2202
2203 static GSList*
2204 copy_cache (GtkIconSet *icon_set,
2205             GtkIconSet *copy_recipient)
2206 {
2207   GSList *tmp_list;
2208   GSList *copy = NULL;
2209
2210   ensure_cache_up_to_date (icon_set);
2211   
2212   tmp_list = icon_set->cache;
2213   while (tmp_list != NULL)
2214     {
2215       CachedIcon *icon = tmp_list->data;
2216       CachedIcon *icon_copy = g_new (CachedIcon, 1);
2217
2218       *icon_copy = *icon;
2219
2220       if (icon_copy->style)
2221         attach_to_style (copy_recipient, icon_copy->style);
2222         
2223       g_object_ref (G_OBJECT (icon_copy->pixbuf));
2224
2225       icon_copy->size = icon->size;
2226       
2227       copy = g_slist_prepend (copy, icon_copy);      
2228       
2229       tmp_list = g_slist_next (tmp_list);
2230     }
2231
2232   return g_slist_reverse (copy);
2233 }
2234
2235 static void
2236 attach_to_style (GtkIconSet *icon_set,
2237                  GtkStyle   *style)
2238 {
2239   GHashTable *table;
2240
2241   table = g_object_get_qdata (G_OBJECT (style),
2242                               g_quark_try_string ("gtk-style-icon-sets"));
2243
2244   if (table == NULL)
2245     {
2246       table = g_hash_table_new (NULL, NULL);
2247       g_object_set_qdata_full (G_OBJECT (style),
2248                                g_quark_from_static_string ("gtk-style-icon-sets"),
2249                                table,
2250                                style_dnotify);
2251     }
2252
2253   g_hash_table_insert (table, icon_set, icon_set);
2254 }
2255
2256 static void
2257 detach_from_style (GtkIconSet *icon_set,
2258                    GtkStyle   *style)
2259 {
2260   GHashTable *table;
2261
2262   table = g_object_get_qdata (G_OBJECT (style),
2263                               g_quark_try_string ("gtk-style-icon-sets"));
2264
2265   if (table != NULL)
2266     g_hash_table_remove (table, icon_set);
2267 }
2268
2269 static void
2270 iconsets_foreach (gpointer key,
2271                   gpointer value,
2272                   gpointer user_data)
2273 {
2274   GtkIconSet *icon_set = key;
2275
2276   /* We only need to remove cache entries for the given style;
2277    * but that complicates things because in destroy notify
2278    * we don't know which style got destroyed, and 95% of the
2279    * time all cache entries will have the same style,
2280    * so this is faster anyway.
2281    */
2282   
2283   clear_cache (icon_set, FALSE);
2284 }
2285
2286 static void
2287 style_dnotify (gpointer data)
2288 {
2289   GHashTable *table = data;
2290   
2291   g_hash_table_foreach (table, iconsets_foreach, NULL);
2292
2293   g_hash_table_destroy (table);
2294 }
2295
2296 /* This allows the icon set to detect that its cache is out of date. */
2297 void
2298 _gtk_icon_set_invalidate_caches (void)
2299 {
2300   ++cache_serial;
2301 }
2302
2303 static void
2304 listify_foreach (gpointer key, gpointer value, gpointer data)
2305 {
2306   GSList **list = data;
2307
2308   *list = g_slist_prepend (*list, key);
2309 }
2310
2311 static GSList *
2312 g_hash_table_get_keys (GHashTable *table)
2313 {
2314   GSList *list = NULL;
2315
2316   g_hash_table_foreach (table, listify_foreach, &list);
2317
2318   return list;
2319 }
2320
2321 /**
2322  * _gtk_icon_factory_list_ids:
2323  * 
2324  * Gets all known IDs stored in an existing icon factory.
2325  * The strings in the returned list aren't copied.
2326  * The list itself should be freed.
2327  * 
2328  * Return value: List of ids in icon factories
2329  **/
2330 GSList*
2331 _gtk_icon_factory_list_ids (void)
2332 {
2333   GSList *tmp_list;
2334   GSList *ids;
2335
2336   ids = NULL;
2337
2338   ensure_default_icons ();
2339   
2340   tmp_list = all_icon_factories;
2341   while (tmp_list != NULL)
2342     {
2343       GSList *these_ids;
2344       
2345       GtkIconFactory *factory = GTK_ICON_FACTORY (tmp_list->data);
2346
2347       these_ids = g_hash_table_get_keys (factory->icons);
2348       
2349       ids = g_slist_concat (ids, these_ids);
2350       
2351       tmp_list = g_slist_next (tmp_list);
2352     }
2353
2354   return ids;
2355 }