]> Pileus Git - ~andy/gtk/blob - gtk/gtkiconfactory.c
Register all stock icons "unscaled" so that we don't have icons only
[~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 /**
1194  * gtk_icon_set_copy:
1195  * @icon_set: a #GtkIconSet
1196  * 
1197  * Copies @icon_set by value. 
1198  * 
1199  * Return value: a new #GtkIconSet identical to the first.
1200  **/
1201 GtkIconSet*
1202 gtk_icon_set_copy (GtkIconSet *icon_set)
1203 {
1204   GtkIconSet *copy;
1205   GSList *tmp_list;
1206   
1207   copy = gtk_icon_set_new ();
1208
1209   tmp_list = icon_set->sources;
1210   while (tmp_list != NULL)
1211     {
1212       copy->sources = g_slist_prepend (copy->sources,
1213                                        gtk_icon_source_copy (tmp_list->data));
1214
1215       tmp_list = g_slist_next (tmp_list);
1216     }
1217
1218   copy->sources = g_slist_reverse (copy->sources);
1219
1220   copy->cache = copy_cache (icon_set, copy);
1221   copy->cache_size = icon_set->cache_size;
1222   copy->cache_serial = icon_set->cache_serial;
1223   
1224   return copy;
1225 }
1226
1227
1228 static gboolean
1229 sizes_equivalent (GtkIconSize lhs,
1230                   GtkIconSize rhs)
1231 {
1232   gint r_w, r_h, l_w, l_h;
1233
1234   gtk_icon_size_lookup (rhs, &r_w, &r_h);
1235   gtk_icon_size_lookup (lhs, &l_w, &l_h);
1236
1237   return r_w == l_w && r_h == l_h;
1238 }
1239
1240 static GtkIconSource*
1241 find_and_prep_icon_source (GtkIconSet       *icon_set,
1242                            GtkTextDirection  direction,
1243                            GtkStateType      state,
1244                            GtkIconSize       size)
1245 {
1246   GtkIconSource *source;
1247   GSList *tmp_list;
1248
1249
1250   /* We need to find the best icon source.  Direction matters more
1251    * than state, state matters more than size. icon_set->sources
1252    * is sorted according to wildness, so if we take the first
1253    * match we find it will be the least-wild match (if there are
1254    * multiple matches for a given "wildness" then the RC file contained
1255    * dumb stuff, and we end up with an arbitrary matching source)
1256    */
1257   
1258   source = NULL;
1259   tmp_list = icon_set->sources;
1260   while (tmp_list != NULL)
1261     {
1262       GtkIconSource *s = tmp_list->data;
1263       
1264       if ((s->any_direction || (s->direction == direction)) &&
1265           (s->any_state || (s->state == state)) &&
1266           (s->any_size || (sizes_equivalent (size, s->size))))
1267         {
1268           source = s;
1269           break;
1270         }
1271       
1272       tmp_list = g_slist_next (tmp_list);
1273     }
1274
1275   if (source == NULL)
1276     return NULL;
1277   
1278   if (source->pixbuf == NULL)
1279     {
1280       GError *error = NULL;
1281       
1282       g_assert (source->filename);
1283       source->pixbuf = gdk_pixbuf_new_from_file (source->filename, &error);
1284
1285       if (source->pixbuf == NULL)
1286         {
1287           /* Remove this icon source so we don't keep trying to
1288            * load it.
1289            */
1290           g_warning (_("Error loading icon: %s"), error->message);
1291           g_error_free (error);
1292           
1293           icon_set->sources = g_slist_remove (icon_set->sources, source);
1294
1295           gtk_icon_source_free (source);
1296
1297           /* Try to fall back to other sources */
1298           if (icon_set->sources != NULL)
1299             return find_and_prep_icon_source (icon_set,
1300                                               direction,
1301                                               state,
1302                                               size);
1303           else
1304             return NULL;
1305         }
1306     }
1307
1308   return source;
1309 }
1310
1311 static GdkPixbuf*
1312 render_fallback_image (GtkStyle          *style,
1313                        GtkTextDirection   direction,
1314                        GtkStateType       state,
1315                        GtkIconSize        size,
1316                        GtkWidget         *widget,
1317                        const char        *detail)
1318 {
1319   /* This icon can be used for any direction/state/size */
1320   static GtkIconSource fallback_source = { NULL, NULL, 0, 0, 0, TRUE, TRUE, TRUE };
1321
1322   if (fallback_source.pixbuf == NULL)
1323     fallback_source.pixbuf = gdk_pixbuf_new_from_inline (-1, stock_missing_image_24, FALSE, NULL);
1324   
1325   return gtk_style_render_icon (style,
1326                                 &fallback_source,
1327                                 direction,
1328                                 state,
1329                                 size,
1330                                 widget,
1331                                 detail);
1332 }
1333
1334 /**
1335  * gtk_icon_set_render_icon:
1336  * @icon_set: a #GtkIconSet
1337  * @style: a #GtkStyle associated with @widget, or %NULL
1338  * @direction: text direction
1339  * @state: widget state
1340  * @size: icon size
1341  * @widget: widget that will display the icon, or %NULL
1342  * @detail: detail to pass to the theme engine, or %NULL
1343  * 
1344  * Renders an icon using gtk_style_render_icon(). In most cases,
1345  * gtk_widget_render_icon() is better, since it automatically provides
1346  * most of the arguments from the current widget settings.  This
1347  * function never returns %NULL; if the icon can't be rendered
1348  * (perhaps because an image file fails to load), a default "missing
1349  * image" icon will be returned instead.
1350  * 
1351  * Return value: a #GdkPixbuf to be displayed
1352  **/
1353 GdkPixbuf*
1354 gtk_icon_set_render_icon (GtkIconSet        *icon_set,
1355                           GtkStyle          *style,
1356                           GtkTextDirection   direction,
1357                           GtkStateType       state,
1358                           GtkIconSize        size,
1359                           GtkWidget         *widget,
1360                           const char        *detail)
1361 {
1362   GdkPixbuf *icon;
1363   GtkIconSource *source;
1364   
1365   g_return_val_if_fail (icon_set != NULL, NULL);
1366   g_return_val_if_fail (GTK_IS_STYLE (style), NULL);
1367
1368   if (icon_set->sources == NULL)
1369     return render_fallback_image (style, direction, state, size, widget, detail);
1370   
1371   icon = find_in_cache (icon_set, style, direction,
1372                         state, size);
1373
1374   if (icon)
1375     {
1376       g_object_ref (G_OBJECT (icon));
1377       return icon;
1378     }
1379
1380   
1381   source = find_and_prep_icon_source (icon_set, direction, state, size);
1382
1383   if (source == NULL)
1384     return render_fallback_image (style, direction, state, size, widget, detail);
1385
1386   g_assert (source->pixbuf != NULL);
1387   
1388   icon = gtk_style_render_icon (style,
1389                                 source,
1390                                 direction,
1391                                 state,
1392                                 size,
1393                                 widget,
1394                                 detail);
1395
1396   if (icon == NULL)
1397     {
1398       g_warning ("Theme engine failed to render icon");
1399       return NULL;
1400     }
1401   
1402   add_to_cache (icon_set, style, direction, state, size, icon);
1403   
1404   return icon;
1405 }
1406
1407 /* Order sources by their "wildness", so that "wilder" sources are
1408  * greater than "specific" sources; for determining ordering,
1409  * direction beats state beats size.
1410  */
1411
1412 static int
1413 icon_source_compare (gconstpointer ap, gconstpointer bp)
1414 {
1415   const GtkIconSource *a = ap;
1416   const GtkIconSource *b = bp;
1417
1418   if (!a->any_direction && b->any_direction)
1419     return -1;
1420   else if (a->any_direction && !b->any_direction)
1421     return 1;
1422   else if (!a->any_state && b->any_state)
1423     return -1;
1424   else if (a->any_state && !b->any_state)
1425     return 1;
1426   else if (!a->any_size && b->any_size)
1427     return -1;
1428   else if (a->any_size && !b->any_size)
1429     return 1;
1430   else
1431     return 0;
1432 }
1433
1434 /**
1435  * gtk_icon_set_add_source:
1436  * @icon_set: a #GtkIconSet
1437  * @source: a #GtkIconSource
1438  *
1439  * Icon sets have a list of #GtkIconSource, which they use as base
1440  * icons for rendering icons in different states and sizes. Icons are
1441  * scaled, made to look insensitive, etc. in
1442  * gtk_icon_set_render_icon(), but #GtkIconSet needs base images to
1443  * work with. The base images and when to use them are described by
1444  * a #GtkIconSource.
1445  * 
1446  * This function copies @source, so you can reuse the same source immediately
1447  * without affecting the icon set.
1448  *
1449  * An example of when you'd use this function: a web browser's "Back
1450  * to Previous Page" icon might point in a different direction in
1451  * Hebrew and in English; it might look different when insensitive;
1452  * and it might change size depending on toolbar mode (small/large
1453  * icons). So a single icon set would contain all those variants of
1454  * the icon, and you might add a separate source for each one.
1455  *
1456  * You should nearly always add a "default" icon source with all
1457  * fields wildcarded, which will be used as a fallback if no more
1458  * specific source matches. #GtkIconSet always prefers more specific
1459  * icon sources to more generic icon sources. The order in which you
1460  * add the sources to the icon set does not matter.
1461  *
1462  * gtk_icon_set_new_from_pixbuf() creates a new icon set with a
1463  * default icon source based on the given pixbuf.
1464  * 
1465  **/
1466 void
1467 gtk_icon_set_add_source (GtkIconSet *icon_set,
1468                          const GtkIconSource *source)
1469 {
1470   g_return_if_fail (icon_set != NULL);
1471   g_return_if_fail (source != NULL);
1472
1473   if (source->pixbuf == NULL &&
1474       source->filename == NULL)
1475     {
1476       g_warning ("Useless GtkIconSource contains NULL filename and pixbuf");
1477       return;
1478     }
1479   
1480   icon_set->sources = g_slist_insert_sorted (icon_set->sources,
1481                                              gtk_icon_source_copy (source),
1482                                              icon_source_compare);
1483 }
1484
1485 /**
1486  * gtk_icon_set_get_sizes:
1487  * @icon_set: a #GtkIconSet
1488  * @sizes: return location for array of sizes
1489  * @n_sizes: location to store number of elements in returned array
1490  *
1491  * Obtains a list of icon sizes this icon set can render. The returned
1492  * array must be freed with g_free().
1493  * 
1494  **/
1495 void
1496 gtk_icon_set_get_sizes (GtkIconSet   *icon_set,
1497                         GtkIconSize **sizes,
1498                         gint         *n_sizes)
1499 {
1500   GSList *tmp_list;
1501   gboolean all_sizes = FALSE;
1502   GSList *specifics = NULL;
1503   
1504   g_return_if_fail (icon_set != NULL);
1505   g_return_if_fail (sizes != NULL);
1506   g_return_if_fail (n_sizes != NULL);
1507   
1508   tmp_list = icon_set->sources;
1509   while (tmp_list != NULL)
1510     {
1511       GtkIconSource *source;
1512
1513       source = tmp_list->data;
1514
1515       if (source->any_size)
1516         {
1517           all_sizes = TRUE;
1518           break;
1519         }
1520       else
1521         specifics = g_slist_prepend (specifics, GINT_TO_POINTER (source->size));
1522       
1523       tmp_list = g_slist_next (tmp_list);
1524     }
1525
1526   if (all_sizes)
1527     {
1528       /* Need to find out what sizes exist */
1529       gint i;
1530
1531       init_icon_sizes ();
1532       
1533       *sizes = g_new (GtkIconSize, icon_sizes_used);
1534       *n_sizes = icon_sizes_used - 1;
1535       
1536       i = 1;      
1537       while (i < icon_sizes_used)
1538         {
1539           (*sizes)[i - 1] = icon_sizes[i].size;
1540           ++i;
1541         }
1542     }
1543   else
1544     {
1545       gint i;
1546       
1547       *n_sizes = g_slist_length (specifics);
1548       *sizes = g_new (GtkIconSize, *n_sizes);
1549
1550       i = 0;
1551       tmp_list = specifics;
1552       while (tmp_list != NULL)
1553         {
1554           (*sizes)[i] = GPOINTER_TO_INT (tmp_list->data);
1555
1556           ++i;
1557           tmp_list = g_slist_next (tmp_list);
1558         }
1559     }
1560
1561   g_slist_free (specifics);
1562 }
1563
1564
1565 /**
1566  * gtk_icon_source_new:
1567  * 
1568  * Creates a new #GtkIconSource. A #GtkIconSource contains a #GdkPixbuf (or
1569  * image filename) that serves as the base image for one or more of the
1570  * icons in a #GtkIconSet, along with a specification for which icons in the
1571  * icon set will be based on that pixbuf or image file. An icon set contains
1572  * a set of icons that represent "the same" logical concept in different states,
1573  * different global text directions, and different sizes.
1574  * 
1575  * So for example a web browser's "Back to Previous Page" icon might
1576  * point in a different direction in Hebrew and in English; it might
1577  * look different when insensitive; and it might change size depending
1578  * on toolbar mode (small/large icons). So a single icon set would
1579  * contain all those variants of the icon. #GtkIconSet contains a list
1580  * of #GtkIconSource from which it can derive specific icon variants in
1581  * the set. 
1582  *
1583  * In the simplest case, #GtkIconSet contains one source pixbuf from
1584  * which it derives all variants. The convenience function
1585  * gtk_icon_set_new_from_pixbuf() handles this case; if you only have
1586  * one source pixbuf, just use that function.
1587  *
1588  * If you want to use a different base pixbuf for different icon
1589  * variants, you create multiple icon sources, mark which variants
1590  * they'll be used to create, and add them to the icon set with
1591  * gtk_icon_set_add_source().
1592  *
1593  * By default, the icon source has all parameters wildcarded. That is,
1594  * the icon source will be used as the base icon for any desired text
1595  * direction, widget state, or icon size.
1596  * 
1597  * Return value: a new #GtkIconSource
1598  **/
1599 GtkIconSource*
1600 gtk_icon_source_new (void)
1601 {
1602   GtkIconSource *src;
1603   
1604   src = g_new0 (GtkIconSource, 1);
1605
1606   src->direction = GTK_TEXT_DIR_NONE;
1607   src->size = GTK_ICON_SIZE_INVALID;
1608   src->state = GTK_STATE_NORMAL;
1609   
1610   src->any_direction = TRUE;
1611   src->any_state = TRUE;
1612   src->any_size = TRUE;
1613   
1614   return src;
1615 }
1616
1617 /**
1618  * gtk_icon_source_copy:
1619  * @source: a #GtkIconSource
1620  * 
1621  * Creates a copy of @source; mostly useful for language bindings.
1622  * 
1623  * Return value: a new #GtkIconSource
1624  **/
1625 GtkIconSource*
1626 gtk_icon_source_copy (const GtkIconSource *source)
1627 {
1628   GtkIconSource *copy;
1629   
1630   g_return_val_if_fail (source != NULL, NULL);
1631
1632   copy = g_new (GtkIconSource, 1);
1633
1634   *copy = *source;
1635   
1636   copy->filename = g_strdup (source->filename);
1637   copy->size = source->size;
1638   if (copy->pixbuf)
1639     g_object_ref (G_OBJECT (copy->pixbuf));
1640
1641   return copy;
1642 }
1643
1644 /**
1645  * gtk_icon_source_free:
1646  * @source: a #GtkIconSource
1647  * 
1648  * Frees a dynamically-allocated icon source, along with its
1649  * filename, size, and pixbuf fields if those are not %NULL.
1650  **/
1651 void
1652 gtk_icon_source_free (GtkIconSource *source)
1653 {
1654   g_return_if_fail (source != NULL);
1655
1656   g_free ((char*) source->filename);
1657   if (source->pixbuf)
1658     g_object_unref (G_OBJECT (source->pixbuf));
1659
1660   g_free (source);
1661 }
1662
1663 /**
1664  * gtk_icon_source_set_filename:
1665  * @source: a #GtkIconSource
1666  * @filename: image file to use
1667  *
1668  * Sets the name of an image file to use as a base image when creating
1669  * icon variants for #GtkIconSet. The filename must be absolute. 
1670  **/
1671 void
1672 gtk_icon_source_set_filename (GtkIconSource *source,
1673                               const gchar   *filename)
1674 {
1675   g_return_if_fail (source != NULL);
1676   g_return_if_fail (filename == NULL || g_path_is_absolute (filename));
1677
1678   if (source->filename == filename)
1679     return;
1680   
1681   if (source->filename)
1682     g_free (source->filename);
1683
1684   source->filename = g_strdup (filename);  
1685 }
1686
1687 /**
1688  * gtk_icon_source_set_pixbuf:
1689  * @source: a #GtkIconSource
1690  * @pixbuf: pixbuf to use as a source
1691  *
1692  * Sets a pixbuf to use as a base image when creating icon variants
1693  * for #GtkIconSet. If an icon source has both a filename and a pixbuf
1694  * set, the pixbuf will take priority.
1695  * 
1696  **/
1697 void
1698 gtk_icon_source_set_pixbuf (GtkIconSource *source,
1699                             GdkPixbuf     *pixbuf)
1700 {
1701   g_return_if_fail (source != NULL);
1702
1703   if (pixbuf)
1704     g_object_ref (G_OBJECT (pixbuf));
1705
1706   if (source->pixbuf)
1707     g_object_unref (G_OBJECT (source->pixbuf));
1708
1709   source->pixbuf = pixbuf;
1710 }
1711
1712 /**
1713  * gtk_icon_source_get_filename:
1714  * @source: a #GtkIconSource
1715  * 
1716  * Retrieves the source filename, or %NULL if none is set.  The
1717  * filename is not a copy, and should not be modified or expected to
1718  * persist beyond the lifetime of the icon source.
1719  * 
1720  * Return value: image filename
1721  **/
1722 G_CONST_RETURN gchar*
1723 gtk_icon_source_get_filename (const GtkIconSource *source)
1724 {
1725   g_return_val_if_fail (source != NULL, NULL);
1726   
1727   return source->filename;
1728 }
1729
1730 /**
1731  * gtk_icon_source_get_pixbuf:
1732  * @source: a #GtkIconSource
1733  * 
1734  * Retrieves the source pixbuf, or %NULL if none is set.
1735  * The reference count on the pixbuf is not incremented.
1736  * 
1737  * Return value: source pixbuf
1738  **/
1739 GdkPixbuf*
1740 gtk_icon_source_get_pixbuf (const GtkIconSource *source)
1741 {
1742   g_return_val_if_fail (source != NULL, NULL);
1743   
1744   return source->pixbuf;
1745 }
1746
1747 /**
1748  * gtk_icon_source_set_direction_wildcarded:
1749  * @source: a #GtkIconSource
1750  * @setting: %TRUE to wildcard the text direction
1751  *
1752  * If the text direction is wildcarded, this source can be used
1753  * as the base image for an icon in any #GtkTextDirection.
1754  * If the text direction is not wildcarded, then the
1755  * text direction the icon source applies to should be set
1756  * with gtk_icon_source_set_direction(), and the icon source
1757  * will only be used with that text direction.
1758  *
1759  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
1760  * wildcarded sources, and will use an exact match when possible.
1761  * 
1762  **/
1763 void
1764 gtk_icon_source_set_direction_wildcarded (GtkIconSource *source,
1765                                           gboolean       setting)
1766 {
1767   g_return_if_fail (source != NULL);
1768
1769   source->any_direction = setting != FALSE;
1770 }
1771
1772 /**
1773  * gtk_icon_source_set_state_wildcarded:
1774  * @source: a #GtkIconSource
1775  * @setting: %TRUE to wildcard the widget state
1776  *
1777  * If the widget state is wildcarded, this source can be used as the
1778  * base image for an icon in any #GtkStateType.  If the widget state
1779  * is not wildcarded, then the state the source applies to should be
1780  * set with gtk_icon_source_set_state() and the icon source will
1781  * only be used with that specific state.
1782  *
1783  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
1784  * wildcarded sources, and will use an exact match when possible.
1785  *
1786  * #GtkIconSet will normally transform wildcarded source images to
1787  * produce an appropriate icon for a given state, for example
1788  * lightening an image on prelight, but will not modify source images
1789  * that match exactly.
1790  **/
1791 void
1792 gtk_icon_source_set_state_wildcarded (GtkIconSource *source,
1793                                       gboolean       setting)
1794 {
1795   g_return_if_fail (source != NULL);
1796
1797   source->any_state = setting != FALSE;
1798 }
1799
1800
1801 /**
1802  * gtk_icon_source_set_size_wildcarded:
1803  * @source: a #GtkIconSource
1804  * @setting: %TRUE to wildcard the widget state
1805  *
1806  * If the icon size is wildcarded, this source can be used as the base
1807  * image for an icon of any size.  If the size is not wildcarded, then
1808  * the size the source applies to should be set with
1809  * gtk_icon_source_set_size() and the icon source will only be used
1810  * with that specific size.
1811  *
1812  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
1813  * wildcarded sources, and will use an exact match when possible.
1814  *
1815  * #GtkIconSet will normally scale wildcarded source images to produce
1816  * an appropriate icon at a given size, but will not change the size
1817  * of source images that match exactly.
1818  **/
1819 void
1820 gtk_icon_source_set_size_wildcarded (GtkIconSource *source,
1821                                      gboolean       setting)
1822 {
1823   g_return_if_fail (source != NULL);
1824
1825   source->any_size = setting != FALSE;  
1826 }
1827
1828 /**
1829  * gtk_icon_source_get_size_wildcarded:
1830  * @source: a #GtkIconSource
1831  * 
1832  * Gets the value set by gtk_icon_source_set_size_wildcarded().
1833  * 
1834  * Return value: %TRUE if this icon source is a base for any icon size variant
1835  **/
1836 gboolean
1837 gtk_icon_source_get_size_wildcarded (const GtkIconSource *source)
1838 {
1839   g_return_val_if_fail (source != NULL, TRUE);
1840   
1841   return source->any_size;
1842 }
1843
1844 /**
1845  * gtk_icon_source_get_state_wildcarded:
1846  * @source: a #GtkIconSource
1847  * 
1848  * Gets the value set by gtk_icon_source_set_state_wildcarded().
1849  * 
1850  * Return value: %TRUE if this icon source is a base for any widget state variant
1851  **/
1852 gboolean
1853 gtk_icon_source_get_state_wildcarded (const GtkIconSource *source)
1854 {
1855   g_return_val_if_fail (source != NULL, TRUE);
1856
1857   return source->any_state;
1858 }
1859
1860 /**
1861  * gtk_icon_source_get_direction_wildcarded:
1862  * @source: a #GtkIconSource
1863  * 
1864  * Gets the value set by gtk_icon_source_set_direction_wildcarded().
1865  * 
1866  * Return value: %TRUE if this icon source is a base for any text direction variant
1867  **/
1868 gboolean
1869 gtk_icon_source_get_direction_wildcarded (const GtkIconSource *source)
1870 {
1871   g_return_val_if_fail (source != NULL, TRUE);
1872
1873   return source->any_direction;
1874 }
1875
1876 /**
1877  * gtk_icon_source_set_direction:
1878  * @source: a #GtkIconSource
1879  * @direction: text direction this source applies to
1880  *
1881  * Sets the text direction this icon source is intended to be used
1882  * with.
1883  * 
1884  * Setting the text direction on an icon source makes no difference
1885  * if the text direction is wildcarded. Therefore, you should usually
1886  * call gtk_icon_source_set_direction_wildcarded() to un-wildcard it
1887  * in addition to calling this function.
1888  * 
1889  **/
1890 void
1891 gtk_icon_source_set_direction (GtkIconSource   *source,
1892                                GtkTextDirection direction)
1893 {
1894   g_return_if_fail (source != NULL);
1895
1896   source->direction = direction;
1897 }
1898
1899 /**
1900  * gtk_icon_source_set_state:
1901  * @source: a #GtkIconSource
1902  * @state: widget state this source applies to
1903  *
1904  * Sets the widget state this icon source is intended to be used
1905  * with.
1906  * 
1907  * Setting the widget state on an icon source makes no difference
1908  * if the state is wildcarded. Therefore, you should usually
1909  * call gtk_icon_source_set_state_wildcarded() to un-wildcard it
1910  * in addition to calling this function.
1911  * 
1912  **/
1913 void
1914 gtk_icon_source_set_state (GtkIconSource *source,
1915                            GtkStateType   state)
1916 {
1917   g_return_if_fail (source != NULL);
1918
1919   source->state = state;
1920 }
1921
1922 /**
1923  * gtk_icon_source_set_size:
1924  * @source: a #GtkIconSource
1925  * @size: icon size this source applies to
1926  *
1927  * Sets the icon size this icon source is intended to be used
1928  * with.
1929  * 
1930  * Setting the icon size on an icon source makes no difference
1931  * if the size is wildcarded. Therefore, you should usually
1932  * call gtk_icon_source_set_size_wildcarded() to un-wildcard it
1933  * in addition to calling this function.
1934  * 
1935  **/
1936 void
1937 gtk_icon_source_set_size (GtkIconSource *source,
1938                           GtkIconSize    size)
1939 {
1940   g_return_if_fail (source != NULL);
1941
1942   source->size = size;
1943 }
1944
1945 /**
1946  * gtk_icon_source_get_direction:
1947  * @source: a #GtkIconSource
1948  * 
1949  * Obtains the text direction this icon source applies to. The return
1950  * value is only useful/meaningful if the text direction is <emphasis>not</emphasis> 
1951  * wildcarded.
1952  * 
1953  * Return value: text direction this source matches
1954  **/
1955 GtkTextDirection
1956 gtk_icon_source_get_direction (const GtkIconSource *source)
1957 {
1958   g_return_val_if_fail (source != NULL, 0);
1959
1960   return source->direction;
1961 }
1962
1963 /**
1964  * gtk_icon_source_get_state:
1965  * @source: a #GtkIconSource
1966  * 
1967  * Obtains the widget state this icon source applies to. The return
1968  * value is only useful/meaningful if the widget state is <emphasis>not</emphasis>
1969  * wildcarded.
1970  * 
1971  * Return value: widget state this source matches
1972  **/
1973 GtkStateType
1974 gtk_icon_source_get_state (const GtkIconSource *source)
1975 {
1976   g_return_val_if_fail (source != NULL, 0);
1977
1978   return source->state;
1979 }
1980
1981 /**
1982  * gtk_icon_source_get_size:
1983  * @source: a #GtkIconSource
1984  * 
1985  * Obtains the icon size this source applies to. The return value
1986  * is only useful/meaningful if the icon size is <emphasis>not</emphasis> wildcarded.
1987  * 
1988  * Return value: icon size this source matches.
1989  **/
1990 GtkIconSize
1991 gtk_icon_source_get_size (const GtkIconSource *source)
1992 {
1993   g_return_val_if_fail (source != NULL, 0);
1994
1995   return source->size;
1996 }
1997
1998 /* Note that the logical maximum is 20 per GtkTextDirection, so we could
1999  * eventually set this to >20 to never throw anything out.
2000  */
2001 #define NUM_CACHED_ICONS 8
2002
2003 typedef struct _CachedIcon CachedIcon;
2004
2005 struct _CachedIcon
2006 {
2007   /* These must all match to use the cached pixbuf.
2008    * If any don't match, we must re-render the pixbuf.
2009    */
2010   GtkStyle *style;
2011   GtkTextDirection direction;
2012   GtkStateType state;
2013   GtkIconSize size;
2014
2015   GdkPixbuf *pixbuf;
2016 };
2017
2018 static void
2019 ensure_cache_up_to_date (GtkIconSet *icon_set)
2020 {
2021   if (icon_set->cache_serial != cache_serial)
2022     clear_cache (icon_set, TRUE);
2023 }
2024
2025 static void
2026 cached_icon_free (CachedIcon *icon)
2027 {
2028   g_object_unref (G_OBJECT (icon->pixbuf));
2029
2030   g_free (icon);
2031 }
2032
2033 static GdkPixbuf *
2034 find_in_cache (GtkIconSet      *icon_set,
2035                GtkStyle        *style,
2036                GtkTextDirection direction,
2037                GtkStateType     state,
2038                GtkIconSize      size)
2039 {
2040   GSList *tmp_list;
2041   GSList *prev;
2042
2043   ensure_cache_up_to_date (icon_set);
2044   
2045   prev = NULL;
2046   tmp_list = icon_set->cache;
2047   while (tmp_list != NULL)
2048     {
2049       CachedIcon *icon = tmp_list->data;
2050
2051       if (icon->style == style &&
2052           icon->direction == direction &&
2053           icon->state == state &&
2054           icon->size == size)
2055         {
2056           if (prev)
2057             {
2058               /* Move this icon to the front of the list. */
2059               prev->next = tmp_list->next;
2060               tmp_list->next = icon_set->cache;
2061               icon_set->cache = tmp_list;
2062             }
2063           
2064           return icon->pixbuf;
2065         }
2066           
2067       prev = tmp_list;
2068       tmp_list = g_slist_next (tmp_list);
2069     }
2070
2071   return NULL;
2072 }
2073
2074 static void
2075 add_to_cache (GtkIconSet      *icon_set,
2076               GtkStyle        *style,
2077               GtkTextDirection direction,
2078               GtkStateType     state,
2079               GtkIconSize      size,
2080               GdkPixbuf       *pixbuf)
2081 {
2082   CachedIcon *icon;
2083
2084   ensure_cache_up_to_date (icon_set);
2085   
2086   g_object_ref (G_OBJECT (pixbuf));
2087
2088   /* We have to ref the style, since if the style was finalized
2089    * its address could be reused by another style, creating a
2090    * really weird bug
2091    */
2092   
2093   if (style)
2094     g_object_ref (G_OBJECT (style));
2095   
2096
2097   icon = g_new (CachedIcon, 1);
2098   icon_set->cache = g_slist_prepend (icon_set->cache, icon);
2099
2100   icon->style = style;
2101   icon->direction = direction;
2102   icon->state = state;
2103   icon->size = size;
2104   icon->pixbuf = pixbuf;
2105
2106   if (icon->style)
2107     attach_to_style (icon_set, icon->style);
2108   
2109   if (icon_set->cache_size >= NUM_CACHED_ICONS)
2110     {
2111       /* Remove oldest item in the cache */
2112       
2113       GSList *tmp_list;
2114       
2115       tmp_list = icon_set->cache;
2116
2117       /* Find next-to-last link */
2118       g_assert (NUM_CACHED_ICONS > 2);
2119       while (tmp_list->next->next)
2120         tmp_list = tmp_list->next;
2121
2122       g_assert (tmp_list != NULL);
2123       g_assert (tmp_list->next != NULL);
2124       g_assert (tmp_list->next->next == NULL);
2125
2126       /* Free the last icon */
2127       icon = tmp_list->next->data;
2128
2129       g_slist_free (tmp_list->next);
2130       tmp_list->next = NULL;
2131
2132       cached_icon_free (icon);
2133     }
2134 }
2135
2136 static void
2137 clear_cache (GtkIconSet *icon_set,
2138              gboolean    style_detach)
2139 {
2140   GSList *tmp_list;
2141   GtkStyle *last_style = NULL;
2142
2143   tmp_list = icon_set->cache;
2144   while (tmp_list != NULL)
2145     {
2146       CachedIcon *icon = tmp_list->data;
2147
2148       if (style_detach)
2149         {
2150           /* simple optimization for the case where the cache
2151            * contains contiguous icons from the same style.
2152            * it's safe to call detach_from_style more than
2153            * once on the same style though.
2154            */
2155           if (last_style != icon->style)
2156             {
2157               detach_from_style (icon_set, icon->style);
2158               last_style = icon->style;
2159             }
2160         }
2161       
2162       cached_icon_free (icon);      
2163       
2164       tmp_list = g_slist_next (tmp_list);
2165     }
2166
2167   g_slist_free (icon_set->cache);
2168   icon_set->cache = NULL;
2169   icon_set->cache_size = 0;
2170 }
2171
2172 static GSList*
2173 copy_cache (GtkIconSet *icon_set,
2174             GtkIconSet *copy_recipient)
2175 {
2176   GSList *tmp_list;
2177   GSList *copy = NULL;
2178
2179   ensure_cache_up_to_date (icon_set);
2180   
2181   tmp_list = icon_set->cache;
2182   while (tmp_list != NULL)
2183     {
2184       CachedIcon *icon = tmp_list->data;
2185       CachedIcon *icon_copy = g_new (CachedIcon, 1);
2186
2187       *icon_copy = *icon;
2188
2189       if (icon_copy->style)
2190         attach_to_style (copy_recipient, icon_copy->style);
2191         
2192       g_object_ref (G_OBJECT (icon_copy->pixbuf));
2193
2194       icon_copy->size = icon->size;
2195       
2196       copy = g_slist_prepend (copy, icon_copy);      
2197       
2198       tmp_list = g_slist_next (tmp_list);
2199     }
2200
2201   return g_slist_reverse (copy);
2202 }
2203
2204 static void
2205 attach_to_style (GtkIconSet *icon_set,
2206                  GtkStyle   *style)
2207 {
2208   GHashTable *table;
2209
2210   table = g_object_get_qdata (G_OBJECT (style),
2211                               g_quark_try_string ("gtk-style-icon-sets"));
2212
2213   if (table == NULL)
2214     {
2215       table = g_hash_table_new (NULL, NULL);
2216       g_object_set_qdata_full (G_OBJECT (style),
2217                                g_quark_from_static_string ("gtk-style-icon-sets"),
2218                                table,
2219                                style_dnotify);
2220     }
2221
2222   g_hash_table_insert (table, icon_set, icon_set);
2223 }
2224
2225 static void
2226 detach_from_style (GtkIconSet *icon_set,
2227                    GtkStyle   *style)
2228 {
2229   GHashTable *table;
2230
2231   table = g_object_get_qdata (G_OBJECT (style),
2232                               g_quark_try_string ("gtk-style-icon-sets"));
2233
2234   if (table != NULL)
2235     g_hash_table_remove (table, icon_set);
2236 }
2237
2238 static void
2239 iconsets_foreach (gpointer key,
2240                   gpointer value,
2241                   gpointer user_data)
2242 {
2243   GtkIconSet *icon_set = key;
2244
2245   /* We only need to remove cache entries for the given style;
2246    * but that complicates things because in destroy notify
2247    * we don't know which style got destroyed, and 95% of the
2248    * time all cache entries will have the same style,
2249    * so this is faster anyway.
2250    */
2251   
2252   clear_cache (icon_set, FALSE);
2253 }
2254
2255 static void
2256 style_dnotify (gpointer data)
2257 {
2258   GHashTable *table = data;
2259   
2260   g_hash_table_foreach (table, iconsets_foreach, NULL);
2261
2262   g_hash_table_destroy (table);
2263 }
2264
2265 /* This allows the icon set to detect that its cache is out of date. */
2266 void
2267 _gtk_icon_set_invalidate_caches (void)
2268 {
2269   ++cache_serial;
2270 }
2271
2272 static void
2273 listify_foreach (gpointer key, gpointer value, gpointer data)
2274 {
2275   GSList **list = data;
2276
2277   *list = g_slist_prepend (*list, key);
2278 }
2279
2280 static GSList *
2281 g_hash_table_get_keys (GHashTable *table)
2282 {
2283   GSList *list = NULL;
2284
2285   g_hash_table_foreach (table, listify_foreach, &list);
2286
2287   return list;
2288 }
2289
2290 /**
2291  * _gtk_icon_factory_list_ids:
2292  * 
2293  * Gets all known IDs stored in an existing icon factory.
2294  * The strings in the returned list aren't copied.
2295  * The list itself should be freed.
2296  * 
2297  * Return value: List of ids in icon factories
2298  **/
2299 GSList*
2300 _gtk_icon_factory_list_ids (void)
2301 {
2302   GSList *tmp_list;
2303   GSList *ids;
2304
2305   ids = NULL;
2306
2307   ensure_default_icons ();
2308   
2309   tmp_list = all_icon_factories;
2310   while (tmp_list != NULL)
2311     {
2312       GSList *these_ids;
2313       
2314       GtkIconFactory *factory = GTK_ICON_FACTORY (tmp_list->data);
2315
2316       these_ids = g_hash_table_get_keys (factory->icons);
2317       
2318       ids = g_slist_concat (ids, these_ids);
2319       
2320       tmp_list = g_slist_next (tmp_list);
2321     }
2322
2323   return ids;
2324 }