1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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.
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.
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/.
27 #include "gtkiconfactory.h"
28 #include "stock-icons/gtkstockpixbufs.h"
35 static gpointer parent_class = NULL;
37 static void gtk_icon_factory_init (GtkIconFactory *icon_factory);
38 static void gtk_icon_factory_class_init (GtkIconFactoryClass *klass);
39 static void gtk_icon_factory_finalize (GObject *object);
40 static void get_default_icons (GtkIconFactory *icon_factory);
43 gtk_icon_factory_get_type (void)
45 static GType object_type = 0;
49 static const GTypeInfo object_info =
51 sizeof (GtkIconFactoryClass),
53 (GBaseFinalizeFunc) NULL,
54 (GClassInitFunc) gtk_icon_factory_class_init,
55 NULL, /* class_finalize */
56 NULL, /* class_data */
57 sizeof (GtkIconFactory),
59 (GInstanceInitFunc) gtk_icon_factory_init,
62 object_type = g_type_register_static (G_TYPE_OBJECT,
71 gtk_icon_factory_init (GtkIconFactory *factory)
73 factory->icons = g_hash_table_new (g_str_hash, g_str_equal);
77 gtk_icon_factory_class_init (GtkIconFactoryClass *klass)
79 GObjectClass *object_class = G_OBJECT_CLASS (klass);
81 parent_class = g_type_class_peek_parent (klass);
83 object_class->finalize = gtk_icon_factory_finalize;
87 free_icon_set (gpointer key, gpointer value, gpointer data)
90 gtk_icon_set_unref (value);
94 gtk_icon_factory_finalize (GObject *object)
96 GtkIconFactory *factory = GTK_ICON_FACTORY (object);
98 g_hash_table_foreach (factory->icons, free_icon_set, NULL);
100 g_hash_table_destroy (factory->icons);
102 G_OBJECT_CLASS (parent_class)->finalize (object);
106 gtk_icon_factory_new (void)
108 return GTK_ICON_FACTORY (g_object_new (GTK_TYPE_ICON_FACTORY, NULL));
112 gtk_icon_factory_add (GtkIconFactory *factory,
113 const gchar *stock_id,
114 GtkIconSet *icon_set)
116 gpointer old_key = NULL;
117 gpointer old_value = NULL;
119 g_return_if_fail (GTK_IS_ICON_FACTORY (factory));
120 g_return_if_fail (stock_id != NULL);
121 g_return_if_fail (icon_set != NULL);
123 g_hash_table_lookup_extended (factory->icons, stock_id,
124 &old_key, &old_value);
126 if (old_value == icon_set)
129 gtk_icon_set_ref (icon_set);
131 /* GHashTable key memory management is so fantastically broken. */
133 g_hash_table_insert (factory->icons, old_key, icon_set);
135 g_hash_table_insert (factory->icons, g_strdup (stock_id), icon_set);
138 gtk_icon_set_unref (old_value);
142 gtk_icon_factory_lookup (GtkIconFactory *factory,
143 const gchar *stock_id)
145 g_return_val_if_fail (GTK_IS_ICON_FACTORY (factory), NULL);
146 g_return_val_if_fail (stock_id != NULL, NULL);
148 return g_hash_table_lookup (factory->icons, stock_id);
151 static GtkIconFactory *gtk_default_icons = NULL;
152 static GSList *default_factories = NULL;
155 gtk_icon_factory_add_default (GtkIconFactory *factory)
157 g_return_if_fail (GTK_IS_ICON_FACTORY (factory));
159 g_object_ref (G_OBJECT (factory));
161 default_factories = g_slist_prepend (default_factories, factory);
165 gtk_icon_factory_remove_default (GtkIconFactory *factory)
167 g_return_if_fail (GTK_IS_ICON_FACTORY (factory));
169 default_factories = g_slist_remove (default_factories, factory);
171 g_object_unref (G_OBJECT (factory));
175 gtk_icon_factory_lookup_default (const gchar *stock_id)
179 g_return_val_if_fail (stock_id != NULL, NULL);
181 tmp_list = default_factories;
182 while (tmp_list != NULL)
184 GtkIconSet *icon_set =
185 gtk_icon_factory_lookup (GTK_ICON_FACTORY (tmp_list->data),
191 tmp_list = g_slist_next (tmp_list);
194 if (gtk_default_icons == NULL)
196 gtk_default_icons = gtk_icon_factory_new ();
198 get_default_icons (gtk_default_icons);
201 return gtk_icon_factory_lookup (gtk_default_icons, stock_id);
205 sized_icon_set_from_inline (const guchar *inline_data,
210 GtkIconSource source = { NULL, NULL, 0, 0, NULL,
215 set = gtk_icon_set_new ();
217 source.pixbuf = gdk_pixbuf_new_from_inline (inline_data, FALSE, -1);
219 g_assert (source.pixbuf);
221 gtk_icon_set_add_source (set, &source);
223 g_object_unref (G_OBJECT (source.pixbuf));
229 unsized_icon_set_from_inline (const guchar *inline_data)
233 /* This icon can be used for any direction/state/size */
234 GtkIconSource source = { NULL, NULL, 0, 0, 0,
237 set = gtk_icon_set_new ();
239 source.pixbuf = gdk_pixbuf_new_from_inline (inline_data, FALSE, -1);
241 g_assert (source.pixbuf);
243 gtk_icon_set_add_source (set, &source);
245 g_object_unref (G_OBJECT (source.pixbuf));
251 add_sized (GtkIconFactory *factory,
252 const guchar *inline_data,
254 const gchar *stock_id)
258 set = sized_icon_set_from_inline (inline_data, size);
260 gtk_icon_factory_add (factory, stock_id, set);
262 gtk_icon_set_unref (set);
266 add_unsized (GtkIconFactory *factory,
267 const guchar *inline_data,
268 const gchar *stock_id)
272 set = unsized_icon_set_from_inline (inline_data);
274 gtk_icon_factory_add (factory, stock_id, set);
276 gtk_icon_set_unref (set);
280 get_default_icons (GtkIconFactory *factory)
282 /* KEEP IN SYNC with gtkstock.c */
284 add_sized (factory, dialog_error, GTK_ICON_SIZE_DIALOG, GTK_STOCK_DIALOG_ERROR);
285 add_sized (factory, dialog_info, GTK_ICON_SIZE_DIALOG, GTK_STOCK_DIALOG_INFO);
286 add_sized (factory, dialog_question, GTK_ICON_SIZE_DIALOG, GTK_STOCK_DIALOG_QUESTION);
287 add_sized (factory, dialog_warning, GTK_ICON_SIZE_DIALOG, GTK_STOCK_DIALOG_WARNING);
289 add_sized (factory, stock_button_apply, GTK_ICON_SIZE_BUTTON, GTK_STOCK_BUTTON_APPLY);
290 add_sized (factory, stock_button_ok, GTK_ICON_SIZE_BUTTON, GTK_STOCK_BUTTON_OK);
291 add_sized (factory, stock_button_cancel, GTK_ICON_SIZE_BUTTON, GTK_STOCK_BUTTON_CANCEL);
292 add_sized (factory, stock_button_close, GTK_ICON_SIZE_BUTTON, GTK_STOCK_BUTTON_CLOSE);
293 add_sized (factory, stock_button_yes, GTK_ICON_SIZE_BUTTON, GTK_STOCK_BUTTON_YES);
294 add_sized (factory, stock_button_no, GTK_ICON_SIZE_BUTTON, GTK_STOCK_BUTTON_NO);
296 add_unsized (factory, stock_close, GTK_STOCK_CLOSE);
297 add_unsized (factory, stock_exit, GTK_STOCK_QUIT);
298 add_unsized (factory, stock_help, GTK_STOCK_HELP);
299 add_unsized (factory, stock_new, GTK_STOCK_NEW);
300 add_unsized (factory, stock_open, GTK_STOCK_OPEN);
301 add_unsized (factory, stock_save, GTK_STOCK_SAVE);
306 static GHashTable *icon_sizes = NULL;
308 typedef struct _IconSize IconSize;
328 icon_size_new (const gchar *name)
332 is = g_new0 (IconSize, 1);
334 is->name = g_strdup (name);
340 icon_size_free (IconSize *is)
345 g_free (is->d.target);
351 icon_size_insert (IconSize *is)
353 gpointer old_key, old_value;
355 /* Remove old ones */
356 if (g_hash_table_lookup_extended (icon_sizes,
358 &old_key, &old_value))
360 g_hash_table_remove (icon_sizes, is->name);
361 icon_size_free (old_value);
364 g_hash_table_insert (icon_sizes,
370 icon_size_lookup (const gchar *name)
374 is = g_hash_table_lookup (icon_sizes,
377 while (is && is->is_alias)
379 is = g_hash_table_lookup (icon_sizes,
388 icon_size_add (const gchar *name,
394 is = icon_size_new (name);
395 is->d.size.width = width;
396 is->d.size.height = height;
398 icon_size_insert (is);
402 icon_alias_add (const gchar *name,
407 is = icon_size_new (name);
410 is->d.target = g_strdup (target);
412 icon_size_insert (is);
416 init_icon_sizes (void)
418 if (icon_sizes == NULL)
422 icon_sizes = g_hash_table_new (g_str_hash, g_str_equal);
424 icon_size_add (GTK_ICON_SIZE_MENU, 16, 16);
425 icon_size_add (GTK_ICON_SIZE_BUTTON, 24, 24);
426 icon_size_add (GTK_ICON_SIZE_SMALL_TOOLBAR, 18, 18);
427 icon_size_add (GTK_ICON_SIZE_LARGE_TOOLBAR, 24, 24);
428 icon_size_add (GTK_ICON_SIZE_DIALOG, 48, 48);
433 gtk_icon_size_lookup (const gchar *alias,
439 g_return_val_if_fail (alias != NULL, FALSE);
443 is = icon_size_lookup (alias);
449 *widthp = is->d.size.width;
452 *heightp = is->d.size.height;
458 gtk_icon_size_register (const gchar *alias,
462 gpointer old_key, old_value;
464 g_return_if_fail (alias != NULL);
465 g_return_if_fail (width > 0);
466 g_return_if_fail (height > 0);
470 icon_size_add (alias, width, height);
474 gtk_icon_size_register_alias (const gchar *alias,
477 g_return_if_fail (alias != NULL);
478 g_return_if_fail (target != NULL);
482 icon_alias_add (alias, target);
488 /* Clear icon set contents, drop references to all contained
489 * GdkPixbuf objects and forget all GtkIconSources. Used to
490 * recycle an icon set.
492 static void gtk_icon_set_clear (GtkIconSet *icon_set);
494 static GdkPixbuf *find_in_cache (GtkIconSet *icon_set,
496 GtkTextDirection direction,
499 static void add_to_cache (GtkIconSet *icon_set,
501 GtkTextDirection direction,
505 static void clear_cache (GtkIconSet *icon_set,
506 gboolean style_detach);
507 static GSList* copy_cache (GtkIconSet *icon_set,
508 GtkIconSet *copy_recipient);
509 static void attach_to_style (GtkIconSet *icon_set,
511 static void detach_from_style (GtkIconSet *icon_set,
513 static void style_dnotify (gpointer data);
521 /* Cache of the last few rendered versions of the icon. */
529 static guint cache_serial = 0;
532 gtk_icon_set_new (void)
534 GtkIconSet *icon_set;
536 icon_set = g_new (GtkIconSet, 1);
538 icon_set->ref_count = 1;
539 icon_set->sources = NULL;
540 icon_set->cache = NULL;
541 icon_set->cache_size = 0;
542 icon_set->cache_serial = cache_serial;
548 gtk_icon_set_ref (GtkIconSet *icon_set)
550 g_return_val_if_fail (icon_set != NULL, NULL);
551 g_return_val_if_fail (icon_set->ref_count > 0, NULL);
553 icon_set->ref_count += 1;
559 gtk_icon_set_unref (GtkIconSet *icon_set)
561 g_return_if_fail (icon_set != NULL);
562 g_return_if_fail (icon_set->ref_count > 0);
564 icon_set->ref_count -= 1;
566 if (icon_set->ref_count == 0)
568 GSList *tmp_list = icon_set->sources;
569 while (tmp_list != NULL)
571 gtk_icon_source_free (tmp_list->data);
573 tmp_list = g_slist_next (tmp_list);
576 clear_cache (icon_set, TRUE);
583 gtk_icon_set_copy (GtkIconSet *icon_set)
588 copy = gtk_icon_set_new ();
590 tmp_list = icon_set->sources;
591 while (tmp_list != NULL)
593 copy->sources = g_slist_prepend (copy->sources,
594 gtk_icon_source_copy (tmp_list->data));
596 tmp_list = g_slist_next (tmp_list);
599 copy->sources = g_slist_reverse (copy->sources);
601 copy->cache = copy_cache (icon_set, copy);
602 copy->cache_size = icon_set->cache_size;
603 copy->cache_serial = icon_set->cache_serial;
610 sizes_equivalent (const gchar *rhs, const gchar *lhs)
612 gint r_w, r_h, l_w, l_h;
614 gtk_icon_size_lookup (rhs, &r_w, &r_h);
615 gtk_icon_size_lookup (lhs, &l_w, &l_h);
617 return r_w == l_w && r_h == l_h;
620 static GtkIconSource*
621 find_and_prep_icon_source (GtkIconSet *icon_set,
622 GtkTextDirection direction,
626 GtkIconSource *source;
630 /* We need to find the best icon source. Direction matters more
631 * than state, state matters more than size. icon_set->sources
632 * is sorted according to wildness, so if we take the first
633 * match we find it will be the least-wild match (if there are
634 * multiple matches for a given "wildness" then the RC file contained
635 * dumb stuff, and we end up with an arbitrary matching source)
639 tmp_list = icon_set->sources;
640 while (tmp_list != NULL)
642 GtkIconSource *s = tmp_list->data;
644 if ((s->any_direction || (s->direction == direction)) &&
645 (s->any_state || (s->state == state)) &&
646 (s->any_size || (sizes_equivalent (size, s->size))))
652 tmp_list = g_slist_next (tmp_list);
658 if (source->pixbuf == NULL)
662 g_assert (source->filename);
664 if (*source->filename != G_DIR_SEPARATOR)
665 full = gtk_rc_find_pixmap_in_path (NULL, source->filename);
667 full = g_strdup (source->filename);
669 source->pixbuf = gdk_pixbuf_new_from_file (full);
673 if (source->pixbuf == NULL)
675 /* Remove this icon source so we don't keep trying to
678 g_warning ("Failed to load icon '%s'", source->filename);
680 icon_set->sources = g_slist_remove (icon_set->sources, source);
682 gtk_icon_source_free (source);
684 /* Try to fall back to other sources */
685 if (icon_set->sources != NULL)
686 return find_and_prep_icon_source (icon_set,
699 gtk_icon_set_render_icon (GtkIconSet *icon_set,
701 GtkTextDirection direction,
708 GtkIconSource *source;
710 /* FIXME conceivably, everywhere this function
711 * returns NULL, we should return some default
712 * dummy icon like a question mark or the image icon
716 g_return_val_if_fail (icon_set != NULL, NULL);
717 g_return_val_if_fail (GTK_IS_STYLE (style), NULL);
719 if (icon_set->sources == NULL)
722 icon = find_in_cache (icon_set, style, direction,
727 g_object_ref (G_OBJECT (icon));
732 source = find_and_prep_icon_source (icon_set, direction, state, size);
737 g_assert (source->pixbuf != NULL);
739 icon = gtk_style_render_icon (style,
749 g_warning ("Theme engine failed to render icon");
753 add_to_cache (icon_set, style, direction, state, size, icon);
758 /* Order sources by their "wildness", so that "wilder" sources are
759 * greater than "specific" sources; for determining ordering,
760 * direction beats state beats size.
764 icon_source_compare (gconstpointer ap, gconstpointer bp)
766 const GtkIconSource *a = ap;
767 const GtkIconSource *b = bp;
769 if (!a->any_direction && b->any_direction)
771 else if (a->any_direction && !b->any_direction)
773 else if (!a->any_state && b->any_state)
775 else if (a->any_state && !b->any_state)
777 else if (!a->any_size && b->any_size)
779 else if (a->any_size && !b->any_size)
786 gtk_icon_set_add_source (GtkIconSet *icon_set,
787 const GtkIconSource *source)
789 g_return_if_fail (icon_set != NULL);
790 g_return_if_fail (source != NULL);
792 if (source->pixbuf == NULL &&
793 source->filename == NULL)
795 g_warning ("Useless GtkIconSource contains NULL filename and pixbuf");
799 icon_set->sources = g_slist_insert_sorted (icon_set->sources,
800 gtk_icon_source_copy (source),
801 icon_source_compare);
805 gtk_icon_source_copy (const GtkIconSource *source)
809 g_return_val_if_fail (source != NULL, NULL);
811 copy = g_new (GtkIconSource, 1);
815 copy->filename = g_strdup (source->filename);
816 copy->size = g_strdup (source->size);
818 g_object_ref (G_OBJECT (copy->pixbuf));
824 gtk_icon_source_free (GtkIconSource *source)
826 g_return_if_fail (source != NULL);
828 g_free ((char*) source->filename);
829 g_free ((char*) source->size);
831 g_object_unref (G_OBJECT (source->pixbuf));
837 gtk_icon_set_clear (GtkIconSet *icon_set)
841 g_return_if_fail (icon_set != NULL);
843 tmp_list = icon_set->sources;
844 while (tmp_list != NULL)
846 gtk_icon_source_free (tmp_list->data);
848 tmp_list = g_slist_next (tmp_list);
851 clear_cache (icon_set, TRUE);
854 /* Note that the logical maximum is 20 per GtkTextDirection, so we could
855 * eventually set this to >20 to never throw anything out.
857 #define NUM_CACHED_ICONS 8
859 typedef struct _CachedIcon CachedIcon;
863 /* These must all match to use the cached pixbuf.
864 * If any don't match, we must re-render the pixbuf.
867 GtkTextDirection direction;
875 ensure_cache_up_to_date (GtkIconSet *icon_set)
877 if (icon_set->cache_serial != cache_serial)
878 clear_cache (icon_set, TRUE);
882 cached_icon_free (CachedIcon *icon)
885 g_object_unref (G_OBJECT (icon->pixbuf));
891 find_in_cache (GtkIconSet *icon_set,
893 GtkTextDirection direction,
900 ensure_cache_up_to_date (icon_set);
903 tmp_list = icon_set->cache;
904 while (tmp_list != NULL)
906 CachedIcon *icon = tmp_list->data;
908 if (icon->style == style &&
909 icon->direction == direction &&
910 icon->state == state &&
911 strcmp (icon->size, size) == 0)
915 /* Move this icon to the front of the list. */
916 prev->next = tmp_list->next;
917 tmp_list->next = icon_set->cache;
918 icon_set->cache = tmp_list;
925 tmp_list = g_slist_next (tmp_list);
932 add_to_cache (GtkIconSet *icon_set,
934 GtkTextDirection direction,
941 ensure_cache_up_to_date (icon_set);
943 g_object_ref (G_OBJECT (pixbuf));
945 /* We have to ref the style, since if the style was finalized
946 * its address could be reused by another style, creating a
951 g_object_ref (G_OBJECT (style));
954 icon = g_new (CachedIcon, 1);
955 icon_set->cache = g_slist_prepend (icon_set->cache, icon);
958 icon->direction = direction;
960 icon->size = g_strdup (size);
961 icon->pixbuf = pixbuf;
964 attach_to_style (icon_set, icon->style);
966 if (icon_set->cache_size >= NUM_CACHED_ICONS)
968 /* Remove oldest item in the cache */
972 tmp_list = icon_set->cache;
974 /* Find next-to-last link */
975 g_assert (NUM_CACHED_ICONS > 2);
976 while (tmp_list->next->next)
977 tmp_list = tmp_list->next;
979 g_assert (tmp_list != NULL);
980 g_assert (tmp_list->next != NULL);
981 g_assert (tmp_list->next->next == NULL);
983 /* Free the last icon */
984 icon = tmp_list->next->data;
986 g_slist_free (tmp_list->next);
987 tmp_list->next = NULL;
989 cached_icon_free (icon);
994 clear_cache (GtkIconSet *icon_set,
995 gboolean style_detach)
998 GtkStyle *last_style = NULL;
1000 tmp_list = icon_set->cache;
1001 while (tmp_list != NULL)
1003 CachedIcon *icon = tmp_list->data;
1007 /* simple optimization for the case where the cache
1008 * contains contiguous icons from the same style.
1009 * it's safe to call detach_from_style more than
1010 * once on the same style though.
1012 if (last_style != icon->style)
1014 detach_from_style (icon_set, icon->style);
1015 last_style = icon->style;
1019 cached_icon_free (icon);
1021 tmp_list = g_slist_next (tmp_list);
1024 g_slist_free (icon_set->cache);
1025 icon_set->cache = NULL;
1026 icon_set->cache_size = 0;
1030 copy_cache (GtkIconSet *icon_set,
1031 GtkIconSet *copy_recipient)
1034 GSList *copy = NULL;
1036 ensure_cache_up_to_date (icon_set);
1038 tmp_list = icon_set->cache;
1039 while (tmp_list != NULL)
1041 CachedIcon *icon = tmp_list->data;
1042 CachedIcon *icon_copy = g_new (CachedIcon, 1);
1046 if (icon_copy->style)
1047 attach_to_style (copy_recipient, icon_copy->style);
1049 g_object_ref (G_OBJECT (icon_copy->pixbuf));
1051 icon_copy->size = g_strdup (icon->size);
1053 copy = g_slist_prepend (copy, icon_copy);
1055 tmp_list = g_slist_next (tmp_list);
1058 return g_slist_reverse (copy);
1062 attach_to_style (GtkIconSet *icon_set,
1067 table = g_object_get_qdata (G_OBJECT (style),
1068 g_quark_try_string ("gtk-style-icon-sets"));
1072 table = g_hash_table_new (NULL, NULL);
1073 g_object_set_qdata_full (G_OBJECT (style),
1074 g_quark_from_static_string ("gtk-style-icon-sets"),
1079 g_hash_table_insert (table, icon_set, icon_set);
1083 detach_from_style (GtkIconSet *icon_set,
1088 table = g_object_get_qdata (G_OBJECT (style),
1089 g_quark_try_string ("gtk-style-icon-sets"));
1092 g_hash_table_remove (table, icon_set);
1096 iconsets_foreach (gpointer key,
1100 GtkIconSet *icon_set = key;
1102 /* We only need to remove cache entries for the given style;
1103 * but that complicates things because in destroy notify
1104 * we don't know which style got destroyed, and 95% of the
1105 * time all cache entries will have the same style,
1106 * so this is faster anyway.
1109 clear_cache (icon_set, FALSE);
1113 style_dnotify (gpointer data)
1115 GHashTable *table = data;
1117 g_hash_table_foreach (table, iconsets_foreach, NULL);
1119 g_hash_table_destroy (table);
1122 /* This allows the icon set to detect that its cache is out of date. */
1124 _gtk_icon_set_invalidate_caches (void)