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