X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;ds=sidebyside;f=gtk%2Fgtkicontheme.c;h=66e03adddd478a061beb2023d3d9030c5003d790;hb=de47b90fd10ac494fda16af4c723ed2d5f6be946;hp=6cb06aeb7bc175f5a5506030ae17c5c1658e8e0d;hpb=bcf7d7e972d1fd814fd8a340edc427a6cc9ee7b5;p=~andy%2Fgtk diff --git a/gtk/gtkicontheme.c b/gtk/gtkicontheme.c index 6cb06aeb7..66e03addd 100644 --- a/gtk/gtkicontheme.c +++ b/gtk/gtkicontheme.c @@ -33,17 +33,115 @@ #ifndef S_ISDIR #define S_ISDIR(mode) ((mode)&_S_IFDIR) #endif +#define WIN32_MEAN_AND_LEAN +#include +#include "win32/gdkwin32.h" #endif /* G_OS_WIN32 */ #include "gtkicontheme.h" +#include "gtkdebug.h" #include "gtkiconfactory.h" #include "gtkiconcache.h" #include "gtkbuiltincache.h" #include "gtkintl.h" #include "gtkmain.h" +#include "gtknumerableiconprivate.h" #include "gtksettings.h" #include "gtkprivate.h" -#include "gtkalias.h" + +#undef GDK_DEPRECATED +#undef GDK_DEPRECATED_FOR +#define GDK_DEPRECATED +#define GDK_DEPRECATED_FOR(f) +#undef GTK_DISABLE_DEPRECATED + +#include "deprecated/gtkstyle.h" + + +/** + * SECTION:gtkicontheme + * @Short_description: Looking up icons by name + * @Title: GtkIconTheme + * + * #GtkIconTheme provides a facility for looking up icons by name + * and size. The main reason for using a name rather than simply + * providing a filename is to allow different icons to be used + * depending on what icon theme is selected + * by the user. The operation of icon themes on Linux and Unix + * follows the Icon + * Theme Specification. There is a default icon theme, + * named hicolor where applications should install + * their icons, but more additional application themes can be + * installed as operating system vendors and users choose. + * + * Named icons are similar to the + * facility, and the distinction between the two may be a bit confusing. + * A few things to keep in mind: + * + * + * Stock images usually are used in conjunction with + * , such as %GTK_STOCK_OK or + * %GTK_STOCK_OPEN. Named icons are easier to set up and therefore + * are more useful for new icons that an application wants to + * add, such as application icons or window icons. + * + * + * Stock images can only be loaded at the symbolic sizes defined + * by the #GtkIconSize enumeration, or by custom sizes defined + * by gtk_icon_size_register(), while named icons are more flexible + * and any pixel size can be specified. + * + * + * Because stock images are closely tied to stock items, and thus + * to actions in the user interface, stock images may come in + * multiple variants for different widget states or writing + * directions. + * + * + * A good rule of thumb is that if there is a stock image for what + * you want to use, use it, otherwise use a named icon. It turns + * out that internally stock images are generally defined in + * terms of one or more named icons. (An example of the + * more than one case is icons that depend on writing direction; + * %GTK_STOCK_GO_FORWARD uses the two themed icons + * "gtk-stock-go-forward-ltr" and "gtk-stock-go-forward-rtl".) + * + * In many cases, named themes are used indirectly, via #GtkImage + * or stock items, rather than directly, but looking up icons + * directly is also simple. The #GtkIconTheme object acts + * as a database of all the icons in the current theme. You + * can create new #GtkIconTheme objects, but its much more + * efficient to use the standard icon theme for the #GdkScreen + * so that the icon information is shared with other people + * looking up icons. In the case where the default screen is + * being used, looking up an icon can be as simple as: + * + * + * GError *error = NULL; + * GtkIconTheme *icon_theme; + * GdkPixbuf *pixbuf; + * + * icon_theme = gtk_icon_theme_get_default (); + * pixbuf = gtk_icon_theme_load_icon (icon_theme, + * "my-icon-name", // icon name + * 48, // size + * 0, // flags + * &error); + * if (!pixbuf) + * { + * g_warning ("Couldn't load icon: %s", error->message); + * g_error_free (error); + * } + * else + * { + * // Use the pixbuf + * g_object_unref (pixbuf); + * } + * + * + */ + #define DEFAULT_THEME_NAME "hicolor" @@ -68,23 +166,24 @@ typedef enum struct _GtkIconThemePrivate { + gchar *current_theme; + gchar *fallback_theme; + gchar **search_path; + gint search_path_len; + guint custom_theme : 1; guint is_screen_singleton : 1; guint pixbuf_supports_svg : 1; guint themes_valid : 1; guint check_reload : 1; - - char *current_theme; - char *fallback_theme; - char **search_path; - int search_path_len; + guint loading_themes : 1; /* A list of all the themes needed to look up icons. * In search order, without duplicates */ GList *themes; GHashTable *unthemed_icons; - + /* Note: The keys of this hashtable are owned by the * themedir and unthemed hashtables. */ @@ -93,9 +192,9 @@ struct _GtkIconThemePrivate /* GdkScreen for the icon theme (may be NULL) */ GdkScreen *screen; - + /* time when we last stat:ed for theme changes */ - long last_stat_time; + glong last_stat_time; GList *dir_mtimes; gulong reset_styles_idle; @@ -106,17 +205,14 @@ struct _GtkIconInfo /* Information about the source */ gchar *filename; -#ifdef G_OS_WIN32 - /* System codepage version of filename, for DLL ABI backward - * compatibility functions. - */ - gchar *cp_filename; -#endif + GLoadableIcon *loadable; + GSList *emblem_infos; + /* Cache pixbuf (if there is any) */ GdkPixbuf *cache_pixbuf; GtkIconData *data; - + /* Information about the directory where * the source was found */ @@ -127,7 +223,11 @@ struct _GtkIconInfo /* Parameters influencing the scaled icon */ gint desired_size; - gboolean raw_coordinates; + guint raw_coordinates : 1; + guint forced_size : 1; + guint emblems_applied : 1; + + guint ref_count; /* Cached information if we go ahead and try to load * the icon. @@ -160,6 +260,7 @@ typedef struct char *dir; char *subdir; + int subdir_index; GtkIconCache *cache; @@ -199,13 +300,16 @@ static GtkIconInfo *theme_lookup_icon (IconTheme *theme, static void theme_list_icons (IconTheme *theme, GHashTable *icons, GQuark context); +static void theme_list_contexts (IconTheme *theme, + GHashTable *contexts); static void theme_subdir_load (GtkIconTheme *icon_theme, IconTheme *theme, GKeyFile *theme_file, char *subdir); static void do_theme_change (GtkIconTheme *icon_theme); -static void blow_themes (GtkIconTheme *icon_themes); +static void blow_themes (GtkIconTheme *icon_themes); +static gboolean rescan_themes (GtkIconTheme *icon_themes); static void icon_data_free (GtkIconData *icon_data); static void load_icon_data (IconThemeDir *dir, @@ -261,8 +365,8 @@ gtk_icon_theme_new (void) * * Gets the icon theme for the default screen. See * gtk_icon_theme_get_for_screen(). - * - * Return value: A unique #GtkIconTheme associated with + * + * Return value: (transfer none): A unique #GtkIconTheme associated with * the default screen. This icon theme is associated with * the screen and can be used as long as the screen * is open. Do not ref or unref it. @@ -287,8 +391,8 @@ gtk_icon_theme_get_default (void) * is usually a better choice than calling than gtk_icon_theme_new() * and setting the screen yourself; by using this function * a single icon theme object will be shared between users. - * - * Return value: A unique #GtkIconTheme associated with + * + * Return value: (transfer none): A unique #GtkIconTheme associated with * the given screen. This icon theme is associated with * the screen and can be used as long as the screen * is open. Do not ref or unref it. @@ -301,7 +405,6 @@ gtk_icon_theme_get_for_screen (GdkScreen *screen) GtkIconTheme *icon_theme; g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); - g_return_val_if_fail (!screen->closed, NULL); icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme"); if (!icon_theme) @@ -377,6 +480,9 @@ display_closed (GdkDisplay *display, static void update_current_theme (GtkIconTheme *icon_theme) { +#define theme_changed(_old, _new) \ + ((_old && !_new) || (!_old && _new) || \ + (_old && _new && strcmp (_old, _new) != 0)) GtkIconThemePrivate *priv = icon_theme->priv; if (!priv->custom_theme) @@ -393,27 +499,25 @@ update_current_theme (GtkIconTheme *icon_theme) "gtk-fallback-icon-theme", &fallback_theme, NULL); } - if (!theme) + /* ensure that the current theme (even when just the default) + * is searched before any fallback theme + */ + if (!theme && fallback_theme) theme = g_strdup (DEFAULT_THEME_NAME); - if (strcmp (priv->current_theme, theme) != 0) + if (theme_changed (priv->current_theme, theme)) { g_free (priv->current_theme); priv->current_theme = theme; - changed = TRUE; } else g_free (theme); - if ((priv->fallback_theme && !fallback_theme) || - (!priv->fallback_theme && fallback_theme) || - (priv->fallback_theme && fallback_theme && - strcmp (priv->fallback_theme, fallback_theme) != 0)) + if (theme_changed (priv->fallback_theme, fallback_theme)) { g_free (priv->fallback_theme); priv->fallback_theme = fallback_theme; - changed = TRUE; } else @@ -422,6 +526,7 @@ update_current_theme (GtkIconTheme *icon_theme) if (changed) do_theme_change (icon_theme); } +#undef theme_changed } /* Callback when the icon theme GtkSetting changes @@ -542,13 +647,13 @@ gtk_icon_theme_init (GtkIconTheme *icon_theme) GtkIconThemePrivate *priv; const gchar * const *xdg_data_dirs; int i, j; - - priv = g_type_instance_get_private ((GTypeInstance *)icon_theme, - GTK_TYPE_ICON_THEME); + + priv = G_TYPE_INSTANCE_GET_PRIVATE (icon_theme, + GTK_TYPE_ICON_THEME, + GtkIconThemePrivate); icon_theme->priv = priv; priv->custom_theme = FALSE; - priv->current_theme = g_strdup (DEFAULT_THEME_NAME); xdg_data_dirs = g_get_system_data_dirs (); for (i = 0; xdg_data_dirs[i]; i++) ; @@ -595,10 +700,7 @@ reset_styles_idle (gpointer user_data) priv = icon_theme->priv; if (priv->screen && priv->is_screen_singleton) - { - GtkSettings *settings = gtk_settings_get_for_screen (priv->screen); - gtk_rc_reset_styles (settings); - } + gtk_style_context_reset_widgets (priv->screen); priv->reset_styles_idle = 0; @@ -609,6 +711,9 @@ static void do_theme_change (GtkIconTheme *icon_theme) { GtkIconThemePrivate *priv = icon_theme->priv; + + if (!priv->themes_valid) + return; GTK_NOTE (ICONTHEME, g_print ("change to icon theme \"%s\"\n", priv->current_theme)); @@ -677,7 +782,8 @@ gtk_icon_theme_finalize (GObject *object) /** * gtk_icon_theme_set_search_path: * @icon_theme: a #GtkIconTheme - * @path: array of directories that are searched for icon themes + * @path: (array length=n_elements) (element-type filename): array of + * directories that are searched for icon themes * @n_elements: number of elements in @path. * * Sets the search path for the icon theme object. When looking @@ -726,19 +832,19 @@ gtk_icon_theme_set_search_path (GtkIconTheme *icon_theme, /** * gtk_icon_theme_get_search_path: * @icon_theme: a #GtkIconTheme - * @path: location to store a list of icon theme path directories or %NULL - * The stored value should be freed with g_strfreev(). - * @n_elements: location to store number of elements - * in @path, or %NULL - * + * @path: (allow-none) (array length=n_elements) (element-type filename) (out): + * location to store a list of icon theme path directories or %NULL. + * The stored value should be freed with g_strfreev(). + * @n_elements: location to store number of elements in @path, or %NULL + * * Gets the current search path. See gtk_icon_theme_set_search_path(). * * Since: 2.4 - **/ + */ void -gtk_icon_theme_get_search_path (GtkIconTheme *icon_theme, - gchar **path[], - gint *n_elements) +gtk_icon_theme_get_search_path (GtkIconTheme *icon_theme, + gchar **path[], + gint *n_elements) { GtkIconThemePrivate *priv; int i; @@ -762,7 +868,7 @@ gtk_icon_theme_get_search_path (GtkIconTheme *icon_theme, /** * gtk_icon_theme_append_search_path: * @icon_theme: a #GtkIconTheme - * @path: directory name to append to the icon path + * @path: (type filename): directory name to append to the icon path * * Appends a directory to the search path. * See gtk_icon_theme_set_search_path(). @@ -791,7 +897,7 @@ gtk_icon_theme_append_search_path (GtkIconTheme *icon_theme, /** * gtk_icon_theme_prepend_search_path: * @icon_theme: a #GtkIconTheme - * @path: directory name to prepend to the icon path + * @path: (type filename): directory name to prepend to the icon path * * Prepends a directory to the search path. * See gtk_icon_theme_set_search_path(). @@ -824,8 +930,8 @@ gtk_icon_theme_prepend_search_path (GtkIconTheme *icon_theme, /** * gtk_icon_theme_set_custom_theme: * @icon_theme: a #GtkIconTheme - * @theme_name: name of icon theme to use instead of configured theme, - * or %NULL to unset a previously set custom theme + * @theme_name: (allow-none): name of icon theme to use instead of + * configured theme, or %NULL to unset a previously set custom theme * * Sets the name of the icon theme that the #GtkIconTheme object uses * overriding system configuration. This function cannot be called @@ -849,7 +955,7 @@ gtk_icon_theme_set_custom_theme (GtkIconTheme *icon_theme, if (theme_name != NULL) { priv->custom_theme = TRUE; - if (strcmp (theme_name, priv->current_theme) != 0) + if (!priv->current_theme || strcmp (theme_name, priv->current_theme) != 0) { g_free (priv->current_theme); priv->current_theme = g_strdup (theme_name); @@ -859,9 +965,12 @@ gtk_icon_theme_set_custom_theme (GtkIconTheme *icon_theme, } else { - priv->custom_theme = FALSE; + if (priv->custom_theme) + { + priv->custom_theme = FALSE; - update_current_theme (icon_theme); + update_current_theme (icon_theme); + } } } @@ -878,7 +987,7 @@ insert_theme (GtkIconTheme *icon_theme, const char *theme_name) GKeyFile *theme_file; GError *error = NULL; IconThemeDirMtime *dir_mtime; - struct stat stat_buf; + GStatBuf stat_buf; priv = icon_theme->priv; @@ -992,10 +1101,8 @@ insert_theme (GtkIconTheme *icon_theme, const char *theme_name) static void free_unthemed_icon (UnthemedIcon *unthemed_icon) { - if (unthemed_icon->svg_filename) - g_free (unthemed_icon->svg_filename); - if (unthemed_icon->no_svg_filename) - g_free (unthemed_icon->no_svg_filename); + g_free (unthemed_icon->svg_filename); + g_free (unthemed_icon->no_svg_filename); g_slice_free (UnthemedIcon, unthemed_icon); } @@ -1024,13 +1131,14 @@ load_themes (GtkIconTheme *icon_theme) IconSuffix old_suffix, new_suffix; GTimeVal tv; IconThemeDirMtime *dir_mtime; - struct stat stat_buf; + GStatBuf stat_buf; priv = icon_theme->priv; priv->all_icons = g_hash_table_new (g_str_hash, g_str_equal); - insert_theme (icon_theme, priv->current_theme); + if (priv->current_theme) + insert_theme (icon_theme, priv->current_theme); /* Always look in the "default" icon theme, and in a fallback theme */ if (priv->fallback_theme) @@ -1115,9 +1223,10 @@ load_themes (GtkIconTheme *icon_theme) else unthemed_icon->no_svg_filename = abs_file; - g_hash_table_insert (priv->unthemed_icons, - base_name, - unthemed_icon); + /* takes ownership of base_name */ + g_hash_table_replace (priv->unthemed_icons, + base_name, + unthemed_icon); g_hash_table_insert (priv->all_icons, base_name, NULL); } @@ -1139,11 +1248,11 @@ _gtk_icon_theme_ensure_builtin_cache (void) IconThemeDir *dir; static IconThemeDir dirs[5] = { - { ICON_THEME_DIR_THRESHOLD, 0, 16, 16, 16, 2, NULL, "16", NULL, NULL, NULL }, - { ICON_THEME_DIR_THRESHOLD, 0, 20, 20, 20, 2, NULL, "20", NULL, NULL, NULL }, - { ICON_THEME_DIR_THRESHOLD, 0, 24, 24, 24, 2, NULL, "24", NULL, NULL, NULL }, - { ICON_THEME_DIR_THRESHOLD, 0, 32, 32, 32, 2, NULL, "32", NULL, NULL, NULL }, - { ICON_THEME_DIR_THRESHOLD, 0, 48, 48, 48, 2, NULL, "48", NULL, NULL, NULL } + { ICON_THEME_DIR_THRESHOLD, 0, 16, 16, 16, 2, NULL, "16", -1, NULL, NULL, NULL }, + { ICON_THEME_DIR_THRESHOLD, 0, 20, 20, 20, 2, NULL, "20", -1, NULL, NULL, NULL }, + { ICON_THEME_DIR_THRESHOLD, 0, 24, 24, 24, 2, NULL, "24", -1, NULL, NULL, NULL }, + { ICON_THEME_DIR_THRESHOLD, 0, 32, 32, 32, 2, NULL, "32", -1, NULL, NULL, NULL }, + { ICON_THEME_DIR_THRESHOLD, 0, 48, 48, 48, 2, NULL, "48", -1, NULL, NULL, NULL } }; gint i; @@ -1157,6 +1266,7 @@ _gtk_icon_theme_ensure_builtin_cache (void) { dir = &(dirs[i]); dir->cache = _gtk_icon_cache_ref (_builtin_cache); + dir->subdir_index = _gtk_icon_cache_get_directory_index (dir->cache, dir->subdir); builtin_dirs = g_list_append (builtin_dirs, dir); } @@ -1170,80 +1280,50 @@ ensure_valid_themes (GtkIconTheme *icon_theme) GTimeVal tv; gboolean was_valid = priv->themes_valid; + if (priv->loading_themes) + return; + priv->loading_themes = TRUE; + _gtk_icon_theme_ensure_builtin_cache (); if (priv->themes_valid) { g_get_current_time (&tv); - if (ABS (tv.tv_sec - priv->last_stat_time) > 5) - gtk_icon_theme_rescan_if_needed (icon_theme); + if (ABS (tv.tv_sec - priv->last_stat_time) > 5 && + rescan_themes (icon_theme)) + blow_themes (icon_theme); } if (!priv->themes_valid) { load_themes (icon_theme); - - if (!priv->check_reload && was_valid && priv->screen) - { - static GdkAtom atom_iconthemes = GDK_NONE; - GdkEvent *event = gdk_event_new (GDK_CLIENT_EVENT); - int i; - - if (!atom_iconthemes) - atom_iconthemes = gdk_atom_intern_static_string ("_GTK_LOAD_ICONTHEMES"); - for (i = 0; i < 5; i++) - event->client.data.l[i] = 0; - event->client.data_format = 32; - event->client.message_type = atom_iconthemes; - - gdk_screen_broadcast_client_message (priv->screen, event); + if (was_valid) + { + g_signal_emit (icon_theme, signal_changed, 0); } } + + priv->loading_themes = FALSE; } -/** - * gtk_icon_theme_lookup_icon: - * @icon_theme: a #GtkIconTheme - * @icon_name: the name of the icon to lookup - * @size: desired icon size - * @flags: flags modifying the behavior of the icon lookup - * - * Looks up a named icon and returns a structure containing - * information such as the filename of the icon. The icon - * can then be rendered into a pixbuf using - * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon() - * combines these two steps if all you need is the pixbuf.) - * - * Return value: a #GtkIconInfo structure containing information - * about the icon, or %NULL if the icon wasn't found. Free with - * gtk_icon_info_free() - * - * Since: 2.4 - **/ -GtkIconInfo * -gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme, - const gchar *icon_name, - gint size, - GtkIconLookupFlags flags) +static GtkIconInfo * +choose_icon (GtkIconTheme *icon_theme, + const gchar *icon_names[], + gint size, + GtkIconLookupFlags flags) { GtkIconThemePrivate *priv; GList *l; GtkIconInfo *icon_info = NULL; - UnthemedIcon *unthemed_icon; + UnthemedIcon *unthemed_icon = NULL; gboolean allow_svg; gboolean use_builtin; - - g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL); - g_return_val_if_fail (icon_name != NULL, NULL); - g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 || - (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL); + gint i; priv = icon_theme->priv; - GTK_NOTE (ICONTHEME, - g_print ("gtk_icon_theme_lookup_icon %s\n", icon_name)); if (flags & GTK_ICON_LOOKUP_NO_SVG) allow_svg = FALSE; else if (flags & GTK_ICON_LOOKUP_FORCE_SVG) @@ -1251,20 +1331,55 @@ gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme, else allow_svg = priv->pixbuf_supports_svg; - use_builtin = (flags & GTK_ICON_LOOKUP_USE_BUILTIN); - + use_builtin = flags & GTK_ICON_LOOKUP_USE_BUILTIN; + ensure_valid_themes (icon_theme); for (l = priv->themes; l; l = l->next) { IconTheme *theme = l->data; - icon_info = theme_lookup_icon (theme, icon_name, size, allow_svg, use_builtin); - if (icon_info) - goto out; + for (i = 0; icon_names[i]; i++) + { + icon_info = theme_lookup_icon (theme, icon_names[i], size, allow_svg, use_builtin); + if (icon_info) + goto out; + } + } + + for (i = 0; icon_names[i]; i++) + { + unthemed_icon = g_hash_table_lookup (priv->unthemed_icons, icon_names[i]); + if (unthemed_icon) + break; + } +#ifdef G_OS_WIN32 + /* Still not found an icon, check if reference to a Win32 resource */ + if (!unthemed_icon) + { + gchar **resources; + HICON hIcon = NULL; + + resources = g_strsplit (icon_names[0], ",", 0); + if (resources[0]) + { + wchar_t *wfile = g_utf8_to_utf16 (resources[0], -1, NULL, NULL, NULL); + ExtractIconExW (wfile, resources[1] ? atoi (resources[1]) : 0, &hIcon, NULL, 1); + g_free (wfile); + } + + if (hIcon) + { + icon_info = icon_info_new (); + icon_info->cache_pixbuf = gdk_win32_icon_to_pixbuf_libgtk_only (hIcon); + DestroyIcon (hIcon); + icon_info->dir_type = ICON_THEME_DIR_UNTHEMED; + icon_info->dir_size = size; + } + g_strfreev (resources); } +#endif - unthemed_icon = g_hash_table_lookup (priv->unthemed_icons, icon_name); if (unthemed_icon) { icon_info = icon_info_new (); @@ -1279,17 +1394,17 @@ gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme, icon_info->filename = g_strdup (unthemed_icon->svg_filename); else if (unthemed_icon->no_svg_filename) icon_info->filename = g_strdup (unthemed_icon->no_svg_filename); -#ifdef G_OS_WIN32 - icon_info->cp_filename = g_locale_from_utf8 (icon_info->filename, - -1, NULL, NULL, NULL); -#endif icon_info->dir_type = ICON_THEME_DIR_UNTHEMED; + icon_info->dir_size = size; } out: - if (icon_info) - icon_info->desired_size = size; + if (icon_info) + { + icon_info->desired_size = size; + icon_info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0; + } else { static gboolean check_for_default_theme = TRUE; @@ -1310,13 +1425,14 @@ gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme, found = g_file_test (default_theme_path, G_FILE_TEST_IS_REGULAR); g_free (default_theme_path); } + if (!found) { - g_warning (_("Could not find the icon '%s'. The '%s' theme\n" - "was not found either, perhaps you need to install it.\n" - "You can get a copy from:\n" - "\t%s"), - icon_name, DEFAULT_THEME_NAME, "http://icon-theme.freedesktop.org/releases"); + g_warning ("Could not find the icon '%s'. The '%s' theme\n" + "was not found either, perhaps you need to install it.\n" + "You can get a copy from:\n" + "\t%s", + icon_names[0], DEFAULT_THEME_NAME, "http://icon-theme.freedesktop.org/releases"); } } } @@ -1324,6 +1440,118 @@ gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme, return icon_info; } + +/** + * gtk_icon_theme_lookup_icon: + * @icon_theme: a #GtkIconTheme + * @icon_name: the name of the icon to lookup + * @size: desired icon size + * @flags: flags modifying the behavior of the icon lookup + * + * Looks up a named icon and returns a structure containing + * information such as the filename of the icon. The icon + * can then be rendered into a pixbuf using + * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon() + * combines these two steps if all you need is the pixbuf.) + * + * Return value: a #GtkIconInfo structure containing information + * about the icon, or %NULL if the icon wasn't found. Free with + * gtk_icon_info_free() + * + * Since: 2.4 + */ +GtkIconInfo * +gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme, + const gchar *icon_name, + gint size, + GtkIconLookupFlags flags) +{ + GtkIconInfo *info; + + g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL); + g_return_val_if_fail (icon_name != NULL, NULL); + g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 || + (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL); + + GTK_NOTE (ICONTHEME, + g_print ("gtk_icon_theme_lookup_icon %s\n", icon_name)); + + if (flags & GTK_ICON_LOOKUP_GENERIC_FALLBACK) + { + gchar **names; + gint dashes, i; + gchar *p; + + dashes = 0; + for (p = (gchar *) icon_name; *p; p++) + if (*p == '-') + dashes++; + + names = g_new (gchar *, dashes + 2); + names[0] = g_strdup (icon_name); + for (i = 1; i <= dashes; i++) + { + names[i] = g_strdup (names[i - 1]); + p = strrchr (names[i], '-'); + *p = '\0'; + } + names[dashes + 1] = NULL; + + info = choose_icon (icon_theme, (const gchar **) names, size, flags); + + g_strfreev (names); + } + else + { + const gchar *names[2]; + + names[0] = icon_name; + names[1] = NULL; + + info = choose_icon (icon_theme, names, size, flags); + } + + return info; +} + +/** + * gtk_icon_theme_choose_icon: + * @icon_theme: a #GtkIconTheme + * @icon_names: (array zero-terminated=1): %NULL-terminated array of + * icon names to lookup + * @size: desired icon size + * @flags: flags modifying the behavior of the icon lookup + * + * Looks up a named icon and returns a structure containing + * information such as the filename of the icon. The icon + * can then be rendered into a pixbuf using + * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon() + * combines these two steps if all you need is the pixbuf.) + * + * If @icon_names contains more than one name, this function + * tries them all in the given order before falling back to + * inherited icon themes. + * + * Return value: a #GtkIconInfo structure containing information + * about the icon, or %NULL if the icon wasn't found. Free with + * gtk_icon_info_free() + * + * Since: 2.12 + */ +GtkIconInfo * +gtk_icon_theme_choose_icon (GtkIconTheme *icon_theme, + const gchar *icon_names[], + gint size, + GtkIconLookupFlags flags) +{ + g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL); + g_return_val_if_fail (icon_names != NULL, NULL); + g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 || + (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL); + + return choose_icon (icon_theme, icon_names, size, flags); +} + /* Error quark */ GQuark gtk_icon_theme_error_quark (void) @@ -1336,27 +1564,28 @@ gtk_icon_theme_error_quark (void) * @icon_theme: a #GtkIconTheme * @icon_name: the name of the icon to lookup * @size: the desired icon size. The resulting icon may not be - * exactly this size; see gtk_icon_info_load_icon(). + * exactly this size; see gtk_icon_info_load_icon(). * @flags: flags modifying the behavior of the icon lookup - * @error: Location to store error information on failure, or %NULL. - * + * @error: (allow-none): Location to store error information on failure, + * or %NULL. + * * Looks up an icon in an icon theme, scales it to the given size * and renders it into a pixbuf. This is a convenience function; * if more details about the icon are needed, use * gtk_icon_theme_lookup_icon() followed by gtk_icon_info_load_icon(). * * Note that you probably want to listen for icon theme changes and - * update the icon. This is usually done by connecting to the + * update the icon. This is usually done by connecting to the * GtkWidget::style-set signal. If for some reason you do not want to * update the icon when the icon theme changes, you should consider * using gdk_pixbuf_copy() to make a private copy of the pixbuf - * returned by this function. Otherwise GTK+ may need to keep the old + * returned by this function. Otherwise GTK+ may need to keep the old * icon theme loaded, which would be a waste of memory. - * - * Return value: the rendered icon; this may be a newly created icon - * or a new reference to an internal icon, so you must not modify - * the icon. Use g_object_unref() to release your reference to the - * icon. %NULL if the icon isn't found. + * + * Return value: (transfer full): the rendered icon; this may be a + * newly created icon or a new reference to an internal icon, so + * you must not modify the icon. Use g_object_unref() to release + * your reference to the icon. %NULL if the icon isn't found. * * Since: 2.4 **/ @@ -1376,8 +1605,8 @@ gtk_icon_theme_load_icon (GtkIconTheme *icon_theme, (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size, - flags | GTK_ICON_LOOKUP_USE_BUILTIN); + icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size, + flags | GTK_ICON_LOOKUP_USE_BUILTIN); if (!icon_info) { g_set_error (error, GTK_ICON_THEME_ERROR, GTK_ICON_THEME_NOT_FOUND, @@ -1412,7 +1641,8 @@ gtk_icon_theme_has_icon (GtkIconTheme *icon_theme, GList *l; g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE); - + g_return_val_if_fail (icon_name != NULL, FALSE); + priv = icon_theme->priv; ensure_valid_themes (icon_theme); @@ -1464,9 +1694,9 @@ add_size (gpointer key, * that the icon is available in a scalable format. The array * is zero-terminated. * - * Return value: An newly allocated array describing the sizes at - * which the icon is available. The array should be freed with g_free() - * when it is no longer needed. + * Return value: (array zero-terminated=1): An newly allocated array + * describing the sizes at which the icon is available. The array + * should be freed with g_free() when it is no longer needed. * * Since: 2.6 **/ @@ -1495,6 +1725,9 @@ gtk_icon_theme_get_icon_sizes (GtkIconTheme *icon_theme, { IconThemeDir *dir = d->data; + if (dir->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir->size), NULL, NULL)) + continue; + suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL); if (suffix != ICON_SUFFIX_NONE) { @@ -1510,6 +1743,9 @@ gtk_icon_theme_get_icon_sizes (GtkIconTheme *icon_theme, { IconThemeDir *dir = d->data; + if (dir->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir->size), NULL, NULL)) + continue; + suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL); if (suffix != ICON_SUFFIX_NONE) { @@ -1564,19 +1800,19 @@ add_key_to_list (gpointer key, /** * gtk_icon_theme_list_icons: * @icon_theme: a #GtkIconTheme - * @context: a string identifying a particular type of icon, - * or %NULL to list all icons. + * @context: (allow-none): a string identifying a particular type of + * icon, or %NULL to list all icons. * * Lists the icons in the current icon theme. Only a subset * of the icons can be listed by providing a context string. * The set of values for the context string is system dependent, * but will typically include such values as "Applications" and * "MimeTypes". - * - * Return value: a #GList list holding the names of all the - * icons in the theme. You must first free each element - * in the list with g_free(), then free the list itself - * with g_list_free(). + * + * Return value: (element-type utf8) (transfer full): a #GList list + * holding the names of all the icons in the theme. You must first + * free each element in the list with g_free(), then free the list + * itself with g_list_free(). * * Since: 2.4 **/ @@ -1628,6 +1864,51 @@ gtk_icon_theme_list_icons (GtkIconTheme *icon_theme, return list; } +/** + * gtk_icon_theme_list_contexts: + * @icon_theme: a #GtkIconTheme + * + * Gets the list of contexts available within the current + * hierarchy of icon themes + * + * Return value: (element-type utf8) (transfer full): a #GList list + * holding the names of all the contexts in the theme. You must first + * free each element in the list with g_free(), then free the list + * itself with g_list_free(). + * + * Since: 2.12 + **/ +GList * +gtk_icon_theme_list_contexts (GtkIconTheme *icon_theme) +{ + GtkIconThemePrivate *priv; + GHashTable *contexts; + GList *list, *l; + + priv = icon_theme->priv; + + ensure_valid_themes (icon_theme); + + contexts = g_hash_table_new (g_str_hash, g_str_equal); + + l = priv->themes; + while (l != NULL) + { + theme_list_contexts (l->data, contexts); + l = l->next; + } + + list = NULL; + + g_hash_table_foreach (contexts, + add_key_to_list, + &list); + + g_hash_table_destroy (contexts); + + return list; +} + /** * gtk_icon_theme_get_example_icon_name: * @icon_theme: a #GtkIconTheme @@ -1667,33 +1948,19 @@ gtk_icon_theme_get_example_icon_name (GtkIconTheme *icon_theme) return NULL; } -/** - * gtk_icon_theme_rescan_if_needed: - * @icon_theme: a #GtkIconTheme - * - * Checks to see if the icon theme has changed; if it has, any - * currently cached information is discarded and will be reloaded - * next time @icon_theme is accessed. - * - * Return value: %TRUE if the icon theme has changed and needed - * to be reloaded. - * - * Since: 2.4 - **/ -gboolean -gtk_icon_theme_rescan_if_needed (GtkIconTheme *icon_theme) + +static gboolean +rescan_themes (GtkIconTheme *icon_theme) { GtkIconThemePrivate *priv; IconThemeDirMtime *dir_mtime; GList *d; int stat_res; - struct stat stat_buf; + GStatBuf stat_buf; GTimeVal tv; - g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE); - priv = icon_theme->priv; - + for (d = priv->dir_mtimes; d != NULL; d = d->next) { dir_mtime = d->data; @@ -1701,7 +1968,7 @@ gtk_icon_theme_rescan_if_needed (GtkIconTheme *icon_theme) stat_res = g_stat (dir_mtime->dir, &stat_buf); /* dir mtime didn't change */ - if (stat_res == 0 && + if (stat_res == 0 && S_ISDIR (stat_buf.st_mode) && dir_mtime->mtime == stat_buf.st_mtime) continue; @@ -1709,26 +1976,52 @@ gtk_icon_theme_rescan_if_needed (GtkIconTheme *icon_theme) if (dir_mtime->mtime == 0 && (stat_res != 0 || !S_ISDIR (stat_buf.st_mode))) continue; - - do_theme_change (icon_theme); + return TRUE; } - + g_get_current_time (&tv); priv->last_stat_time = tv.tv_sec; return FALSE; } -static void -theme_destroy (IconTheme *theme) -{ - g_free (theme->display_name); - g_free (theme->comment); - g_free (theme->name); - g_free (theme->example); - - g_list_foreach (theme->dirs, (GFunc)theme_dir_destroy, NULL); +/** + * gtk_icon_theme_rescan_if_needed: + * @icon_theme: a #GtkIconTheme + * + * Checks to see if the icon theme has changed; if it has, any + * currently cached information is discarded and will be reloaded + * next time @icon_theme is accessed. + * + * Return value: %TRUE if the icon theme has changed and needed + * to be reloaded. + * + * Since: 2.4 + **/ +gboolean +gtk_icon_theme_rescan_if_needed (GtkIconTheme *icon_theme) +{ + gboolean retval; + + g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE); + + retval = rescan_themes (icon_theme); + if (retval) + do_theme_change (icon_theme); + + return retval; +} + +static void +theme_destroy (IconTheme *theme) +{ + g_free (theme->display_name); + g_free (theme->comment); + g_free (theme->name); + g_free (theme->example); + + g_list_foreach (theme->dirs, (GFunc)theme_dir_destroy, NULL); g_list_free (theme->dirs); g_free (theme); @@ -1845,7 +2138,7 @@ theme_dir_get_icon_suffix (IconThemeDir *dir, { suffix = (IconSuffix)_gtk_icon_cache_get_icon_flags (dir->cache, icon_name, - dir->subdir); + dir->subdir_index); if (has_icon_file) *has_icon_file = suffix & HAS_ICON_FILE; @@ -1873,17 +2166,18 @@ theme_lookup_icon (IconTheme *theme, char *file; int min_difference, difference; BuiltinIcon *closest_builtin = NULL; - gboolean smaller, has_larger; + gboolean smaller, has_larger, match; IconSuffix suffix; min_difference = G_MAXINT; min_dir = NULL; has_larger = FALSE; + match = FALSE; /* Builtin icons are logically part of the default theme and * are searched before other subdirectories of the default theme. */ - if (strcmp (theme->name, DEFAULT_THEME_NAME) == 0 && use_builtin) + if (use_builtin && strcmp (theme->name, DEFAULT_THEME_NAME) == 0) { closest_builtin = find_builtin_icon (icon_name, size, @@ -1903,7 +2197,7 @@ theme_lookup_icon (IconTheme *theme, { dir = l->data; - GTK_NOTE (ICONTHEME, + GTK_NOTE (ICONTHEME, g_print ("theme_lookup_icon dir %s\n", dir->dir)); suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL); if (best_suffix (suffix, allow_svg) != ICON_SUFFIX_NONE) @@ -1912,31 +2206,55 @@ theme_lookup_icon (IconTheme *theme, if (difference == 0) { - min_dir = dir; - break; - } - - if (!has_larger) - { - if (difference < min_difference || smaller) - { - min_difference = difference; - min_dir = dir; - closest_builtin = NULL; - has_larger = smaller; - } - } - else - { - if (difference < min_difference && smaller) - { - min_difference = difference; - min_dir = dir; - closest_builtin = NULL; - } + if (dir->type == ICON_THEME_DIR_SCALABLE) + { + /* don't pick scalable if we already found + * a matching non-scalable dir + */ + if (!match) + { + min_dir = dir; + break; + } + } + else + { + /* for a matching non-scalable dir keep + * going and look for a closer match + */ + difference = abs (size - dir->size); + if (!match || difference < min_difference) + { + match = TRUE; + min_difference = difference; + min_dir = dir; + } + if (difference == 0) + break; + } + } + + if (!match) + { + if (!has_larger) + { + if (difference < min_difference || smaller) + { + min_difference = difference; + min_dir = dir; + has_larger = smaller; + } + } + else + { + if (difference < min_difference && smaller) + { + min_difference = difference; + min_dir = dir; + } + } } - - } + } l = l->next; @@ -1947,9 +2265,6 @@ theme_lookup_icon (IconTheme *theme, } } - if (closest_builtin) - return icon_info_new_builtin (closest_builtin); - if (min_dir) { GtkIconInfo *icon_info = icon_info_new (); @@ -1959,20 +2274,23 @@ theme_lookup_icon (IconTheme *theme, suffix = best_suffix (suffix, allow_svg); g_assert (suffix != ICON_SUFFIX_NONE); - file = g_strconcat (icon_name, string_from_suffix (suffix), NULL); - icon_info->filename = g_build_filename (min_dir->dir, file, NULL); - g_free (file); -#ifdef G_OS_WIN32 - icon_info->cp_filename = g_locale_from_utf8 (icon_info->filename, - -1, NULL, NULL, NULL); -#endif + if (min_dir->dir) + { + file = g_strconcat (icon_name, string_from_suffix (suffix), NULL); + icon_info->filename = g_build_filename (min_dir->dir, file, NULL); + g_free (file); + } + else + { + icon_info->filename = NULL; + } if (min_dir->icon_data != NULL) icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name); if (icon_info->data == NULL && min_dir->cache != NULL) { - icon_info->data = _gtk_icon_cache_get_icon_data (min_dir->cache, icon_name, min_dir->subdir); + icon_info->data = _gtk_icon_cache_get_icon_data (min_dir->cache, icon_name, min_dir->subdir_index); if (icon_info->data) { if (min_dir->icon_data == NULL) @@ -2006,7 +2324,7 @@ theme_lookup_icon (IconTheme *theme, if (min_dir->cache) { icon_info->cache_pixbuf = _gtk_icon_cache_get_icon (min_dir->cache, icon_name, - min_dir->subdir); + min_dir->subdir_index); } icon_info->dir_type = min_dir->type; @@ -2015,7 +2333,10 @@ theme_lookup_icon (IconTheme *theme, return icon_info; } - + + if (closest_builtin) + return icon_info_new_builtin (closest_builtin); + return NULL; } @@ -2052,6 +2373,25 @@ theme_list_icons (IconTheme *theme, } } +static void +theme_list_contexts (IconTheme *theme, + GHashTable *contexts) +{ + GList *l = theme->dirs; + IconThemeDir *dir; + const char *context; + + while (l != NULL) + { + dir = l->data; + + context = g_quark_to_string (dir->context); + g_hash_table_replace (contexts, (gpointer) context, NULL); + + l = l->next; + } +} + static void load_icon_data (IconThemeDir *dir, const char *path, const char *name) { @@ -2081,6 +2421,7 @@ load_icon_data (IconThemeDir *dir, const char *path, const char *name) base_name = strip_suffix (name); data = g_slice_new0 (GtkIconData); + /* takes ownership of base_name */ g_hash_table_replace (dir->icon_data, base_name, data); ivalues = g_key_file_get_integer_list (icon_file, @@ -2178,8 +2519,9 @@ scan_directory (GtkIconThemePrivate *icon_theme, base_name = strip_suffix (name); hash_suffix = GPOINTER_TO_INT (g_hash_table_lookup (dir->icons, base_name)); + g_hash_table_replace (icon_theme->all_icons, base_name, NULL); + /* takes ownership of base_name */ g_hash_table_replace (dir->icons, base_name, GUINT_TO_POINTER (hash_suffix| suffix)); - g_hash_table_insert (icon_theme->all_icons, base_name, NULL); } g_dir_close (gdir); @@ -2236,32 +2578,20 @@ theme_subdir_load (GtkIconTheme *icon_theme, g_free (context_string); } - max_size = g_key_file_get_integer (theme_file, subdir, "MaxSize", &error); - if (error) - { - max_size = size; - - g_error_free (error); - error = NULL; - } - - min_size = g_key_file_get_integer (theme_file, subdir, "MinSize", &error); - if (error) - { - min_size = size; + if (g_key_file_has_key (theme_file, subdir, "MaxSize", NULL)) + max_size = g_key_file_get_integer (theme_file, subdir, "MaxSize", NULL); + else + max_size = size; - g_error_free (error); - error = NULL; - } - - threshold = g_key_file_get_integer (theme_file, subdir, "Threshold", &error); - if (error) - { - threshold = 2; + if (g_key_file_has_key (theme_file, subdir, "MinSize", NULL)) + min_size = g_key_file_get_integer (theme_file, subdir, "MinSize", NULL); + else + min_size = size; - g_error_free (error); - error = NULL; - } + if (g_key_file_has_key (theme_file, subdir, "Threshold", NULL)) + threshold = g_key_file_get_integer (theme_file, subdir, "Threshold", NULL); + else + threshold = 2; for (d = icon_theme->priv->dir_mtimes; d; d = d->next) { @@ -2292,10 +2622,14 @@ theme_subdir_load (GtkIconTheme *icon_theme, dir->icon_data = NULL; dir->subdir = g_strdup (subdir); if (dir_mtime->cache != NULL) - dir->cache = _gtk_icon_cache_ref (dir_mtime->cache); + { + dir->cache = _gtk_icon_cache_ref (dir_mtime->cache); + dir->subdir_index = _gtk_icon_cache_get_directory_index (dir->cache, dir->subdir); + } else { dir->cache = NULL; + dir->subdir_index = -1; scan_directory (icon_theme->priv, dir, full_dir); } @@ -2317,19 +2651,10 @@ icon_data_free (GtkIconData *icon_data) /* * GtkIconInfo */ -GType -gtk_icon_info_get_type (void) -{ - static GType our_type = 0; - - if (our_type == 0) - our_type = g_boxed_type_register_static (I_("GtkIconInfo"), - (GBoxedCopyFunc) gtk_icon_info_copy, - (GBoxedFreeFunc) gtk_icon_info_free); - - return our_type; -} +G_DEFINE_BOXED_TYPE (GtkIconInfo, gtk_icon_info, + gtk_icon_info_copy, + gtk_icon_info_free) static GtkIconInfo * icon_info_new (void) @@ -2337,6 +2662,7 @@ icon_info_new (void) GtkIconInfo *icon_info = g_slice_new0 (GtkIconInfo); icon_info->scale = -1.; + icon_info->ref_count = 1; return icon_info; } @@ -2350,7 +2676,7 @@ icon_info_new_builtin (BuiltinIcon *icon) icon_info->dir_type = ICON_THEME_DIR_THRESHOLD; icon_info->dir_size = icon->size; icon_info->threshold = 2; - + return icon_info; } @@ -2367,25 +2693,12 @@ icon_info_new_builtin (BuiltinIcon *icon) GtkIconInfo * gtk_icon_info_copy (GtkIconInfo *icon_info) { - GtkIconInfo *copy; g_return_val_if_fail (icon_info != NULL, NULL); - copy = memcpy (g_slice_new (GtkIconInfo), icon_info, sizeof (GtkIconInfo)); - if (copy->cache_pixbuf) - g_object_ref (copy->cache_pixbuf); - if (copy->pixbuf) - g_object_ref (copy->pixbuf); - if (copy->load_error) - copy->load_error = g_error_copy (copy->load_error); - if (copy->filename) - copy->filename = g_strdup (copy->filename); -#ifdef G_OS_WIN32 - if (copy->cp_filename) - copy->cp_filename = g_strdup (copy->cp_filename); -#endif + icon_info->ref_count++; - return copy; + return icon_info; } /** @@ -2401,12 +2714,15 @@ gtk_icon_info_free (GtkIconInfo *icon_info) { g_return_if_fail (icon_info != NULL); - if (icon_info->filename) - g_free (icon_info->filename); -#ifdef G_OS_WIN32 - if (icon_info->cp_filename) - g_free (icon_info->cp_filename); -#endif + icon_info->ref_count--; + if (icon_info->ref_count > 0) + return; + + g_free (icon_info->filename); + if (icon_info->loadable) + g_object_unref (icon_info->loadable); + g_slist_foreach (icon_info->emblem_infos, (GFunc)gtk_icon_info_free, NULL); + g_slist_free (icon_info->emblem_infos); if (icon_info->pixbuf) g_object_unref (icon_info->pixbuf); if (icon_info->cache_pixbuf) @@ -2451,14 +2767,13 @@ gtk_icon_info_get_base_size (GtkIconInfo *icon_info) * no filename if a builtin icon is returned; in this * case, you should use gtk_icon_info_get_builtin_pixbuf(). * - * Return value: the filename for the icon, or %NULL - * if gtk_icon_info_get_builtin_pixbuf() should - * be used instead. The return value is owned by - * GTK+ and should not be modified or freed. + * Return value: (type filename): the filename for the icon, or %NULL + * if gtk_icon_info_get_builtin_pixbuf() should be used instead. The + * return value is owned by GTK+ and should not be modified or freed. * * Since: 2.4 **/ -G_CONST_RETURN gchar * +const gchar * gtk_icon_info_get_filename (GtkIconInfo *icon_info) { g_return_val_if_fail (icon_info != NULL, NULL); @@ -2474,8 +2789,8 @@ gtk_icon_info_get_filename (GtkIconInfo *icon_info) * GTK+ to use built in icon images, you must pass the * %GTK_ICON_LOOKUP_USE_BUILTIN to * gtk_icon_theme_lookup_icon(). - * - * Return value: the built-in image pixbuf, or %NULL. No + * + * Return value: (transfer none): the built-in image pixbuf, or %NULL. No * extra reference is added to the returned pixbuf, so if * you want to keep it around, you must use g_object_ref(). * The returned image must not be modified. @@ -2493,56 +2808,102 @@ gtk_icon_info_get_builtin_pixbuf (GtkIconInfo *icon_info) return icon_info->cache_pixbuf; } -static GdkPixbuf * -load_svg_at_size (const gchar *filename, - gint size, - GError **error) +static gboolean icon_info_ensure_scale_and_pixbuf (GtkIconInfo*, gboolean); + +/* Combine the icon with all emblems, the first emblem is placed + * in the southeast corner. Scale emblems to be at most 3/4 of the + * size of the icon itself. + */ +static void +apply_emblems (GtkIconInfo *info) { - GdkPixbuf *pixbuf = NULL; - GdkPixbufLoader *loader = NULL; - gchar *contents = NULL; - gsize length; - - if (!g_file_get_contents (filename, - &contents, &length, error)) - goto bail; - - loader = gdk_pixbuf_loader_new_with_type ("svg", error); - if (loader == NULL) - goto bail; + GdkPixbuf *icon = NULL; + gint w, h, pos; + GSList *l; - gdk_pixbuf_loader_set_size (loader, size, size); - - if (!gdk_pixbuf_loader_write (loader, contents, length, error)) + if (info->emblem_infos == NULL) + return; + + if (info->emblems_applied) + return; + + w = gdk_pixbuf_get_width (info->pixbuf); + h = gdk_pixbuf_get_height (info->pixbuf); + + for (l = info->emblem_infos, pos = 0; l; l = l->next, pos++) { - gdk_pixbuf_loader_close (loader, NULL); - goto bail; - } - - if (!gdk_pixbuf_loader_close (loader, error)) - goto bail; - - pixbuf = g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader)); - - bail: - if (loader) - g_object_unref (loader); - if (contents) - g_free (contents); - - return pixbuf; -} + GtkIconInfo *emblem_info = l->data; -/* This function contains the complicatd logic for deciding + if (icon_info_ensure_scale_and_pixbuf (emblem_info, FALSE)) + { + GdkPixbuf *emblem = emblem_info->pixbuf; + gint ew, eh; + gint x = 0, y = 0; /* silence compiler */ + gdouble scale; + + ew = gdk_pixbuf_get_width (emblem); + eh = gdk_pixbuf_get_height (emblem); + if (ew >= w) + { + scale = 0.75; + ew = ew * 0.75; + eh = eh * 0.75; + } + else + scale = 1.0; + + switch (pos % 4) + { + case 0: + x = w - ew; + y = h - eh; + break; + case 1: + x = w - ew; + y = 0; + break; + case 2: + x = 0; + y = h - eh; + break; + case 3: + x = 0; + y = 0; + break; + } + + if (icon == NULL) + { + icon = gdk_pixbuf_copy (info->pixbuf); + if (icon == NULL) + break; + } + + gdk_pixbuf_composite (emblem, icon, x, y, ew, eh, x, y, + scale, scale, GDK_INTERP_BILINEAR, 255); + } + } + + if (icon) + { + g_object_unref (info->pixbuf); + info->pixbuf = icon; + } + + info->emblems_applied = TRUE; +} + +/* This function contains the complicated logic for deciding * on the size at which to load the icon and loading it at * that size. */ static gboolean -icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info, - gboolean scale_only) +icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info, + gboolean scale_only) { int image_width, image_height; GdkPixbuf *source_pixbuf; + gboolean is_svg; /* First check if we already succeeded have the necessary * information (or failed earlier) @@ -2551,7 +2912,10 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info, return TRUE; if (icon_info->pixbuf) - return TRUE; + { + apply_emblems (icon_info); + return TRUE; + } if (icon_info->load_error) return FALSE; @@ -2559,18 +2923,68 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info, /* SVG icons are a special case - we just immediately scale them * to the desired size */ - if (icon_info->filename && g_str_has_suffix (icon_info->filename, ".svg")) + if (icon_info->filename && !icon_info->loadable) + { + GFile *file; + + file = g_file_new_for_path (icon_info->filename); + icon_info->loadable = G_LOADABLE_ICON (g_file_icon_new (file)); + g_object_unref (file); + } + + is_svg = FALSE; + if (G_IS_FILE_ICON (icon_info->loadable)) + { + GFile *file; + GFileInfo *file_info; + const gchar *content_type; + + file = g_file_icon_get_file (G_FILE_ICON (icon_info->loadable)); + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + if (file_info) + { + content_type = g_file_info_get_content_type (file_info); + + if (content_type && strcmp (content_type, "image/svg+xml") == 0) + is_svg = TRUE; + + g_object_unref (file_info); + } + } + + if (is_svg) { + GInputStream *stream; + icon_info->scale = icon_info->desired_size / 1000.; if (scale_only) return TRUE; - icon_info->pixbuf = load_svg_at_size (icon_info->filename, - icon_info->desired_size, - &icon_info->load_error); - - return icon_info->pixbuf != NULL; + stream = g_loadable_icon_load (icon_info->loadable, + icon_info->desired_size, + NULL, NULL, + &icon_info->load_error); + if (stream) + { + icon_info->pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream, + icon_info->desired_size, + icon_info->desired_size, + TRUE, + NULL, + &icon_info->load_error); + g_object_unref (stream); + } + + if (!icon_info->pixbuf) + return FALSE; + + apply_emblems (icon_info); + + return TRUE; } /* In many cases, the scale can be determined without actual access @@ -2578,7 +2992,9 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info, * for the directory where the icon is; the image size doesn't * matter in that case. */ - if (icon_info->dir_type == ICON_THEME_DIR_FIXED) + if (icon_info->forced_size) + icon_info->scale = -1; + else if (icon_info->dir_type == ICON_THEME_DIR_FIXED) icon_info->scale = 1.0; else if (icon_info->dir_type == ICON_THEME_DIR_THRESHOLD) { @@ -2598,19 +3014,31 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info, return TRUE; /* At this point, we need to actually get the icon; either from the - * builting image or by loading the file + * builtin image or by loading the file */ + source_pixbuf = NULL; if (icon_info->cache_pixbuf) source_pixbuf = g_object_ref (icon_info->cache_pixbuf); else { + GInputStream *stream; - source_pixbuf = gdk_pixbuf_new_from_file (icon_info->filename, - &icon_info->load_error); - if (!source_pixbuf) - return FALSE; + stream = g_loadable_icon_load (icon_info->loadable, + icon_info->desired_size, + NULL, NULL, + &icon_info->load_error); + if (stream) + { + source_pixbuf = gdk_pixbuf_new_from_stream (stream, + NULL, + &icon_info->load_error); + g_object_unref (stream); + } } + if (!source_pixbuf) + return FALSE; + /* Do scale calculations that depend on the image size */ image_width = gdk_pixbuf_get_width (source_pixbuf); @@ -2620,11 +3048,12 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info, { gint image_size = MAX (image_width, image_height); if (image_size > 0) - icon_info->scale = icon_info->desired_size / (gdouble)image_size; + icon_info->scale = (gdouble)icon_info->desired_size / (gdouble)image_size; else icon_info->scale = 1.0; - if (icon_info->dir_type == ICON_THEME_DIR_UNTHEMED) + if (icon_info->dir_type == ICON_THEME_DIR_UNTHEMED && + !icon_info->forced_size) icon_info->scale = MIN (icon_info->scale, 1.0); } @@ -2633,9 +3062,8 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info, * the job. This is a bit of a waste when we scale here * and never get the final pixbuf; at the cost of a bit of * extra complexity, we could keep the source pixbuf around - * but not actually scale it until neede. + * but not actually scale it until needed. */ - if (icon_info->scale == 1.0) icon_info->pixbuf = source_pixbuf; else @@ -2647,14 +3075,17 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info, g_object_unref (source_pixbuf); } + apply_emblems (icon_info); + return TRUE; } /** * gtk_icon_info_load_icon: * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon() - * @error: location to store error information on failure, or %NULL. - * + * @error: (allow-none): location to store error information on failure, + * or %NULL. + * * Renders an icon previously looked up in an icon theme using * gtk_icon_theme_lookup_icon(); the size will be based on the size * passed to gtk_icon_theme_lookup_icon(). Note that the resulting @@ -2662,12 +3093,15 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info, * that differ slightly from their nominal sizes, and in addition GTK+ * will avoid scaling icons that it considers sufficiently close to the * requested size or for which the source image would have to be scaled - * up too far. (This maintains sharpness.) - * - * Return value: the rendered icon; this may be a newly created icon - * or a new reference to an internal icon, so you must not modify - * the icon. Use g_object_unref() to release your reference to the - * icon. + * up too far. (This maintains sharpness.). This behaviour can be changed + * by passing the %GTK_ICON_LOOKUP_FORCE_SIZE flag when obtaining + * the #GtkIconInfo. If this flag has been specified, the pixbuf + * returned by this function will be scaled to the exact size. + * + * Return value: (transfer full): the rendered icon; this may be a newly + * created icon or a new reference to an internal icon, so you must + * not modify the icon. Use g_object_unref() to release your reference + * to the icon. * * Since: 2.4 **/ @@ -2675,22 +3109,368 @@ GdkPixbuf * gtk_icon_info_load_icon (GtkIconInfo *icon_info, GError **error) { - g_return_val_if_fail (icon_info != NULL, NULL); - g_return_val_if_fail (icon_info != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - icon_info_ensure_scale_and_pixbuf (icon_info, FALSE); - - if (icon_info->load_error) + if (!icon_info_ensure_scale_and_pixbuf (icon_info, FALSE)) { - g_propagate_error (error, icon_info->load_error); + if (icon_info->load_error) + g_propagate_error (error, icon_info->load_error); + else + g_set_error_literal (error, + GTK_ICON_THEME_ERROR, + GTK_ICON_THEME_NOT_FOUND, + _("Failed to load icon")); + return NULL; } return g_object_ref (icon_info->pixbuf); } +static gchar * +gdk_color_to_css (GdkColor *color) +{ + return g_strdup_printf ("rgb(%d,%d,%d)", + color->red >> 8, + color->green >> 8, + color->blue >> 8); +} + +static gchar * +gdk_rgba_to_css (const GdkRGBA *color) +{ + /* drop alpha for now, since librsvg does not understand rgba() */ + return g_strdup_printf ("rgb(%d,%d,%d)", + (gint)(color->red * 255), + (gint)(color->green * 255), + (gint)(color->blue * 255)); +} + +static GdkPixbuf * +_gtk_icon_info_load_symbolic_internal (GtkIconInfo *icon_info, + const gchar *css_fg, + const gchar *css_success, + const gchar *css_warning, + const gchar *css_error, + GError **error) +{ + GInputStream *stream; + GdkPixbuf *pixbuf; + gchar *data; + gchar *success, *warning, *err; + + /* css_fg can't possibly have failed, otherwise + * that would mean we have a broken style */ + g_return_val_if_fail (css_fg != NULL, NULL); + + success = warning = err = NULL; + + if (!css_success) + { + GdkColor success_default_color = { 0, 0x4e00, 0x9a00, 0x0600 }; + success = gdk_color_to_css (&success_default_color); + } + if (!css_warning) + { + GdkColor warning_default_color = { 0, 0xf500, 0x7900, 0x3e00 }; + warning = gdk_color_to_css (&warning_default_color); + } + if (!css_error) + { + GdkColor error_default_color = { 0, 0xcc00, 0x0000, 0x0000 }; + err = gdk_color_to_css (&error_default_color); + } + + + data = g_strconcat ("\n" + "\n" + " \n" + " filename, "\"/>\n" + "", + NULL); + g_free (warning); + g_free (err); + g_free (success); + + stream = g_memory_input_stream_new_from_data (data, -1, g_free); + pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream, + icon_info->desired_size, + icon_info->desired_size, + TRUE, + NULL, + error); + g_object_unref (stream); + + return pixbuf; +} + +/** + * gtk_icon_info_load_symbolic: + * @icon_info: a #GtkIconInfo + * @fg: a #GdkRGBA representing the foreground color of the icon + * @success_color: (allow-none): a #GdkRGBA representing the warning color + * of the icon or %NULL to use the default color + * @warning_color: (allow-none): a #GdkRGBA representing the warning color + * of the icon or %NULL to use the default color + * @error_color: (allow-none): a #GdkRGBA representing the error color + * of the icon or %NULL to use the default color (allow-none) + * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the + * loaded icon was a symbolic one and whether the @fg color was + * applied to it. + * @error: (allow-none): location to store error information on failure, + * or %NULL. + * + * Loads an icon, modifying it to match the system colours for the foreground, + * success, warning and error colors provided. If the icon is not a symbolic + * one, the function will return the result from gtk_icon_info_load_icon(). + * + * This allows loading symbolic icons that will match the system theme. + * + * Unless you are implementing a widget, you will want to use + * g_themed_icon_new_with_default_fallbacks() to load the icon. + * + * As implementation details, the icon loaded needs to be of SVG type, + * contain the "symbolic" term as the last component of the icon name, + * and use the 'fg', 'success', 'warning' and 'error' CSS styles in the + * SVG file itself. + * + * See the Symbolic Icons spec + * for more information about symbolic icons. + * + * Return value: (transfer full): a #GdkPixbuf representing the loaded icon + * + * Since: 3.0 + **/ +GdkPixbuf * +gtk_icon_info_load_symbolic (GtkIconInfo *icon_info, + const GdkRGBA *fg, + const GdkRGBA *success_color, + const GdkRGBA *warning_color, + const GdkRGBA *error_color, + gboolean *was_symbolic, + GError **error) +{ + GdkPixbuf *pixbuf; + gchar *css_fg; + gchar *css_success; + gchar *css_warning; + gchar *css_error; + + g_return_val_if_fail (fg != NULL, NULL); + + if (!icon_info->filename || + !g_str_has_suffix (icon_info->filename, "-symbolic.svg")) + { + if (was_symbolic) + *was_symbolic = FALSE; + return gtk_icon_info_load_icon (icon_info, error); + } + + if (was_symbolic) + *was_symbolic = TRUE; + + css_fg = gdk_rgba_to_css (fg); + + css_success = css_warning = css_error = NULL; + + if (warning_color) + css_warning = gdk_rgba_to_css (warning_color); + + if (error_color) + css_error = gdk_rgba_to_css (error_color); + + if (success_color) + css_success = gdk_rgba_to_css (success_color); + + pixbuf = _gtk_icon_info_load_symbolic_internal (icon_info, + css_fg, css_success, + css_warning, css_error, + error); + g_free (css_fg); + g_free (css_warning); + g_free (css_success); + g_free (css_error); + + return pixbuf; +} + +/** + * gtk_icon_info_load_symbolic_for_context: + * @icon_info: a #GtkIconInfo + * @context: a #GtkStyleContext + * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the + * loaded icon was a symbolic one and whether the @fg color was + * applied to it. + * @error: (allow-none): location to store error information on failure, + * or %NULL. + * + * Loads an icon, modifying it to match the system colors for the foreground, + * success, warning and error colors provided. If the icon is not a symbolic + * one, the function will return the result from gtk_icon_info_load_icon(). + * This function uses the regular foreground color and the symbolic colors + * with the names "success_color", "warning_color" and "error_color" from + * the context. + * + * This allows loading symbolic icons that will match the system theme. + * + * See gtk_icon_info_load_symbolic() for more details. + * + * Return value: (transfer full): a #GdkPixbuf representing the loaded icon + * + * Since: 3.0 + **/ +GdkPixbuf * +gtk_icon_info_load_symbolic_for_context (GtkIconInfo *icon_info, + GtkStyleContext *context, + gboolean *was_symbolic, + GError **error) +{ + GdkPixbuf *pixbuf; + GdkRGBA *color = NULL; + GdkRGBA rgba; + gchar *css_fg = NULL, *css_success; + gchar *css_warning, *css_error; + GtkStateFlags state; + + if (!icon_info->filename || + !g_str_has_suffix (icon_info->filename, "-symbolic.svg")) + { + if (was_symbolic) + *was_symbolic = FALSE; + return gtk_icon_info_load_icon (icon_info, error); + } + + if (was_symbolic) + *was_symbolic = TRUE; + + state = gtk_style_context_get_state (context); + gtk_style_context_get (context, state, "color", &color, NULL); + if (color) + { + css_fg = gdk_rgba_to_css (color); + gdk_rgba_free (color); + } + + css_success = css_warning = css_error = NULL; + + if (gtk_style_context_lookup_color (context, "success_color", &rgba)) + css_success = gdk_rgba_to_css (&rgba); + + if (gtk_style_context_lookup_color (context, "warning_color", &rgba)) + css_warning = gdk_rgba_to_css (&rgba); + + if (gtk_style_context_lookup_color (context, "error_color", &rgba)) + css_error = gdk_rgba_to_css (&rgba); + + pixbuf = _gtk_icon_info_load_symbolic_internal (icon_info, + css_fg, css_success, + css_warning, css_error, + error); + + g_free (css_fg); + g_free (css_success); + g_free (css_warning); + g_free (css_error); + + return pixbuf; +} + +/** + * gtk_icon_info_load_symbolic_for_style: + * @icon_info: a #GtkIconInfo + * @style: a #GtkStyle to take the colors from + * @state: the widget state to use for colors + * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the + * loaded icon was a symbolic one and whether the @fg color was + * applied to it. + * @error: (allow-none): location to store error information on failure, + * or %NULL. + * + * Loads an icon, modifying it to match the system colours for the foreground, + * success, warning and error colors provided. If the icon is not a symbolic + * one, the function will return the result from gtk_icon_info_load_icon(). + * + * This allows loading symbolic icons that will match the system theme. + * + * See gtk_icon_info_load_symbolic() for more details. + * + * Return value: (transfer full): a #GdkPixbuf representing the loaded icon + * + * Since: 3.0 + * + * Deprecated: 3.0: Use gtk_icon_info_load_symbolic_for_context() instead + **/ +GdkPixbuf * +gtk_icon_info_load_symbolic_for_style (GtkIconInfo *icon_info, + GtkStyle *style, + GtkStateType state, + gboolean *was_symbolic, + GError **error) +{ + GdkPixbuf *pixbuf; + GdkColor success_color; + GdkColor warning_color; + GdkColor error_color; + GdkColor *fg; + gchar *css_fg, *css_success; + gchar *css_warning, *css_error; + + if (!icon_info->filename || + !g_str_has_suffix (icon_info->filename, "-symbolic.svg")) + { + if (was_symbolic) + *was_symbolic = FALSE; + return gtk_icon_info_load_icon (icon_info, error); + } + + if (was_symbolic) + *was_symbolic = TRUE; + + fg = &style->fg[state]; + css_fg = gdk_color_to_css (fg); + + css_success = css_warning = css_error = NULL; + + if (gtk_style_lookup_color (style, "success_color", &success_color)) + css_success = gdk_color_to_css (&success_color); + + if (gtk_style_lookup_color (style, "warning_color", &warning_color)) + css_warning = gdk_color_to_css (&warning_color); + + if (gtk_style_lookup_color (style, "error_color", &error_color)) + css_error = gdk_color_to_css (&error_color); + + pixbuf = _gtk_icon_info_load_symbolic_internal (icon_info, + css_fg, css_success, + css_warning, css_error, + error); + + g_free (css_fg); + g_free (css_success); + g_free (css_warning); + g_free (css_error); + + return pixbuf; +} + /** * gtk_icon_info_set_raw_coordinates: * @icon_info: a #GtkIconInfo @@ -2754,7 +3534,7 @@ icon_info_scale_point (GtkIconInfo *icon_info, /** * gtk_icon_info_get_embedded_rect: * @icon_info: a #GtkIconInfo - * @rectangle: #GdkRectangle in which to store embedded + * @rectangle: (out): #GdkRectangle in which to store embedded * rectangle coordinates; coordinates are only stored * when this function returns %TRUE. * @@ -2804,9 +3584,9 @@ gtk_icon_info_get_embedded_rect (GtkIconInfo *icon_info, /** * gtk_icon_info_get_attach_points: * @icon_info: a #GtkIconInfo - * @points: location to store pointer to an array of points, or %NULL + * @points: (allow-none) (array length=n_points) (out): location to store pointer to an array of points, or %NULL * free the array of points with g_free(). - * @n_points: location to store the number of points in @points, or %NULL + * @n_points: (allow-none): location to store the number of points in @points, or %NULL * * Fetches the set of attach points for an icon. An attach point * is a location in the icon that can be used as anchor points for attaching @@ -2869,7 +3649,7 @@ gtk_icon_info_get_attach_points (GtkIconInfo *icon_info, * * Since: 2.4 **/ -G_CONST_RETURN gchar * +const gchar * gtk_icon_info_get_display_name (GtkIconInfo *icon_info) { g_return_val_if_fail (icon_info != NULL, NULL); @@ -3037,96 +3817,145 @@ _gtk_icon_theme_check_reload (GdkDisplay *display) } } -#ifdef G_OS_WIN32 - -/* DLL ABI stability backward compatibility versions */ - -#undef gtk_icon_theme_set_search_path -void -gtk_icon_theme_set_search_path (GtkIconTheme *icon_theme, - const gchar *path[], - gint n_elements) +/** + * gtk_icon_theme_lookup_by_gicon: + * @icon_theme: a #GtkIconTheme + * @icon: the #GIcon to look up + * @size: desired icon size + * @flags: flags modifying the behavior of the icon lookup + * + * Looks up an icon and returns a structure containing + * information such as the filename of the icon. + * The icon can then be rendered into a pixbuf using + * gtk_icon_info_load_icon(). + * + * Return value: a #GtkIconInfo structure containing + * information about the icon, or %NULL if the icon + * wasn't found. Free with gtk_icon_info_free() + * + * Since: 2.14 + */ +GtkIconInfo * +gtk_icon_theme_lookup_by_gicon (GtkIconTheme *icon_theme, + GIcon *icon, + gint size, + GtkIconLookupFlags flags) { - const gchar **utf8_path; - gint i; - - utf8_path = g_new (const gchar *, n_elements); - - for (i = 0; i < n_elements; i++) - utf8_path[i] = g_locale_to_utf8 (path[i], -1, NULL, NULL, NULL); + GtkIconInfo *info; - gtk_icon_theme_set_search_path_utf8 (icon_theme, utf8_path, n_elements); + g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL); + g_return_val_if_fail (G_IS_ICON (icon), NULL); - for (i = 0; i < n_elements; i++) - g_free ((gchar *) utf8_path[i]); + if (G_IS_LOADABLE_ICON (icon)) + { + info = icon_info_new (); + info->loadable = G_LOADABLE_ICON (g_object_ref (icon)); - g_free (utf8_path); -} + info->dir_type = ICON_THEME_DIR_UNTHEMED; + info->dir_size = size; + info->desired_size = size; + info->threshold = 2; + info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0; -#undef gtk_icon_theme_get_search_path + return info; + } + else if (G_IS_THEMED_ICON (icon)) + { + const gchar **names; -void -gtk_icon_theme_get_search_path (GtkIconTheme *icon_theme, - gchar **path[], - gint *n_elements) -{ - gint i, n; + names = (const gchar **)g_themed_icon_get_names (G_THEMED_ICON (icon)); + info = gtk_icon_theme_choose_icon (icon_theme, names, size, flags); - gtk_icon_theme_get_search_path_utf8 (icon_theme, path, &n); + return info; + } + else if (G_IS_EMBLEMED_ICON (icon)) + { + GIcon *base, *emblem; + GList *list, *l; + GtkIconInfo *emblem_info; - if (n_elements) - *n_elements = n; + if (GTK_IS_NUMERABLE_ICON (icon)) + _gtk_numerable_icon_set_background_icon_size (GTK_NUMERABLE_ICON (icon), size / 2); - if (path) - { - for (i = 0; i < n; i++) - { - gchar *tem = (*path)[i]; + base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon)); + info = gtk_icon_theme_lookup_by_gicon (icon_theme, base, size, flags); + if (info) + { + list = g_emblemed_icon_get_emblems (G_EMBLEMED_ICON (icon)); + for (l = list; l; l = l->next) + { + emblem = g_emblem_get_icon (G_EMBLEM (l->data)); + /* always force size for emblems */ + emblem_info = gtk_icon_theme_lookup_by_gicon (icon_theme, emblem, size / 2, flags | GTK_ICON_LOOKUP_FORCE_SIZE); + if (emblem_info) + info->emblem_infos = g_slist_prepend (info->emblem_infos, emblem_info); + } + } - (*path)[i] = g_locale_from_utf8 ((*path)[i], -1, NULL, NULL, NULL); - g_free (tem); - } + return info; } -} + else if (GDK_IS_PIXBUF (icon)) + { + GdkPixbuf *pixbuf; -#undef gtk_icon_theme_append_search_path + pixbuf = GDK_PIXBUF (icon); -void -gtk_icon_theme_append_search_path (GtkIconTheme *icon_theme, - const gchar *path) -{ - gchar *utf8_path = g_locale_from_utf8 (path, -1, NULL, NULL, NULL); + if ((flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0) + { + gint width, height, max; + gdouble scale; + GdkPixbuf *scaled; - gtk_icon_theme_append_search_path_utf8 (icon_theme, utf8_path); + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + max = MAX (width, height); + scale = (gdouble) size / (gdouble) max; - g_free (utf8_path); -} + scaled = gdk_pixbuf_scale_simple (pixbuf, + 0.5 + width * scale, + 0.5 + height * scale, + GDK_INTERP_BILINEAR); -#undef gtk_icon_theme_prepend_search_path + info = gtk_icon_info_new_for_pixbuf (icon_theme, scaled); -void -gtk_icon_theme_prepend_search_path (GtkIconTheme *icon_theme, - const gchar *path) -{ - gchar *utf8_path = g_locale_from_utf8 (path, -1, NULL, NULL, NULL); + g_object_unref (scaled); + } + else + { + info = gtk_icon_info_new_for_pixbuf (icon_theme, pixbuf); + } - gtk_icon_theme_prepend_search_path_utf8 (icon_theme, utf8_path); + return info; + } - g_free (utf8_path); + return NULL; } -#undef gtk_icon_info_get_filename - -G_CONST_RETURN gchar * -gtk_icon_info_get_filename (GtkIconInfo *icon_info) +/** + * gtk_icon_info_new_for_pixbuf: + * @icon_theme: a #GtkIconTheme + * @pixbuf: the pixbuf to wrap in a #GtkIconInfo + * + * Creates a #GtkIconInfo for a #GdkPixbuf. + * + * Returns: a #GtkIconInfo + * + * Since: 2.14 + */ +GtkIconInfo * +gtk_icon_info_new_for_pixbuf (GtkIconTheme *icon_theme, + GdkPixbuf *pixbuf) { - g_return_val_if_fail (icon_info != NULL, NULL); + GtkIconInfo *info; - return icon_info->cp_filename; -} + g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL); + g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); -#endif + info = icon_info_new (); + info->pixbuf = g_object_ref (pixbuf); + info->scale = 1.0; + info->dir_type = ICON_THEME_DIR_UNTHEMED; -#define __GTK_ICON_THEME_C__ -#include "gtkaliasdef.c" + return info; +}