1 /* GtkIconTheme - a loader for icon themes
2 * gtk-icon-theme.c Copyright (C) 2002, 2003 Red Hat, Inc.
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.
22 #include <sys/types.h>
33 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
35 #endif /* G_OS_WIN32 */
37 #include "gtkicontheme.h"
38 #include "gtkiconthemeparser.h"
39 /* #include "gtkintl.h" */
40 #include <gtk/gtksettings.h>
41 #include <gtk/gtkprivate.h>
43 #define DEFAULT_THEME_NAME "hicolor"
45 typedef struct _GtkIconData GtkIconData
;
50 ICON_THEME_DIR_SCALABLE
,
51 ICON_THEME_DIR_THRESHOLD
,
52 ICON_THEME_DIR_UNTHEMED
55 /* In reverse search order: */
59 ICON_SUFFIX_XPM
= 1 << 0,
60 ICON_SUFFIX_SVG
= 1 << 1,
61 ICON_SUFFIX_PNG
= 1 << 2,
64 struct _GtkIconThemePrivate
66 guint custom_theme
: 1;
67 guint is_screen_singleton
: 1;
68 guint pixbuf_supports_svg
: 1;
74 gboolean themes_valid
;
75 /* A list of all the themes needed to look up icons.
76 * In search order, without duplicates
79 GHashTable
*unthemed_icons
;
81 /* Note: The keys of this hashtable are owned by the
82 * themedir and unthemed hashtables.
84 GHashTable
*all_icons
;
86 /* GdkScreen for the icon theme (may be NULL)
90 /* time when we last stat:ed for theme changes */
99 /* Information about the source
102 GdkPixbuf
*builtin_pixbuf
;
106 /* Information about the directory where
107 * the source was found
109 IconThemeDirType dir_type
;
113 /* Parameters influencing the scaled icon
116 gboolean raw_coordinates
;
118 /* Cached information if we go ahead and try to load
133 /* In search order */
139 gboolean has_embedded_rect
;
142 GdkPoint
*attach_points
;
143 gint n_attach_points
;
150 IconThemeDirType type
;
161 GHashTable
*icon_data
;
167 char *no_svg_filename
;
179 time_t mtime
; /* 0 == not existing or not a dir */
182 static void gtk_icon_theme_class_init (GtkIconThemeClass
*klass
);
183 static void gtk_icon_theme_init (GtkIconTheme
*icon_theme
);
184 static void gtk_icon_theme_finalize (GObject
*object
);
185 static void theme_dir_destroy (IconThemeDir
*dir
);
187 static void theme_destroy (IconTheme
*theme
);
188 static GtkIconInfo
*theme_lookup_icon (IconTheme
*theme
,
189 const char *icon_name
,
192 gboolean use_default_icons
);
193 static void theme_list_icons (IconTheme
*theme
,
196 static void theme_subdir_load (GtkIconTheme
*icon_theme
,
198 GtkIconThemeFile
*theme_file
,
200 static void do_theme_change (GtkIconTheme
*icon_theme
);
202 static void blow_themes (GtkIconTheme
*icon_themes
);
204 static void icon_data_free (GtkIconData
*icon_data
);
206 static GtkIconInfo
*icon_info_new (void);
207 static GtkIconInfo
*icon_info_new_builtin (BuiltinIcon
*icon
);
209 static IconSuffix
suffix_from_name (const char *name
);
211 static BuiltinIcon
*find_builtin_icon (const gchar
*icon_name
,
213 gint
*min_difference_p
,
214 gboolean
*has_larger_p
);
216 static guint signal_changed
= 0;
218 static GHashTable
*icon_theme_builtin_icons
;
221 gtk_icon_theme_get_type (void)
223 static GType type
= 0;
227 static const GTypeInfo info
=
229 sizeof (GtkIconThemeClass
),
230 NULL
, /* base_init */
231 NULL
, /* base_finalize */
232 (GClassInitFunc
) gtk_icon_theme_class_init
,
233 NULL
, /* class_finalize */
234 NULL
, /* class_data */
235 sizeof (GtkIconTheme
),
237 (GInstanceInitFunc
) gtk_icon_theme_init
,
240 type
= g_type_register_static (G_TYPE_OBJECT
, "GtkIconTheme", &info
, 0);
247 * gtk_icon_theme_new:
249 * Creates a new icon theme object. Icon theme objects are used
250 * to lookup up an icon by name in a particular icon theme.
251 * Usually, you'll want to use gtk_icon_theme_get_default()
252 * or gtk_icon_theme_get_for_screen() rather than creating
253 * a new icon theme object for scratch.
255 * Return value: the newly created #GtkIconTheme object.
258 gtk_icon_theme_new (void)
260 return g_object_new (GTK_TYPE_ICON_THEME
, NULL
);
264 * gtk_icon_theme_get_default:
266 * Gets the icon theme for the default screen. See
267 * gtk_icon_theme_get_for_screen().
269 * Return value: A unique #GtkIconTheme associated with
270 * the default screen. This icon theme is associated with
271 * the screen and can be used as long as the screen
275 gtk_icon_theme_get_default (void)
277 return gtk_icon_theme_get_for_screen (gdk_screen_get_default ());
281 * gtk_icon_theme_get_for_screen:
282 * @screen: a #GdkScreen
284 * Gets the icon theme object associated with @screen; if this
285 * function has not previously been called for the given
286 * screen, a new icon theme object will be created and
287 * associated with the screen. Icon theme objects are
288 * fairly expensive to create, so using this function
289 * is usually a better choice than calling than gtk_icon_theme_new()
290 * and setting the screen yourself; by using this function
291 * a single icon theme object will be shared between users.
293 * Return value: A unique #GtkIconTheme associated with
294 * the given screen. This icon theme is associated with
295 * the screen and can be used as long as the screen
299 gtk_icon_theme_get_for_screen (GdkScreen
*screen
)
301 GtkIconTheme
*icon_theme
;
303 g_return_val_if_fail (GDK_IS_SCREEN (screen
), NULL
);
304 g_return_val_if_fail (!screen
->closed
, NULL
);
306 icon_theme
= g_object_get_data (G_OBJECT (screen
), "gtk-icon-theme");
309 GtkIconThemePrivate
*priv
;
311 icon_theme
= gtk_icon_theme_new ();
312 gtk_icon_theme_set_screen (icon_theme
, screen
);
314 priv
= icon_theme
->priv
;
315 priv
->is_screen_singleton
= TRUE
;
317 g_object_set_data (G_OBJECT (screen
), "gtk-icon-theme", icon_theme
);
324 gtk_icon_theme_class_init (GtkIconThemeClass
*klass
)
326 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
328 gobject_class
->finalize
= gtk_icon_theme_finalize
;
331 * GtkIconTheme::changed
332 * @icon_theme: the icon theme
334 * Emitted when the current icon theme is switched or GTK+ detects
335 * that a change has occurred in the contents of the current
338 signal_changed
= g_signal_new ("changed",
339 G_TYPE_FROM_CLASS (klass
),
341 G_STRUCT_OFFSET (GtkIconThemeClass
, changed
),
343 g_cclosure_marshal_VOID__VOID
,
346 /* g_type_class_add_private (klass, sizeof (GtkIconThemePrivate)); */
350 /* Callback when the display that the icon theme is attached to
351 * is closed; unset the screen, and if it's the unique theme
352 * for the screen, drop the reference
355 display_closed (GdkDisplay
*display
,
357 GtkIconTheme
*icon_theme
)
359 GtkIconThemePrivate
*priv
= icon_theme
->priv
;
360 GdkScreen
*screen
= priv
->screen
;
361 gboolean was_screen_singleton
= priv
->is_screen_singleton
;
363 if (was_screen_singleton
)
365 g_object_set_data (G_OBJECT (screen
), "gtk-icon-theme", NULL
);
366 priv
->is_screen_singleton
= FALSE
;
369 gtk_icon_theme_set_screen (icon_theme
, NULL
);
371 if (was_screen_singleton
)
373 g_object_unref (icon_theme
);
378 update_current_theme (GtkIconTheme
*icon_theme
)
380 GtkIconThemePrivate
*priv
= icon_theme
->priv
;
382 if (!priv
->custom_theme
)
386 if (0 && priv
->screen
)
388 GtkSettings
*settings
= gtk_settings_get_for_screen (priv
->screen
);
389 g_object_get (settings
, "gtk-icon-theme-name", &theme
, NULL
);
393 theme
= g_strdup (DEFAULT_THEME_NAME
);
395 if (strcmp (priv
->current_theme
, theme
) != 0)
397 g_free (priv
->current_theme
);
398 priv
->current_theme
= theme
;
400 do_theme_change (icon_theme
);
407 /* Callback when the icon theme GtkSetting changes
410 theme_changed (GtkSettings
*settings
,
412 GtkIconTheme
*icon_theme
)
414 update_current_theme (icon_theme
);
418 unset_screen (GtkIconTheme
*icon_theme
)
420 GtkIconThemePrivate
*priv
= icon_theme
->priv
;
421 GtkSettings
*settings
;
426 settings
= gtk_settings_get_for_screen (priv
->screen
);
427 display
= gdk_screen_get_display (priv
->screen
);
429 g_signal_handlers_disconnect_by_func (display
,
430 (gpointer
) display_closed
,
432 g_signal_handlers_disconnect_by_func (settings
,
433 (gpointer
) theme_changed
,
441 * gtk_icon_theme_set_screen:
442 * @icon_theme: a #GtkIconTheme
443 * @screen: a #GdkScreen
445 * Sets the screen for an icon theme; the screen is used
446 * to track the user's currently configured icon theme,
447 * which might be different for different screens.
450 gtk_icon_theme_set_screen (GtkIconTheme
*icon_theme
,
453 GtkIconThemePrivate
*priv
;
454 GtkSettings
*settings
;
457 g_return_if_fail (GTK_ICON_THEME (icon_theme
));
458 g_return_if_fail (screen
== NULL
|| GDK_IS_SCREEN (screen
));
460 priv
= icon_theme
->priv
;
462 unset_screen (icon_theme
);
466 display
= gdk_screen_get_display (screen
);
467 settings
= gtk_settings_get_for_screen (screen
);
469 priv
->screen
= screen
;
471 g_signal_connect (display
, "closed",
472 G_CALLBACK (display_closed
), icon_theme
);
473 g_signal_connect (settings
, "notify::gtk-icon-theme-name",
474 G_CALLBACK (theme_changed
), icon_theme
);
477 update_current_theme (icon_theme
);
480 /* Checks whether a loader for SVG files has been registered
484 pixbuf_supports_svg ()
486 GSList
*formats
= gdk_pixbuf_get_formats ();
488 gboolean found_svg
= FALSE
;
490 for (tmp_list
= formats
; tmp_list
&& !found_svg
; tmp_list
= tmp_list
->next
)
492 gchar
**mime_types
= gdk_pixbuf_format_get_mime_types (tmp_list
->data
);
495 for (mime_type
= mime_types
; *mime_type
&& !found_svg
; mime_type
++)
497 if (strcmp (*mime_type
, "image/svg") == 0)
501 g_strfreev (mime_types
);
504 g_slist_free (formats
);
509 #define GTK_DATA_PREFIX "/tmp/"
511 gtk_icon_theme_init (GtkIconTheme
*icon_theme
)
513 GtkIconThemePrivate
*priv
;
515 priv
= g_new0(GtkIconThemePrivate
, 1);
516 icon_theme
->priv
= priv
;
518 priv
->custom_theme
= FALSE
;
519 priv
->current_theme
= g_strdup (DEFAULT_THEME_NAME
);
520 priv
->search_path
= g_new (char *, 5);
523 priv
->search_path
[0] = g_build_filename (g_get_home_dir (),
526 priv
->search_path
[1] = g_build_filename (GTK_DATA_PREFIX
, "pixmaps", NULL
);
527 priv
->search_path
[2] = g_build_filename (GTK_DATA_PREFIX
, "icons", NULL
);
528 priv
->search_path
[3] = g_strdup ("/usr/share/icons");
529 priv
->search_path
[4] = g_strdup ("/usr/share/pixmaps");
530 priv
->search_path_len
= 5;
532 priv
->themes_valid
= FALSE
;
534 priv
->unthemed_icons
= NULL
;
536 priv
->pixbuf_supports_svg
= pixbuf_supports_svg ();
540 free_dir_mtime (IconThemeDirMtime
*dir_mtime
)
542 g_free (dir_mtime
->dir
);
547 do_theme_change (GtkIconTheme
*icon_theme
)
549 GtkIconThemePrivate
*priv
= icon_theme
->priv
;
551 blow_themes (icon_theme
);
552 g_signal_emit (G_OBJECT (icon_theme
), signal_changed
, 0);
554 if (priv
->screen
&& priv
->is_screen_singleton
)
556 GtkSettings
*settings
= gtk_settings_get_for_screen (priv
->screen
);
557 _gtk_rc_reset_styles (settings
);
562 blow_themes (GtkIconTheme
*icon_theme
)
564 GtkIconThemePrivate
*priv
= icon_theme
->priv
;
566 if (priv
->themes_valid
)
568 g_hash_table_destroy (priv
->all_icons
);
569 g_list_foreach (priv
->themes
, (GFunc
)theme_destroy
, NULL
);
570 g_list_free (priv
->themes
);
571 g_list_foreach (priv
->dir_mtimes
, (GFunc
)free_dir_mtime
, NULL
);
572 g_list_free (priv
->dir_mtimes
);
573 g_hash_table_destroy (priv
->unthemed_icons
);
576 priv
->unthemed_icons
= NULL
;
577 priv
->dir_mtimes
= NULL
;
578 priv
->all_icons
= NULL
;
579 priv
->themes_valid
= FALSE
;
583 gtk_icon_theme_finalize (GObject
*object
)
585 GtkIconTheme
*icon_theme
;
586 GtkIconThemePrivate
*priv
;
589 icon_theme
= GTK_ICON_THEME (object
);
590 priv
= icon_theme
->priv
;
592 unset_screen (icon_theme
);
594 g_free (priv
->current_theme
);
595 priv
->current_theme
= NULL
;
597 for (i
=0; i
< priv
->search_path_len
; i
++)
598 g_free (priv
->search_path
[i
]);
600 g_free (priv
->search_path
);
601 priv
->search_path
= NULL
;
603 blow_themes (icon_theme
);
607 * gtk_icon_theme_set_search_path:
608 * @icon_theme: a #GtkIconTheme
609 * @path: array of directories that are searched for icon themes
610 * @n_elements: number of elements in @path.
612 * Sets the search path for the icon theme object. When looking
613 * for an icon theme, GTK+ will search for a subdirectory of
614 * one or more of the directories in @path with the same name
615 * as the icon theme. (Themes from multiple of the path elements
616 * are combined to allow themes to be extended by adding icons
617 * in the user's home directory.)
619 * In addition if an icon found isn't found either in the current
620 * icon theme or the default icon theme, and an image file with
621 * the right name is found directly in one of the elements of
622 * @path, then that image will be used for the icon name.
623 * (This is legacy feature, and new icons should be put
624 * into the default icon theme, which is called "hicolor", rather than
625 * directly on the icon path.)
628 gtk_icon_theme_set_search_path (GtkIconTheme
*icon_theme
,
632 GtkIconThemePrivate
*priv
;
635 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme
));
637 priv
= icon_theme
->priv
;
638 for (i
= 0; i
< priv
->search_path_len
; i
++)
639 g_free (priv
->search_path
[i
]);
641 g_free (priv
->search_path
);
643 priv
->search_path
= g_new (gchar
*, n_elements
);
644 priv
->search_path_len
= n_elements
;
645 for (i
= 0; i
< priv
->search_path_len
; i
++)
646 priv
->search_path
[i
] = g_strdup (path
[i
]);
648 do_theme_change (icon_theme
);
653 * gtk_icon_theme_get_search_path:
654 * @icon_theme: a #GtkIconTheme
655 * @path: location to store a list of icon theme path directories or %NULL
656 * The stored value should be freed with g_strfreev().
657 * @n_elements: location to store number of elements
660 * Gets the current search path. See gtk_icon_theme_set_search_path().
663 gtk_icon_theme_get_search_path (GtkIconTheme
*icon_theme
,
667 GtkIconThemePrivate
*priv
;
670 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme
));
672 priv
= icon_theme
->priv
;
675 *n_elements
= priv
->search_path_len
;
679 *path
= g_new (gchar
*, priv
->search_path_len
+ 1);
680 for (i
= 0; i
< priv
->search_path_len
; i
++)
681 (*path
)[i
] = g_strdup (priv
->search_path
[i
] + 1);
687 * gtk_icon_theme_append_search_path:
688 * @icon_theme: a #GtkIconTheme
689 * @path: directory name to append to the icon path
691 * Appends a directory to the search path. See gtk_icon_theme_set_search_path().
694 gtk_icon_theme_append_search_path (GtkIconTheme
*icon_theme
,
697 GtkIconThemePrivate
*priv
;
699 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme
));
700 g_return_if_fail (path
!= NULL
);
702 priv
= icon_theme
->priv
;
704 priv
->search_path_len
++;
705 priv
->search_path
= g_renew (gchar
*, priv
->search_path
, priv
->search_path_len
);
706 priv
->search_path
[priv
->search_path_len
-1] = g_strdup (path
);
708 do_theme_change (icon_theme
);
712 * gtk_icon_theme_prepend_search_path:
713 * @icon_theme: a #GtkIconTheme
714 * @path: directory name to prepend to the icon path
716 * Prepends a directory to the search path. See gtk_icon_theme_set_search_path().
719 gtk_icon_theme_prepend_search_path (GtkIconTheme
*icon_theme
,
722 GtkIconThemePrivate
*priv
;
725 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme
));
726 g_return_if_fail (path
!= NULL
);
728 priv
= icon_theme
->priv
;
730 priv
->search_path_len
++;
731 priv
->search_path
= g_renew (gchar
*, priv
->search_path
, priv
->search_path_len
);
733 for (i
= 0; i
< priv
->search_path_len
- 1; i
++)
734 priv
->search_path
[i
+1] = priv
->search_path
[i
];
736 priv
->search_path
[0] = g_strdup (path
);
738 do_theme_change (icon_theme
);
742 * gtk_icon_theme_set_custom_theme:
743 * @icon_theme: a #GtkIconTheme
744 * @theme_name: name of icon theme to use instead of configured theme
746 * Sets the name of the icon theme that the #GtkIconTheme object uses
747 * overriding system configuration. This function cannot be called
748 * on the icon theme objects returned from gtk_icon_theme_get_default()
749 * and gtk_icon_theme_get_default().
752 gtk_icon_theme_set_custom_theme (GtkIconTheme
*icon_theme
,
753 const gchar
*theme_name
)
755 GtkIconThemePrivate
*priv
;
757 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme
));
759 priv
= icon_theme
->priv
;
761 g_return_if_fail (!priv
->is_screen_singleton
);
763 if (theme_name
!= NULL
)
765 priv
->custom_theme
= TRUE
;
766 if (strcmp (theme_name
, priv
->current_theme
) != 0)
768 g_free (priv
->current_theme
);
769 priv
->current_theme
= g_strdup (theme_name
);
771 do_theme_change (icon_theme
);
776 priv
->custom_theme
= FALSE
;
778 update_current_theme (icon_theme
);
783 insert_theme (GtkIconTheme
*icon_theme
, const char *theme_name
)
789 GtkIconThemePrivate
*priv
;
795 GtkIconThemeFile
*theme_file
;
796 IconThemeDirMtime
*dir_mtime
;
797 struct stat stat_buf
;
799 priv
= icon_theme
->priv
;
801 for (l
= priv
->themes
; l
!= NULL
; l
= l
->next
)
804 if (strcmp (theme
->name
, theme_name
) == 0)
808 for (i
= 0; i
< priv
->search_path_len
; i
++)
810 path
= g_build_filename (priv
->search_path
[i
],
813 dir_mtime
= g_new (IconThemeDirMtime
, 1);
814 dir_mtime
->dir
= path
;
815 if (stat (path
, &stat_buf
) == 0 && S_ISDIR (stat_buf
.st_mode
))
816 dir_mtime
->mtime
= stat_buf
.st_mtime
;
818 dir_mtime
->mtime
= 0;
820 priv
->dir_mtimes
= g_list_prepend (priv
->dir_mtimes
, dir_mtime
);
824 for (i
= 0; i
< priv
->search_path_len
; i
++)
826 path
= g_build_filename (priv
->search_path
[i
],
830 if (g_file_test (path
, G_FILE_TEST_IS_REGULAR
)) {
831 if (g_file_get_contents (path
, &contents
, NULL
, NULL
)) {
832 theme_file
= _gtk_icon_theme_file_new_from_string (contents
, NULL
);
841 if (theme_file
== NULL
)
844 theme
= g_new (IconTheme
, 1);
845 if (!_gtk_icon_theme_file_get_locale_string (theme_file
,
848 &theme
->display_name
))
850 g_warning ("Theme file for %s has no name\n", theme_name
);
852 _gtk_icon_theme_file_free (theme_file
);
856 if (!_gtk_icon_theme_file_get_string (theme_file
,
861 g_warning ("Theme file for %s has no directories\n", theme_name
);
862 g_free (theme
->display_name
);
864 _gtk_icon_theme_file_free (theme_file
);
868 theme
->name
= g_strdup (theme_name
);
869 _gtk_icon_theme_file_get_locale_string (theme_file
,
873 _gtk_icon_theme_file_get_string (theme_file
,
878 dirs
= g_strsplit (directories
, ",", 0);
881 for (i
= 0; dirs
[i
] != NULL
; i
++)
882 theme_subdir_load (icon_theme
, theme
, theme_file
, dirs
[i
]);
886 theme
->dirs
= g_list_reverse (theme
->dirs
);
888 g_free (directories
);
890 /* Prepend the finished theme */
891 priv
->themes
= g_list_prepend (priv
->themes
, theme
);
893 if (_gtk_icon_theme_file_get_string (theme_file
,
898 themes
= g_strsplit (inherits
, ",", 0);
900 for (i
= 0; themes
[i
] != NULL
; i
++)
901 insert_theme (icon_theme
, themes
[i
]);
908 _gtk_icon_theme_file_free (theme_file
);
912 free_unthemed_icon (UnthemedIcon
*unthemed_icon
)
914 if (unthemed_icon
->svg_filename
)
915 g_free (unthemed_icon
->svg_filename
);
916 if (unthemed_icon
->svg_filename
)
917 g_free (unthemed_icon
->no_svg_filename
);
918 g_free (unthemed_icon
);
922 load_themes (GtkIconTheme
*icon_theme
)
924 GtkIconThemePrivate
*priv
;
927 char *dir
, *base_name
, *dot
;
930 UnthemedIcon
*unthemed_icon
;
931 IconSuffix old_suffix
, new_suffix
;
934 priv
= icon_theme
->priv
;
936 priv
->all_icons
= g_hash_table_new (g_str_hash
, g_str_equal
);
938 insert_theme (icon_theme
, priv
->current_theme
);
940 /* Always look in the "default" icon theme */
941 insert_theme (icon_theme
, DEFAULT_THEME_NAME
);
942 priv
->themes
= g_list_reverse (priv
->themes
);
944 priv
->unthemed_icons
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
945 g_free
, (GDestroyNotify
)free_unthemed_icon
);
947 for (base
= 0; base
< icon_theme
->priv
->search_path_len
; base
++)
949 dir
= icon_theme
->priv
->search_path
[base
];
950 gdir
= g_dir_open (dir
, 0, NULL
);
955 while ((file
= g_dir_read_name (gdir
)))
957 new_suffix
= suffix_from_name (file
);
959 if (new_suffix
!= ICON_SUFFIX_NONE
)
961 abs_file
= g_build_filename (dir
, file
, NULL
);
963 base_name
= g_strdup (file
);
965 dot
= strrchr (base_name
, '.');
969 if ((unthemed_icon
= g_hash_table_lookup (priv
->unthemed_icons
,
972 if (new_suffix
== ICON_SUFFIX_SVG
)
974 if (unthemed_icon
->no_svg_filename
)
977 unthemed_icon
->svg_filename
= abs_file
;
981 if (unthemed_icon
->no_svg_filename
)
983 old_suffix
= suffix_from_name (unthemed_icon
->no_svg_filename
);
984 if (new_suffix
> old_suffix
)
986 g_free (unthemed_icon
->no_svg_filename
);
987 unthemed_icon
->no_svg_filename
= abs_file
;
993 unthemed_icon
->no_svg_filename
= abs_file
;
1000 unthemed_icon
= g_new0 (UnthemedIcon
, 1);
1002 if (new_suffix
== ICON_SUFFIX_SVG
)
1003 unthemed_icon
->svg_filename
= abs_file
;
1005 unthemed_icon
->svg_filename
= abs_file
;
1007 g_hash_table_insert (priv
->unthemed_icons
,
1010 g_hash_table_insert (priv
->all_icons
,
1018 priv
->themes_valid
= TRUE
;
1020 g_get_current_time(&tv
);
1021 priv
->last_stat_time
= tv
.tv_sec
;
1025 ensure_valid_themes (GtkIconTheme
*icon_theme
)
1027 GtkIconThemePrivate
*priv
= icon_theme
->priv
;
1030 if (priv
->themes_valid
)
1032 g_get_current_time(&tv
);
1034 if (ABS (tv
.tv_sec
- priv
->last_stat_time
) > 5)
1035 gtk_icon_theme_rescan_if_needed (icon_theme
);
1038 if (!priv
->themes_valid
)
1039 load_themes (icon_theme
);
1043 * gtk_icon_theme_lookup_icon:
1044 * @icon_theme: a #GtkIconTheme
1045 * @icon_name: the name of the icon to lookup
1046 * @size: desired icon size
1047 * @flags: flags modifying the behavior of the icon lookup
1049 * Looks up a named icon and returns a structure containing
1050 * information such as the filename of the icon. The icon
1051 * can then be rendered into a pixbuf using
1052 * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
1053 * combines these two steps if all you need is the pixbuf.)
1055 * Return value: a #GtkIconInfo structure containing information
1056 * about the icon, or %NULL if the icon wasn't found. Free with
1057 * gtk_icon_info_free()
1060 gtk_icon_theme_lookup_icon (GtkIconTheme
*icon_theme
,
1061 const gchar
*icon_name
,
1063 GtkIconLookupFlags flags
)
1065 GtkIconThemePrivate
*priv
;
1067 GtkIconInfo
*icon_info
= NULL
;
1068 UnthemedIcon
*unthemed_icon
;
1070 gboolean use_builtin
;
1071 gboolean found_default
;
1073 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme
), NULL
);
1074 g_return_val_if_fail (icon_name
!= NULL
, NULL
);
1075 g_return_val_if_fail ((flags
& GTK_ICON_LOOKUP_NO_SVG
) == 0 ||
1076 (flags
& GTK_ICON_LOOKUP_FORCE_SVG
) == 0, NULL
);
1078 priv
= icon_theme
->priv
;
1080 if (flags
& GTK_ICON_LOOKUP_NO_SVG
)
1082 else if (flags
& GTK_ICON_LOOKUP_FORCE_SVG
)
1085 allow_svg
= priv
->pixbuf_supports_svg
;
1087 use_builtin
= (flags
& GTK_ICON_LOOKUP_USE_BUILTIN
);
1089 ensure_valid_themes (icon_theme
);
1091 found_default
= FALSE
;
1095 IconTheme
*icon_theme
= l
->data
;
1097 if (strcmp (icon_theme
->name
, DEFAULT_THEME_NAME
) == 0)
1098 found_default
= TRUE
;
1100 icon_info
= theme_lookup_icon (icon_theme
, icon_name
, size
, allow_svg
, use_builtin
);
1109 BuiltinIcon
*builtin
= find_builtin_icon (icon_name
, size
, NULL
, NULL
);
1112 icon_info
= icon_info_new_builtin (builtin
);
1117 unthemed_icon
= g_hash_table_lookup (priv
->unthemed_icons
, icon_name
);
1120 icon_info
= icon_info_new ();
1122 /* A SVG icon, when allowed, beats out a XPM icon, but not
1126 unthemed_icon
->svg_filename
&&
1127 (!unthemed_icon
->no_svg_filename
||
1128 suffix_from_name (unthemed_icon
->no_svg_filename
) != ICON_SUFFIX_PNG
))
1129 icon_info
->filename
= g_strdup (unthemed_icon
->svg_filename
);
1130 else if (unthemed_icon
->no_svg_filename
)
1131 icon_info
->filename
= g_strdup (unthemed_icon
->no_svg_filename
);
1133 icon_info
->dir_type
= ICON_THEME_DIR_UNTHEMED
;
1138 icon_info
->desired_size
= size
;
1145 gtk_icon_theme_error_quark (void)
1147 static GQuark q
= 0;
1149 q
= g_quark_from_static_string ("gtk-icon-theme-error-quark");
1155 * gtk_icon_theme_load_icon:
1156 * @icon_theme: a #GtkIconTheme
1157 * @icon_name: the name of the icon to lookup
1158 * @size: the desired icon size. The resulting icon may not be
1159 * exactly this size; see gtk_icon_info_load_icon().
1160 * @flags: flags modifying the behavior of the icon lookup
1161 * @error: Location to store error information on failure, or %NULL.
1163 * Looks up an icon in an icon theme, scales it to the given size
1164 * and renders it into a pixbuf. This is a convenience function;
1165 * if more details about the icon are needed, use
1166 * gtk_icon_theme_lookup_icon() followed by gtk_icon_info_load_icon().
1168 * Return value: the rendered icon; this may be a newly created icon
1169 * or a new reference to an internal icon, so you must not modify
1170 * the icon. Use g_object_unref() to release your reference to the
1171 * icon. %NULL if the icon isn't found.
1174 gtk_icon_theme_load_icon (GtkIconTheme
*icon_theme
,
1175 const gchar
*icon_name
,
1177 GtkIconLookupFlags flags
,
1180 GtkIconInfo
*icon_info
;
1181 GdkPixbuf
*pixbuf
= NULL
;
1183 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme
), NULL
);
1184 g_return_val_if_fail (icon_name
!= NULL
, NULL
);
1185 g_return_val_if_fail ((flags
& GTK_ICON_LOOKUP_NO_SVG
) == 0 ||
1186 (flags
& GTK_ICON_LOOKUP_FORCE_SVG
) == 0, NULL
);
1187 g_return_val_if_fail (error
== NULL
|| *error
== NULL
, NULL
);
1189 icon_info
= gtk_icon_theme_lookup_icon (icon_theme
, icon_name
, size
,
1190 flags
| GTK_ICON_LOOKUP_USE_BUILTIN
);
1193 g_set_error (error
, GTK_ICON_THEME_ERROR
, GTK_ICON_THEME_NOT_FOUND
,
1194 _("Icon '%s' not present in theme"), icon_name
);
1198 pixbuf
= gtk_icon_info_load_icon (icon_info
, error
);
1199 gtk_icon_info_free (icon_info
);
1205 * gtk_icon_theme_has_icon:
1206 * @icon_theme: a #GtkIconTheme
1207 * @icon_name: the name of an icon
1209 * Checks whether an icon theme includes an icon
1210 * for a particular name.
1212 * Return value: %TRUE if @icon_theme includes an
1213 * icon for @icon_name.
1216 gtk_icon_theme_has_icon (GtkIconTheme
*icon_theme
,
1217 const char *icon_name
)
1219 GtkIconThemePrivate
*priv
;
1221 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme
), FALSE
);
1223 priv
= icon_theme
->priv
;
1225 ensure_valid_themes (icon_theme
);
1227 if (g_hash_table_lookup_extended (priv
->all_icons
,
1228 icon_name
, NULL
, NULL
))
1230 if (g_hash_table_lookup_extended (icon_theme_builtin_icons
,
1231 icon_name
, NULL
, NULL
))
1239 add_key_to_hash (gpointer key
,
1243 GHashTable
*hash
= user_data
;
1245 g_hash_table_insert (hash
, key
, NULL
);
1249 add_key_to_list (gpointer key
,
1253 GList
**list
= user_data
;
1255 *list
= g_list_prepend (*list
, g_strdup (key
));
1259 * gtk_icon_theme_list_icons:
1260 * @icon_theme: a #GtkIconTheme
1261 * @context: a string identifying a particular type of icon,
1262 * or %NULL to list all icons.
1264 * Lists the icons in the current icon theme. Only a subset
1265 * of the icons can be listed by providing a context string.
1266 * The set of values for the context string is system dependent,
1267 * but will typically include such values as 'apps' and
1270 * Return value: a #GList list holding the names of all the
1271 * icons in the theme. You must first free each element
1272 * in the list with g_free(), then free the list itself
1273 * with g_list_free().
1276 gtk_icon_theme_list_icons (GtkIconTheme
*icon_theme
,
1277 const char *context
)
1279 GtkIconThemePrivate
*priv
;
1282 GQuark context_quark
;
1284 priv
= icon_theme
->priv
;
1286 ensure_valid_themes (icon_theme
);
1290 context_quark
= g_quark_try_string (context
);
1298 icons
= g_hash_table_new (g_str_hash
, g_str_equal
);
1303 theme_list_icons (l
->data
, icons
, context_quark
);
1307 if (context_quark
== 0)
1308 g_hash_table_foreach (priv
->unthemed_icons
,
1314 g_hash_table_foreach (icons
,
1318 g_hash_table_destroy (icons
);
1324 * gtk_icon_theme_get_example_icon_name:
1325 * @icon_theme: a #GtkIconTheme
1327 * Gets the name of an icon that is representative of the
1328 * current theme (for instance, to use when presenting
1329 * a list of themes to the user.)
1331 * Return value: the name of an example icon or %NULL.
1332 * Free with g_free().
1335 gtk_icon_theme_get_example_icon_name (GtkIconTheme
*icon_theme
)
1337 GtkIconThemePrivate
*priv
;
1341 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme
), NULL
);
1343 priv
= icon_theme
->priv
;
1345 ensure_valid_themes (icon_theme
);
1352 return g_strdup (theme
->example
);
1361 * gtk_icon_theme_rescan_if_needed:
1362 * @icon_theme: a #GtkIconTheme
1364 * Checks to see if the icon theme has changed; if it has, any
1365 * currently cached information is discarded and will be reloaded
1366 * next time @icon_theme is accessed.
1368 * Return value: %TRUE if the icon theme has changed and needed
1372 gtk_icon_theme_rescan_if_needed (GtkIconTheme
*icon_theme
)
1374 GtkIconThemePrivate
*priv
;
1375 IconThemeDirMtime
*dir_mtime
;
1378 struct stat stat_buf
;
1381 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme
), FALSE
);
1383 priv
= icon_theme
->priv
;
1385 for (d
= priv
->dir_mtimes
; d
!= NULL
; d
= d
->next
)
1387 dir_mtime
= d
->data
;
1389 stat_res
= stat (dir_mtime
->dir
, &stat_buf
);
1391 /* dir mtime didn't change */
1392 if (stat_res
== 0 &&
1393 S_ISDIR (stat_buf
.st_mode
) &&
1394 dir_mtime
->mtime
== stat_buf
.st_mtime
)
1396 /* didn't exist before, and still doesn't */
1397 if (dir_mtime
->mtime
== 0 &&
1398 (stat_res
!= 0 || !S_ISDIR (stat_buf
.st_mode
)))
1401 do_theme_change (icon_theme
);
1405 g_get_current_time (&tv
);
1406 priv
->last_stat_time
= tv
.tv_sec
;
1412 theme_destroy (IconTheme
*theme
)
1414 g_free (theme
->display_name
);
1415 g_free (theme
->comment
);
1416 g_free (theme
->name
);
1417 g_free (theme
->example
);
1419 g_list_foreach (theme
->dirs
, (GFunc
)theme_dir_destroy
, NULL
);
1420 g_list_free (theme
->dirs
);
1425 theme_dir_destroy (IconThemeDir
*dir
)
1427 g_hash_table_destroy (dir
->icons
);
1429 g_hash_table_destroy (dir
->icon_data
);
1435 theme_dir_size_difference (IconThemeDir
*dir
, int size
, gboolean
*smaller
)
1440 case ICON_THEME_DIR_FIXED
:
1441 *smaller
= size
< dir
->size
;
1442 return abs (size
- dir
->size
);
1444 case ICON_THEME_DIR_SCALABLE
:
1445 *smaller
= size
< dir
->min_size
;
1446 if (size
< dir
->min_size
)
1447 return dir
->min_size
- size
;
1448 if (size
> dir
->max_size
)
1449 return size
- dir
->max_size
;
1452 case ICON_THEME_DIR_THRESHOLD
:
1453 min
= dir
->size
- dir
->threshold
;
1454 max
= dir
->size
+ dir
->threshold
;
1455 *smaller
= size
< min
;
1462 case ICON_THEME_DIR_UNTHEMED
:
1463 g_assert_not_reached ();
1466 g_assert_not_reached ();
1471 string_from_suffix (IconSuffix suffix
)
1475 case ICON_SUFFIX_XPM
:
1477 case ICON_SUFFIX_SVG
:
1479 case ICON_SUFFIX_PNG
:
1482 g_assert_not_reached();
1488 suffix_from_name (const char *name
)
1492 if (g_str_has_suffix (name
, ".png"))
1493 retval
= ICON_SUFFIX_PNG
;
1494 else if (g_str_has_suffix (name
, ".svg"))
1495 retval
= ICON_SUFFIX_SVG
;
1496 else if (g_str_has_suffix (name
, ".xpm"))
1497 retval
= ICON_SUFFIX_XPM
;
1499 retval
= ICON_SUFFIX_NONE
;
1505 best_suffix (IconSuffix suffix
,
1508 if ((suffix
& ICON_SUFFIX_PNG
) != 0)
1509 return ICON_SUFFIX_PNG
;
1510 else if (allow_svg
&& ((suffix
& ICON_SUFFIX_SVG
) != 0))
1511 return ICON_SUFFIX_SVG
;
1512 else if ((suffix
& ICON_SUFFIX_XPM
) != 0)
1513 return ICON_SUFFIX_XPM
;
1515 return ICON_SUFFIX_NONE
;
1518 static GtkIconInfo
*
1519 theme_lookup_icon (IconTheme
*theme
,
1520 const char *icon_name
,
1523 gboolean use_builtin
)
1526 IconThemeDir
*dir
, *min_dir
;
1528 int min_difference
, difference
;
1529 BuiltinIcon
*closest_builtin
= NULL
;
1530 gboolean smaller
, has_larger
;
1533 min_difference
= G_MAXINT
;
1537 /* Builtin icons are logically part of the default theme and
1538 * are searched before other subdirectories of the default theme.
1540 if (strcmp (theme
->name
, DEFAULT_THEME_NAME
) == 0 && use_builtin
)
1542 closest_builtin
= find_builtin_icon (icon_name
, size
,
1546 if (min_difference
== 0)
1547 return icon_info_new_builtin (closest_builtin
);
1555 suffix
= GPOINTER_TO_UINT (g_hash_table_lookup (dir
->icons
, icon_name
));
1557 if (suffix
!= ICON_SUFFIX_NONE
&&
1558 (allow_svg
|| suffix
!= ICON_SUFFIX_SVG
))
1560 difference
= theme_dir_size_difference (dir
, size
, &smaller
);
1562 if (difference
== 0)
1570 if (difference
< min_difference
|| smaller
)
1572 min_difference
= difference
;
1574 closest_builtin
= NULL
;
1575 has_larger
= smaller
;
1580 if (difference
< min_difference
&& smaller
)
1582 min_difference
= difference
;
1584 closest_builtin
= NULL
;
1593 if (closest_builtin
)
1594 return icon_info_new_builtin (closest_builtin
);
1598 GtkIconInfo
*icon_info
= icon_info_new ();
1600 suffix
= GPOINTER_TO_UINT (g_hash_table_lookup (min_dir
->icons
, icon_name
));
1601 suffix
= best_suffix (suffix
, allow_svg
);
1602 g_assert (suffix
!= ICON_SUFFIX_NONE
);
1604 file
= g_strconcat (icon_name
, string_from_suffix (suffix
), NULL
);
1605 icon_info
->filename
= g_build_filename (min_dir
->dir
, file
, NULL
);
1608 if (min_dir
->icon_data
!= NULL
)
1609 icon_info
->data
= g_hash_table_lookup (min_dir
->icon_data
, icon_name
);
1611 icon_info
->dir_type
= min_dir
->type
;
1612 icon_info
->dir_size
= min_dir
->size
;
1613 icon_info
->threshold
= min_dir
->threshold
;
1622 theme_list_icons (IconTheme
*theme
, GHashTable
*icons
,
1625 GList
*l
= theme
->dirs
;
1632 if (context
== dir
->context
||
1634 g_hash_table_foreach (dir
->icons
,
1643 load_icon_data (IconThemeDir
*dir
, const char *path
, const char *name
)
1645 GtkIconThemeFile
*icon_file
;
1656 if (g_file_get_contents (path
, &contents
, NULL
, NULL
))
1658 icon_file
= _gtk_icon_theme_file_new_from_string (contents
, NULL
);
1662 base_name
= g_strdup (name
);
1663 dot
= strrchr (base_name
, '.');
1666 data
= g_new0 (GtkIconData
, 1);
1667 g_hash_table_replace (dir
->icon_data
, base_name
, data
);
1669 if (_gtk_icon_theme_file_get_string (icon_file
, "Icon Data",
1670 "EmbeddedTextRectangle",
1673 split
= g_strsplit (str
, ",", 4);
1676 while (split
[i
] != NULL
)
1681 data
->has_embedded_rect
= TRUE
;
1682 data
->x0
= atoi (split
[0]);
1683 data
->y0
= atoi (split
[1]);
1684 data
->x1
= atoi (split
[2]);
1685 data
->y1
= atoi (split
[3]);
1693 if (_gtk_icon_theme_file_get_string (icon_file
, "Icon Data",
1697 split
= g_strsplit (str
, "|", -1);
1700 while (split
[i
] != NULL
)
1703 data
->n_attach_points
= i
;
1704 data
->attach_points
= g_malloc (sizeof (GdkPoint
) * i
);
1707 while (split
[i
] != NULL
&& i
< data
->n_attach_points
)
1709 split_point
= strchr (split
[i
], ',');
1714 data
->attach_points
[i
].x
= atoi (split
[i
]);
1715 data
->attach_points
[i
].y
= atoi (split_point
);
1724 _gtk_icon_theme_file_get_locale_string (icon_file
, "Icon Data",
1726 &data
->display_name
);
1728 _gtk_icon_theme_file_free (icon_file
);
1736 scan_directory (GtkIconThemePrivate
*icon_theme
,
1737 IconThemeDir
*dir
, char *full_dir
)
1741 char *base_name
, *dot
;
1743 IconSuffix suffix
, hash_suffix
;
1745 dir
->icons
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
1748 gdir
= g_dir_open (full_dir
, 0, NULL
);
1753 while ((name
= g_dir_read_name (gdir
)))
1755 if (g_str_has_suffix (name
, ".icon"))
1757 if (dir
->icon_data
== NULL
)
1758 dir
->icon_data
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
1759 g_free
, (GDestroyNotify
)icon_data_free
);
1761 path
= g_build_filename (full_dir
, name
, NULL
);
1762 if (g_file_test (path
, G_FILE_TEST_IS_REGULAR
))
1763 load_icon_data (dir
, path
, name
);
1770 suffix
= suffix_from_name (name
);
1771 if (suffix
== ICON_SUFFIX_NONE
)
1774 base_name
= g_strdup (name
);
1775 dot
= strrchr (base_name
, '.');
1778 hash_suffix
= GPOINTER_TO_INT (g_hash_table_lookup (dir
->icons
, base_name
));
1779 g_hash_table_replace (dir
->icons
, base_name
, GUINT_TO_POINTER (hash_suffix
| suffix
));
1780 g_hash_table_insert (icon_theme
->all_icons
, base_name
, NULL
);
1787 theme_subdir_load (GtkIconTheme
*icon_theme
,
1789 GtkIconThemeFile
*theme_file
,
1795 IconThemeDirType type
;
1796 char *context_string
;
1804 if (!_gtk_icon_theme_file_get_integer (theme_file
,
1809 g_warning ("Theme directory %s of theme %s has no size field\n", subdir
, theme
->name
);
1813 type
= ICON_THEME_DIR_THRESHOLD
;
1814 if (_gtk_icon_theme_file_get_string (theme_file
, subdir
, "Type", &type_string
))
1816 if (strcmp (type_string
, "Fixed") == 0)
1817 type
= ICON_THEME_DIR_FIXED
;
1818 else if (strcmp (type_string
, "Scalable") == 0)
1819 type
= ICON_THEME_DIR_SCALABLE
;
1820 else if (strcmp (type_string
, "Threshold") == 0)
1821 type
= ICON_THEME_DIR_THRESHOLD
;
1823 g_free (type_string
);
1827 if (_gtk_icon_theme_file_get_string (theme_file
, subdir
, "Context", &context_string
))
1829 context
= g_quark_from_string (context_string
);
1830 g_free (context_string
);
1833 if (!_gtk_icon_theme_file_get_integer (theme_file
,
1839 if (!_gtk_icon_theme_file_get_integer (theme_file
,
1845 if (!_gtk_icon_theme_file_get_integer (theme_file
,
1851 for (base
= 0; base
< icon_theme
->priv
->search_path_len
; base
++)
1853 full_dir
= g_build_filename (icon_theme
->priv
->search_path
[base
],
1857 if (g_file_test (full_dir
, G_FILE_TEST_IS_DIR
))
1859 dir
= g_new (IconThemeDir
, 1);
1861 dir
->context
= context
;
1863 dir
->min_size
= min_size
;
1864 dir
->max_size
= max_size
;
1865 dir
->threshold
= threshold
;
1866 dir
->dir
= full_dir
;
1867 dir
->icon_data
= NULL
;
1869 scan_directory (icon_theme
->priv
, dir
, full_dir
);
1871 theme
->dirs
= g_list_append (theme
->dirs
, dir
);
1879 icon_data_free (GtkIconData
*icon_data
)
1881 g_free (icon_data
->attach_points
);
1882 g_free (icon_data
->display_name
);
1890 gtk_icon_info_get_type (void)
1892 static GType our_type
= 0;
1895 our_type
= g_boxed_type_register_static ("GtkIconInfo",
1896 (GBoxedCopyFunc
) gtk_icon_info_copy
,
1897 (GBoxedFreeFunc
) gtk_icon_info_free
);
1902 static GtkIconInfo
*
1903 icon_info_new (void)
1905 GtkIconInfo
*icon_info
= g_new0 (GtkIconInfo
, 1);
1907 icon_info
->ref_count
= 1;
1908 icon_info
->scale
= -1.;
1913 static GtkIconInfo
*
1914 icon_info_new_builtin (BuiltinIcon
*icon
)
1916 GtkIconInfo
*icon_info
= icon_info_new ();
1918 icon_info
->builtin_pixbuf
= g_object_ref (icon
->pixbuf
);
1919 icon_info
->dir_type
= ICON_THEME_DIR_THRESHOLD
;
1920 icon_info
->dir_size
= icon
->size
;
1921 icon_info
->threshold
= 2;
1927 * gtk_icon_info_copy:
1928 * @icon_info: a #GtkIconInfo
1930 * Make a copy of a #GtkIconInfo.
1932 * Return value: the new GtkIconInfo
1935 gtk_icon_info_copy (GtkIconInfo
*icon_info
)
1939 g_return_val_if_fail (icon_info
!= NULL
, NULL
);
1941 copy
= g_memdup (icon_info
, sizeof (GtkIconInfo
));
1942 if (copy
->builtin_pixbuf
)
1943 g_object_ref (copy
->builtin_pixbuf
);
1945 g_object_ref (copy
->pixbuf
);
1946 if (copy
->load_error
)
1947 copy
->load_error
= g_error_copy (copy
->load_error
);
1949 copy
->filename
= g_strdup (copy
->filename
);
1955 * gtk_icon_info_free:
1956 * @icon_info: a #GtkIconInfo
1958 * Free a #GtkIconInfo and associated information
1961 gtk_icon_info_free (GtkIconInfo
*icon_info
)
1963 g_return_if_fail (icon_info
!= NULL
);
1965 if (icon_info
->filename
)
1966 g_free (icon_info
->filename
);
1967 if (icon_info
->builtin_pixbuf
)
1968 g_object_unref (icon_info
->builtin_pixbuf
);
1969 if (icon_info
->pixbuf
)
1970 g_object_unref (icon_info
->pixbuf
);
1976 * gtk_icon_info_get_base_size:
1977 * @icon_info: a #GtkIconInfo
1979 * Gets the base size for the icon. The base size
1980 * is a size for the icon that was specified by
1981 * the icon theme creator. This may be different
1982 * than the actual size of image; an example of
1983 * this is small emblem icons that can be attached
1984 * to a larger icon. These icons will be given
1985 * the same base size as the larger icons to which
1986 * they are attached.
1988 * Return value: the base size, or 0, if no base
1989 * size is known for the icon.
1992 gtk_icon_info_get_base_size (GtkIconInfo
*icon_info
)
1994 g_return_val_if_fail (icon_info
!= NULL
, 0);
1996 return icon_info
->dir_size
;
2000 * gtk_icon_info_get_filename:
2001 * @icon_info: a #GtkIconInfo
2003 * Gets the filename for the icon. If the
2004 * %GTK_ICON_LOOKUP_USE_BUILTIN flag was passed
2005 * to gtk_icon_theme_lookup_icon(), there may be
2006 * no filename if a builtin icon is returned; in this
2007 * case, you should use gtk_icon_info_get_builtin_pixbuf().
2009 * Return value: the filename for the icon, or %NULL
2010 * if gtk_icon_info_get_builtin_pixbuf() should
2011 * be used instead. The return value is owned by
2012 * GTK+ and should not be modified or freed.
2014 G_CONST_RETURN gchar
*
2015 gtk_icon_info_get_filename (GtkIconInfo
*icon_info
)
2017 g_return_val_if_fail (icon_info
!= NULL
, NULL
);
2019 return icon_info
->filename
;
2023 * gtk_icon_info_get_builtin_pixbuf:
2024 * @icon_info: a #GtkIconInfo structure
2026 * Gets the built-in image for this icon, if any. To allow
2027 * GTK+ to use built in icon images, you must pass the
2028 * %GTK_ICON_LOOKUP_USE_BUILTIN to
2029 * gtk_icon_theme_lookup_icon().
2031 * Return value: the built-in image pixbuf, or %NULL. No
2032 * extra reference is added to the returned pixbuf, so if
2033 * you want to keep it around, you must use g_object_ref().
2034 * The returned image must not be modified.
2037 gtk_icon_info_get_builtin_pixbuf (GtkIconInfo
*icon_info
)
2039 g_return_val_if_fail (icon_info
!= NULL
, NULL
);
2041 return icon_info
->builtin_pixbuf
;
2045 load_svg_at_size (const gchar
*filename
,
2049 GdkPixbuf
*pixbuf
= NULL
;
2050 GdkPixbufLoader
*loader
= NULL
;
2054 if (!g_file_get_contents (filename
,
2055 &contents
, &length
, error
))
2058 loader
= gdk_pixbuf_loader_new ();
2059 gdk_pixbuf_loader_set_size (loader
, size
, size
);
2061 if (!gdk_pixbuf_loader_write (loader
, contents
, length
, error
))
2063 gdk_pixbuf_loader_close (loader
, NULL
);
2067 if (!gdk_pixbuf_loader_close (loader
, error
))
2070 pixbuf
= g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader
));
2074 g_object_unref (loader
);
2079 /* This function contains the complicatd logic for deciding
2080 * on the size at which to load the icon and loading it at
2084 icon_info_ensure_scale_and_pixbuf (GtkIconInfo
*icon_info
,
2085 gboolean scale_only
)
2087 int image_width
, image_height
;
2088 GdkPixbuf
*source_pixbuf
;
2090 /* First check if we already succeeded have the necessary
2091 * information (or failed earlier)
2093 if (scale_only
&& icon_info
->scale
>= 0)
2096 if (icon_info
->pixbuf
)
2099 if (icon_info
->load_error
)
2102 /* SVG icons are a special case - we just immediately scale them
2103 * to the desired size
2105 if (icon_info
->filename
&& g_str_has_suffix (icon_info
->filename
, ".svg"))
2107 icon_info
->scale
= icon_info
->desired_size
/ 1000.;
2112 icon_info
->pixbuf
= load_svg_at_size (icon_info
->filename
,
2113 icon_info
->desired_size
,
2114 &icon_info
->load_error
);
2116 return icon_info
->pixbuf
!= NULL
;
2119 /* In many cases, the scale can be determined without actual access
2120 * to the icon file. This is generally true when we have a size
2121 * for the directory where the icon is; the image size doesn't
2122 * matter in that case.
2124 if (icon_info
->dir_type
== ICON_THEME_DIR_FIXED
)
2125 icon_info
->scale
= 1.0;
2126 else if (icon_info
->dir_type
== ICON_THEME_DIR_THRESHOLD
)
2128 if (icon_info
->desired_size
>= icon_info
->dir_size
- icon_info
->threshold
&&
2129 icon_info
->desired_size
<= icon_info
->dir_size
+ icon_info
->threshold
)
2130 icon_info
->scale
= 1.0;
2131 else if (icon_info
->dir_size
> 0)
2132 icon_info
->scale
=(gdouble
) icon_info
->desired_size
/ icon_info
->dir_size
;
2134 else if (icon_info
->dir_type
== ICON_THEME_DIR_SCALABLE
)
2136 if (icon_info
->dir_size
> 0)
2137 icon_info
->scale
= (gdouble
) icon_info
->desired_size
/ icon_info
->dir_size
;
2140 if (icon_info
->scale
>= 0. && scale_only
)
2143 /* At this point, we need to actually get the icon; either from the
2144 * builting image or by loading the file
2146 if (icon_info
->builtin_pixbuf
)
2147 source_pixbuf
= g_object_ref (icon_info
->builtin_pixbuf
);
2150 source_pixbuf
= gdk_pixbuf_new_from_file (icon_info
->filename
,
2151 &icon_info
->load_error
);
2156 /* Do scale calculations that depend on the image size
2158 image_width
= gdk_pixbuf_get_width (source_pixbuf
);
2159 image_height
= gdk_pixbuf_get_height (source_pixbuf
);
2161 if (icon_info
->scale
< 0.0)
2163 gint image_size
= MAX (image_width
, image_height
);
2165 icon_info
->scale
= icon_info
->desired_size
/ image_size
;
2167 icon_info
->scale
= 1.0;
2169 if (icon_info
->dir_type
== ICON_THEME_DIR_UNTHEMED
)
2170 icon_info
->scale
= MAX (icon_info
->scale
, 1.0);
2173 /* We don't short-circuit out here for scale_only, since, now
2174 * we've loaded the icon, we might as well go ahead and finish
2175 * the job. This is a bit of a waste when we scale here
2176 * and never get the final pixbuf; at the cost of a bit of
2177 * extra complexity, we could keep the source pixbuf around
2178 * but not actually scale it until neede.
2181 if (icon_info
->scale
== 1.0)
2182 icon_info
->pixbuf
= source_pixbuf
;
2185 icon_info
->pixbuf
= gdk_pixbuf_scale_simple (source_pixbuf
,
2186 0.5 + image_width
* icon_info
->scale
,
2187 0.5 + image_height
* icon_info
->scale
,
2188 GDK_INTERP_BILINEAR
);
2189 g_object_unref (source_pixbuf
);
2196 * gtk_icon_info_load_icon:
2197 * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
2200 * Renders an icon previously looked up in an icon theme using
2201 * gtk_icon_theme_lookup_icon(); the size will be based on the size
2202 * pssed to gtk_icon_theme_lookup_icon(). Note that the resulting
2203 * pixbuf may not be exactly this size; an icon theme may have icons
2204 * that differ slightly from their nominal sizes, and in addition GTK+
2205 * will avoid scaling icons that it considers sufficiently close to the
2206 * requested size. (This maintains sharpness.)
2208 * Return value: the rendered icon; this may be a newly created icon
2209 * or a new reference to an internal icon, so you must not modify
2210 * the icon. Use g_object_unref() to release your reference to the
2214 gtk_icon_info_load_icon (GtkIconInfo
*icon_info
,
2217 g_return_val_if_fail (icon_info
!= NULL
, NULL
);
2219 g_return_val_if_fail (icon_info
!= NULL
, NULL
);
2220 g_return_val_if_fail (error
== NULL
|| *error
== NULL
, NULL
);
2222 icon_info_ensure_scale_and_pixbuf (icon_info
, FALSE
);
2224 if (icon_info
->load_error
)
2226 g_propagate_error (error
, icon_info
->load_error
);
2230 return g_object_ref (icon_info
->pixbuf
);
2234 * gtk_icon_info_set_raw_coordinates:
2235 * @icon_info: a #GtkIconInfo
2236 * @raw_coordinates: whether the coordinates of embedded rectangles
2237 * and attached points should be returned in their original
2240 * Sets whether the coordinates returned by gtk_icon_info_get_embedded_rect()
2241 * and gtk_icon_info_get_attach_points() should be returned in their
2242 * original form as specified in the icon theme, instead of scaled
2243 * appropriately for the pixbuf returned by gtk_icon_info_load_icon().
2245 * Raw coordinates are somewhat strange; they are specified to be with
2246 * respect to the unscaled pixmap for PNG and XPM icons, but for SVG
2247 * icons, they are in a 1000x1000 coordinate space that is scaled
2248 * to the final size of the icon. You can determine if the icon is an SVG
2249 * icon by using gtk_icon_info_get_filename(), and seeing if it is non-%NULL
2250 * and ends in '.svg'.
2252 * This function is provided primarily to allow compatibility wrappers
2253 * for older API's, and is not expected to be useful for applications.
2256 gtk_icon_info_set_raw_coordinates (GtkIconInfo
*icon_info
,
2257 gboolean raw_coordinates
)
2259 g_return_if_fail (icon_info
!= NULL
);
2261 icon_info
->raw_coordinates
= raw_coordinates
!= FALSE
;
2264 /* Scale coordinates from the icon data prior to returning
2268 icon_info_scale_point (GtkIconInfo
*icon_info
,
2274 if (icon_info
->raw_coordinates
)
2281 if (!icon_info_ensure_scale_and_pixbuf (icon_info
, TRUE
))
2284 *x_out
= 0.5 + x
* icon_info
->scale
;
2285 *y_out
= 0.5 + y
* icon_info
->scale
;
2292 * gtk_icon_info_get_embedded_rect:
2293 * @icon_info: a #GtkIconInfo
2294 * @rectangle: #GdkRectangle in which to store embedded
2295 * rectangle coordinates; coordinates are only stored
2296 * when this function returns %TRUE.
2298 * Gets the coordinates of a rectangle within the icon
2299 * that can be used for display of information such
2300 * as a preview of the contents of a text file.
2301 * See gtk_icon_info_set_raw_coordinates() for further
2302 * information about the coordinate system.
2304 * Return value: %TRUE if the icon has an embedded rectangle
2307 gtk_icon_info_get_embedded_rect (GtkIconInfo
*icon_info
,
2308 GdkRectangle
*rectangle
)
2310 g_return_val_if_fail (icon_info
!= NULL
, FALSE
);
2312 if (icon_info
->data
&& icon_info
->data
->has_embedded_rect
&&
2313 icon_info_ensure_scale_and_pixbuf (icon_info
, TRUE
))
2315 gint scaled_x0
, scaled_y0
;
2316 gint scaled_x1
, scaled_y1
;
2320 icon_info_scale_point (icon_info
,
2321 icon_info
->data
->x0
, icon_info
->data
->y0
,
2322 &scaled_x0
, &scaled_y0
);
2323 icon_info_scale_point (icon_info
,
2324 icon_info
->data
->x0
, icon_info
->data
->y0
,
2325 &scaled_x1
, &scaled_y1
);
2327 rectangle
->x
= scaled_x0
;
2328 rectangle
->y
= scaled_y0
;
2329 rectangle
->width
= scaled_x1
- rectangle
->x
;
2330 rectangle
->height
= scaled_y1
- rectangle
->y
;
2340 * gtk_icon_info_get_attach_points:
2341 * @icon_info: a #GtkIconInfo
2342 * @points: location to store pointer to an array of points, or %NULL
2343 * free the array of points with g_free().
2344 * @n_points: location to store the number of points in @points, or %NULL
2346 * Fetches the set of attach points for an icon. An attach point
2347 * is a location in the icon that can be used as anchor points for attaching
2348 * emblems or overlays to the icon.
2350 * Return value: %TRUE if there are any attach points for the icon.
2353 gtk_icon_info_get_attach_points (GtkIconInfo
*icon_info
,
2357 g_return_val_if_fail (icon_info
!= NULL
, FALSE
);
2359 if (icon_info
->data
&& icon_info
->data
->n_attach_points
&&
2360 icon_info_ensure_scale_and_pixbuf (icon_info
, TRUE
))
2366 *points
= g_new (GdkPoint
, icon_info
->data
->n_attach_points
);
2367 for (i
= 0; i
< icon_info
->data
->n_attach_points
; i
++)
2368 icon_info_scale_point (icon_info
,
2369 icon_info
->data
->attach_points
[i
].x
,
2370 icon_info
->data
->attach_points
[i
].y
,
2376 *n_points
= icon_info
->data
->n_attach_points
;
2392 * gtk_icon_info_get_display_name:
2393 * @icon_info: a #GtkIconInfo
2395 * Gets the display name for an icon. A display name is a
2396 * string to be used in place of the icon name in a user
2397 * visible context like a list of icons.
2399 * Return value: the display name for the icon or %NULL, if
2400 * the icon doesn't have a specified display name. This value
2401 * is owned @icon_info and must not be modified or free.
2403 G_CONST_RETURN gchar
*
2404 gtk_icon_info_get_display_name (GtkIconInfo
*icon_info
)
2406 g_return_val_if_fail (icon_info
!= NULL
, NULL
);
2408 if (icon_info
->data
)
2409 return icon_info
->data
->display_name
;
2420 * gtk_icon_theme_add_builtin_icon:
2421 * @icon_name: the name of the icon to register
2422 * @size: the size at which to register the icon (different
2423 * images can be registered for the same icon name
2424 * at different sizes.)
2425 * @pixbuf: #GdkPixbuf that contains the image to use
2428 * Registers a built-in icon for icon theme lookups. The idea
2429 * of built-in icons is to allow an application or library
2430 * that uses themed icons to function requiring files to
2431 * be present in the file system. For instance, the default
2432 * images for all of GTK+'s stock icons are registered
2435 * In general, if you use gtk_icon_theme_add_builtin_icon()
2436 * you should also install the icon in the icon theme, so
2437 * that the icon is generally available.
2439 * This function will generally be used with pixbufs loaded
2440 * via gdk_pixbuf_new_from_inline ().
2443 gtk_icon_theme_add_builtin_icon (const gchar
*icon_name
,
2447 BuiltinIcon
*default_icon
;
2451 g_return_if_fail (icon_name
!= NULL
);
2452 g_return_if_fail (GDK_IS_PIXBUF (pixbuf
));
2454 if (!icon_theme_builtin_icons
)
2455 icon_theme_builtin_icons
= g_hash_table_new (g_str_hash
, g_str_equal
);
2457 icons
= g_hash_table_lookup (icon_theme_builtin_icons
, icon_name
);
2459 key
= g_strdup (icon_name
);
2461 key
= (gpointer
)icon_name
; /* Won't get stored */
2463 default_icon
= g_new (BuiltinIcon
, 1);
2464 default_icon
->size
= size
;
2465 default_icon
->pixbuf
= g_object_ref (pixbuf
);
2466 icons
= g_slist_prepend (icons
, default_icon
);
2468 /* Replaces value, leaves key untouched
2470 g_hash_table_insert (icon_theme_builtin_icons
, key
, icons
);
2473 /* Look up a builtin icon; the min_difference_p and
2474 * has_larger_p out parameters allow us to combine
2475 * this lookup with searching through the actual directories
2476 * of the "hicolor" icon theme. See theme_lookup_icon()
2477 * for how they are used.
2479 static BuiltinIcon
*
2480 find_builtin_icon (const gchar
*icon_name
,
2482 gint
*min_difference_p
,
2483 gboolean
*has_larger_p
)
2485 GSList
*icons
= NULL
;
2486 gint min_difference
= G_MAXINT
;
2487 gboolean has_larger
= FALSE
;
2488 BuiltinIcon
*min_icon
= NULL
;
2490 if (!icon_theme_builtin_icons
)
2493 icons
= g_hash_table_lookup (icon_theme_builtin_icons
, icon_name
);
2497 BuiltinIcon
*default_icon
= icons
->data
;
2498 int min
, max
, difference
;
2501 min
= default_icon
->size
- 2;
2502 max
= default_icon
->size
+ 2;
2503 smaller
= size
< min
;
2505 difference
= min
- size
;
2507 difference
= size
- max
;
2511 if (difference
== 0)
2513 min_icon
= default_icon
;
2519 if (difference
< min_difference
|| smaller
)
2521 min_difference
= difference
;
2522 min_icon
= default_icon
;
2523 has_larger
= smaller
;
2528 if (difference
< min_difference
&& smaller
)
2530 min_difference
= difference
;
2531 min_icon
= default_icon
;
2535 icons
= icons
->next
;
2538 if (min_difference_p
)
2539 *min_difference_p
= min_difference
;
2541 *has_larger_p
= has_larger
;