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