]> Pileus Git - ~andy/gtk/blob - gtk/gtkiconfactory.c
79eda20e10f1012cb169cc224b34f928cef443f0
[~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_redo_24,
643                            stock_redo_16,
644                            GTK_ICON_SIZE_MENU,
645                            GTK_STOCK_REDO);
646
647   add_sized_with_fallback (factory,
648                            stock_refresh_24,
649                            stock_refresh_16,
650                            GTK_ICON_SIZE_MENU,
651                            GTK_STOCK_REFRESH);
652
653   add_sized_with_fallback (factory,
654                            stock_revert_24,
655                            stock_revert_16,
656                            GTK_ICON_SIZE_MENU,
657                            GTK_STOCK_REVERT_TO_SAVED);
658
659   add_sized_with_fallback (factory,
660                            stock_right_arrow_24,
661                            stock_right_arrow_16,
662                            GTK_ICON_SIZE_MENU,
663                            GTK_STOCK_GO_FORWARD);
664
665   add_sized_with_fallback (factory,
666                            stock_save_24,
667                            stock_save_16,
668                            GTK_ICON_SIZE_MENU,
669                            GTK_STOCK_SAVE);
670
671   add_sized_with_fallback (factory,
672                            stock_save_24,
673                            stock_save_16,
674                            GTK_ICON_SIZE_MENU,
675                            GTK_STOCK_FLOPPY);
676
677   add_sized_with_fallback (factory,
678                            stock_save_as_24,
679                            stock_save_as_16,
680                            GTK_ICON_SIZE_MENU,
681                            GTK_STOCK_SAVE_AS);
682
683   add_sized_with_fallback (factory,
684                            stock_search_24,
685                            stock_search_16,
686                            GTK_ICON_SIZE_MENU,
687                            GTK_STOCK_FIND);
688
689   add_sized_with_fallback (factory,
690                            stock_search_replace_24,
691                            stock_search_replace_16,
692                            GTK_ICON_SIZE_MENU,
693                            GTK_STOCK_FIND_AND_REPLACE);
694
695   add_sized_with_fallback (factory,
696                            stock_sort_descending_24,
697                            stock_sort_descending_16,
698                            GTK_ICON_SIZE_MENU,
699                            GTK_STOCK_SORT_DESCENDING);
700
701   add_sized_with_fallback (factory,
702                            stock_sort_ascending_24,
703                            stock_sort_ascending_16,
704                            GTK_ICON_SIZE_MENU,
705                            GTK_STOCK_SORT_ASCENDING);
706
707   add_sized_with_fallback (factory,
708                            stock_spellcheck_24,
709                            stock_spellcheck_16,
710                            GTK_ICON_SIZE_MENU,
711                            GTK_STOCK_SPELL_CHECK);
712
713   add_sized_with_fallback (factory,
714                            stock_stop_24,
715                            stock_stop_16,
716                            GTK_ICON_SIZE_MENU,
717                            GTK_STOCK_STOP);
718
719   add_sized_with_fallback (factory,
720                            stock_text_bold_24,
721                            stock_text_bold_16,
722                            GTK_ICON_SIZE_MENU,
723                            GTK_STOCK_BOLD);
724
725   add_sized_with_fallback (factory,
726                            stock_text_italic_24,
727                            stock_text_italic_16,
728                            GTK_ICON_SIZE_MENU,
729                            GTK_STOCK_ITALIC);
730
731   add_sized_with_fallback (factory,
732                            stock_text_strikethrough_24,
733                            stock_text_strikethrough_16,
734                            GTK_ICON_SIZE_MENU,
735                            GTK_STOCK_STRIKETHROUGH);
736
737   add_sized_with_fallback (factory,
738                            stock_text_underline_24,
739                            stock_text_underline_16,
740                            GTK_ICON_SIZE_MENU,
741                            GTK_STOCK_UNDERLINE);
742
743   add_sized_with_fallback (factory,
744                            stock_top_24,
745                            stock_top_16,
746                            GTK_ICON_SIZE_MENU,
747                            GTK_STOCK_GOTO_TOP);
748
749   add_sized_with_fallback (factory,
750                            stock_trash_24,
751                            stock_trash_16,
752                            GTK_ICON_SIZE_MENU,
753                            GTK_STOCK_DELETE);
754
755   add_sized_with_fallback (factory,
756                            stock_undelete_24,
757                            stock_undelete_16,
758                            GTK_ICON_SIZE_MENU,
759                            GTK_STOCK_UNDELETE);
760
761   add_sized_with_fallback (factory,
762                            stock_undo_24,
763                            stock_undo_16,
764                            GTK_ICON_SIZE_MENU,
765                            GTK_STOCK_UNDO);
766
767   add_sized_with_fallback (factory,
768                            stock_up_arrow_24,
769                            stock_up_arrow_16,
770                            GTK_ICON_SIZE_MENU,
771                            GTK_STOCK_GO_UP);
772
773 /* Generic size only */
774
775   add_unsized (factory, stock_add_24, GTK_STOCK_ADD);
776   add_unsized (factory, stock_clear_24, GTK_STOCK_CLEAR);
777   add_unsized (factory, stock_colorselector_24, GTK_STOCK_SELECT_COLOR);
778   add_unsized (factory, stock_index_24, GTK_STOCK_INDEX);
779   add_unsized (factory, stock_properties_24, GTK_STOCK_PROPERTIES);
780   add_unsized (factory, stock_remove_24, GTK_STOCK_REMOVE);
781   add_unsized (factory, stock_zoom_1_24, GTK_STOCK_ZOOM_100);
782   add_unsized (factory, stock_zoom_fit_24, GTK_STOCK_ZOOM_FIT);
783   add_unsized (factory, stock_zoom_in_24, GTK_STOCK_ZOOM_IN);
784   add_unsized (factory, stock_zoom_out_24, GTK_STOCK_ZOOM_OUT);
785 }
786
787 /* Sizes */
788
789 typedef struct _IconSize IconSize;
790
791 struct _IconSize
792 {
793   gint size;
794   gchar *name;
795   
796   gint width;
797   gint height;
798 };
799
800 typedef struct _IconAlias IconAlias;
801
802 struct _IconAlias
803 {
804   gchar *name;
805   gint   target;
806 };
807
808 static GHashTable *icon_aliases = NULL;
809 static IconSize *icon_sizes = NULL;
810 static gint      icon_sizes_allocated = 0;
811 static gint      icon_sizes_used = 0;
812
813 static void
814 init_icon_sizes (void)
815 {
816   if (icon_sizes == NULL)
817     {
818 #define NUM_BUILTIN_SIZES 7
819       gint i;
820
821       icon_aliases = g_hash_table_new (g_str_hash, g_str_equal);
822       
823       icon_sizes = g_new (IconSize, NUM_BUILTIN_SIZES);
824       icon_sizes_allocated = NUM_BUILTIN_SIZES;
825       icon_sizes_used = NUM_BUILTIN_SIZES;
826
827       icon_sizes[GTK_ICON_SIZE_INVALID].size = 0;
828       icon_sizes[GTK_ICON_SIZE_INVALID].name = NULL;
829       icon_sizes[GTK_ICON_SIZE_INVALID].width = 0;
830       icon_sizes[GTK_ICON_SIZE_INVALID].height = 0;
831
832       /* the name strings aren't copied since we don't ever remove
833        * icon sizes, so we don't need to know whether they're static.
834        * Even if we did I suppose removing the builtin sizes would be
835        * disallowed.
836        */
837       
838       icon_sizes[GTK_ICON_SIZE_MENU].size = GTK_ICON_SIZE_MENU;
839       icon_sizes[GTK_ICON_SIZE_MENU].name = "gtk-menu";
840       icon_sizes[GTK_ICON_SIZE_MENU].width = 16;
841       icon_sizes[GTK_ICON_SIZE_MENU].height = 16;
842
843       icon_sizes[GTK_ICON_SIZE_BUTTON].size = GTK_ICON_SIZE_BUTTON;
844       icon_sizes[GTK_ICON_SIZE_BUTTON].name = "gtk-button";
845       icon_sizes[GTK_ICON_SIZE_BUTTON].width = 20;
846       icon_sizes[GTK_ICON_SIZE_BUTTON].height = 20;
847
848       icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].size = GTK_ICON_SIZE_SMALL_TOOLBAR;
849       icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].name = "gtk-small-toolbar";
850       icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].width = 18;
851       icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].height = 18;
852       
853       icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].size = GTK_ICON_SIZE_LARGE_TOOLBAR;
854       icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].name = "gtk-large-toolbar";
855       icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].width = 24;
856       icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].height = 24;
857
858       icon_sizes[GTK_ICON_SIZE_DND].size = GTK_ICON_SIZE_DND;
859       icon_sizes[GTK_ICON_SIZE_DND].name = "gtk-dnd";
860       icon_sizes[GTK_ICON_SIZE_DND].width = 32;
861       icon_sizes[GTK_ICON_SIZE_DND].height = 32;
862
863       icon_sizes[GTK_ICON_SIZE_DIALOG].size = GTK_ICON_SIZE_DIALOG;
864       icon_sizes[GTK_ICON_SIZE_DIALOG].name = "gtk-dialog";
865       icon_sizes[GTK_ICON_SIZE_DIALOG].width = 48;
866       icon_sizes[GTK_ICON_SIZE_DIALOG].height = 48;
867
868       g_assert ((GTK_ICON_SIZE_DIALOG + 1) == NUM_BUILTIN_SIZES);
869
870       /* Alias everything to itself. */
871       i = 1; /* skip invalid size */
872       while (i < NUM_BUILTIN_SIZES)
873         {
874           gtk_icon_size_register_alias (icon_sizes[i].name, icon_sizes[i].size);
875           
876           ++i;
877         }
878       
879 #undef NUM_BUILTIN_SIZES
880     }
881 }
882
883 /**
884  * gtk_icon_size_lookup:
885  * @size: an icon size
886  * @width: location to store icon width
887  * @height: location to store icon height
888  *
889  * Obtains the pixel size of a semantic icon size, normally @size would be
890  * #GTK_ICON_SIZE_MENU, #GTK_ICON_SIZE_BUTTON, etc.  This function
891  * isn't normally needed, gtk_widget_render_icon() is the usual
892  * way to get an icon for rendering, then just look at the size of
893  * the rendered pixbuf. The rendered pixbuf may not even correspond to
894  * the width/height returned by gtk_icon_size_lookup(), because themes
895  * are free to render the pixbuf however they like, including changing
896  * the usual size.
897  * 
898  * Return value: %TRUE if @size was a valid size
899  **/
900 gboolean
901 gtk_icon_size_lookup (GtkIconSize  size,
902                       gint        *widthp,
903                       gint        *heightp)
904 {
905   init_icon_sizes ();
906
907   if (size >= icon_sizes_used)
908     return FALSE;
909
910   if (size == GTK_ICON_SIZE_INVALID)
911     return FALSE;
912   
913   if (widthp)
914     *widthp = icon_sizes[size].width;
915
916   if (heightp)
917     *heightp = icon_sizes[size].height;
918
919   return TRUE;
920 }
921
922 /**
923  * gtk_icon_size_register:
924  * @name: name of the icon size
925  * @width: the icon width
926  * @height: the icon height
927  *
928  * Registers a new icon size, along the same lines as #GTK_ICON_SIZE_MENU,
929  * etc. Returns the integer value for the size.
930  *
931  * Returns: integer value representing the size
932  * 
933  **/
934 GtkIconSize
935 gtk_icon_size_register (const gchar *name,
936                         gint         width,
937                         gint         height)
938 {
939   g_return_val_if_fail (name != NULL, 0);
940   g_return_val_if_fail (width > 0, 0);
941   g_return_val_if_fail (height > 0, 0);
942   
943   init_icon_sizes ();
944
945   if (icon_sizes_used == icon_sizes_allocated)
946     {
947       icon_sizes_allocated *= 2;
948       icon_sizes = g_renew (IconSize, icon_sizes, icon_sizes_allocated);
949     }
950   
951   icon_sizes[icon_sizes_used].size = icon_sizes_used;
952   icon_sizes[icon_sizes_used].name = g_strdup (name);
953   icon_sizes[icon_sizes_used].width = width;
954   icon_sizes[icon_sizes_used].height = height;
955
956   ++icon_sizes_used;
957
958   /* alias to self. */
959   gtk_icon_size_register_alias (name, icon_sizes_used - 1);
960   
961   return icon_sizes_used - 1;
962 }
963
964 /**
965  * gtk_icon_size_register_alias:
966  * @alias: an alias for @target
967  * @target: an existing icon size
968  *
969  * Registers @alias as another name for @target.
970  * So calling gtk_icon_size_from_name() with @alias as argument
971  * will return @target.
972  *
973  **/
974 void
975 gtk_icon_size_register_alias (const gchar *alias,
976                               GtkIconSize  target)
977 {
978   IconAlias *ia;
979   
980   g_return_if_fail (alias != NULL);
981
982   init_icon_sizes ();
983
984   if (g_hash_table_lookup (icon_aliases, alias))
985     g_warning ("gtk_icon_size_register_alias: Icon size name '%s' already exists", alias);
986
987   if (!gtk_icon_size_lookup (target, NULL, NULL))
988     g_warning ("gtk_icon_size_register_alias: Icon size %d does not exist", target);
989   
990   ia = g_new (IconAlias, 1);
991   ia->name = g_strdup (alias);
992   ia->target = target;
993
994   g_hash_table_insert (icon_aliases, ia->name, ia);
995 }
996
997 /** 
998  * gtk_icon_size_from_name:
999  * @name: the name to look up.
1000  * @returns: the icon size with the given name.
1001  * 
1002  * Looks up the icon size associated with @name.
1003  **/
1004 GtkIconSize
1005 gtk_icon_size_from_name (const gchar *name)
1006 {
1007   IconAlias *ia;
1008
1009   init_icon_sizes ();
1010   
1011   ia = g_hash_table_lookup (icon_aliases, name);
1012
1013   if (ia)
1014     return ia->target;
1015   else
1016     return GTK_ICON_SIZE_INVALID;
1017 }
1018
1019 /**
1020  * gtk_icon_size_get_name:
1021  * @size: a #GtkIconSize.
1022  * @returns: the name of the given icon size.
1023  * 
1024  * Gets the canonical name of the given icon size. The returned string 
1025  * is statically allocated and should not be freed.
1026  **/
1027 G_CONST_RETURN gchar*
1028 gtk_icon_size_get_name (GtkIconSize  size)
1029 {
1030   if (size >= icon_sizes_used)
1031     return NULL;
1032   else
1033     return icon_sizes[size].name;
1034 }
1035
1036 /* Icon Set */
1037
1038
1039 /* Clear icon set contents, drop references to all contained
1040  * GdkPixbuf objects and forget all GtkIconSources. Used to
1041  * recycle an icon set.
1042  */
1043 static GdkPixbuf *find_in_cache     (GtkIconSet       *icon_set,
1044                                      GtkStyle         *style,
1045                                      GtkTextDirection  direction,
1046                                      GtkStateType      state,
1047                                      GtkIconSize       size);
1048 static void       add_to_cache      (GtkIconSet       *icon_set,
1049                                      GtkStyle         *style,
1050                                      GtkTextDirection  direction,
1051                                      GtkStateType      state,
1052                                      GtkIconSize       size,
1053                                      GdkPixbuf        *pixbuf);
1054 static void       clear_cache       (GtkIconSet       *icon_set,
1055                                      gboolean          style_detach);
1056 static GSList*    copy_cache        (GtkIconSet       *icon_set,
1057                                      GtkIconSet       *copy_recipient);
1058 static void       attach_to_style   (GtkIconSet       *icon_set,
1059                                      GtkStyle         *style);
1060 static void       detach_from_style (GtkIconSet       *icon_set,
1061                                      GtkStyle         *style);
1062 static void       style_dnotify     (gpointer          data);
1063
1064 struct _GtkIconSet
1065 {
1066   guint ref_count;
1067
1068   GSList *sources;
1069
1070   /* Cache of the last few rendered versions of the icon. */
1071   GSList *cache;
1072
1073   guint cache_size;
1074
1075   guint cache_serial;
1076 };
1077
1078 static guint cache_serial = 0;
1079
1080 /**
1081  * gtk_icon_set_new:
1082  * 
1083  * Creates a new #GtkIconSet. A #GtkIconSet represents a single icon
1084  * in various sizes and widget states. It can provide a #GdkPixbuf
1085  * for a given size and state on request, and automatically caches
1086  * some of the rendered #GdkPixbuf objects.
1087  *
1088  * Normally you would use gtk_widget_render_icon() instead of
1089  * using #GtkIconSet directly. The one case where you'd use
1090  * #GtkIconSet is to create application-specific icon sets to place in
1091  * a #GtkIconFactory.
1092  * 
1093  * Return value: a new #GtkIconSet
1094  **/
1095 GtkIconSet*
1096 gtk_icon_set_new (void)
1097 {
1098   GtkIconSet *icon_set;
1099
1100   icon_set = g_new (GtkIconSet, 1);
1101
1102   icon_set->ref_count = 1;
1103   icon_set->sources = NULL;
1104   icon_set->cache = NULL;
1105   icon_set->cache_size = 0;
1106   icon_set->cache_serial = cache_serial;
1107   
1108   return icon_set;
1109 }
1110
1111 /**
1112  * gtk_icon_set_new_from_pixbuf:
1113  * @pixbuf: a #GdkPixbuf
1114  * 
1115  * Creates a new #GtkIconSet with @pixbuf as the default/fallback
1116  * source image. If you don't add any additional #GtkIconSource to the
1117  * icon set, all variants of the icon will be created from @pixbuf,
1118  * using scaling, pixelation, etc. as required to adjust the icon size
1119  * or make the icon look insensitive/prelighted.
1120  * 
1121  * Return value: a new #GtkIconSet
1122  **/
1123 GtkIconSet *
1124 gtk_icon_set_new_from_pixbuf (GdkPixbuf *pixbuf)
1125 {
1126   GtkIconSet *set;
1127
1128   GtkIconSource source = { NULL, NULL, 0, 0, 0,
1129                            TRUE, TRUE, TRUE };
1130
1131   g_return_val_if_fail (pixbuf != NULL, NULL);
1132
1133   set = gtk_icon_set_new ();
1134
1135   source.pixbuf = pixbuf;
1136
1137   gtk_icon_set_add_source (set, &source);
1138   
1139   return set;
1140 }
1141
1142
1143 /**
1144  * gtk_icon_set_ref:
1145  * @icon_set: a #GtkIconSet.
1146  * 
1147  * Increments the reference count on @icon_set.
1148  * 
1149  * Return value: @icon_set.
1150  **/
1151 GtkIconSet*
1152 gtk_icon_set_ref (GtkIconSet *icon_set)
1153 {
1154   g_return_val_if_fail (icon_set != NULL, NULL);
1155   g_return_val_if_fail (icon_set->ref_count > 0, NULL);
1156
1157   icon_set->ref_count += 1;
1158
1159   return icon_set;
1160 }
1161
1162 /**
1163  * gtk_icon_set_unref:
1164  * @icon_set: a #GtkIconSet
1165  * 
1166  * Decrements the reference count on @icon_set, and frees memory
1167  * if the reference count reaches 0.
1168  **/
1169 void
1170 gtk_icon_set_unref (GtkIconSet *icon_set)
1171 {
1172   g_return_if_fail (icon_set != NULL);
1173   g_return_if_fail (icon_set->ref_count > 0);
1174
1175   icon_set->ref_count -= 1;
1176
1177   if (icon_set->ref_count == 0)
1178     {
1179       GSList *tmp_list = icon_set->sources;
1180       while (tmp_list != NULL)
1181         {
1182           gtk_icon_source_free (tmp_list->data);
1183
1184           tmp_list = g_slist_next (tmp_list);
1185         }
1186
1187       clear_cache (icon_set, TRUE);
1188
1189       g_free (icon_set);
1190     }
1191 }
1192
1193 GType
1194 gtk_icon_set_get_type (void)
1195 {
1196   static GType our_type = 0;
1197   
1198   if (our_type == 0)
1199     our_type = g_boxed_type_register_static ("GtkTypeIconSet",
1200                                              (GBoxedCopyFunc) gtk_icon_set_ref,
1201                                              (GBoxedFreeFunc) gtk_icon_set_unref);
1202
1203   return our_type;
1204 }
1205
1206 /**
1207  * gtk_icon_set_copy:
1208  * @icon_set: a #GtkIconSet
1209  * 
1210  * Copies @icon_set by value. 
1211  * 
1212  * Return value: a new #GtkIconSet identical to the first.
1213  **/
1214 GtkIconSet*
1215 gtk_icon_set_copy (GtkIconSet *icon_set)
1216 {
1217   GtkIconSet *copy;
1218   GSList *tmp_list;
1219   
1220   copy = gtk_icon_set_new ();
1221
1222   tmp_list = icon_set->sources;
1223   while (tmp_list != NULL)
1224     {
1225       copy->sources = g_slist_prepend (copy->sources,
1226                                        gtk_icon_source_copy (tmp_list->data));
1227
1228       tmp_list = g_slist_next (tmp_list);
1229     }
1230
1231   copy->sources = g_slist_reverse (copy->sources);
1232
1233   copy->cache = copy_cache (icon_set, copy);
1234   copy->cache_size = icon_set->cache_size;
1235   copy->cache_serial = icon_set->cache_serial;
1236   
1237   return copy;
1238 }
1239
1240
1241 static gboolean
1242 sizes_equivalent (GtkIconSize lhs,
1243                   GtkIconSize rhs)
1244 {
1245   gint r_w, r_h, l_w, l_h;
1246
1247   gtk_icon_size_lookup (rhs, &r_w, &r_h);
1248   gtk_icon_size_lookup (lhs, &l_w, &l_h);
1249
1250   return r_w == l_w && r_h == l_h;
1251 }
1252
1253 static GtkIconSource*
1254 find_and_prep_icon_source (GtkIconSet       *icon_set,
1255                            GtkTextDirection  direction,
1256                            GtkStateType      state,
1257                            GtkIconSize       size)
1258 {
1259   GtkIconSource *source;
1260   GSList *tmp_list;
1261
1262
1263   /* We need to find the best icon source.  Direction matters more
1264    * than state, state matters more than size. icon_set->sources
1265    * is sorted according to wildness, so if we take the first
1266    * match we find it will be the least-wild match (if there are
1267    * multiple matches for a given "wildness" then the RC file contained
1268    * dumb stuff, and we end up with an arbitrary matching source)
1269    */
1270   
1271   source = NULL;
1272   tmp_list = icon_set->sources;
1273   while (tmp_list != NULL)
1274     {
1275       GtkIconSource *s = tmp_list->data;
1276       
1277       if ((s->any_direction || (s->direction == direction)) &&
1278           (s->any_state || (s->state == state)) &&
1279           (s->any_size || (sizes_equivalent (size, s->size))))
1280         {
1281           source = s;
1282           break;
1283         }
1284       
1285       tmp_list = g_slist_next (tmp_list);
1286     }
1287
1288   if (source == NULL)
1289     return NULL;
1290   
1291   if (source->pixbuf == NULL)
1292     {
1293       GError *error = NULL;
1294       
1295       g_assert (source->filename);
1296       source->pixbuf = gdk_pixbuf_new_from_file (source->filename, &error);
1297
1298       if (source->pixbuf == NULL)
1299         {
1300           /* Remove this icon source so we don't keep trying to
1301            * load it.
1302            */
1303           g_warning (_("Error loading icon: %s"), error->message);
1304           g_error_free (error);
1305           
1306           icon_set->sources = g_slist_remove (icon_set->sources, source);
1307
1308           gtk_icon_source_free (source);
1309
1310           /* Try to fall back to other sources */
1311           if (icon_set->sources != NULL)
1312             return find_and_prep_icon_source (icon_set,
1313                                               direction,
1314                                               state,
1315                                               size);
1316           else
1317             return NULL;
1318         }
1319     }
1320
1321   return source;
1322 }
1323
1324 static GdkPixbuf*
1325 render_fallback_image (GtkStyle          *style,
1326                        GtkTextDirection   direction,
1327                        GtkStateType       state,
1328                        GtkIconSize        size,
1329                        GtkWidget         *widget,
1330                        const char        *detail)
1331 {
1332   /* This icon can be used for any direction/state/size */
1333   static GtkIconSource fallback_source = { NULL, NULL, 0, 0, 0, TRUE, TRUE, TRUE };
1334
1335   if (fallback_source.pixbuf == NULL)
1336     fallback_source.pixbuf = gdk_pixbuf_new_from_inline (-1, stock_missing_image_24, FALSE, NULL);
1337   
1338   return gtk_style_render_icon (style,
1339                                 &fallback_source,
1340                                 direction,
1341                                 state,
1342                                 size,
1343                                 widget,
1344                                 detail);
1345 }
1346
1347 /**
1348  * gtk_icon_set_render_icon:
1349  * @icon_set: a #GtkIconSet
1350  * @style: a #GtkStyle associated with @widget, or %NULL
1351  * @direction: text direction
1352  * @state: widget state
1353  * @size: icon size
1354  * @widget: widget that will display the icon, or %NULL
1355  * @detail: detail to pass to the theme engine, or %NULL
1356  * 
1357  * Renders an icon using gtk_style_render_icon(). In most cases,
1358  * gtk_widget_render_icon() is better, since it automatically provides
1359  * most of the arguments from the current widget settings.  This
1360  * function never returns %NULL; if the icon can't be rendered
1361  * (perhaps because an image file fails to load), a default "missing
1362  * image" icon will be returned instead.
1363  * 
1364  * Return value: a #GdkPixbuf to be displayed
1365  **/
1366 GdkPixbuf*
1367 gtk_icon_set_render_icon (GtkIconSet        *icon_set,
1368                           GtkStyle          *style,
1369                           GtkTextDirection   direction,
1370                           GtkStateType       state,
1371                           GtkIconSize        size,
1372                           GtkWidget         *widget,
1373                           const char        *detail)
1374 {
1375   GdkPixbuf *icon;
1376   GtkIconSource *source;
1377   
1378   g_return_val_if_fail (icon_set != NULL, NULL);
1379   g_return_val_if_fail (GTK_IS_STYLE (style), NULL);
1380
1381   if (icon_set->sources == NULL)
1382     return render_fallback_image (style, direction, state, size, widget, detail);
1383   
1384   icon = find_in_cache (icon_set, style, direction,
1385                         state, size);
1386
1387   if (icon)
1388     {
1389       g_object_ref (G_OBJECT (icon));
1390       return icon;
1391     }
1392
1393   
1394   source = find_and_prep_icon_source (icon_set, direction, state, size);
1395
1396   if (source == NULL)
1397     return render_fallback_image (style, direction, state, size, widget, detail);
1398
1399   g_assert (source->pixbuf != NULL);
1400   
1401   icon = gtk_style_render_icon (style,
1402                                 source,
1403                                 direction,
1404                                 state,
1405                                 size,
1406                                 widget,
1407                                 detail);
1408
1409   if (icon == NULL)
1410     {
1411       g_warning ("Theme engine failed to render icon");
1412       return NULL;
1413     }
1414   
1415   add_to_cache (icon_set, style, direction, state, size, icon);
1416   
1417   return icon;
1418 }
1419
1420 /* Order sources by their "wildness", so that "wilder" sources are
1421  * greater than "specific" sources; for determining ordering,
1422  * direction beats state beats size.
1423  */
1424
1425 static int
1426 icon_source_compare (gconstpointer ap, gconstpointer bp)
1427 {
1428   const GtkIconSource *a = ap;
1429   const GtkIconSource *b = bp;
1430
1431   if (!a->any_direction && b->any_direction)
1432     return -1;
1433   else if (a->any_direction && !b->any_direction)
1434     return 1;
1435   else if (!a->any_state && b->any_state)
1436     return -1;
1437   else if (a->any_state && !b->any_state)
1438     return 1;
1439   else if (!a->any_size && b->any_size)
1440     return -1;
1441   else if (a->any_size && !b->any_size)
1442     return 1;
1443   else
1444     return 0;
1445 }
1446
1447 /**
1448  * gtk_icon_set_add_source:
1449  * @icon_set: a #GtkIconSet
1450  * @source: a #GtkIconSource
1451  *
1452  * Icon sets have a list of #GtkIconSource, which they use as base
1453  * icons for rendering icons in different states and sizes. Icons are
1454  * scaled, made to look insensitive, etc. in
1455  * gtk_icon_set_render_icon(), but #GtkIconSet needs base images to
1456  * work with. The base images and when to use them are described by
1457  * a #GtkIconSource.
1458  * 
1459  * This function copies @source, so you can reuse the same source immediately
1460  * without affecting the icon set.
1461  *
1462  * An example of when you'd use this function: a web browser's "Back
1463  * to Previous Page" icon might point in a different direction in
1464  * Hebrew and in English; it might look different when insensitive;
1465  * and it might change size depending on toolbar mode (small/large
1466  * icons). So a single icon set would contain all those variants of
1467  * the icon, and you might add a separate source for each one.
1468  *
1469  * You should nearly always add a "default" icon source with all
1470  * fields wildcarded, which will be used as a fallback if no more
1471  * specific source matches. #GtkIconSet always prefers more specific
1472  * icon sources to more generic icon sources. The order in which you
1473  * add the sources to the icon set does not matter.
1474  *
1475  * gtk_icon_set_new_from_pixbuf() creates a new icon set with a
1476  * default icon source based on the given pixbuf.
1477  * 
1478  **/
1479 void
1480 gtk_icon_set_add_source (GtkIconSet *icon_set,
1481                          const GtkIconSource *source)
1482 {
1483   g_return_if_fail (icon_set != NULL);
1484   g_return_if_fail (source != NULL);
1485
1486   if (source->pixbuf == NULL &&
1487       source->filename == NULL)
1488     {
1489       g_warning ("Useless GtkIconSource contains NULL filename and pixbuf");
1490       return;
1491     }
1492   
1493   icon_set->sources = g_slist_insert_sorted (icon_set->sources,
1494                                              gtk_icon_source_copy (source),
1495                                              icon_source_compare);
1496 }
1497
1498 /**
1499  * gtk_icon_set_get_sizes:
1500  * @icon_set: a #GtkIconSet
1501  * @sizes: return location for array of sizes
1502  * @n_sizes: location to store number of elements in returned array
1503  *
1504  * Obtains a list of icon sizes this icon set can render. The returned
1505  * array must be freed with g_free().
1506  * 
1507  **/
1508 void
1509 gtk_icon_set_get_sizes (GtkIconSet   *icon_set,
1510                         GtkIconSize **sizes,
1511                         gint         *n_sizes)
1512 {
1513   GSList *tmp_list;
1514   gboolean all_sizes = FALSE;
1515   GSList *specifics = NULL;
1516   
1517   g_return_if_fail (icon_set != NULL);
1518   g_return_if_fail (sizes != NULL);
1519   g_return_if_fail (n_sizes != NULL);
1520   
1521   tmp_list = icon_set->sources;
1522   while (tmp_list != NULL)
1523     {
1524       GtkIconSource *source;
1525
1526       source = tmp_list->data;
1527
1528       if (source->any_size)
1529         {
1530           all_sizes = TRUE;
1531           break;
1532         }
1533       else
1534         specifics = g_slist_prepend (specifics, GINT_TO_POINTER (source->size));
1535       
1536       tmp_list = g_slist_next (tmp_list);
1537     }
1538
1539   if (all_sizes)
1540     {
1541       /* Need to find out what sizes exist */
1542       gint i;
1543
1544       init_icon_sizes ();
1545       
1546       *sizes = g_new (GtkIconSize, icon_sizes_used);
1547       *n_sizes = icon_sizes_used - 1;
1548       
1549       i = 1;      
1550       while (i < icon_sizes_used)
1551         {
1552           (*sizes)[i - 1] = icon_sizes[i].size;
1553           ++i;
1554         }
1555     }
1556   else
1557     {
1558       gint i;
1559       
1560       *n_sizes = g_slist_length (specifics);
1561       *sizes = g_new (GtkIconSize, *n_sizes);
1562
1563       i = 0;
1564       tmp_list = specifics;
1565       while (tmp_list != NULL)
1566         {
1567           (*sizes)[i] = GPOINTER_TO_INT (tmp_list->data);
1568
1569           ++i;
1570           tmp_list = g_slist_next (tmp_list);
1571         }
1572     }
1573
1574   g_slist_free (specifics);
1575 }
1576
1577
1578 /**
1579  * gtk_icon_source_new:
1580  * 
1581  * Creates a new #GtkIconSource. A #GtkIconSource contains a #GdkPixbuf (or
1582  * image filename) that serves as the base image for one or more of the
1583  * icons in a #GtkIconSet, along with a specification for which icons in the
1584  * icon set will be based on that pixbuf or image file. An icon set contains
1585  * a set of icons that represent "the same" logical concept in different states,
1586  * different global text directions, and different sizes.
1587  * 
1588  * So for example a web browser's "Back to Previous Page" icon might
1589  * point in a different direction in Hebrew and in English; it might
1590  * look different when insensitive; and it might change size depending
1591  * on toolbar mode (small/large icons). So a single icon set would
1592  * contain all those variants of the icon. #GtkIconSet contains a list
1593  * of #GtkIconSource from which it can derive specific icon variants in
1594  * the set. 
1595  *
1596  * In the simplest case, #GtkIconSet contains one source pixbuf from
1597  * which it derives all variants. The convenience function
1598  * gtk_icon_set_new_from_pixbuf() handles this case; if you only have
1599  * one source pixbuf, just use that function.
1600  *
1601  * If you want to use a different base pixbuf for different icon
1602  * variants, you create multiple icon sources, mark which variants
1603  * they'll be used to create, and add them to the icon set with
1604  * gtk_icon_set_add_source().
1605  *
1606  * By default, the icon source has all parameters wildcarded. That is,
1607  * the icon source will be used as the base icon for any desired text
1608  * direction, widget state, or icon size.
1609  * 
1610  * Return value: a new #GtkIconSource
1611  **/
1612 GtkIconSource*
1613 gtk_icon_source_new (void)
1614 {
1615   GtkIconSource *src;
1616   
1617   src = g_new0 (GtkIconSource, 1);
1618
1619   src->direction = GTK_TEXT_DIR_NONE;
1620   src->size = GTK_ICON_SIZE_INVALID;
1621   src->state = GTK_STATE_NORMAL;
1622   
1623   src->any_direction = TRUE;
1624   src->any_state = TRUE;
1625   src->any_size = TRUE;
1626   
1627   return src;
1628 }
1629
1630 /**
1631  * gtk_icon_source_copy:
1632  * @source: a #GtkIconSource
1633  * 
1634  * Creates a copy of @source; mostly useful for language bindings.
1635  * 
1636  * Return value: a new #GtkIconSource
1637  **/
1638 GtkIconSource*
1639 gtk_icon_source_copy (const GtkIconSource *source)
1640 {
1641   GtkIconSource *copy;
1642   
1643   g_return_val_if_fail (source != NULL, NULL);
1644
1645   copy = g_new (GtkIconSource, 1);
1646
1647   *copy = *source;
1648   
1649   copy->filename = g_strdup (source->filename);
1650   copy->size = source->size;
1651   if (copy->pixbuf)
1652     g_object_ref (G_OBJECT (copy->pixbuf));
1653
1654   return copy;
1655 }
1656
1657 /**
1658  * gtk_icon_source_free:
1659  * @source: a #GtkIconSource
1660  * 
1661  * Frees a dynamically-allocated icon source, along with its
1662  * filename, size, and pixbuf fields if those are not %NULL.
1663  **/
1664 void
1665 gtk_icon_source_free (GtkIconSource *source)
1666 {
1667   g_return_if_fail (source != NULL);
1668
1669   g_free ((char*) source->filename);
1670   if (source->pixbuf)
1671     g_object_unref (G_OBJECT (source->pixbuf));
1672
1673   g_free (source);
1674 }
1675
1676 GType
1677 gtk_icon_source_get_type (void)
1678 {
1679   static GType our_type = 0;
1680   
1681   if (our_type == 0)
1682     our_type = g_boxed_type_register_static ("GtkTypeIconSource",
1683                                              (GBoxedCopyFunc) gtk_icon_source_copy,
1684                                              (GBoxedFreeFunc) gtk_icon_source_free);
1685
1686   return our_type;
1687 }
1688
1689 /**
1690  * gtk_icon_source_set_filename:
1691  * @source: a #GtkIconSource
1692  * @filename: image file to use
1693  *
1694  * Sets the name of an image file to use as a base image when creating
1695  * icon variants for #GtkIconSet. The filename must be absolute. 
1696  **/
1697 void
1698 gtk_icon_source_set_filename (GtkIconSource *source,
1699                               const gchar   *filename)
1700 {
1701   g_return_if_fail (source != NULL);
1702   g_return_if_fail (filename == NULL || g_path_is_absolute (filename));
1703
1704   if (source->filename == filename)
1705     return;
1706   
1707   if (source->filename)
1708     g_free (source->filename);
1709
1710   source->filename = g_strdup (filename);  
1711 }
1712
1713 /**
1714  * gtk_icon_source_set_pixbuf:
1715  * @source: a #GtkIconSource
1716  * @pixbuf: pixbuf to use as a source
1717  *
1718  * Sets a pixbuf to use as a base image when creating icon variants
1719  * for #GtkIconSet. If an icon source has both a filename and a pixbuf
1720  * set, the pixbuf will take priority.
1721  * 
1722  **/
1723 void
1724 gtk_icon_source_set_pixbuf (GtkIconSource *source,
1725                             GdkPixbuf     *pixbuf)
1726 {
1727   g_return_if_fail (source != NULL);
1728
1729   if (pixbuf)
1730     g_object_ref (G_OBJECT (pixbuf));
1731
1732   if (source->pixbuf)
1733     g_object_unref (G_OBJECT (source->pixbuf));
1734
1735   source->pixbuf = pixbuf;
1736 }
1737
1738 /**
1739  * gtk_icon_source_get_filename:
1740  * @source: a #GtkIconSource
1741  * 
1742  * Retrieves the source filename, or %NULL if none is set.  The
1743  * filename is not a copy, and should not be modified or expected to
1744  * persist beyond the lifetime of the icon source.
1745  * 
1746  * Return value: image filename
1747  **/
1748 G_CONST_RETURN gchar*
1749 gtk_icon_source_get_filename (const GtkIconSource *source)
1750 {
1751   g_return_val_if_fail (source != NULL, NULL);
1752   
1753   return source->filename;
1754 }
1755
1756 /**
1757  * gtk_icon_source_get_pixbuf:
1758  * @source: a #GtkIconSource
1759  * 
1760  * Retrieves the source pixbuf, or %NULL if none is set.
1761  * The reference count on the pixbuf is not incremented.
1762  * 
1763  * Return value: source pixbuf
1764  **/
1765 GdkPixbuf*
1766 gtk_icon_source_get_pixbuf (const GtkIconSource *source)
1767 {
1768   g_return_val_if_fail (source != NULL, NULL);
1769   
1770   return source->pixbuf;
1771 }
1772
1773 /**
1774  * gtk_icon_source_set_direction_wildcarded:
1775  * @source: a #GtkIconSource
1776  * @setting: %TRUE to wildcard the text direction
1777  *
1778  * If the text direction is wildcarded, this source can be used
1779  * as the base image for an icon in any #GtkTextDirection.
1780  * If the text direction is not wildcarded, then the
1781  * text direction the icon source applies to should be set
1782  * with gtk_icon_source_set_direction(), and the icon source
1783  * will only be used with that text direction.
1784  *
1785  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
1786  * wildcarded sources, and will use an exact match when possible.
1787  * 
1788  **/
1789 void
1790 gtk_icon_source_set_direction_wildcarded (GtkIconSource *source,
1791                                           gboolean       setting)
1792 {
1793   g_return_if_fail (source != NULL);
1794
1795   source->any_direction = setting != FALSE;
1796 }
1797
1798 /**
1799  * gtk_icon_source_set_state_wildcarded:
1800  * @source: a #GtkIconSource
1801  * @setting: %TRUE to wildcard the widget state
1802  *
1803  * If the widget state is wildcarded, this source can be used as the
1804  * base image for an icon in any #GtkStateType.  If the widget state
1805  * is not wildcarded, then the state the source applies to should be
1806  * set with gtk_icon_source_set_state() and the icon source will
1807  * only be used with that specific state.
1808  *
1809  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
1810  * wildcarded sources, and will use an exact match when possible.
1811  *
1812  * #GtkIconSet will normally transform wildcarded source images to
1813  * produce an appropriate icon for a given state, for example
1814  * lightening an image on prelight, but will not modify source images
1815  * that match exactly.
1816  **/
1817 void
1818 gtk_icon_source_set_state_wildcarded (GtkIconSource *source,
1819                                       gboolean       setting)
1820 {
1821   g_return_if_fail (source != NULL);
1822
1823   source->any_state = setting != FALSE;
1824 }
1825
1826
1827 /**
1828  * gtk_icon_source_set_size_wildcarded:
1829  * @source: a #GtkIconSource
1830  * @setting: %TRUE to wildcard the widget state
1831  *
1832  * If the icon size is wildcarded, this source can be used as the base
1833  * image for an icon of any size.  If the size is not wildcarded, then
1834  * the size the source applies to should be set with
1835  * gtk_icon_source_set_size() and the icon source will only be used
1836  * with that specific size.
1837  *
1838  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
1839  * wildcarded sources, and will use an exact match when possible.
1840  *
1841  * #GtkIconSet will normally scale wildcarded source images to produce
1842  * an appropriate icon at a given size, but will not change the size
1843  * of source images that match exactly.
1844  **/
1845 void
1846 gtk_icon_source_set_size_wildcarded (GtkIconSource *source,
1847                                      gboolean       setting)
1848 {
1849   g_return_if_fail (source != NULL);
1850
1851   source->any_size = setting != FALSE;  
1852 }
1853
1854 /**
1855  * gtk_icon_source_get_size_wildcarded:
1856  * @source: a #GtkIconSource
1857  * 
1858  * Gets the value set by gtk_icon_source_set_size_wildcarded().
1859  * 
1860  * Return value: %TRUE if this icon source is a base for any icon size variant
1861  **/
1862 gboolean
1863 gtk_icon_source_get_size_wildcarded (const GtkIconSource *source)
1864 {
1865   g_return_val_if_fail (source != NULL, TRUE);
1866   
1867   return source->any_size;
1868 }
1869
1870 /**
1871  * gtk_icon_source_get_state_wildcarded:
1872  * @source: a #GtkIconSource
1873  * 
1874  * Gets the value set by gtk_icon_source_set_state_wildcarded().
1875  * 
1876  * Return value: %TRUE if this icon source is a base for any widget state variant
1877  **/
1878 gboolean
1879 gtk_icon_source_get_state_wildcarded (const GtkIconSource *source)
1880 {
1881   g_return_val_if_fail (source != NULL, TRUE);
1882
1883   return source->any_state;
1884 }
1885
1886 /**
1887  * gtk_icon_source_get_direction_wildcarded:
1888  * @source: a #GtkIconSource
1889  * 
1890  * Gets the value set by gtk_icon_source_set_direction_wildcarded().
1891  * 
1892  * Return value: %TRUE if this icon source is a base for any text direction variant
1893  **/
1894 gboolean
1895 gtk_icon_source_get_direction_wildcarded (const GtkIconSource *source)
1896 {
1897   g_return_val_if_fail (source != NULL, TRUE);
1898
1899   return source->any_direction;
1900 }
1901
1902 /**
1903  * gtk_icon_source_set_direction:
1904  * @source: a #GtkIconSource
1905  * @direction: text direction this source applies to
1906  *
1907  * Sets the text direction this icon source is intended to be used
1908  * with.
1909  * 
1910  * Setting the text direction on an icon source makes no difference
1911  * if the text direction is wildcarded. Therefore, you should usually
1912  * call gtk_icon_source_set_direction_wildcarded() to un-wildcard it
1913  * in addition to calling this function.
1914  * 
1915  **/
1916 void
1917 gtk_icon_source_set_direction (GtkIconSource   *source,
1918                                GtkTextDirection direction)
1919 {
1920   g_return_if_fail (source != NULL);
1921
1922   source->direction = direction;
1923 }
1924
1925 /**
1926  * gtk_icon_source_set_state:
1927  * @source: a #GtkIconSource
1928  * @state: widget state this source applies to
1929  *
1930  * Sets the widget state this icon source is intended to be used
1931  * with.
1932  * 
1933  * Setting the widget state on an icon source makes no difference
1934  * if the state is wildcarded. Therefore, you should usually
1935  * call gtk_icon_source_set_state_wildcarded() to un-wildcard it
1936  * in addition to calling this function.
1937  * 
1938  **/
1939 void
1940 gtk_icon_source_set_state (GtkIconSource *source,
1941                            GtkStateType   state)
1942 {
1943   g_return_if_fail (source != NULL);
1944
1945   source->state = state;
1946 }
1947
1948 /**
1949  * gtk_icon_source_set_size:
1950  * @source: a #GtkIconSource
1951  * @size: icon size this source applies to
1952  *
1953  * Sets the icon size this icon source is intended to be used
1954  * with.
1955  * 
1956  * Setting the icon size on an icon source makes no difference
1957  * if the size is wildcarded. Therefore, you should usually
1958  * call gtk_icon_source_set_size_wildcarded() to un-wildcard it
1959  * in addition to calling this function.
1960  * 
1961  **/
1962 void
1963 gtk_icon_source_set_size (GtkIconSource *source,
1964                           GtkIconSize    size)
1965 {
1966   g_return_if_fail (source != NULL);
1967
1968   source->size = size;
1969 }
1970
1971 /**
1972  * gtk_icon_source_get_direction:
1973  * @source: a #GtkIconSource
1974  * 
1975  * Obtains the text direction this icon source applies to. The return
1976  * value is only useful/meaningful if the text direction is <emphasis>not</emphasis> 
1977  * wildcarded.
1978  * 
1979  * Return value: text direction this source matches
1980  **/
1981 GtkTextDirection
1982 gtk_icon_source_get_direction (const GtkIconSource *source)
1983 {
1984   g_return_val_if_fail (source != NULL, 0);
1985
1986   return source->direction;
1987 }
1988
1989 /**
1990  * gtk_icon_source_get_state:
1991  * @source: a #GtkIconSource
1992  * 
1993  * Obtains the widget state this icon source applies to. The return
1994  * value is only useful/meaningful if the widget state is <emphasis>not</emphasis>
1995  * wildcarded.
1996  * 
1997  * Return value: widget state this source matches
1998  **/
1999 GtkStateType
2000 gtk_icon_source_get_state (const GtkIconSource *source)
2001 {
2002   g_return_val_if_fail (source != NULL, 0);
2003
2004   return source->state;
2005 }
2006
2007 /**
2008  * gtk_icon_source_get_size:
2009  * @source: a #GtkIconSource
2010  * 
2011  * Obtains the icon size this source applies to. The return value
2012  * is only useful/meaningful if the icon size is <emphasis>not</emphasis> wildcarded.
2013  * 
2014  * Return value: icon size this source matches.
2015  **/
2016 GtkIconSize
2017 gtk_icon_source_get_size (const GtkIconSource *source)
2018 {
2019   g_return_val_if_fail (source != NULL, 0);
2020
2021   return source->size;
2022 }
2023
2024 /* Note that the logical maximum is 20 per GtkTextDirection, so we could
2025  * eventually set this to >20 to never throw anything out.
2026  */
2027 #define NUM_CACHED_ICONS 8
2028
2029 typedef struct _CachedIcon CachedIcon;
2030
2031 struct _CachedIcon
2032 {
2033   /* These must all match to use the cached pixbuf.
2034    * If any don't match, we must re-render the pixbuf.
2035    */
2036   GtkStyle *style;
2037   GtkTextDirection direction;
2038   GtkStateType state;
2039   GtkIconSize size;
2040
2041   GdkPixbuf *pixbuf;
2042 };
2043
2044 static void
2045 ensure_cache_up_to_date (GtkIconSet *icon_set)
2046 {
2047   if (icon_set->cache_serial != cache_serial)
2048     clear_cache (icon_set, TRUE);
2049 }
2050
2051 static void
2052 cached_icon_free (CachedIcon *icon)
2053 {
2054   g_object_unref (G_OBJECT (icon->pixbuf));
2055
2056   g_free (icon);
2057 }
2058
2059 static GdkPixbuf *
2060 find_in_cache (GtkIconSet      *icon_set,
2061                GtkStyle        *style,
2062                GtkTextDirection direction,
2063                GtkStateType     state,
2064                GtkIconSize      size)
2065 {
2066   GSList *tmp_list;
2067   GSList *prev;
2068
2069   ensure_cache_up_to_date (icon_set);
2070   
2071   prev = NULL;
2072   tmp_list = icon_set->cache;
2073   while (tmp_list != NULL)
2074     {
2075       CachedIcon *icon = tmp_list->data;
2076
2077       if (icon->style == style &&
2078           icon->direction == direction &&
2079           icon->state == state &&
2080           icon->size == size)
2081         {
2082           if (prev)
2083             {
2084               /* Move this icon to the front of the list. */
2085               prev->next = tmp_list->next;
2086               tmp_list->next = icon_set->cache;
2087               icon_set->cache = tmp_list;
2088             }
2089           
2090           return icon->pixbuf;
2091         }
2092           
2093       prev = tmp_list;
2094       tmp_list = g_slist_next (tmp_list);
2095     }
2096
2097   return NULL;
2098 }
2099
2100 static void
2101 add_to_cache (GtkIconSet      *icon_set,
2102               GtkStyle        *style,
2103               GtkTextDirection direction,
2104               GtkStateType     state,
2105               GtkIconSize      size,
2106               GdkPixbuf       *pixbuf)
2107 {
2108   CachedIcon *icon;
2109
2110   ensure_cache_up_to_date (icon_set);
2111   
2112   g_object_ref (G_OBJECT (pixbuf));
2113
2114   /* We have to ref the style, since if the style was finalized
2115    * its address could be reused by another style, creating a
2116    * really weird bug
2117    */
2118   
2119   if (style)
2120     g_object_ref (G_OBJECT (style));
2121   
2122
2123   icon = g_new (CachedIcon, 1);
2124   icon_set->cache = g_slist_prepend (icon_set->cache, icon);
2125
2126   icon->style = style;
2127   icon->direction = direction;
2128   icon->state = state;
2129   icon->size = size;
2130   icon->pixbuf = pixbuf;
2131
2132   if (icon->style)
2133     attach_to_style (icon_set, icon->style);
2134   
2135   if (icon_set->cache_size >= NUM_CACHED_ICONS)
2136     {
2137       /* Remove oldest item in the cache */
2138       
2139       GSList *tmp_list;
2140       
2141       tmp_list = icon_set->cache;
2142
2143       /* Find next-to-last link */
2144       g_assert (NUM_CACHED_ICONS > 2);
2145       while (tmp_list->next->next)
2146         tmp_list = tmp_list->next;
2147
2148       g_assert (tmp_list != NULL);
2149       g_assert (tmp_list->next != NULL);
2150       g_assert (tmp_list->next->next == NULL);
2151
2152       /* Free the last icon */
2153       icon = tmp_list->next->data;
2154
2155       g_slist_free (tmp_list->next);
2156       tmp_list->next = NULL;
2157
2158       cached_icon_free (icon);
2159     }
2160 }
2161
2162 static void
2163 clear_cache (GtkIconSet *icon_set,
2164              gboolean    style_detach)
2165 {
2166   GSList *tmp_list;
2167   GtkStyle *last_style = NULL;
2168
2169   tmp_list = icon_set->cache;
2170   while (tmp_list != NULL)
2171     {
2172       CachedIcon *icon = tmp_list->data;
2173
2174       if (style_detach)
2175         {
2176           /* simple optimization for the case where the cache
2177            * contains contiguous icons from the same style.
2178            * it's safe to call detach_from_style more than
2179            * once on the same style though.
2180            */
2181           if (last_style != icon->style)
2182             {
2183               detach_from_style (icon_set, icon->style);
2184               last_style = icon->style;
2185             }
2186         }
2187       
2188       cached_icon_free (icon);      
2189       
2190       tmp_list = g_slist_next (tmp_list);
2191     }
2192
2193   g_slist_free (icon_set->cache);
2194   icon_set->cache = NULL;
2195   icon_set->cache_size = 0;
2196 }
2197
2198 static GSList*
2199 copy_cache (GtkIconSet *icon_set,
2200             GtkIconSet *copy_recipient)
2201 {
2202   GSList *tmp_list;
2203   GSList *copy = NULL;
2204
2205   ensure_cache_up_to_date (icon_set);
2206   
2207   tmp_list = icon_set->cache;
2208   while (tmp_list != NULL)
2209     {
2210       CachedIcon *icon = tmp_list->data;
2211       CachedIcon *icon_copy = g_new (CachedIcon, 1);
2212
2213       *icon_copy = *icon;
2214
2215       if (icon_copy->style)
2216         attach_to_style (copy_recipient, icon_copy->style);
2217         
2218       g_object_ref (G_OBJECT (icon_copy->pixbuf));
2219
2220       icon_copy->size = icon->size;
2221       
2222       copy = g_slist_prepend (copy, icon_copy);      
2223       
2224       tmp_list = g_slist_next (tmp_list);
2225     }
2226
2227   return g_slist_reverse (copy);
2228 }
2229
2230 static void
2231 attach_to_style (GtkIconSet *icon_set,
2232                  GtkStyle   *style)
2233 {
2234   GHashTable *table;
2235
2236   table = g_object_get_qdata (G_OBJECT (style),
2237                               g_quark_try_string ("gtk-style-icon-sets"));
2238
2239   if (table == NULL)
2240     {
2241       table = g_hash_table_new (NULL, NULL);
2242       g_object_set_qdata_full (G_OBJECT (style),
2243                                g_quark_from_static_string ("gtk-style-icon-sets"),
2244                                table,
2245                                style_dnotify);
2246     }
2247
2248   g_hash_table_insert (table, icon_set, icon_set);
2249 }
2250
2251 static void
2252 detach_from_style (GtkIconSet *icon_set,
2253                    GtkStyle   *style)
2254 {
2255   GHashTable *table;
2256
2257   table = g_object_get_qdata (G_OBJECT (style),
2258                               g_quark_try_string ("gtk-style-icon-sets"));
2259
2260   if (table != NULL)
2261     g_hash_table_remove (table, icon_set);
2262 }
2263
2264 static void
2265 iconsets_foreach (gpointer key,
2266                   gpointer value,
2267                   gpointer user_data)
2268 {
2269   GtkIconSet *icon_set = key;
2270
2271   /* We only need to remove cache entries for the given style;
2272    * but that complicates things because in destroy notify
2273    * we don't know which style got destroyed, and 95% of the
2274    * time all cache entries will have the same style,
2275    * so this is faster anyway.
2276    */
2277   
2278   clear_cache (icon_set, FALSE);
2279 }
2280
2281 static void
2282 style_dnotify (gpointer data)
2283 {
2284   GHashTable *table = data;
2285   
2286   g_hash_table_foreach (table, iconsets_foreach, NULL);
2287
2288   g_hash_table_destroy (table);
2289 }
2290
2291 /* This allows the icon set to detect that its cache is out of date. */
2292 void
2293 _gtk_icon_set_invalidate_caches (void)
2294 {
2295   ++cache_serial;
2296 }
2297
2298 static void
2299 listify_foreach (gpointer key, gpointer value, gpointer data)
2300 {
2301   GSList **list = data;
2302
2303   *list = g_slist_prepend (*list, key);
2304 }
2305
2306 static GSList *
2307 g_hash_table_get_keys (GHashTable *table)
2308 {
2309   GSList *list = NULL;
2310
2311   g_hash_table_foreach (table, listify_foreach, &list);
2312
2313   return list;
2314 }
2315
2316 /**
2317  * _gtk_icon_factory_list_ids:
2318  * 
2319  * Gets all known IDs stored in an existing icon factory.
2320  * The strings in the returned list aren't copied.
2321  * The list itself should be freed.
2322  * 
2323  * Return value: List of ids in icon factories
2324  **/
2325 GSList*
2326 _gtk_icon_factory_list_ids (void)
2327 {
2328   GSList *tmp_list;
2329   GSList *ids;
2330
2331   ids = NULL;
2332
2333   ensure_default_icons ();
2334   
2335   tmp_list = all_icon_factories;
2336   while (tmp_list != NULL)
2337     {
2338       GSList *these_ids;
2339       
2340       GtkIconFactory *factory = GTK_ICON_FACTORY (tmp_list->data);
2341
2342       these_ids = g_hash_table_get_keys (factory->icons);
2343       
2344       ids = g_slist_concat (ids, these_ids);
2345       
2346       tmp_list = g_slist_next (tmp_list);
2347     }
2348
2349   return ids;
2350 }