]> Pileus Git - ~andy/gtk/blob - gtk/gtkiconfactory.c
add variables and checks for specific versions of dependencies. Previously
[~andy/gtk] / gtk / gtkiconfactory.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2000 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "gtkiconfactory.h"
28 #include "stock-icons/gtkstockpixbufs.h"
29 #include "gtkstock.h"
30 #include "gtkintl.h"
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <ctype.h>
34 #include <string.h>
35
36 static GSList *all_icon_factories = NULL;
37
38 struct _GtkIconSource
39 {
40   /* Either filename or pixbuf can be NULL. If both are non-NULL,
41    * the pixbuf is assumed to be the already-loaded contents of the
42    * file.
43    */
44   gchar *filename;
45   GdkPixbuf *pixbuf;
46
47   GtkTextDirection direction;
48   GtkStateType state;
49   GtkIconSize size;
50
51   /* If TRUE, then the parameter is wildcarded, and the above
52    * fields should be ignored. If FALSE, the parameter is
53    * specified, and the above fields should be valid.
54    */
55   guint any_direction : 1;
56   guint any_state : 1;
57   guint any_size : 1;
58 };
59
60 /* FIXME use a better icon for this */
61 #define MISSING_IMAGE_INLINE dialog_error
62
63 static gpointer parent_class = NULL;
64
65 static void gtk_icon_factory_init       (GtkIconFactory      *icon_factory);
66 static void gtk_icon_factory_class_init (GtkIconFactoryClass *klass);
67 static void gtk_icon_factory_finalize   (GObject             *object);
68 static void get_default_icons           (GtkIconFactory      *icon_factory);
69
70 GType
71 gtk_icon_factory_get_type (void)
72 {
73   static GType object_type = 0;
74
75   if (!object_type)
76     {
77       static const GTypeInfo object_info =
78       {
79         sizeof (GtkIconFactoryClass),
80         (GBaseInitFunc) NULL,
81         (GBaseFinalizeFunc) NULL,
82         (GClassInitFunc) gtk_icon_factory_class_init,
83         NULL,           /* class_finalize */
84         NULL,           /* class_data */
85         sizeof (GtkIconFactory),
86         0,              /* n_preallocs */
87         (GInstanceInitFunc) gtk_icon_factory_init,
88       };
89       
90       object_type = g_type_register_static (G_TYPE_OBJECT,
91                                             "GtkIconFactory",
92                                             &object_info, 0);
93     }
94   
95   return object_type;
96 }
97
98 static void
99 gtk_icon_factory_init (GtkIconFactory *factory)
100 {
101   factory->icons = g_hash_table_new (g_str_hash, g_str_equal);
102   all_icon_factories = g_slist_prepend (all_icon_factories, factory);
103 }
104
105 static void
106 gtk_icon_factory_class_init (GtkIconFactoryClass *klass)
107 {
108   GObjectClass *object_class = G_OBJECT_CLASS (klass);
109   
110   parent_class = g_type_class_peek_parent (klass);
111
112   object_class->finalize = gtk_icon_factory_finalize;
113 }
114
115 static void
116 free_icon_set (gpointer key, gpointer value, gpointer data)
117 {
118   g_free (key);
119   gtk_icon_set_unref (value);
120 }
121
122 static void
123 gtk_icon_factory_finalize (GObject *object)
124 {
125   GtkIconFactory *factory = GTK_ICON_FACTORY (object);
126
127   all_icon_factories = g_slist_remove (all_icon_factories, factory);
128   
129   g_hash_table_foreach (factory->icons, free_icon_set, NULL);
130   
131   g_hash_table_destroy (factory->icons);
132   
133   G_OBJECT_CLASS (parent_class)->finalize (object);
134 }
135
136 /**
137  * gtk_icon_factory_new:
138  *
139  * Creates a new #GtkIconFactory. An icon factory manages a collection
140  * of #GtkIconSet; a #GtkIconSet manages a set of variants of a
141  * particular icon (i.e. a #GtkIconSet contains variants for different
142  * sizes and widget states). Icons in an icon factory are named by a
143  * stock ID, which is a simple string identifying the icon. Each
144  * #GtkStyle has a list of #GtkIconFactory derived from the current
145  * theme; those icon factories are consulted first when searching for
146  * an icon. If the theme doesn't set a particular icon, GTK+ looks for
147  * the icon in a list of default icon factories, maintained by
148  * gtk_icon_factory_add_default() and
149  * gtk_icon_factory_remove_default(). Applications with icons should
150  * add a default icon factory with their icons, which will allow
151  * themes to override the icons for the application.
152  * 
153  * Return value: a new #GtkIconFactory
154  **/
155 GtkIconFactory*
156 gtk_icon_factory_new (void)
157 {
158   return GTK_ICON_FACTORY (g_object_new (GTK_TYPE_ICON_FACTORY, NULL));
159 }
160
161 /**
162  * gtk_icon_factory_add:
163  * @factory: a #GtkIconFactory
164  * @stock_id: icon name
165  * @icon_set: icon set
166  *
167  * Adds the given @icon_set to the icon factory, under the name
168  * @stock_id.  @stock_id should be namespaced for your application,
169  * e.g. "myapp-whatever-icon".  Normally applications create a
170  * #GtkIconFactory, then add it to the list of default factories with
171  * gtk_icon_factory_add_default(). Then they pass the @stock_id to
172  * widgets such as #GtkImage to display the icon. Themes can provide
173  * an icon with the same name (such as "myapp-whatever-icon") to
174  * override your application's default icons. If an icon already
175  * existed in @factory for @stock_id, it is unreferenced and replaced
176  * with the new @icon_set.
177  * 
178  **/
179 void
180 gtk_icon_factory_add (GtkIconFactory *factory,
181                       const gchar    *stock_id,
182                       GtkIconSet     *icon_set)
183 {
184   gpointer old_key = NULL;
185   gpointer old_value = NULL;
186
187   g_return_if_fail (GTK_IS_ICON_FACTORY (factory));
188   g_return_if_fail (stock_id != NULL);
189   g_return_if_fail (icon_set != NULL);  
190
191   g_hash_table_lookup_extended (factory->icons, stock_id,
192                                 &old_key, &old_value);
193
194   if (old_value == icon_set)
195     return;
196   
197   gtk_icon_set_ref (icon_set);
198
199   /* GHashTable key memory management is so fantastically broken. */
200   if (old_key)
201     g_hash_table_insert (factory->icons, old_key, icon_set);
202   else
203     g_hash_table_insert (factory->icons, g_strdup (stock_id), icon_set);
204
205   if (old_value)
206     gtk_icon_set_unref (old_value);
207 }
208
209 /**
210  * gtk_icon_factory_lookup:
211  * @factory: a #GtkIconFactory
212  * @stock_id: an icon name
213  * 
214  * Looks up @stock_id in the icon factory, returning an icon set
215  * if found, otherwise %NULL. For display to the user, you should
216  * use gtk_style_lookup_icon_set() on the #GtkStyle for the
217  * widget that will display the icon, instead of using this
218  * function directly, so that themes are taken into account.
219  * 
220  * Return value: icon set of @stock_id.
221  **/
222 GtkIconSet *
223 gtk_icon_factory_lookup (GtkIconFactory *factory,
224                          const gchar    *stock_id)
225 {
226   g_return_val_if_fail (GTK_IS_ICON_FACTORY (factory), NULL);
227   g_return_val_if_fail (stock_id != NULL, NULL);
228   
229   return g_hash_table_lookup (factory->icons, stock_id);
230 }
231
232 static GtkIconFactory *gtk_default_icons = NULL;
233 static GSList *default_factories = NULL;
234
235 /**
236  * gtk_icon_factory_add_default:
237  * @factory: a #GtkIconFactory
238  * 
239  * Adds an icon factory to the list of icon factories searched by
240  * gtk_style_lookup_icon_set(). This means that, for example,
241  * gtk_image_new_from_stock() will be able to find icons in @factory.
242  * There will normally be an icon factory added for each library or
243  * application that comes with icons. The default icon factories
244  * can be overridden by themes.
245  * 
246  **/
247 void
248 gtk_icon_factory_add_default (GtkIconFactory *factory)
249 {
250   g_return_if_fail (GTK_IS_ICON_FACTORY (factory));
251
252   g_object_ref (G_OBJECT (factory));
253   
254   default_factories = g_slist_prepend (default_factories, factory);
255 }
256
257 /**
258  * gtk_icon_factory_remove_default:
259  * @factory: a #GtkIconFactory previously added with gtk_icon_factory_add_default()
260  *
261  * Removes an icon factory from the list of default icon
262  * factories. Not normally used; you might use it for a library that
263  * can be unloaded or shut down.
264  * 
265  **/
266 void
267 gtk_icon_factory_remove_default (GtkIconFactory  *factory)
268 {
269   g_return_if_fail (GTK_IS_ICON_FACTORY (factory));
270
271   default_factories = g_slist_remove (default_factories, factory);
272
273   g_object_unref (G_OBJECT (factory));
274 }
275
276 static void
277 ensure_default_icons (void)
278 {
279   if (gtk_default_icons == NULL)
280     {
281       gtk_default_icons = gtk_icon_factory_new ();
282
283       get_default_icons (gtk_default_icons);
284     }
285 }
286
287 /**
288  * gtk_icon_factory_lookup_default:
289  * @stock_id: an icon name
290  *
291  * Looks for an icon in the list of default icon factories.  For
292  * display to the user, you should use gtk_style_lookup_icon_set() on
293  * the #GtkStyle for the widget that will display the icon, instead of
294  * using this function directly, so that themes are taken into
295  * account.
296  * 
297  * 
298  * Return value: a #GtkIconSet, or %NULL
299  **/
300 GtkIconSet *
301 gtk_icon_factory_lookup_default (const gchar *stock_id)
302 {
303   GSList *tmp_list;
304
305   g_return_val_if_fail (stock_id != NULL, NULL);
306   
307   tmp_list = default_factories;
308   while (tmp_list != NULL)
309     {
310       GtkIconSet *icon_set =
311         gtk_icon_factory_lookup (GTK_ICON_FACTORY (tmp_list->data),
312                                  stock_id);
313
314       if (icon_set)
315         return icon_set;
316       
317       tmp_list = g_slist_next (tmp_list);
318     }
319
320   ensure_default_icons ();
321   
322   return gtk_icon_factory_lookup (gtk_default_icons, stock_id);
323 }
324
325 static GtkIconSet *
326 sized_icon_set_from_inline (const guchar *inline_data,
327                             GtkIconSize   size)
328 {
329   GtkIconSet *set;
330
331   GtkIconSource source = { NULL, NULL, 0, 0, 0,
332                            TRUE, TRUE, FALSE };
333
334   source.size = size;
335
336   set = gtk_icon_set_new ();
337
338   source.pixbuf = gdk_pixbuf_new_from_stream (-1, inline_data, FALSE, NULL);
339   
340   g_assert (source.pixbuf);
341
342   gtk_icon_set_add_source (set, &source);
343
344   g_object_unref (G_OBJECT (source.pixbuf));
345   
346   return set;
347 }
348
349
350 static GtkIconSet *
351 sized_with_fallback_icon_set_from_inline (const guchar *fallback_data,
352                                           const guchar *inline_data,
353                                           GtkIconSize   size)
354 {
355   GtkIconSet *set;
356
357   GtkIconSource source = { NULL, NULL, 0, 0, 0,
358                            TRUE, TRUE, FALSE };
359
360   source.size = size;
361
362   set = gtk_icon_set_new ();
363
364   source.pixbuf = gdk_pixbuf_new_from_stream (-1, inline_data, FALSE, NULL);
365
366   g_assert (source.pixbuf);
367
368   gtk_icon_set_add_source (set, &source);
369
370   g_object_unref (G_OBJECT (source.pixbuf));
371   
372   source.any_size = TRUE;
373
374   source.pixbuf = gdk_pixbuf_new_from_stream (-1, fallback_data, FALSE, NULL);
375
376   g_assert (source.pixbuf);
377
378   gtk_icon_set_add_source (set, &source);
379
380   g_object_unref (G_OBJECT (source.pixbuf));  
381   
382   return set;
383 }
384
385 static GtkIconSet *
386 unsized_icon_set_from_inline (const guchar *inline_data)
387 {
388   GtkIconSet *set;
389
390   /* This icon can be used for any direction/state/size */
391   GtkIconSource source = { NULL, NULL, 0, 0, 0,
392                            TRUE, TRUE, TRUE };
393
394   set = gtk_icon_set_new ();
395
396   source.pixbuf = gdk_pixbuf_new_from_stream (-1, inline_data, FALSE, NULL);
397
398   g_assert (source.pixbuf);
399
400   gtk_icon_set_add_source (set, &source);
401
402   g_object_unref (G_OBJECT (source.pixbuf));
403   
404   return set;
405 }
406
407 static void
408 add_sized (GtkIconFactory *factory,
409            const guchar   *inline_data,
410            GtkIconSize     size,
411            const gchar    *stock_id)
412 {
413   GtkIconSet *set;
414   
415   set = sized_icon_set_from_inline (inline_data, size);
416   
417   gtk_icon_factory_add (factory, stock_id, set);
418
419   gtk_icon_set_unref (set);
420 }
421
422 static void
423 add_sized_with_fallback (GtkIconFactory *factory,
424                          const guchar   *fallback_data,
425                          const guchar   *inline_data,
426                          GtkIconSize     size,
427                          const gchar    *stock_id)
428 {
429   GtkIconSet *set;
430   
431   set = sized_with_fallback_icon_set_from_inline (fallback_data, inline_data, size);
432   
433   gtk_icon_factory_add (factory, stock_id, set);
434
435   gtk_icon_set_unref (set);
436 }
437
438 static void
439 add_unsized (GtkIconFactory *factory,
440              const guchar   *inline_data,
441              const gchar    *stock_id)
442 {
443   GtkIconSet *set;
444   
445   set = unsized_icon_set_from_inline (inline_data);
446   
447   gtk_icon_factory_add (factory, stock_id, set);
448
449   gtk_icon_set_unref (set);
450 }
451
452 static void
453 get_default_icons (GtkIconFactory *factory)
454 {
455   /* KEEP IN SYNC with gtkstock.c */
456
457   add_unsized (factory, MISSING_IMAGE_INLINE, GTK_STOCK_MISSING_IMAGE);
458   
459   add_sized (factory, dialog_error, GTK_ICON_SIZE_DIALOG, GTK_STOCK_DIALOG_ERROR);
460   add_sized (factory, dialog_info, GTK_ICON_SIZE_DIALOG, GTK_STOCK_DIALOG_INFO);
461   add_sized (factory, dialog_question, GTK_ICON_SIZE_DIALOG, GTK_STOCK_DIALOG_QUESTION);
462   add_sized (factory, dialog_warning, GTK_ICON_SIZE_DIALOG, GTK_STOCK_DIALOG_WARNING);
463   
464   /* dnd size only */
465   add_sized (factory, stock_new, GTK_ICON_SIZE_DND, GTK_STOCK_DND);
466   add_sized (factory, stock_dnd_multiple, GTK_ICON_SIZE_DND, GTK_STOCK_DND_MULTIPLE);
467   
468   /* Only have button sizes */
469   add_sized (factory, stock_button_apply, GTK_ICON_SIZE_BUTTON, GTK_STOCK_APPLY);
470   add_sized (factory, stock_button_cancel, GTK_ICON_SIZE_BUTTON, GTK_STOCK_CANCEL);
471   add_sized (factory, stock_button_no, GTK_ICON_SIZE_BUTTON, GTK_STOCK_NO);
472   add_sized (factory, stock_button_ok, GTK_ICON_SIZE_BUTTON, GTK_STOCK_OK);
473   add_sized (factory, stock_button_yes, GTK_ICON_SIZE_BUTTON, GTK_STOCK_YES);
474
475   /* Generic + button sizes */
476   add_sized_with_fallback (factory,
477                            stock_close,
478                            stock_button_close,
479                            GTK_ICON_SIZE_BUTTON,
480                            GTK_STOCK_CLOSE);
481
482   /* Generic + menu sizes */  
483
484   add_sized_with_fallback (factory,
485                            stock_print_preview,
486                            stock_menu_print_preview,
487                            GTK_ICON_SIZE_MENU,
488                            GTK_STOCK_PRINT_PREVIEW);
489
490   add_sized_with_fallback (factory,
491                            stock_sort_descending,
492                            stock_menu_sort_descending,
493                            GTK_ICON_SIZE_MENU,
494                            GTK_STOCK_SORT_DESCENDING);
495   
496
497   add_sized_with_fallback (factory,
498                            stock_sort_ascending,
499                            stock_menu_sort_ascending,
500                            GTK_ICON_SIZE_MENU,
501                            GTK_STOCK_SORT_ASCENDING);
502   
503 /* Generic size only */
504
505   add_unsized (factory, stock_add, GTK_STOCK_ADD);
506   add_unsized (factory, stock_align_center, GTK_STOCK_JUSTIFY_CENTER);
507   add_unsized (factory, stock_align_justify, GTK_STOCK_JUSTIFY_FILL);
508   add_unsized (factory, stock_align_left, GTK_STOCK_JUSTIFY_LEFT);
509   add_unsized (factory, stock_align_right, GTK_STOCK_JUSTIFY_RIGHT);
510   add_unsized (factory, stock_bottom, GTK_STOCK_GOTO_BOTTOM);  
511   add_unsized (factory, stock_cdrom, GTK_STOCK_CDROM);
512   add_unsized (factory, stock_clear, GTK_STOCK_CLEAR);
513   add_unsized (factory, stock_colorselector, GTK_STOCK_SELECT_COLOR);
514   add_unsized (factory, stock_convert, GTK_STOCK_CONVERT);
515   add_unsized (factory, stock_copy, GTK_STOCK_COPY);
516   add_unsized (factory, stock_cut, GTK_STOCK_CUT);
517   add_unsized (factory, stock_down_arrow, GTK_STOCK_GO_DOWN);
518   add_unsized (factory, stock_exec, GTK_STOCK_EXECUTE);
519   add_unsized (factory, stock_exit, GTK_STOCK_QUIT);
520   add_unsized (factory, stock_first, GTK_STOCK_GOTO_FIRST);
521   add_unsized (factory, stock_font, GTK_STOCK_SELECT_FONT);
522   add_unsized (factory, stock_help, GTK_STOCK_HELP);
523   add_unsized (factory, stock_home, GTK_STOCK_HOME);
524   add_unsized (factory, stock_index, GTK_STOCK_INDEX);
525   add_unsized (factory, stock_jump_to, GTK_STOCK_JUMP_TO);
526   add_unsized (factory, stock_last, GTK_STOCK_GOTO_LAST);
527   add_unsized (factory, stock_left_arrow, GTK_STOCK_GO_BACK);
528   add_unsized (factory, stock_new, GTK_STOCK_NEW);
529   add_unsized (factory, stock_open, GTK_STOCK_OPEN);
530   add_unsized (factory, stock_paste, GTK_STOCK_PASTE);
531   add_unsized (factory, stock_preferences, GTK_STOCK_PREFERENCES);
532   add_unsized (factory, stock_print, GTK_STOCK_PRINT);
533   add_unsized (factory, stock_properties, GTK_STOCK_PROPERTIES);
534   add_unsized (factory, stock_redo, GTK_STOCK_REDO);
535   add_unsized (factory, stock_refresh, GTK_STOCK_REFRESH);
536   add_unsized (factory, stock_remove, GTK_STOCK_REMOVE);
537   add_unsized (factory, stock_revert, GTK_STOCK_REVERT_TO_SAVED);
538   add_unsized (factory, stock_right_arrow, GTK_STOCK_GO_FORWARD);
539   add_unsized (factory, stock_save, GTK_STOCK_FLOPPY);
540   add_unsized (factory, stock_save, GTK_STOCK_SAVE);
541   add_unsized (factory, stock_save_as, GTK_STOCK_SAVE_AS);
542   add_unsized (factory, stock_search, GTK_STOCK_FIND);
543   add_unsized (factory, stock_search_replace, GTK_STOCK_FIND_AND_REPLACE);
544   add_unsized (factory, stock_spellcheck, GTK_STOCK_SPELL_CHECK);
545   add_unsized (factory, stock_stop, GTK_STOCK_STOP);
546   add_unsized (factory, stock_text_bold, GTK_STOCK_BOLD);
547   add_unsized (factory, stock_text_italic, GTK_STOCK_ITALIC);
548   add_unsized (factory, stock_text_strikeout, GTK_STOCK_STRIKETHROUGH);
549   add_unsized (factory, stock_text_underline, GTK_STOCK_UNDERLINE);
550   add_unsized (factory, stock_top, GTK_STOCK_GOTO_TOP);
551   add_unsized (factory, stock_trash, GTK_STOCK_DELETE);
552   add_unsized (factory, stock_undelete, GTK_STOCK_UNDELETE);
553   add_unsized (factory, stock_undo, GTK_STOCK_UNDO);
554   add_unsized (factory, stock_up_arrow, GTK_STOCK_GO_UP);
555   add_unsized (factory, stock_zoom_1, GTK_STOCK_ZOOM_100);
556   add_unsized (factory, stock_zoom_fit, GTK_STOCK_ZOOM_FIT);
557   add_unsized (factory, stock_zoom_in, GTK_STOCK_ZOOM_IN);
558   add_unsized (factory, stock_zoom_out, GTK_STOCK_ZOOM_OUT);
559 }
560
561 /* Sizes */
562
563 typedef struct _IconSize IconSize;
564
565 struct _IconSize
566 {
567   gint size;
568   gchar *name;
569   
570   gint width;
571   gint height;
572 };
573
574 typedef struct _IconAlias IconAlias;
575
576 struct _IconAlias
577 {
578   gchar *name;
579   gint   target;
580 };
581
582 static GHashTable *icon_aliases = NULL;
583 static IconSize *icon_sizes = NULL;
584 static gint      icon_sizes_allocated = 0;
585 static gint      icon_sizes_used = 0;
586
587 static void
588 init_icon_sizes (void)
589 {
590   if (icon_sizes == NULL)
591     {
592 #define NUM_BUILTIN_SIZES 7
593       gint i;
594
595       icon_aliases = g_hash_table_new (g_str_hash, g_str_equal);
596       
597       icon_sizes = g_new (IconSize, NUM_BUILTIN_SIZES);
598       icon_sizes_allocated = NUM_BUILTIN_SIZES;
599       icon_sizes_used = NUM_BUILTIN_SIZES;
600
601       icon_sizes[GTK_ICON_SIZE_INVALID].size = 0;
602       icon_sizes[GTK_ICON_SIZE_INVALID].name = NULL;
603       icon_sizes[GTK_ICON_SIZE_INVALID].width = 0;
604       icon_sizes[GTK_ICON_SIZE_INVALID].height = 0;
605
606       /* the name strings aren't copied since we don't ever remove
607        * icon sizes, so we don't need to know whether they're static.
608        * Even if we did I suppose removing the builtin sizes would be
609        * disallowed.
610        */
611       
612       icon_sizes[GTK_ICON_SIZE_MENU].size = GTK_ICON_SIZE_MENU;
613       icon_sizes[GTK_ICON_SIZE_MENU].name = "gtk-menu";
614       icon_sizes[GTK_ICON_SIZE_MENU].width = 16;
615       icon_sizes[GTK_ICON_SIZE_MENU].height = 16;
616
617       icon_sizes[GTK_ICON_SIZE_BUTTON].size = GTK_ICON_SIZE_BUTTON;
618       icon_sizes[GTK_ICON_SIZE_BUTTON].name = "gtk-button";
619       icon_sizes[GTK_ICON_SIZE_BUTTON].width = 24;
620       icon_sizes[GTK_ICON_SIZE_BUTTON].height = 24;
621
622       icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].size = GTK_ICON_SIZE_SMALL_TOOLBAR;
623       icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].name = "gtk-small-toolbar";
624       icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].width = 18;
625       icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].height = 18;
626       
627       icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].size = GTK_ICON_SIZE_LARGE_TOOLBAR;
628       icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].name = "gtk-large-toolbar";
629       icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].width = 24;
630       icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].height = 24;
631
632       icon_sizes[GTK_ICON_SIZE_DND].size = GTK_ICON_SIZE_DND;
633       icon_sizes[GTK_ICON_SIZE_DND].name = "gtk-dnd";
634       icon_sizes[GTK_ICON_SIZE_DND].width = 32;
635       icon_sizes[GTK_ICON_SIZE_DND].height = 32;
636
637       icon_sizes[GTK_ICON_SIZE_DIALOG].size = GTK_ICON_SIZE_DIALOG;
638       icon_sizes[GTK_ICON_SIZE_DIALOG].name = "gtk-dialog";
639       icon_sizes[GTK_ICON_SIZE_DIALOG].width = 48;
640       icon_sizes[GTK_ICON_SIZE_DIALOG].height = 48;
641
642       g_assert ((GTK_ICON_SIZE_DIALOG + 1) == NUM_BUILTIN_SIZES);
643
644       /* Alias everything to itself. */
645       i = 1; /* skip invalid size */
646       while (i < NUM_BUILTIN_SIZES)
647         {
648           gtk_icon_size_register_alias (icon_sizes[i].name, icon_sizes[i].size);
649           
650           ++i;
651         }
652       
653 #undef NUM_BUILTIN_SIZES
654     }
655 }
656
657 /**
658  * gtk_icon_size_lookup:
659  * @size: an icon size
660  * @width: location to store icon width
661  * @height: location to store icon height
662  *
663  * Obtains the pixel size of a semantic icon size, normally @size would be
664  * #GTK_ICON_SIZE_MENU, #GTK_ICON_SIZE_BUTTON, etc.  This function
665  * isn't normally needed, gtk_widget_render_icon() is the usual
666  * way to get an icon for rendering, then just look at the size of
667  * the rendered pixbuf. The rendered pixbuf may not even correspond to
668  * the width/height returned by gtk_icon_size_lookup(), because themes
669  * are free to render the pixbuf however they like, including changing
670  * the usual size.
671  * 
672  * Return value: %TRUE if @size was a valid size
673  **/
674 gboolean
675 gtk_icon_size_lookup (GtkIconSize  size,
676                       gint        *widthp,
677                       gint        *heightp)
678 {
679   init_icon_sizes ();
680
681   if (size >= icon_sizes_used)
682     return FALSE;
683
684   if (size == GTK_ICON_SIZE_INVALID)
685     return FALSE;
686   
687   if (widthp)
688     *widthp = icon_sizes[size].width;
689
690   if (heightp)
691     *heightp = icon_sizes[size].height;
692
693   return TRUE;
694 }
695
696 /**
697  * gtk_icon_size_register:
698  * @name: name of the icon size
699  * @width: the icon width
700  * @height: the icon height
701  *
702  * Registers a new icon size, along the same lines as #GTK_ICON_SIZE_MENU,
703  * etc. Returns the integer value for the size.
704  *
705  * Returns: integer value representing the size
706  * 
707  **/
708 GtkIconSize
709 gtk_icon_size_register (const gchar *name,
710                         gint         width,
711                         gint         height)
712 {
713   g_return_val_if_fail (name != NULL, 0);
714   g_return_val_if_fail (width > 0, 0);
715   g_return_val_if_fail (height > 0, 0);
716   
717   init_icon_sizes ();
718
719   if (icon_sizes_used == icon_sizes_allocated)
720     {
721       icon_sizes_allocated *= 2;
722       icon_sizes = g_renew (IconSize, icon_sizes, icon_sizes_allocated);
723     }
724   
725   icon_sizes[icon_sizes_used].size = icon_sizes_used;
726   icon_sizes[icon_sizes_used].name = g_strdup (name);
727   icon_sizes[icon_sizes_used].width = width;
728   icon_sizes[icon_sizes_used].height = height;
729
730   ++icon_sizes_used;
731
732   /* alias to self. */
733   gtk_icon_size_register_alias (name, icon_sizes_used - 1);
734   
735   return icon_sizes_used - 1;
736 }
737
738 /**
739  * gtk_icon_size_register_alias:
740  * @alias: an alias for @target
741  * @target: an existing icon size
742  *
743  * Registers @alias as another name for @target.
744  * So calling gtk_icon_size_from_name() with @alias as argument
745  * will return @target.
746  *
747  **/
748 void
749 gtk_icon_size_register_alias (const gchar *alias,
750                               GtkIconSize  target)
751 {
752   IconAlias *ia;
753   
754   g_return_if_fail (alias != NULL);
755
756   init_icon_sizes ();
757
758   if (g_hash_table_lookup (icon_aliases, alias))
759     g_warning ("gtk_icon_size_register_alias: Icon size name '%s' already exists", alias);
760
761   if (!gtk_icon_size_lookup (target, NULL, NULL))
762     g_warning ("gtk_icon_size_register_alias: Icon size %d does not exist", target);
763   
764   ia = g_new (IconAlias, 1);
765   ia->name = g_strdup (alias);
766   ia->target = target;
767
768   g_hash_table_insert (icon_aliases, ia->name, ia);
769 }
770
771 GtkIconSize
772 gtk_icon_size_from_name (const gchar *name)
773 {
774   IconAlias *ia;
775
776   init_icon_sizes ();
777   
778   ia = g_hash_table_lookup (icon_aliases, name);
779
780   if (ia)
781     return ia->target;
782   else
783     return GTK_ICON_SIZE_INVALID;
784 }
785
786 G_CONST_RETURN gchar*
787 gtk_icon_size_get_name (GtkIconSize  size)
788 {
789   if (size >= icon_sizes_used)
790     return NULL;
791   else
792     return icon_sizes[size].name;
793 }
794
795 /* Icon Set */
796
797
798 /* Clear icon set contents, drop references to all contained
799  * GdkPixbuf objects and forget all GtkIconSources. Used to
800  * recycle an icon set.
801  */
802 static GdkPixbuf *find_in_cache     (GtkIconSet       *icon_set,
803                                      GtkStyle         *style,
804                                      GtkTextDirection  direction,
805                                      GtkStateType      state,
806                                      GtkIconSize       size);
807 static void       add_to_cache      (GtkIconSet       *icon_set,
808                                      GtkStyle         *style,
809                                      GtkTextDirection  direction,
810                                      GtkStateType      state,
811                                      GtkIconSize       size,
812                                      GdkPixbuf        *pixbuf);
813 static void       clear_cache       (GtkIconSet       *icon_set,
814                                      gboolean          style_detach);
815 static GSList*    copy_cache        (GtkIconSet       *icon_set,
816                                      GtkIconSet       *copy_recipient);
817 static void       attach_to_style   (GtkIconSet       *icon_set,
818                                      GtkStyle         *style);
819 static void       detach_from_style (GtkIconSet       *icon_set,
820                                      GtkStyle         *style);
821 static void       style_dnotify     (gpointer          data);
822
823 struct _GtkIconSet
824 {
825   guint ref_count;
826
827   GSList *sources;
828
829   /* Cache of the last few rendered versions of the icon. */
830   GSList *cache;
831
832   guint cache_size;
833
834   guint cache_serial;
835 };
836
837 static guint cache_serial = 0;
838
839 /**
840  * gtk_icon_set_new:
841  * 
842  * Creates a new #GtkIconSet. A #GtkIconSet represents a single icon
843  * in various sizes and widget states. It can provide a #GdkPixbuf
844  * for a given size and state on request, and automatically caches
845  * some of the rendered #GdkPixbuf objects.
846  *
847  * Normally you would use gtk_widget_render_icon() instead of
848  * using #GtkIconSet directly. The one case where you'd use
849  * #GtkIconSet is to create application-specific icon sets to place in
850  * a #GtkIconFactory.
851  * 
852  * Return value: a new #GtkIconSet
853  **/
854 GtkIconSet*
855 gtk_icon_set_new (void)
856 {
857   GtkIconSet *icon_set;
858
859   icon_set = g_new (GtkIconSet, 1);
860
861   icon_set->ref_count = 1;
862   icon_set->sources = NULL;
863   icon_set->cache = NULL;
864   icon_set->cache_size = 0;
865   icon_set->cache_serial = cache_serial;
866   
867   return icon_set;
868 }
869
870 /**
871  * gtk_icon_set_new_from_pixbuf:
872  * @pixbuf: a #GdkPixbuf
873  * 
874  * Creates a new #GtkIconSet with @pixbuf as the default/fallback
875  * source image. If you don't add any additional #GtkIconSource to the
876  * icon set, all variants of the icon will be created from @pixbuf,
877  * using scaling, pixelation, etc. as required to adjust the icon size
878  * or make the icon look insensitive/prelighted.
879  * 
880  * Return value: a new #GtkIconSet
881  **/
882 GtkIconSet *
883 gtk_icon_set_new_from_pixbuf (GdkPixbuf *pixbuf)
884 {
885   GtkIconSet *set;
886
887   GtkIconSource source = { NULL, NULL, 0, 0, 0,
888                            TRUE, TRUE, TRUE };
889
890   g_return_val_if_fail (pixbuf != NULL, NULL);
891
892   set = gtk_icon_set_new ();
893
894   source.pixbuf = pixbuf;
895
896   gtk_icon_set_add_source (set, &source);
897   
898   return set;
899 }
900
901
902 /**
903  * gtk_icon_set_ref:
904  * @icon_set: a #GtkIconSet
905  * 
906  * Increments the reference count on @icon_set
907  * 
908  * Return value: @icon_set is returned
909  **/
910 GtkIconSet*
911 gtk_icon_set_ref (GtkIconSet *icon_set)
912 {
913   g_return_val_if_fail (icon_set != NULL, NULL);
914   g_return_val_if_fail (icon_set->ref_count > 0, NULL);
915
916   icon_set->ref_count += 1;
917
918   return icon_set;
919 }
920
921 /**
922  * gtk_icon_set_unref:
923  * @icon_set: a #GtkIconSet
924  * 
925  * Decrements the reference count on @icon_set, and frees memory
926  * if the reference count reaches 0.
927  **/
928 void
929 gtk_icon_set_unref (GtkIconSet *icon_set)
930 {
931   g_return_if_fail (icon_set != NULL);
932   g_return_if_fail (icon_set->ref_count > 0);
933
934   icon_set->ref_count -= 1;
935
936   if (icon_set->ref_count == 0)
937     {
938       GSList *tmp_list = icon_set->sources;
939       while (tmp_list != NULL)
940         {
941           gtk_icon_source_free (tmp_list->data);
942
943           tmp_list = g_slist_next (tmp_list);
944         }
945
946       clear_cache (icon_set, TRUE);
947
948       g_free (icon_set);
949     }
950 }
951
952 /**
953  * gtk_icon_set_copy:
954  * @icon_set: a #GtkIconSet
955  * 
956  * Copies @icon_set by value. 
957  * 
958  * Return value: a new #GtkIconSet identical to the first.
959  **/
960 GtkIconSet*
961 gtk_icon_set_copy (GtkIconSet *icon_set)
962 {
963   GtkIconSet *copy;
964   GSList *tmp_list;
965   
966   copy = gtk_icon_set_new ();
967
968   tmp_list = icon_set->sources;
969   while (tmp_list != NULL)
970     {
971       copy->sources = g_slist_prepend (copy->sources,
972                                        gtk_icon_source_copy (tmp_list->data));
973
974       tmp_list = g_slist_next (tmp_list);
975     }
976
977   copy->sources = g_slist_reverse (copy->sources);
978
979   copy->cache = copy_cache (icon_set, copy);
980   copy->cache_size = icon_set->cache_size;
981   copy->cache_serial = icon_set->cache_serial;
982   
983   return copy;
984 }
985
986
987 static gboolean
988 sizes_equivalent (GtkIconSize lhs,
989                   GtkIconSize rhs)
990 {
991   gint r_w, r_h, l_w, l_h;
992
993   gtk_icon_size_lookup (rhs, &r_w, &r_h);
994   gtk_icon_size_lookup (lhs, &l_w, &l_h);
995
996   return r_w == l_w && r_h == l_h;
997 }
998
999 static GtkIconSource*
1000 find_and_prep_icon_source (GtkIconSet       *icon_set,
1001                            GtkTextDirection  direction,
1002                            GtkStateType      state,
1003                            GtkIconSize       size)
1004 {
1005   GtkIconSource *source;
1006   GSList *tmp_list;
1007
1008
1009   /* We need to find the best icon source.  Direction matters more
1010    * than state, state matters more than size. icon_set->sources
1011    * is sorted according to wildness, so if we take the first
1012    * match we find it will be the least-wild match (if there are
1013    * multiple matches for a given "wildness" then the RC file contained
1014    * dumb stuff, and we end up with an arbitrary matching source)
1015    */
1016   
1017   source = NULL;
1018   tmp_list = icon_set->sources;
1019   while (tmp_list != NULL)
1020     {
1021       GtkIconSource *s = tmp_list->data;
1022       
1023       if ((s->any_direction || (s->direction == direction)) &&
1024           (s->any_state || (s->state == state)) &&
1025           (s->any_size || (sizes_equivalent (size, s->size))))
1026         {
1027           source = s;
1028           break;
1029         }
1030       
1031       tmp_list = g_slist_next (tmp_list);
1032     }
1033
1034   if (source == NULL)
1035     return NULL;
1036   
1037   if (source->pixbuf == NULL)
1038     {
1039       GError *error = NULL;
1040       
1041       g_assert (source->filename);
1042       source->pixbuf = gdk_pixbuf_new_from_file (source->filename, &error);
1043
1044       if (source->pixbuf == NULL)
1045         {
1046           /* Remove this icon source so we don't keep trying to
1047            * load it.
1048            */
1049           g_warning (_("Error loading icon: %s"), error->message);
1050           g_error_free (error);
1051           
1052           icon_set->sources = g_slist_remove (icon_set->sources, source);
1053
1054           gtk_icon_source_free (source);
1055
1056           /* Try to fall back to other sources */
1057           if (icon_set->sources != NULL)
1058             return find_and_prep_icon_source (icon_set,
1059                                               direction,
1060                                               state,
1061                                               size);
1062           else
1063             return NULL;
1064         }
1065     }
1066
1067   return source;
1068 }
1069
1070 static GdkPixbuf*
1071 render_fallback_image (GtkStyle          *style,
1072                        GtkTextDirection   direction,
1073                        GtkStateType       state,
1074                        GtkIconSize        size,
1075                        GtkWidget         *widget,
1076                        const char        *detail)
1077 {
1078   /* This icon can be used for any direction/state/size */
1079   static GtkIconSource fallback_source = { NULL, NULL, 0, 0, 0, TRUE, TRUE, TRUE };
1080
1081   if (fallback_source.pixbuf == NULL)
1082     fallback_source.pixbuf = gdk_pixbuf_new_from_stream (-1, MISSING_IMAGE_INLINE, FALSE, NULL);
1083   
1084   return gtk_style_render_icon (style,
1085                                 &fallback_source,
1086                                 direction,
1087                                 state,
1088                                 size,
1089                                 widget,
1090                                 detail);
1091 }
1092
1093 /**
1094  * gtk_icon_set_render_icon:
1095  * @icon_set: a #GtkIconSet
1096  * @style: a #GtkStyle associated with @widget, or %NULL
1097  * @direction: text direction
1098  * @state: widget state
1099  * @size: icon size
1100  * @widget: widget that will display the icon, or %NULL
1101  * @detail: detail to pass to the theme engine, or %NULL
1102  * 
1103  * Renders an icon using gtk_style_render_icon(). In most cases,
1104  * gtk_widget_render_icon() is better, since it automatically provides
1105  * most of the arguments from the current widget settings.  This
1106  * function never returns %NULL; if the icon can't be rendered
1107  * (perhaps because an image file fails to load), a default "missing
1108  * image" icon will be returned instead.
1109  * 
1110  * Return value: a #GdkPixbuf to be displayed
1111  **/
1112 GdkPixbuf*
1113 gtk_icon_set_render_icon (GtkIconSet        *icon_set,
1114                           GtkStyle          *style,
1115                           GtkTextDirection   direction,
1116                           GtkStateType       state,
1117                           GtkIconSize        size,
1118                           GtkWidget         *widget,
1119                           const char        *detail)
1120 {
1121   GdkPixbuf *icon;
1122   GtkIconSource *source;
1123   
1124   g_return_val_if_fail (icon_set != NULL, NULL);
1125   g_return_val_if_fail (GTK_IS_STYLE (style), NULL);
1126
1127   if (icon_set->sources == NULL)
1128     return render_fallback_image (style, direction, state, size, widget, detail);
1129   
1130   icon = find_in_cache (icon_set, style, direction,
1131                         state, size);
1132
1133   if (icon)
1134     {
1135       g_object_ref (G_OBJECT (icon));
1136       return icon;
1137     }
1138
1139   
1140   source = find_and_prep_icon_source (icon_set, direction, state, size);
1141
1142   if (source == NULL)
1143     return render_fallback_image (style, direction, state, size, widget, detail);
1144
1145   g_assert (source->pixbuf != NULL);
1146   
1147   icon = gtk_style_render_icon (style,
1148                                 source,
1149                                 direction,
1150                                 state,
1151                                 size,
1152                                 widget,
1153                                 detail);
1154
1155   if (icon == NULL)
1156     {
1157       g_warning ("Theme engine failed to render icon");
1158       return NULL;
1159     }
1160   
1161   add_to_cache (icon_set, style, direction, state, size, icon);
1162   
1163   return icon;
1164 }
1165
1166 /* Order sources by their "wildness", so that "wilder" sources are
1167  * greater than "specific" sources; for determining ordering,
1168  * direction beats state beats size.
1169  */
1170
1171 static int
1172 icon_source_compare (gconstpointer ap, gconstpointer bp)
1173 {
1174   const GtkIconSource *a = ap;
1175   const GtkIconSource *b = bp;
1176
1177   if (!a->any_direction && b->any_direction)
1178     return -1;
1179   else if (a->any_direction && !b->any_direction)
1180     return 1;
1181   else if (!a->any_state && b->any_state)
1182     return -1;
1183   else if (a->any_state && !b->any_state)
1184     return 1;
1185   else if (!a->any_size && b->any_size)
1186     return -1;
1187   else if (a->any_size && !b->any_size)
1188     return 1;
1189   else
1190     return 0;
1191 }
1192
1193 /**
1194  * gtk_icon_set_add_source:
1195  * @icon_set: a #GtkIconSet
1196  * @source: a #GtkIconSource
1197  *
1198  * Icon sets have a list of #GtkIconSource, which they use as base
1199  * icons for rendering icons in different states and sizes. Icons are
1200  * scaled, made to look insensitive, etc. in
1201  * gtk_icon_set_render_icon(), but #GtkIconSet needs base images to
1202  * work with. The base images and when to use them are described by
1203  * a #GtkIconSource.
1204  * 
1205  * This function copies @source, so you can reuse the same source immediately
1206  * without affecting the icon set.
1207  *
1208  * An example of when you'd use this function: a web browser's "Back
1209  * to Previous Page" icon might point in a different direction in
1210  * Hebrew and in English; it might look different when insensitive;
1211  * and it might change size depending on toolbar mode (small/large
1212  * icons). So a single icon set would contain all those variants of
1213  * the icon, and you might add a separate source for each one.
1214  *
1215  * You should nearly always add a "default" icon source with all
1216  * fields wildcarded, which will be used as a fallback if no more
1217  * specific source matches. #GtkIconSet always prefers more specific
1218  * icon sources to more generic icon sources. The order in which you
1219  * add the sources to the icon set does not matter.
1220  *
1221  * gtk_icon_set_new_from_pixbuf() creates a new icon set with a
1222  * default icon source based on the given pixbuf.
1223  * 
1224  **/
1225 void
1226 gtk_icon_set_add_source (GtkIconSet *icon_set,
1227                          const GtkIconSource *source)
1228 {
1229   g_return_if_fail (icon_set != NULL);
1230   g_return_if_fail (source != NULL);
1231
1232   if (source->pixbuf == NULL &&
1233       source->filename == NULL)
1234     {
1235       g_warning ("Useless GtkIconSource contains NULL filename and pixbuf");
1236       return;
1237     }
1238   
1239   icon_set->sources = g_slist_insert_sorted (icon_set->sources,
1240                                              gtk_icon_source_copy (source),
1241                                              icon_source_compare);
1242 }
1243
1244 /**
1245  * gtk_icon_set_get_sizes:
1246  * @icon_set: a #GtkIconSet
1247  * @sizes: return location for array of sizes
1248  * @n_sizes: location to store number of elements in returned array
1249  *
1250  * Obtains a list of icon sizes this icon set can render. The returned
1251  * array must be freed with g_free().
1252  * 
1253  **/
1254 void
1255 gtk_icon_set_get_sizes (GtkIconSet   *icon_set,
1256                         GtkIconSize **sizes,
1257                         gint         *n_sizes)
1258 {
1259   GSList *tmp_list;
1260   gboolean all_sizes = FALSE;
1261   GSList *specifics = NULL;
1262   
1263   g_return_if_fail (icon_set != NULL);
1264   g_return_if_fail (sizes != NULL);
1265   g_return_if_fail (n_sizes != NULL);
1266   
1267   tmp_list = icon_set->sources;
1268   while (tmp_list != NULL)
1269     {
1270       GtkIconSource *source;
1271
1272       source = tmp_list->data;
1273
1274       if (source->any_size)
1275         {
1276           all_sizes = TRUE;
1277           break;
1278         }
1279       else
1280         specifics = g_slist_prepend (specifics, GINT_TO_POINTER (source->size));
1281       
1282       tmp_list = g_slist_next (tmp_list);
1283     }
1284
1285   if (all_sizes)
1286     {
1287       /* Need to find out what sizes exist */
1288       gint i;
1289
1290       init_icon_sizes ();
1291       
1292       *sizes = g_new (GtkIconSize, icon_sizes_used);
1293       *n_sizes = icon_sizes_used;
1294       
1295       i = 0;      
1296       while (i < icon_sizes_used)
1297         {
1298           (*sizes)[i] = icon_sizes[i].size;
1299           ++i;
1300         }
1301     }
1302   else
1303     {
1304       gint i;
1305       
1306       *n_sizes = g_slist_length (specifics);
1307       *sizes = g_new (GtkIconSize, *n_sizes);
1308
1309       i = 0;
1310       tmp_list = specifics;
1311       while (tmp_list != NULL)
1312         {
1313           (*sizes)[i] = GPOINTER_TO_INT (tmp_list->data);
1314
1315           ++i;
1316           tmp_list = g_slist_next (tmp_list);
1317         }
1318     }
1319
1320   g_slist_free (specifics);
1321 }
1322
1323
1324 /**
1325  * gtk_icon_source_new:
1326  * 
1327  * Creates a new #GtkIconSource. A #GtkIconSource contains a #GdkPixbuf (or
1328  * image filename) that serves as the base image for one or more of the
1329  * icons in a #GtkIconSet, along with a specification for which icons in the
1330  * icon set will be based on that pixbuf or image file. An icon set contains
1331  * a set of icons that represent "the same" logical concept in different states,
1332  * different global text directions, and different sizes.
1333  * 
1334  * So for example a web browser's "Back to Previous Page" icon might
1335  * point in a different direction in Hebrew and in English; it might
1336  * look different when insensitive; and it might change size depending
1337  * on toolbar mode (small/large icons). So a single icon set would
1338  * contain all those variants of the icon. #GtkIconSet contains a list
1339  * of #GtkIconSource from which it can derive specific icon variants in
1340  * the set. 
1341  *
1342  * In the simplest case, #GtkIconSet contains one source pixbuf from
1343  * which it derives all variants. The convenience function
1344  * gtk_icon_set_new_from_pixbuf() handles this case; if you only have
1345  * one source pixbuf, just use that function.
1346  *
1347  * If you want to use a different base pixbuf for different icon
1348  * variants, you create multiple icon sources, mark which variants
1349  * they'll be used to create, and add them to the icon set with
1350  * gtk_icon_set_add_source().
1351  *
1352  * By default, the icon source has all parameters wildcarded. That is,
1353  * the icon source will be used as the base icon for any desired text
1354  * direction, widget state, or icon size.
1355  * 
1356  * Return value: a new #GtkIconSource
1357  **/
1358 GtkIconSource*
1359 gtk_icon_source_new (void)
1360 {
1361   GtkIconSource *src;
1362   
1363   src = g_new0 (GtkIconSource, 1);
1364
1365   src->direction = GTK_TEXT_DIR_NONE;
1366   src->size = GTK_ICON_SIZE_INVALID;
1367   src->state = GTK_STATE_NORMAL;
1368   
1369   src->any_direction = TRUE;
1370   src->any_state = TRUE;
1371   src->any_size = TRUE;
1372   
1373   return src;
1374 }
1375
1376 /**
1377  * gtk_icon_source_copy:
1378  * @source: a #GtkIconSource
1379  * 
1380  * Creates a copy of @source; mostly useful for language bindings.
1381  * 
1382  * Return value: a new #GtkIconSource
1383  **/
1384 GtkIconSource*
1385 gtk_icon_source_copy (const GtkIconSource *source)
1386 {
1387   GtkIconSource *copy;
1388   
1389   g_return_val_if_fail (source != NULL, NULL);
1390
1391   copy = g_new (GtkIconSource, 1);
1392
1393   *copy = *source;
1394   
1395   copy->filename = g_strdup (source->filename);
1396   copy->size = source->size;
1397   if (copy->pixbuf)
1398     g_object_ref (G_OBJECT (copy->pixbuf));
1399
1400   return copy;
1401 }
1402
1403 /**
1404  * gtk_icon_source_free:
1405  * @source: a #GtkIconSource
1406  * 
1407  * Frees a dynamically-allocated icon source, along with its
1408  * filename, size, and pixbuf fields if those are not %NULL.
1409  **/
1410 void
1411 gtk_icon_source_free (GtkIconSource *source)
1412 {
1413   g_return_if_fail (source != NULL);
1414
1415   g_free ((char*) source->filename);
1416   if (source->pixbuf)
1417     g_object_unref (G_OBJECT (source->pixbuf));
1418
1419   g_free (source);
1420 }
1421
1422 /**
1423  * gtk_icon_source_set_filename:
1424  * @source: a #GtkIconSource
1425  * @filename: image file to use
1426  *
1427  * Sets the name of an image file to use as a base image when creating
1428  * icon variants for #GtkIconSet. The filename must be absolute. 
1429  **/
1430 void
1431 gtk_icon_source_set_filename (GtkIconSource *source,
1432                               const gchar   *filename)
1433 {
1434   g_return_if_fail (source != NULL);
1435   g_return_if_fail (filename == NULL || g_path_is_absolute (filename));
1436
1437   if (source->filename == filename)
1438     return;
1439   
1440   if (source->filename)
1441     g_free (source->filename);
1442
1443   source->filename = g_strdup (filename);  
1444 }
1445
1446 /**
1447  * gtk_icon_source_set_pixbuf:
1448  * @source: a #GtkIconSource
1449  * @pixbuf: pixbuf to use as a source
1450  *
1451  * Sets a pixbuf to use as a base image when creating icon variants
1452  * for #GtkIconSet. If an icon source has both a filename and a pixbuf
1453  * set, the pixbuf will take priority.
1454  * 
1455  **/
1456 void
1457 gtk_icon_source_set_pixbuf (GtkIconSource *source,
1458                             GdkPixbuf     *pixbuf)
1459 {
1460   g_return_if_fail (source != NULL);
1461
1462   if (pixbuf)
1463     g_object_ref (G_OBJECT (pixbuf));
1464
1465   if (source->pixbuf)
1466     g_object_unref (G_OBJECT (source->pixbuf));
1467
1468   source->pixbuf = pixbuf;
1469 }
1470
1471 /**
1472  * gtk_icon_source_get_filename:
1473  * @source: a #GtkIconSource
1474  * 
1475  * Retrieves the source filename, or %NULL if none is set.  The
1476  * filename is not a copy, and should not be modified or expected to
1477  * persist beyond the lifetime of the icon source.
1478  * 
1479  * Return value: image filename
1480  **/
1481 G_CONST_RETURN gchar*
1482 gtk_icon_source_get_filename (const GtkIconSource *source)
1483 {
1484   g_return_val_if_fail (source != NULL, NULL);
1485   
1486   return source->filename;
1487 }
1488
1489 /**
1490  * gtk_icon_source_get_pixbuf:
1491  * @source: a #GtkIconSource
1492  * 
1493  * Retrieves the source pixbuf, or %NULL if none is set.
1494  * The reference count on the pixbuf is not incremented.
1495  * 
1496  * Return value: source pixbuf
1497  **/
1498 GdkPixbuf*
1499 gtk_icon_source_get_pixbuf (const GtkIconSource *source)
1500 {
1501   g_return_val_if_fail (source != NULL, NULL);
1502   
1503   return source->pixbuf;
1504 }
1505
1506 /**
1507  * gtk_icon_source_set_direction_wildcarded:
1508  * @source: a #GtkIconSource
1509  * @setting: %TRUE to wildcard the text direction
1510  *
1511  * If the text direction is wildcarded, this source can be used
1512  * as the base image for an icon in any #GtkTextDirection.
1513  * If the text direction is not wildcarded, then the
1514  * text direction the icon source applies to should be set
1515  * with gtk_icon_source_set_direction(), and the icon source
1516  * will only be used with that text direction.
1517  *
1518  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
1519  * wildcarded sources, and will use an exact match when possible.
1520  * 
1521  **/
1522 void
1523 gtk_icon_source_set_direction_wildcarded (GtkIconSource *source,
1524                                           gboolean       setting)
1525 {
1526   g_return_if_fail (source != NULL);
1527
1528   source->any_direction = setting != FALSE;
1529 }
1530
1531 /**
1532  * gtk_icon_source_set_state_wildcarded:
1533  * @source: a #GtkIconSource
1534  * @setting: %TRUE to wildcard the widget state
1535  *
1536  * If the widget state is wildcarded, this source can be used as the
1537  * base image for an icon in any #GtkStateType.  If the widget state
1538  * is not wildcarded, then the state the source applies to should be
1539  * set with gtk_icon_source_set_state() and the icon source will
1540  * only be used with that specific state.
1541  *
1542  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
1543  * wildcarded sources, and will use an exact match when possible.
1544  *
1545  * #GtkIconSet will normally transform wildcarded source images to
1546  * produce an appropriate icon for a given state, for example
1547  * lightening an image on prelight, but will not modify source images
1548  * that match exactly.
1549  **/
1550 void
1551 gtk_icon_source_set_state_wildcarded (GtkIconSource *source,
1552                                       gboolean       setting)
1553 {
1554   g_return_if_fail (source != NULL);
1555
1556   source->any_state = setting != FALSE;
1557 }
1558
1559
1560 /**
1561  * gtk_icon_source_set_size_wildcarded:
1562  * @source: a #GtkIconSource
1563  * @setting: %TRUE to wildcard the widget state
1564  *
1565  * If the icon size is wildcarded, this source can be used as the base
1566  * image for an icon of any size.  If the size is not wildcarded, then
1567  * the size the source applies to should be set with
1568  * gtk_icon_source_set_size() and the icon source will only be used
1569  * with that specific size.
1570  *
1571  * #GtkIconSet prefers non-wildcarded sources (exact matches) over
1572  * wildcarded sources, and will use an exact match when possible.
1573  *
1574  * #GtkIconSet will normally scale wildcarded source images to produce
1575  * an appropriate icon at a given size, but will not change the size
1576  * of source images that match exactly.
1577  **/
1578 void
1579 gtk_icon_source_set_size_wildcarded (GtkIconSource *source,
1580                                      gboolean       setting)
1581 {
1582   g_return_if_fail (source != NULL);
1583
1584   source->any_size = setting != FALSE;  
1585 }
1586
1587 /**
1588  * gtk_icon_source_get_size_wildcarded:
1589  * @source: a #GtkIconSource
1590  * 
1591  * Gets the value set by gtk_icon_source_set_size_wildcarded().
1592  * 
1593  * Return value: %TRUE if this icon source is a base for any icon size variant
1594  **/
1595 gboolean
1596 gtk_icon_source_get_size_wildcarded (const GtkIconSource *source)
1597 {
1598   g_return_val_if_fail (source != NULL, TRUE);
1599   
1600   return source->any_size;
1601 }
1602
1603 /**
1604  * gtk_icon_source_get_state_wildcarded:
1605  * @source: a #GtkIconSource
1606  * 
1607  * Gets the value set by gtk_icon_source_set_state_wildcarded().
1608  * 
1609  * Return value: %TRUE if this icon source is a base for any widget state variant
1610  **/
1611 gboolean
1612 gtk_icon_source_get_state_wildcarded (const GtkIconSource *source)
1613 {
1614   g_return_val_if_fail (source != NULL, TRUE);
1615
1616   return source->any_state;
1617 }
1618
1619 /**
1620  * gtk_icon_source_get_direction_wildcarded:
1621  * @source: a #GtkIconSource
1622  * 
1623  * Gets the value set by gtk_icon_source_set_direction_wildcarded().
1624  * 
1625  * Return value: %TRUE if this icon source is a base for any text direction variant
1626  **/
1627 gboolean
1628 gtk_icon_source_get_direction_wildcarded (const GtkIconSource *source)
1629 {
1630   g_return_val_if_fail (source != NULL, TRUE);
1631
1632   return source->any_direction;
1633 }
1634
1635 /**
1636  * gtk_icon_source_set_direction:
1637  * @source: a #GtkIconSource
1638  * @direction: text direction this source applies to
1639  *
1640  * Sets the text direction this icon source is intended to be used
1641  * with.
1642  * 
1643  * Setting the text direction on an icon source makes no difference
1644  * if the text direction is wildcarded. Therefore, you should usually
1645  * call gtk_icon_source_set_direction_wildcarded() to un-wildcard it
1646  * in addition to calling this function.
1647  * 
1648  **/
1649 void
1650 gtk_icon_source_set_direction (GtkIconSource   *source,
1651                                GtkTextDirection direction)
1652 {
1653   g_return_if_fail (source != NULL);
1654
1655   source->direction = direction;
1656 }
1657
1658 /**
1659  * gtk_icon_source_set_state:
1660  * @source: a #GtkIconSource
1661  * @state: widget state this source applies to
1662  *
1663  * Sets the widget state this icon source is intended to be used
1664  * with.
1665  * 
1666  * Setting the widget state on an icon source makes no difference
1667  * if the state is wildcarded. Therefore, you should usually
1668  * call gtk_icon_source_set_state_wildcarded() to un-wildcard it
1669  * in addition to calling this function.
1670  * 
1671  **/
1672 void
1673 gtk_icon_source_set_state (GtkIconSource *source,
1674                            GtkStateType   state)
1675 {
1676   g_return_if_fail (source != NULL);
1677
1678   source->state = state;
1679 }
1680
1681 /**
1682  * gtk_icon_source_set_size:
1683  * @source: a #GtkIconSource
1684  * @size: icon size this source applies to
1685  *
1686  * Sets the icon size this icon source is intended to be used
1687  * with.
1688  * 
1689  * Setting the icon size on an icon source makes no difference
1690  * if the size is wildcarded. Therefore, you should usually
1691  * call gtk_icon_source_set_size_wildcarded() to un-wildcard it
1692  * in addition to calling this function.
1693  * 
1694  **/
1695 void
1696 gtk_icon_source_set_size (GtkIconSource *source,
1697                           GtkIconSize    size)
1698 {
1699   g_return_if_fail (source != NULL);
1700
1701   source->size = size;
1702 }
1703
1704 /**
1705  * gtk_icon_source_get_direction:
1706  * @source: a #GtkIconSource
1707  * 
1708  * Obtains the text direction this icon source applies to. The return
1709  * value is only useful/meaningful if the text direction is NOT
1710  * wildcarded.
1711  * 
1712  * Return value: text direction this source matches
1713  **/
1714 GtkTextDirection
1715 gtk_icon_source_get_direction (const GtkIconSource *source)
1716 {
1717   g_return_val_if_fail (source != NULL, 0);
1718
1719   return source->direction;
1720 }
1721
1722 /**
1723  * gtk_icon_source_get_state:
1724  * @source: a #GtkIconSource
1725  * 
1726  * Obtains the widget state this icon source applies to. The return
1727  * value is only useful/meaningful if the widget state is NOT
1728  * wildcarded.
1729  * 
1730  * Return value: widget state this source matches
1731  **/
1732 GtkStateType
1733 gtk_icon_source_get_state (const GtkIconSource *source)
1734 {
1735   g_return_val_if_fail (source != NULL, 0);
1736
1737   return source->state;
1738 }
1739
1740 /**
1741  * gtk_icon_source_get_size:
1742  * @source: a #GtkIconSource
1743  * 
1744  * Obtains the icon size this source applies to. The return value
1745  * is only useful/meaningful if the icon size is NOT wildcarded.
1746  * 
1747  * Return value: icon size this source matches.
1748  **/
1749 GtkIconSize
1750 gtk_icon_source_get_size (const GtkIconSource *source)
1751 {
1752   g_return_val_if_fail (source != NULL, 0);
1753
1754   return source->size;
1755 }
1756
1757 /* Note that the logical maximum is 20 per GtkTextDirection, so we could
1758  * eventually set this to >20 to never throw anything out.
1759  */
1760 #define NUM_CACHED_ICONS 8
1761
1762 typedef struct _CachedIcon CachedIcon;
1763
1764 struct _CachedIcon
1765 {
1766   /* These must all match to use the cached pixbuf.
1767    * If any don't match, we must re-render the pixbuf.
1768    */
1769   GtkStyle *style;
1770   GtkTextDirection direction;
1771   GtkStateType state;
1772   GtkIconSize size;
1773
1774   GdkPixbuf *pixbuf;
1775 };
1776
1777 static void
1778 ensure_cache_up_to_date (GtkIconSet *icon_set)
1779 {
1780   if (icon_set->cache_serial != cache_serial)
1781     clear_cache (icon_set, TRUE);
1782 }
1783
1784 static void
1785 cached_icon_free (CachedIcon *icon)
1786 {
1787   g_object_unref (G_OBJECT (icon->pixbuf));
1788
1789   g_free (icon);
1790 }
1791
1792 static GdkPixbuf *
1793 find_in_cache (GtkIconSet      *icon_set,
1794                GtkStyle        *style,
1795                GtkTextDirection direction,
1796                GtkStateType     state,
1797                GtkIconSize      size)
1798 {
1799   GSList *tmp_list;
1800   GSList *prev;
1801
1802   ensure_cache_up_to_date (icon_set);
1803   
1804   prev = NULL;
1805   tmp_list = icon_set->cache;
1806   while (tmp_list != NULL)
1807     {
1808       CachedIcon *icon = tmp_list->data;
1809
1810       if (icon->style == style &&
1811           icon->direction == direction &&
1812           icon->state == state &&
1813           icon->size == size)
1814         {
1815           if (prev)
1816             {
1817               /* Move this icon to the front of the list. */
1818               prev->next = tmp_list->next;
1819               tmp_list->next = icon_set->cache;
1820               icon_set->cache = tmp_list;
1821             }
1822           
1823           return icon->pixbuf;
1824         }
1825           
1826       prev = tmp_list;
1827       tmp_list = g_slist_next (tmp_list);
1828     }
1829
1830   return NULL;
1831 }
1832
1833 static void
1834 add_to_cache (GtkIconSet      *icon_set,
1835               GtkStyle        *style,
1836               GtkTextDirection direction,
1837               GtkStateType     state,
1838               GtkIconSize      size,
1839               GdkPixbuf       *pixbuf)
1840 {
1841   CachedIcon *icon;
1842
1843   ensure_cache_up_to_date (icon_set);
1844   
1845   g_object_ref (G_OBJECT (pixbuf));
1846
1847   /* We have to ref the style, since if the style was finalized
1848    * its address could be reused by another style, creating a
1849    * really weird bug
1850    */
1851   
1852   if (style)
1853     g_object_ref (G_OBJECT (style));
1854   
1855
1856   icon = g_new (CachedIcon, 1);
1857   icon_set->cache = g_slist_prepend (icon_set->cache, icon);
1858
1859   icon->style = style;
1860   icon->direction = direction;
1861   icon->state = state;
1862   icon->size = size;
1863   icon->pixbuf = pixbuf;
1864
1865   if (icon->style)
1866     attach_to_style (icon_set, icon->style);
1867   
1868   if (icon_set->cache_size >= NUM_CACHED_ICONS)
1869     {
1870       /* Remove oldest item in the cache */
1871       
1872       GSList *tmp_list;
1873       
1874       tmp_list = icon_set->cache;
1875
1876       /* Find next-to-last link */
1877       g_assert (NUM_CACHED_ICONS > 2);
1878       while (tmp_list->next->next)
1879         tmp_list = tmp_list->next;
1880
1881       g_assert (tmp_list != NULL);
1882       g_assert (tmp_list->next != NULL);
1883       g_assert (tmp_list->next->next == NULL);
1884
1885       /* Free the last icon */
1886       icon = tmp_list->next->data;
1887
1888       g_slist_free (tmp_list->next);
1889       tmp_list->next = NULL;
1890
1891       cached_icon_free (icon);
1892     }
1893 }
1894
1895 static void
1896 clear_cache (GtkIconSet *icon_set,
1897              gboolean    style_detach)
1898 {
1899   GSList *tmp_list;
1900   GtkStyle *last_style = NULL;
1901
1902   tmp_list = icon_set->cache;
1903   while (tmp_list != NULL)
1904     {
1905       CachedIcon *icon = tmp_list->data;
1906
1907       if (style_detach)
1908         {
1909           /* simple optimization for the case where the cache
1910            * contains contiguous icons from the same style.
1911            * it's safe to call detach_from_style more than
1912            * once on the same style though.
1913            */
1914           if (last_style != icon->style)
1915             {
1916               detach_from_style (icon_set, icon->style);
1917               last_style = icon->style;
1918             }
1919         }
1920       
1921       cached_icon_free (icon);      
1922       
1923       tmp_list = g_slist_next (tmp_list);
1924     }
1925
1926   g_slist_free (icon_set->cache);
1927   icon_set->cache = NULL;
1928   icon_set->cache_size = 0;
1929 }
1930
1931 static GSList*
1932 copy_cache (GtkIconSet *icon_set,
1933             GtkIconSet *copy_recipient)
1934 {
1935   GSList *tmp_list;
1936   GSList *copy = NULL;
1937
1938   ensure_cache_up_to_date (icon_set);
1939   
1940   tmp_list = icon_set->cache;
1941   while (tmp_list != NULL)
1942     {
1943       CachedIcon *icon = tmp_list->data;
1944       CachedIcon *icon_copy = g_new (CachedIcon, 1);
1945
1946       *icon_copy = *icon;
1947
1948       if (icon_copy->style)
1949         attach_to_style (copy_recipient, icon_copy->style);
1950         
1951       g_object_ref (G_OBJECT (icon_copy->pixbuf));
1952
1953       icon_copy->size = icon->size;
1954       
1955       copy = g_slist_prepend (copy, icon_copy);      
1956       
1957       tmp_list = g_slist_next (tmp_list);
1958     }
1959
1960   return g_slist_reverse (copy);
1961 }
1962
1963 static void
1964 attach_to_style (GtkIconSet *icon_set,
1965                  GtkStyle   *style)
1966 {
1967   GHashTable *table;
1968
1969   table = g_object_get_qdata (G_OBJECT (style),
1970                               g_quark_try_string ("gtk-style-icon-sets"));
1971
1972   if (table == NULL)
1973     {
1974       table = g_hash_table_new (NULL, NULL);
1975       g_object_set_qdata_full (G_OBJECT (style),
1976                                g_quark_from_static_string ("gtk-style-icon-sets"),
1977                                table,
1978                                style_dnotify);
1979     }
1980
1981   g_hash_table_insert (table, icon_set, icon_set);
1982 }
1983
1984 static void
1985 detach_from_style (GtkIconSet *icon_set,
1986                    GtkStyle   *style)
1987 {
1988   GHashTable *table;
1989
1990   table = g_object_get_qdata (G_OBJECT (style),
1991                               g_quark_try_string ("gtk-style-icon-sets"));
1992
1993   if (table != NULL)
1994     g_hash_table_remove (table, icon_set);
1995 }
1996
1997 static void
1998 iconsets_foreach (gpointer key,
1999                   gpointer value,
2000                   gpointer user_data)
2001 {
2002   GtkIconSet *icon_set = key;
2003
2004   /* We only need to remove cache entries for the given style;
2005    * but that complicates things because in destroy notify
2006    * we don't know which style got destroyed, and 95% of the
2007    * time all cache entries will have the same style,
2008    * so this is faster anyway.
2009    */
2010   
2011   clear_cache (icon_set, FALSE);
2012 }
2013
2014 static void
2015 style_dnotify (gpointer data)
2016 {
2017   GHashTable *table = data;
2018   
2019   g_hash_table_foreach (table, iconsets_foreach, NULL);
2020
2021   g_hash_table_destroy (table);
2022 }
2023
2024 /* This allows the icon set to detect that its cache is out of date. */
2025 void
2026 _gtk_icon_set_invalidate_caches (void)
2027 {
2028   ++cache_serial;
2029 }
2030
2031 static void
2032 listify_foreach (gpointer key, gpointer value, gpointer data)
2033 {
2034   GSList **list = data;
2035
2036   *list = g_slist_prepend (*list, key);
2037 }
2038
2039 static GSList *
2040 g_hash_table_get_keys (GHashTable *table)
2041 {
2042   GSList *list = NULL;
2043
2044   g_hash_table_foreach (table, listify_foreach, &list);
2045
2046   return list;
2047 }
2048
2049 /**
2050  * _gtk_icon_factory_list_ids:
2051  * 
2052  * Gets all known IDs stored in an existing icon factory.
2053  * The strings in the returned list aren't copied.
2054  * The list itself should be freed.
2055  * 
2056  * Return value: List of ids in icon factories
2057  **/
2058 GSList*
2059 _gtk_icon_factory_list_ids (void)
2060 {
2061   GSList *tmp_list;
2062   GSList *ids;
2063
2064   ids = NULL;
2065
2066   ensure_default_icons ();
2067   
2068   tmp_list = all_icon_factories;
2069   while (tmp_list != NULL)
2070     {
2071       GSList *these_ids;
2072       
2073       GtkIconFactory *factory = GTK_ICON_FACTORY (tmp_list->data);
2074
2075       these_ids = g_hash_table_get_keys (factory->icons);
2076       
2077       ids = g_slist_concat (ids, these_ids);
2078       
2079       tmp_list = g_slist_next (tmp_list);
2080     }
2081
2082   return ids;
2083 }