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