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