]> Pileus Git - ~andy/gtk/blob - gtk/gtkiconfactory.c
Document 2.2 API additions.
[~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  * Since: 2.2
1217  **/
1218 gboolean
1219 gtk_icon_size_lookup_for_settings (GtkSettings *settings,
1220                                    GtkIconSize  size,
1221                                    gint        *width,
1222                                    gint        *height)
1223 {
1224   g_return_val_if_fail (GTK_IS_SETTINGS (settings), FALSE);
1225
1226   return icon_size_lookup_intern (settings, size, width, height);
1227 }
1228
1229 /**
1230  * gtk_icon_size_lookup:
1231  * @size: an icon size
1232  * @width: location to store icon width
1233  * @height: location to store icon height
1234  *
1235  * Obtains the pixel size of a semantic icon size, possibly
1236  * modified by user preferences for the default #GtkSettings.
1237  * (See gtk_icon_size_lookup_for_settings().)
1238  * Normally @size would be
1239  * #GTK_ICON_SIZE_MENU, #GTK_ICON_SIZE_BUTTON, etc.  This function
1240  * isn't normally needed, gtk_widget_render_icon() is the usual
1241  * way to get an icon for rendering, then just look at the size of
1242  * the rendered pixbuf. The rendered pixbuf may not even correspond to
1243  * the width/height returned by gtk_icon_size_lookup(), because themes
1244  * are free to render the pixbuf however they like, including changing
1245  * the usual size.
1246  * 
1247  * Return value: %TRUE if @size was a valid size
1248  **/
1249 gboolean
1250 gtk_icon_size_lookup (GtkIconSize  size,
1251                       gint        *widthp,
1252                       gint        *heightp)
1253 {
1254   GTK_NOTE (MULTIHEAD,
1255             g_warning ("gtk_icon_size_lookup ()) is not multihead safe"));
1256
1257   return gtk_icon_size_lookup_for_settings (gtk_settings_get_default (),
1258                                             size, widthp, heightp);
1259 }
1260
1261 static GtkIconSize
1262 icon_size_register_intern (const gchar *name,
1263                            gint         width,
1264                            gint         height)
1265 {
1266   IconAlias *old_alias;
1267   GtkIconSize size;
1268   
1269   init_icon_sizes ();
1270
1271   old_alias = g_hash_table_lookup (icon_aliases, name);
1272   if (old_alias && icon_sizes[old_alias->target].width > 0)
1273     {
1274       g_warning ("Icon size name '%s' already exists", name);
1275       return GTK_ICON_SIZE_INVALID;
1276     }
1277
1278   if (old_alias)
1279     {
1280       size = old_alias->target;
1281     }
1282   else
1283     {
1284       if (icon_sizes_used == icon_sizes_allocated)
1285         {
1286           icon_sizes_allocated *= 2;
1287           icon_sizes = g_renew (IconSize, icon_sizes, icon_sizes_allocated);
1288         }
1289
1290       size = icon_sizes_used++;
1291
1292       /* alias to self. */
1293       gtk_icon_size_register_alias (name, size);
1294
1295       icon_sizes[size].size = size;
1296       icon_sizes[size].name = g_strdup (name);
1297     }
1298
1299   icon_sizes[size].width = width;
1300   icon_sizes[size].height = height;
1301
1302   return size;
1303 }
1304
1305 /**
1306  * gtk_icon_size_register:
1307  * @name: name of the icon size
1308  * @width: the icon width
1309  * @height: the icon height
1310  *
1311  * Registers a new icon size, along the same lines as #GTK_ICON_SIZE_MENU,
1312  * etc. Returns the integer value for the size.
1313  *
1314  * Returns: integer value representing the size
1315  * 
1316  **/
1317 GtkIconSize
1318 gtk_icon_size_register (const gchar *name,
1319                         gint         width,
1320                         gint         height)
1321 {
1322   g_return_val_if_fail (name != NULL, 0);
1323   g_return_val_if_fail (width > 0, 0);
1324   g_return_val_if_fail (height > 0, 0);
1325   
1326   return icon_size_register_intern (name, width, height);
1327 }
1328
1329 /**
1330  * gtk_icon_size_register_alias:
1331  * @alias: an alias for @target
1332  * @target: an existing icon size
1333  *
1334  * Registers @alias as another name for @target.
1335  * So calling gtk_icon_size_from_name() with @alias as argument
1336  * will return @target.
1337  *
1338  **/
1339 void
1340 gtk_icon_size_register_alias (const gchar *alias,
1341                               GtkIconSize  target)
1342 {
1343   IconAlias *ia;
1344   
1345   g_return_if_fail (alias != NULL);
1346
1347   init_icon_sizes ();
1348
1349   if (!icon_size_lookup_intern (NULL, target, NULL, NULL))
1350     g_warning ("gtk_icon_size_register_alias: Icon size %d does not exist", target);
1351
1352   ia = g_hash_table_lookup (icon_aliases, alias);
1353   if (ia)
1354     {
1355       if (icon_sizes[ia->target].width > 0)
1356         {
1357           g_warning ("gtk_icon_size_register_alias: Icon size name '%s' already exists", alias);
1358           return;
1359         }
1360
1361       ia->target = target;
1362     }
1363
1364   if (!ia)
1365     {
1366       ia = g_new (IconAlias, 1);
1367       ia->name = g_strdup (alias);
1368       ia->target = target;
1369
1370       g_hash_table_insert (icon_aliases, ia->name, ia);
1371     }
1372 }
1373
1374 /** 
1375  * gtk_icon_size_from_name:
1376  * @name: the name to look up.
1377  * @returns: the icon size with the given name.
1378  * 
1379  * Looks up the icon size associated with @name.
1380  **/
1381 GtkIconSize
1382 gtk_icon_size_from_name (const gchar *name)
1383 {
1384   IconAlias *ia;
1385
1386   init_icon_sizes ();
1387   
1388   ia = g_hash_table_lookup (icon_aliases, name);
1389
1390   if (ia && icon_sizes[ia->target].width > 0)
1391     return ia->target;
1392   else
1393     return GTK_ICON_SIZE_INVALID;
1394 }
1395
1396 /**
1397  * gtk_icon_size_get_name:
1398  * @size: a #GtkIconSize.
1399  * @returns: the name of the given icon size.
1400  * 
1401  * Gets the canonical name of the given icon size. The returned string 
1402  * is statically allocated and should not be freed.
1403  **/
1404 G_CONST_RETURN gchar*
1405 gtk_icon_size_get_name (GtkIconSize  size)
1406 {
1407   if (size >= icon_sizes_used)
1408     return NULL;
1409   else
1410     return icon_sizes[size].name;
1411 }
1412
1413 /************************************************************/
1414
1415 /* Icon Set */
1416
1417
1418 /* Clear icon set contents, drop references to all contained
1419  * GdkPixbuf objects and forget all GtkIconSources. Used to
1420  * recycle an icon set.
1421  */
1422 static GdkPixbuf *find_in_cache     (GtkIconSet       *icon_set,
1423                                      GtkStyle         *style,
1424                                      GtkTextDirection  direction,
1425                                      GtkStateType      state,
1426                                      GtkIconSize       size);
1427 static void       add_to_cache      (GtkIconSet       *icon_set,
1428                                      GtkStyle         *style,
1429                                      GtkTextDirection  direction,
1430                                      GtkStateType      state,
1431                                      GtkIconSize       size,
1432                                      GdkPixbuf        *pixbuf);
1433 static void       clear_cache       (GtkIconSet       *icon_set,
1434                                      gboolean          style_detach);
1435 static GSList*    copy_cache        (GtkIconSet       *icon_set,
1436                                      GtkIconSet       *copy_recipient);
1437 static void       attach_to_style   (GtkIconSet       *icon_set,
1438                                      GtkStyle         *style);
1439 static void       detach_from_style (GtkIconSet       *icon_set,
1440                                      GtkStyle         *style);
1441 static void       style_dnotify     (gpointer          data);
1442
1443 struct _GtkIconSet
1444 {
1445   guint ref_count;
1446
1447   GSList *sources;
1448
1449   /* Cache of the last few rendered versions of the icon. */
1450   GSList *cache;
1451
1452   guint cache_size;
1453
1454   guint cache_serial;
1455 };
1456
1457 static guint cache_serial = 0;
1458
1459 /**
1460  * gtk_icon_set_new:
1461  * 
1462  * Creates a new #GtkIconSet. A #GtkIconSet represents a single icon
1463  * in various sizes and widget states. It can provide a #GdkPixbuf
1464  * for a given size and state on request, and automatically caches
1465  * some of the rendered #GdkPixbuf objects.
1466  *
1467  * Normally you would use gtk_widget_render_icon() instead of
1468  * using #GtkIconSet directly. The one case where you'd use
1469  * #GtkIconSet is to create application-specific icon sets to place in
1470  * a #GtkIconFactory.
1471  * 
1472  * Return value: a new #GtkIconSet
1473  **/
1474 GtkIconSet*
1475 gtk_icon_set_new (void)
1476 {
1477   GtkIconSet *icon_set;
1478
1479   icon_set = g_new (GtkIconSet, 1);
1480
1481   icon_set->ref_count = 1;
1482   icon_set->sources = NULL;
1483   icon_set->cache = NULL;
1484   icon_set->cache_size = 0;
1485   icon_set->cache_serial = cache_serial;
1486   
1487   return icon_set;
1488 }
1489
1490 /**
1491  * gtk_icon_set_new_from_pixbuf:
1492  * @pixbuf: a #GdkPixbuf
1493  * 
1494  * Creates a new #GtkIconSet with @pixbuf as the default/fallback
1495  * source image. If you don't add any additional #GtkIconSource to the
1496  * icon set, all variants of the icon will be created from @pixbuf,
1497  * using scaling, pixelation, etc. as required to adjust the icon size
1498  * or make the icon look insensitive/prelighted.
1499  * 
1500  * Return value: a new #GtkIconSet
1501  **/
1502 GtkIconSet *
1503 gtk_icon_set_new_from_pixbuf (GdkPixbuf *pixbuf)
1504 {
1505   GtkIconSet *set;
1506
1507   GtkIconSource source = { NULL, NULL, 0, 0, 0,
1508                            TRUE, TRUE, TRUE };
1509
1510   g_return_val_if_fail (pixbuf != NULL, NULL);
1511
1512   set = gtk_icon_set_new ();
1513
1514   source.pixbuf = pixbuf;
1515
1516   gtk_icon_set_add_source (set, &source);
1517   
1518   return set;
1519 }
1520
1521
1522 /**
1523  * gtk_icon_set_ref:
1524  * @icon_set: a #GtkIconSet.
1525  * 
1526  * Increments the reference count on @icon_set.
1527  * 
1528  * Return value: @icon_set.
1529  **/
1530 GtkIconSet*
1531 gtk_icon_set_ref (GtkIconSet *icon_set)
1532 {
1533   g_return_val_if_fail (icon_set != NULL, NULL);
1534   g_return_val_if_fail (icon_set->ref_count > 0, NULL);
1535
1536   icon_set->ref_count += 1;
1537
1538   return icon_set;
1539 }
1540
1541 /**
1542  * gtk_icon_set_unref:
1543  * @icon_set: a #GtkIconSet
1544  * 
1545  * Decrements the reference count on @icon_set, and frees memory
1546  * if the reference count reaches 0.
1547  **/
1548 void
1549 gtk_icon_set_unref (GtkIconSet *icon_set)
1550 {
1551   g_return_if_fail (icon_set != NULL);
1552   g_return_if_fail (icon_set->ref_count > 0);
1553
1554   icon_set->ref_count -= 1;
1555
1556   if (icon_set->ref_count == 0)
1557     {
1558       GSList *tmp_list = icon_set->sources;
1559       while (tmp_list != NULL)
1560         {
1561           gtk_icon_source_free (tmp_list->data);
1562
1563           tmp_list = g_slist_next (tmp_list);
1564         }
1565
1566       clear_cache (icon_set, TRUE);
1567
1568       g_free (icon_set);
1569     }
1570 }
1571
1572 GType
1573 gtk_icon_set_get_type (void)
1574 {
1575   static GType our_type = 0;
1576   
1577   if (our_type == 0)
1578     our_type = g_boxed_type_register_static ("GtkIconSet",
1579                                              (GBoxedCopyFunc) gtk_icon_set_ref,
1580                                              (GBoxedFreeFunc) gtk_icon_set_unref);
1581
1582   return our_type;
1583 }
1584
1585 /**
1586  * gtk_icon_set_copy:
1587  * @icon_set: a #GtkIconSet
1588  * 
1589  * Copies @icon_set by value. 
1590  * 
1591  * Return value: a new #GtkIconSet identical to the first.
1592  **/
1593 GtkIconSet*
1594 gtk_icon_set_copy (GtkIconSet *icon_set)
1595 {
1596   GtkIconSet *copy;
1597   GSList *tmp_list;
1598   
1599   copy = gtk_icon_set_new ();
1600
1601   tmp_list = icon_set->sources;
1602   while (tmp_list != NULL)
1603     {
1604       copy->sources = g_slist_prepend (copy->sources,
1605                                        gtk_icon_source_copy (tmp_list->data));
1606
1607       tmp_list = g_slist_next (tmp_list);
1608     }
1609
1610   copy->sources = g_slist_reverse (copy->sources);
1611
1612   copy->cache = copy_cache (icon_set, copy);
1613   copy->cache_size = icon_set->cache_size;
1614   copy->cache_serial = icon_set->cache_serial;
1615   
1616   return copy;
1617 }
1618
1619 static gboolean
1620 sizes_equivalent (GtkIconSize lhs,
1621                   GtkIconSize rhs)
1622 {
1623   /* We used to consider sizes equivalent if they were
1624    * the same pixel size, but we don't have the GtkSettings
1625    * here, so we can't do that. Plus, it's not clear that
1626    * it is right... it was just a workaround for the fact
1627    * that we register icons by logical size, not pixel size.
1628    */
1629 #if 1
1630   return lhs == rhs;
1631 #else  
1632   
1633   gint r_w, r_h, l_w, l_h;
1634
1635   icon_size_lookup_intern (NULL, rhs, &r_w, &r_h);
1636   icon_size_lookup_intern (NULL, lhs, &l_w, &l_h);
1637
1638   return r_w == l_w && r_h == l_h;
1639 #endif
1640 }
1641
1642 static GtkIconSource*
1643 find_and_prep_icon_source (GtkIconSet       *icon_set,
1644                            GtkTextDirection  direction,
1645                            GtkStateType      state,
1646                            GtkIconSize       size)
1647 {
1648   GtkIconSource *source;
1649   GSList *tmp_list;
1650
1651
1652   /* We need to find the best icon source.  Direction matters more
1653    * than state, state matters more than size. icon_set->sources
1654    * is sorted according to wildness, so if we take the first
1655    * match we find it will be the least-wild match (if there are
1656    * multiple matches for a given "wildness" then the RC file contained
1657    * dumb stuff, and we end up with an arbitrary matching source)
1658    */
1659   
1660   source = NULL;
1661   tmp_list = icon_set->sources;
1662   while (tmp_list != NULL)
1663     {
1664       GtkIconSource *s = tmp_list->data;
1665       
1666       if ((s->any_direction || (s->direction == direction)) &&
1667           (s->any_state || (s->state == state)) &&
1668           (s->any_size || (sizes_equivalent (size, s->size))))
1669         {
1670           source = s;
1671           break;
1672         }
1673       
1674       tmp_list = g_slist_next (tmp_list);
1675     }
1676
1677   if (source == NULL)
1678     return NULL;
1679   
1680   if (source->pixbuf == NULL)
1681     {
1682       GError *error = NULL;
1683       
1684       g_assert (source->filename);
1685       source->pixbuf = gdk_pixbuf_new_from_file (source->filename, &error);
1686
1687       if (source->pixbuf == NULL)
1688         {
1689           /* Remove this icon source so we don't keep trying to
1690            * load it.
1691            */
1692           g_warning (_("Error loading icon: %s"), error->message);
1693           g_error_free (error);
1694           
1695           icon_set->sources = g_slist_remove (icon_set->sources, source);
1696
1697           gtk_icon_source_free (source);
1698
1699           /* Try to fall back to other sources */
1700           if (icon_set->sources != NULL)
1701             return find_and_prep_icon_source (icon_set,
1702                                               direction,
1703                                               state,
1704                                               size);
1705           else
1706             return NULL;
1707         }
1708     }
1709
1710   return source;
1711 }
1712
1713 static GdkPixbuf*
1714 render_fallback_image (GtkStyle          *style,
1715                        GtkTextDirection   direction,
1716                        GtkStateType       state,
1717                        GtkIconSize        size,
1718                        GtkWidget         *widget,
1719                        const char        *detail)
1720 {
1721   /* This icon can be used for any direction/state/size */
1722   static GtkIconSource fallback_source = { NULL, NULL, 0, 0, 0, TRUE, TRUE, TRUE };
1723
1724   if (fallback_source.pixbuf == NULL)
1725     fallback_source.pixbuf = gdk_pixbuf_new_from_inline (-1, stock_missing_image_24, FALSE, NULL);
1726   
1727   return gtk_style_render_icon (style,
1728                                 &fallback_source,
1729                                 direction,
1730                                 state,
1731                                 size,
1732                                 widget,
1733                                 detail);
1734 }
1735
1736 /**
1737  * gtk_icon_set_render_icon:
1738  * @icon_set: a #GtkIconSet
1739  * @style: a #GtkStyle associated with @widget, or %NULL
1740  * @direction: text direction
1741  * @state: widget state
1742  * @size: icon size
1743  * @widget: widget that will display the icon, or %NULL
1744  * @detail: detail to pass to the theme engine, or %NULL
1745  * 
1746  * Renders an icon using gtk_style_render_icon(). In most cases,
1747  * gtk_widget_render_icon() is better, since it automatically provides
1748  * most of the arguments from the current widget settings.  This
1749  * function never returns %NULL; if the icon can't be rendered
1750  * (perhaps because an image file fails to load), a default "missing
1751  * image" icon will be returned instead.
1752  * 
1753  * Return value: a #GdkPixbuf to be displayed
1754  **/
1755 GdkPixbuf*
1756 gtk_icon_set_render_icon (GtkIconSet        *icon_set,
1757                           GtkStyle          *style,
1758                           GtkTextDirection   direction,
1759                           GtkStateType       state,
1760                           GtkIconSize        size,
1761                           GtkWidget         *widget,
1762                           const char        *detail)
1763 {
1764   GdkPixbuf *icon;
1765   GtkIconSource *source;
1766   
1767   g_return_val_if_fail (icon_set != NULL, NULL);
1768   g_return_val_if_fail (GTK_IS_STYLE (style), NULL);
1769
1770   if (icon_set->sources == NULL)
1771     return render_fallback_image (style, direction, state, size, widget, detail);
1772   
1773   icon = find_in_cache (icon_set, style, direction,
1774                         state, size);
1775
1776   if (icon)
1777     {
1778       g_object_ref (icon);
1779       return icon;
1780     }
1781
1782   
1783   source = find_and_prep_icon_source (icon_set, direction, state, size);
1784
1785   if (source == NULL)
1786     return render_fallback_image (style, direction, state, size, widget, detail);
1787
1788   g_assert (source->pixbuf != NULL);
1789   
1790   icon = gtk_style_render_icon (style,
1791                                 source,
1792                                 direction,
1793                                 state,
1794                                 size,
1795                                 widget,
1796                                 detail);
1797
1798   if (icon == NULL)
1799     {
1800       g_warning ("Theme engine failed to render icon");
1801       return NULL;
1802     }
1803   
1804   add_to_cache (icon_set, style, direction, state, size, icon);
1805   
1806   return icon;
1807 }
1808
1809 /* Order sources by their "wildness", so that "wilder" sources are
1810  * greater than "specific" sources; for determining ordering,
1811  * direction beats state beats size.
1812  */
1813
1814 static int
1815 icon_source_compare (gconstpointer ap, gconstpointer bp)
1816 {
1817   const GtkIconSource *a = ap;
1818   const GtkIconSource *b = bp;
1819
1820   if (!a->any_direction && b->any_direction)
1821     return -1;
1822   else if (a->any_direction && !b->any_direction)
1823     return 1;
1824   else if (!a->any_state && b->any_state)
1825     return -1;
1826   else if (a->any_state && !b->any_state)
1827     return 1;
1828   else if (!a->any_size && b->any_size)
1829     return -1;
1830   else if (a->any_size && !b->any_size)
1831     return 1;
1832   else
1833     return 0;
1834 }
1835
1836 /**
1837  * gtk_icon_set_add_source:
1838  * @icon_set: a #GtkIconSet
1839  * @source: a #GtkIconSource
1840  *
1841  * Icon sets have a list of #GtkIconSource, which they use as base
1842  * icons for rendering icons in different states and sizes. Icons are
1843  * scaled, made to look insensitive, etc. in
1844  * gtk_icon_set_render_icon(), but #GtkIconSet needs base images to
1845  * work with. The base images and when to use them are described by
1846  * a #GtkIconSource.
1847  * 
1848  * This function copies @source, so you can reuse the same source immediately
1849  * without affecting the icon set.
1850  *
1851  * An example of when you'd use this function: a web browser's "Back
1852  * to Previous Page" icon might point in a different direction in
1853  * Hebrew and in English; it might look different when insensitive;
1854  * and it might change size depending on toolbar mode (small/large
1855  * icons). So a single icon set would contain all those variants of
1856  * the icon, and you might add a separate source for each one.
1857  *
1858  * You should nearly always add a "default" icon source with all
1859  * fields wildcarded, which will be used as a fallback if no more
1860  * specific source matches. #GtkIconSet always prefers more specific
1861  * icon sources to more generic icon sources. The order in which you
1862  * add the sources to the icon set does not matter.
1863  *
1864  * gtk_icon_set_new_from_pixbuf() creates a new icon set with a
1865  * default icon source based on the given pixbuf.
1866  * 
1867  **/
1868 void
1869 gtk_icon_set_add_source (GtkIconSet *icon_set,
1870                          const GtkIconSource *source)
1871 {
1872   g_return_if_fail (icon_set != NULL);
1873   g_return_if_fail (source != NULL);
1874
1875   if (source->pixbuf == NULL &&
1876       source->filename == NULL)
1877     {
1878       g_warning ("Useless GtkIconSource contains NULL filename and pixbuf");
1879       return;
1880     }
1881   
1882   icon_set->sources = g_slist_insert_sorted (icon_set->sources,
1883                                              gtk_icon_source_copy (source),
1884                                              icon_source_compare);
1885 }
1886
1887 /**
1888  * gtk_icon_set_get_sizes:
1889  * @icon_set: a #GtkIconSet
1890  * @sizes: return location for array of sizes
1891  * @n_sizes: location to store number of elements in returned array
1892  *
1893  * Obtains a list of icon sizes this icon set can render. The returned
1894  * array must be freed with g_free().
1895  * 
1896  **/
1897 void
1898 gtk_icon_set_get_sizes (GtkIconSet   *icon_set,
1899                         GtkIconSize **sizes,
1900                         gint         *n_sizes)
1901 {
1902   GSList *tmp_list;
1903   gboolean all_sizes = FALSE;
1904   GSList *specifics = NULL;
1905   
1906   g_return_if_fail (icon_set != NULL);
1907   g_return_if_fail (sizes != NULL);
1908   g_return_if_fail (n_sizes != NULL);
1909   
1910   tmp_list = icon_set->sources;
1911   while (tmp_list != NULL)
1912     {
1913       GtkIconSource *source;
1914
1915       source = tmp_list->data;
1916
1917       if (source->any_size)
1918         {
1919           all_sizes = TRUE;
1920           break;
1921         }
1922       else
1923         specifics = g_slist_prepend (specifics, GINT_TO_POINTER (source->size));
1924       
1925       tmp_list = g_slist_next (tmp_list);
1926     }
1927
1928   if (all_sizes)
1929     {
1930       /* Need to find out what sizes exist */
1931       gint i;
1932
1933       init_icon_sizes ();
1934       
1935       *sizes = g_new (GtkIconSize, icon_sizes_used);
1936       *n_sizes = icon_sizes_used - 1;
1937       
1938       i = 1;      
1939       while (i < icon_sizes_used)
1940         {
1941           (*sizes)[i - 1] = icon_sizes[i].size;
1942           ++i;
1943         }
1944     }
1945   else
1946     {
1947       gint i;
1948       
1949       *n_sizes = g_slist_length (specifics);
1950       *sizes = g_new (GtkIconSize, *n_sizes);
1951
1952       i = 0;
1953       tmp_list = specifics;
1954       while (tmp_list != NULL)
1955         {
1956           (*sizes)[i] = GPOINTER_TO_INT (tmp_list->data);
1957
1958           ++i;
1959           tmp_list = g_slist_next (tmp_list);
1960         }
1961     }
1962
1963   g_slist_free (specifics);
1964 }
1965
1966
1967 /**
1968  * gtk_icon_source_new:
1969  * 
1970  * Creates a new #GtkIconSource. A #GtkIconSource contains a #GdkPixbuf (or
1971  * image filename) that serves as the base image for one or more of the
1972  * icons in a #GtkIconSet, along with a specification for which icons in the
1973  * icon set will be based on that pixbuf or image file. An icon set contains
1974  * a set of icons that represent "the same" logical concept in different states,
1975  * different global text directions, and different sizes.
1976  * 
1977  * So for example a web browser's "Back to Previous Page" icon might
1978  * point in a different direction in Hebrew and in English; it might
1979  * look different when insensitive; and it might change size depending
1980  * on toolbar mode (small/large icons). So a single icon set would
1981  * contain all those variants of the icon. #GtkIconSet contains a list
1982  * of #GtkIconSource from which it can derive specific icon variants in
1983  * the set. 
1984  *
1985  * In the simplest case, #GtkIconSet contains one source pixbuf from
1986  * which it derives all variants. The convenience function
1987  * gtk_icon_set_new_from_pixbuf() handles this case; if you only have
1988  * one source pixbuf, just use that function.
1989  *
1990  * If you want to use a different base pixbuf for different icon
1991  * variants, you create multiple icon sources, mark which variants
1992  * they'll be used to create, and add them to the icon set with
1993  * gtk_icon_set_add_source().
1994  *
1995  * By default, the icon source has all parameters wildcarded. That is,
1996  * the icon source will be used as the base icon for any desired text
1997  * direction, widget state, or icon size.
1998  * 
1999  * Return value: a new #GtkIconSource
2000  **/
2001 GtkIconSource*
2002 gtk_icon_source_new (void)
2003 {
2004   GtkIconSource *src;
2005   
2006   src = g_new0 (GtkIconSource, 1);
2007
2008   src->direction = GTK_TEXT_DIR_NONE;
2009   src->size = GTK_ICON_SIZE_INVALID;
2010   src->state = GTK_STATE_NORMAL;
2011   
2012   src->any_direction = TRUE;
2013   src->any_state = TRUE;
2014   src->any_size = TRUE;
2015   
2016   return src;
2017 }
2018
2019 /**
2020  * gtk_icon_source_copy:
2021  * @source: a #GtkIconSource
2022  * 
2023  * Creates a copy of @source; mostly useful for language bindings.
2024  * 
2025  * Return value: a new #GtkIconSource
2026  **/
2027 GtkIconSource*
2028 gtk_icon_source_copy (const GtkIconSource *source)
2029 {
2030   GtkIconSource *copy;
2031   
2032   g_return_val_if_fail (source != NULL, NULL);
2033
2034   copy = g_new (GtkIconSource, 1);
2035
2036   *copy = *source;
2037   
2038   copy->filename = g_strdup (source->filename);
2039   copy->size = source->size;
2040   if (copy->pixbuf)
2041     g_object_ref (copy->pixbuf);
2042
2043   return copy;
2044 }
2045
2046 /**
2047  * gtk_icon_source_free:
2048  * @source: a #GtkIconSource
2049  * 
2050  * Frees a dynamically-allocated icon source, along with its
2051  * filename, size, and pixbuf fields if those are not %NULL.
2052  **/
2053 void
2054 gtk_icon_source_free (GtkIconSource *source)
2055 {
2056   g_return_if_fail (source != NULL);
2057
2058   g_free ((char*) source->filename);
2059   if (source->pixbuf)
2060     g_object_unref (source->pixbuf);
2061
2062   g_free (source);
2063 }
2064
2065 GType
2066 gtk_icon_source_get_type (void)
2067 {
2068   static GType our_type = 0;
2069   
2070   if (our_type == 0)
2071     our_type = g_boxed_type_register_static ("GtkIconSource",
2072                                              (GBoxedCopyFunc) gtk_icon_source_copy,
2073                                              (GBoxedFreeFunc) gtk_icon_source_free);
2074
2075   return our_type;
2076 }
2077
2078 /**
2079  * gtk_icon_source_set_filename:
2080  * @source: a #GtkIconSource
2081  * @filename: image file to use
2082  *
2083  * Sets the name of an image file to use as a base image when creating
2084  * icon variants for #GtkIconSet. The filename must be absolute. 
2085  **/
2086 void
2087 gtk_icon_source_set_filename (GtkIconSource *source,
2088                               const gchar   *filename)
2089 {
2090   g_return_if_fail (source != NULL);
2091   g_return_if_fail (filename == NULL || g_path_is_absolute (filename));
2092
2093   if (source->filename == filename)
2094     return;
2095   
2096   if (source->filename)
2097     g_free (source->filename);
2098
2099   source->filename = g_strdup (filename);  
2100 }
2101
2102 /**
2103  * gtk_icon_source_set_pixbuf:
2104  * @source: a #GtkIconSource
2105  * @pixbuf: pixbuf to use as a source
2106  *
2107  * Sets a pixbuf to use as a base image when creating icon variants
2108  * for #GtkIconSet. If an icon source has both a filename and a pixbuf
2109  * set, the pixbuf will take priority.
2110  * 
2111  **/
2112 void
2113 gtk_icon_source_set_pixbuf (GtkIconSource *source,
2114                             GdkPixbuf     *pixbuf)
2115 {
2116   g_return_if_fail (source != NULL);
2117
2118   if (pixbuf)
2119     g_object_ref (pixbuf);
2120
2121   if (source->pixbuf)
2122     g_object_unref (source->pixbuf);
2123
2124   source->pixbuf = pixbuf;
2125 }
2126
2127 /**
2128  * gtk_icon_source_get_filename:
2129  * @source: a #GtkIconSource
2130  * 
2131  * Retrieves the source filename, or %NULL if none is set. The
2132  * filename is not a copy, and should not be modified or expected to
2133  * persist beyond the lifetime of the icon source.
2134  * 
2135  * Return value: image filename. This string must not be modified
2136  * or freed.
2137  **/
2138 G_CONST_RETURN gchar*
2139 gtk_icon_source_get_filename (const GtkIconSource *source)
2140 {
2141   g_return_val_if_fail (source != NULL, NULL);
2142   
2143   return source->filename;
2144 }
2145
2146 /**
2147  * gtk_icon_source_get_pixbuf:
2148  * @source: a #GtkIconSource
2149  * 
2150  * Retrieves the source pixbuf, or %NULL if none is set.
2151  * The reference count on the pixbuf is not incremented.
2152  * 
2153  * Return value: source pixbuf
2154  **/
2155 GdkPixbuf*
2156 gtk_icon_source_get_pixbuf (const GtkIconSource *source)
2157 {
2158   g_return_val_if_fail (source != NULL, NULL);
2159   
2160   return source->pixbuf;
2161 }
2162
2163 /**
2164  * gtk_icon_source_set_direction_wildcarded:
2165  * @source: a #GtkIconSource
2166  * @setting: %TRUE to wildcard the text direction
2167  *
2168  * If the text direction is wildcarded, this source can be used
2169  * as the base image for an icon in any #GtkTextDirection.
2170  * If the text direction is not wildcarded, then the
2171  * text direction the icon source applies to should be set
2172  * with gtk_icon_source_set_direction(), and the icon source
2173  * will only be used with that text direction.
2174  *
2175  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
2176  * wildcarded sources, and will use an exact match when possible.
2177  * 
2178  **/
2179 void
2180 gtk_icon_source_set_direction_wildcarded (GtkIconSource *source,
2181                                           gboolean       setting)
2182 {
2183   g_return_if_fail (source != NULL);
2184
2185   source->any_direction = setting != FALSE;
2186 }
2187
2188 /**
2189  * gtk_icon_source_set_state_wildcarded:
2190  * @source: a #GtkIconSource
2191  * @setting: %TRUE to wildcard the widget state
2192  *
2193  * If the widget state is wildcarded, this source can be used as the
2194  * base image for an icon in any #GtkStateType.  If the widget state
2195  * is not wildcarded, then the state the source applies to should be
2196  * set with gtk_icon_source_set_state() and the icon source will
2197  * only be used with that specific state.
2198  *
2199  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
2200  * wildcarded sources, and will use an exact match when possible.
2201  *
2202  * #GtkIconSet will normally transform wildcarded source images to
2203  * produce an appropriate icon for a given state, for example
2204  * lightening an image on prelight, but will not modify source images
2205  * that match exactly.
2206  **/
2207 void
2208 gtk_icon_source_set_state_wildcarded (GtkIconSource *source,
2209                                       gboolean       setting)
2210 {
2211   g_return_if_fail (source != NULL);
2212
2213   source->any_state = setting != FALSE;
2214 }
2215
2216
2217 /**
2218  * gtk_icon_source_set_size_wildcarded:
2219  * @source: a #GtkIconSource
2220  * @setting: %TRUE to wildcard the widget state
2221  *
2222  * If the icon size is wildcarded, this source can be used as the base
2223  * image for an icon of any size.  If the size is not wildcarded, then
2224  * the size the source applies to should be set with
2225  * gtk_icon_source_set_size() and the icon source will only be used
2226  * with that specific size.
2227  *
2228  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
2229  * wildcarded sources, and will use an exact match when possible.
2230  *
2231  * #GtkIconSet will normally scale wildcarded source images to produce
2232  * an appropriate icon at a given size, but will not change the size
2233  * of source images that match exactly.
2234  **/
2235 void
2236 gtk_icon_source_set_size_wildcarded (GtkIconSource *source,
2237                                      gboolean       setting)
2238 {
2239   g_return_if_fail (source != NULL);
2240
2241   source->any_size = setting != FALSE;  
2242 }
2243
2244 /**
2245  * gtk_icon_source_get_size_wildcarded:
2246  * @source: a #GtkIconSource
2247  * 
2248  * Gets the value set by gtk_icon_source_set_size_wildcarded().
2249  * 
2250  * Return value: %TRUE if this icon source is a base for any icon size variant
2251  **/
2252 gboolean
2253 gtk_icon_source_get_size_wildcarded (const GtkIconSource *source)
2254 {
2255   g_return_val_if_fail (source != NULL, TRUE);
2256   
2257   return source->any_size;
2258 }
2259
2260 /**
2261  * gtk_icon_source_get_state_wildcarded:
2262  * @source: a #GtkIconSource
2263  * 
2264  * Gets the value set by gtk_icon_source_set_state_wildcarded().
2265  * 
2266  * Return value: %TRUE if this icon source is a base for any widget state variant
2267  **/
2268 gboolean
2269 gtk_icon_source_get_state_wildcarded (const GtkIconSource *source)
2270 {
2271   g_return_val_if_fail (source != NULL, TRUE);
2272
2273   return source->any_state;
2274 }
2275
2276 /**
2277  * gtk_icon_source_get_direction_wildcarded:
2278  * @source: a #GtkIconSource
2279  * 
2280  * Gets the value set by gtk_icon_source_set_direction_wildcarded().
2281  * 
2282  * Return value: %TRUE if this icon source is a base for any text direction variant
2283  **/
2284 gboolean
2285 gtk_icon_source_get_direction_wildcarded (const GtkIconSource *source)
2286 {
2287   g_return_val_if_fail (source != NULL, TRUE);
2288
2289   return source->any_direction;
2290 }
2291
2292 /**
2293  * gtk_icon_source_set_direction:
2294  * @source: a #GtkIconSource
2295  * @direction: text direction this source applies to
2296  *
2297  * Sets the text direction this icon source is intended to be used
2298  * with.
2299  * 
2300  * Setting the text direction on an icon source makes no difference
2301  * if the text direction is wildcarded. Therefore, you should usually
2302  * call gtk_icon_source_set_direction_wildcarded() to un-wildcard it
2303  * in addition to calling this function.
2304  * 
2305  **/
2306 void
2307 gtk_icon_source_set_direction (GtkIconSource   *source,
2308                                GtkTextDirection direction)
2309 {
2310   g_return_if_fail (source != NULL);
2311
2312   source->direction = direction;
2313 }
2314
2315 /**
2316  * gtk_icon_source_set_state:
2317  * @source: a #GtkIconSource
2318  * @state: widget state this source applies to
2319  *
2320  * Sets the widget state this icon source is intended to be used
2321  * with.
2322  * 
2323  * Setting the widget state on an icon source makes no difference
2324  * if the state is wildcarded. Therefore, you should usually
2325  * call gtk_icon_source_set_state_wildcarded() to un-wildcard it
2326  * in addition to calling this function.
2327  * 
2328  **/
2329 void
2330 gtk_icon_source_set_state (GtkIconSource *source,
2331                            GtkStateType   state)
2332 {
2333   g_return_if_fail (source != NULL);
2334
2335   source->state = state;
2336 }
2337
2338 /**
2339  * gtk_icon_source_set_size:
2340  * @source: a #GtkIconSource
2341  * @size: icon size this source applies to
2342  *
2343  * Sets the icon size this icon source is intended to be used
2344  * with.
2345  * 
2346  * Setting the icon size on an icon source makes no difference
2347  * if the size is wildcarded. Therefore, you should usually
2348  * call gtk_icon_source_set_size_wildcarded() to un-wildcard it
2349  * in addition to calling this function.
2350  * 
2351  **/
2352 void
2353 gtk_icon_source_set_size (GtkIconSource *source,
2354                           GtkIconSize    size)
2355 {
2356   g_return_if_fail (source != NULL);
2357
2358   source->size = size;
2359 }
2360
2361 /**
2362  * gtk_icon_source_get_direction:
2363  * @source: a #GtkIconSource
2364  * 
2365  * Obtains the text direction this icon source applies to. The return
2366  * value is only useful/meaningful if the text direction is <emphasis>not</emphasis> 
2367  * wildcarded.
2368  * 
2369  * Return value: text direction this source matches
2370  **/
2371 GtkTextDirection
2372 gtk_icon_source_get_direction (const GtkIconSource *source)
2373 {
2374   g_return_val_if_fail (source != NULL, 0);
2375
2376   return source->direction;
2377 }
2378
2379 /**
2380  * gtk_icon_source_get_state:
2381  * @source: a #GtkIconSource
2382  * 
2383  * Obtains the widget state this icon source applies to. The return
2384  * value is only useful/meaningful if the widget state is <emphasis>not</emphasis>
2385  * wildcarded.
2386  * 
2387  * Return value: widget state this source matches
2388  **/
2389 GtkStateType
2390 gtk_icon_source_get_state (const GtkIconSource *source)
2391 {
2392   g_return_val_if_fail (source != NULL, 0);
2393
2394   return source->state;
2395 }
2396
2397 /**
2398  * gtk_icon_source_get_size:
2399  * @source: a #GtkIconSource
2400  * 
2401  * Obtains the icon size this source applies to. The return value
2402  * is only useful/meaningful if the icon size is <emphasis>not</emphasis> wildcarded.
2403  * 
2404  * Return value: icon size this source matches.
2405  **/
2406 GtkIconSize
2407 gtk_icon_source_get_size (const GtkIconSource *source)
2408 {
2409   g_return_val_if_fail (source != NULL, 0);
2410
2411   return source->size;
2412 }
2413
2414 /* Note that the logical maximum is 20 per GtkTextDirection, so we could
2415  * eventually set this to >20 to never throw anything out.
2416  */
2417 #define NUM_CACHED_ICONS 8
2418
2419 typedef struct _CachedIcon CachedIcon;
2420
2421 struct _CachedIcon
2422 {
2423   /* These must all match to use the cached pixbuf.
2424    * If any don't match, we must re-render the pixbuf.
2425    */
2426   GtkStyle *style;
2427   GtkTextDirection direction;
2428   GtkStateType state;
2429   GtkIconSize size;
2430
2431   GdkPixbuf *pixbuf;
2432 };
2433
2434 static void
2435 ensure_cache_up_to_date (GtkIconSet *icon_set)
2436 {
2437   if (icon_set->cache_serial != cache_serial)
2438     clear_cache (icon_set, TRUE);
2439 }
2440
2441 static void
2442 cached_icon_free (CachedIcon *icon)
2443 {
2444   g_object_unref (icon->pixbuf);
2445
2446   g_free (icon);
2447 }
2448
2449 static GdkPixbuf *
2450 find_in_cache (GtkIconSet      *icon_set,
2451                GtkStyle        *style,
2452                GtkTextDirection direction,
2453                GtkStateType     state,
2454                GtkIconSize      size)
2455 {
2456   GSList *tmp_list;
2457   GSList *prev;
2458
2459   ensure_cache_up_to_date (icon_set);
2460   
2461   prev = NULL;
2462   tmp_list = icon_set->cache;
2463   while (tmp_list != NULL)
2464     {
2465       CachedIcon *icon = tmp_list->data;
2466
2467       if (icon->style == style &&
2468           icon->direction == direction &&
2469           icon->state == state &&
2470           icon->size == size)
2471         {
2472           if (prev)
2473             {
2474               /* Move this icon to the front of the list. */
2475               prev->next = tmp_list->next;
2476               tmp_list->next = icon_set->cache;
2477               icon_set->cache = tmp_list;
2478             }
2479           
2480           return icon->pixbuf;
2481         }
2482           
2483       prev = tmp_list;
2484       tmp_list = g_slist_next (tmp_list);
2485     }
2486
2487   return NULL;
2488 }
2489
2490 static void
2491 add_to_cache (GtkIconSet      *icon_set,
2492               GtkStyle        *style,
2493               GtkTextDirection direction,
2494               GtkStateType     state,
2495               GtkIconSize      size,
2496               GdkPixbuf       *pixbuf)
2497 {
2498   CachedIcon *icon;
2499
2500   ensure_cache_up_to_date (icon_set);
2501   
2502   g_object_ref (pixbuf);
2503
2504   /* We have to ref the style, since if the style was finalized
2505    * its address could be reused by another style, creating a
2506    * really weird bug
2507    */
2508   
2509   if (style)
2510     g_object_ref (style);
2511   
2512
2513   icon = g_new (CachedIcon, 1);
2514   icon_set->cache = g_slist_prepend (icon_set->cache, icon);
2515
2516   icon->style = style;
2517   icon->direction = direction;
2518   icon->state = state;
2519   icon->size = size;
2520   icon->pixbuf = pixbuf;
2521
2522   if (icon->style)
2523     attach_to_style (icon_set, icon->style);
2524   
2525   if (icon_set->cache_size >= NUM_CACHED_ICONS)
2526     {
2527       /* Remove oldest item in the cache */
2528       
2529       GSList *tmp_list;
2530       
2531       tmp_list = icon_set->cache;
2532
2533       /* Find next-to-last link */
2534       g_assert (NUM_CACHED_ICONS > 2);
2535       while (tmp_list->next->next)
2536         tmp_list = tmp_list->next;
2537
2538       g_assert (tmp_list != NULL);
2539       g_assert (tmp_list->next != NULL);
2540       g_assert (tmp_list->next->next == NULL);
2541
2542       /* Free the last icon */
2543       icon = tmp_list->next->data;
2544
2545       g_slist_free (tmp_list->next);
2546       tmp_list->next = NULL;
2547
2548       cached_icon_free (icon);
2549     }
2550 }
2551
2552 static void
2553 clear_cache (GtkIconSet *icon_set,
2554              gboolean    style_detach)
2555 {
2556   GSList *tmp_list;
2557   GtkStyle *last_style = NULL;
2558
2559   tmp_list = icon_set->cache;
2560   while (tmp_list != NULL)
2561     {
2562       CachedIcon *icon = tmp_list->data;
2563
2564       if (style_detach)
2565         {
2566           /* simple optimization for the case where the cache
2567            * contains contiguous icons from the same style.
2568            * it's safe to call detach_from_style more than
2569            * once on the same style though.
2570            */
2571           if (last_style != icon->style)
2572             {
2573               detach_from_style (icon_set, icon->style);
2574               last_style = icon->style;
2575             }
2576         }
2577       
2578       cached_icon_free (icon);      
2579       
2580       tmp_list = g_slist_next (tmp_list);
2581     }
2582
2583   g_slist_free (icon_set->cache);
2584   icon_set->cache = NULL;
2585   icon_set->cache_size = 0;
2586 }
2587
2588 static GSList*
2589 copy_cache (GtkIconSet *icon_set,
2590             GtkIconSet *copy_recipient)
2591 {
2592   GSList *tmp_list;
2593   GSList *copy = NULL;
2594
2595   ensure_cache_up_to_date (icon_set);
2596   
2597   tmp_list = icon_set->cache;
2598   while (tmp_list != NULL)
2599     {
2600       CachedIcon *icon = tmp_list->data;
2601       CachedIcon *icon_copy = g_new (CachedIcon, 1);
2602
2603       *icon_copy = *icon;
2604
2605       if (icon_copy->style)
2606         attach_to_style (copy_recipient, icon_copy->style);
2607         
2608       g_object_ref (icon_copy->pixbuf);
2609
2610       icon_copy->size = icon->size;
2611       
2612       copy = g_slist_prepend (copy, icon_copy);      
2613       
2614       tmp_list = g_slist_next (tmp_list);
2615     }
2616
2617   return g_slist_reverse (copy);
2618 }
2619
2620 static void
2621 attach_to_style (GtkIconSet *icon_set,
2622                  GtkStyle   *style)
2623 {
2624   GHashTable *table;
2625
2626   table = g_object_get_qdata (G_OBJECT (style),
2627                               g_quark_try_string ("gtk-style-icon-sets"));
2628
2629   if (table == NULL)
2630     {
2631       table = g_hash_table_new (NULL, NULL);
2632       g_object_set_qdata_full (G_OBJECT (style),
2633                                g_quark_from_static_string ("gtk-style-icon-sets"),
2634                                table,
2635                                style_dnotify);
2636     }
2637
2638   g_hash_table_insert (table, icon_set, icon_set);
2639 }
2640
2641 static void
2642 detach_from_style (GtkIconSet *icon_set,
2643                    GtkStyle   *style)
2644 {
2645   GHashTable *table;
2646
2647   table = g_object_get_qdata (G_OBJECT (style),
2648                               g_quark_try_string ("gtk-style-icon-sets"));
2649
2650   if (table != NULL)
2651     g_hash_table_remove (table, icon_set);
2652 }
2653
2654 static void
2655 iconsets_foreach (gpointer key,
2656                   gpointer value,
2657                   gpointer user_data)
2658 {
2659   GtkIconSet *icon_set = key;
2660
2661   /* We only need to remove cache entries for the given style;
2662    * but that complicates things because in destroy notify
2663    * we don't know which style got destroyed, and 95% of the
2664    * time all cache entries will have the same style,
2665    * so this is faster anyway.
2666    */
2667   
2668   clear_cache (icon_set, FALSE);
2669 }
2670
2671 static void
2672 style_dnotify (gpointer data)
2673 {
2674   GHashTable *table = data;
2675   
2676   g_hash_table_foreach (table, iconsets_foreach, NULL);
2677
2678   g_hash_table_destroy (table);
2679 }
2680
2681 /* This allows the icon set to detect that its cache is out of date. */
2682 void
2683 _gtk_icon_set_invalidate_caches (void)
2684 {
2685   ++cache_serial;
2686 }
2687
2688 static void
2689 listify_foreach (gpointer key, gpointer value, gpointer data)
2690 {
2691   GSList **list = data;
2692
2693   *list = g_slist_prepend (*list, key);
2694 }
2695
2696 static GSList *
2697 g_hash_table_get_keys (GHashTable *table)
2698 {
2699   GSList *list = NULL;
2700
2701   g_hash_table_foreach (table, listify_foreach, &list);
2702
2703   return list;
2704 }
2705
2706 /**
2707  * _gtk_icon_factory_list_ids:
2708  * 
2709  * Gets all known IDs stored in an existing icon factory.
2710  * The strings in the returned list aren't copied.
2711  * The list itself should be freed.
2712  * 
2713  * Return value: List of ids in icon factories
2714  **/
2715 GSList*
2716 _gtk_icon_factory_list_ids (void)
2717 {
2718   GSList *tmp_list;
2719   GSList *ids;
2720
2721   ids = NULL;
2722
2723   ensure_default_icons ();
2724   
2725   tmp_list = all_icon_factories;
2726   while (tmp_list != NULL)
2727     {
2728       GSList *these_ids;
2729       
2730       GtkIconFactory *factory = GTK_ICON_FACTORY (tmp_list->data);
2731
2732       these_ids = g_hash_table_get_keys (factory->icons);
2733       
2734       ids = g_slist_concat (ids, these_ids);
2735       
2736       tmp_list = g_slist_next (tmp_list);
2737     }
2738
2739   return ids;
2740 }