| /* GTK - The GIMP Toolkit |
| * Copyright (C) 2011 Red Hat, Inc. |
| * |
| * Authors: Cosimo Cecchi <cosimoc@gnome.org> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "config.h" |
| |
| #include "gtkiconhelperprivate.h" |
| |
| G_DEFINE_TYPE (GtkIconHelper, _gtk_icon_helper, G_TYPE_OBJECT) |
| |
| struct _GtkIconHelperPrivate { |
| GtkImageType storage_type; |
| |
| GdkPixbuf *orig_pixbuf; |
| GdkPixbufAnimation *animation; |
| GIcon *gicon; |
| GtkIconSet *icon_set; |
| gchar *icon_name; |
| gchar *stock_id; |
| |
| GtkIconSize icon_size; |
| gint pixel_size; |
| |
| guint use_fallback : 1; |
| guint force_scale_pixbuf : 1; |
| |
| GdkPixbuf *rendered_pixbuf; |
| GtkStateFlags last_rendered_state; |
| }; |
| |
| void |
| _gtk_icon_helper_clear (GtkIconHelper *self) |
| { |
| g_clear_object (&self->priv->gicon); |
| g_clear_object (&self->priv->orig_pixbuf); |
| g_clear_object (&self->priv->animation); |
| g_clear_object (&self->priv->rendered_pixbuf); |
| |
| if (self->priv->icon_set != NULL) |
| { |
| gtk_icon_set_unref (self->priv->icon_set); |
| self->priv->icon_set = NULL; |
| } |
| |
| g_free (self->priv->icon_name); |
| self->priv->icon_name = NULL; |
| |
| g_free (self->priv->stock_id); |
| self->priv->stock_id = NULL; |
| |
| self->priv->storage_type = GTK_IMAGE_EMPTY; |
| self->priv->icon_size = GTK_ICON_SIZE_INVALID; |
| self->priv->last_rendered_state = GTK_STATE_FLAG_NORMAL; |
| } |
| |
| void |
| _gtk_icon_helper_invalidate (GtkIconHelper *self) |
| { |
| g_clear_object (&self->priv->rendered_pixbuf); |
| } |
| |
| static void |
| gtk_icon_helper_finalize (GObject *object) |
| { |
| GtkIconHelper *self = GTK_ICON_HELPER (object); |
| |
| _gtk_icon_helper_clear (self); |
| |
| G_OBJECT_CLASS (_gtk_icon_helper_parent_class)->finalize (object); |
| } |
| |
| static void |
| _gtk_icon_helper_class_init (GtkIconHelperClass *klass) |
| { |
| GObjectClass *oclass; |
| |
| oclass = G_OBJECT_CLASS (klass); |
| oclass->finalize = gtk_icon_helper_finalize; |
| |
| g_type_class_add_private (klass, sizeof (GtkIconHelperPrivate)); |
| } |
| |
| static void |
| _gtk_icon_helper_init (GtkIconHelper *self) |
| { |
| self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTK_TYPE_ICON_HELPER, GtkIconHelperPrivate); |
| |
| self->priv->storage_type = GTK_IMAGE_EMPTY; |
| self->priv->icon_size = GTK_ICON_SIZE_INVALID; |
| self->priv->pixel_size = -1; |
| self->priv->last_rendered_state = GTK_STATE_FLAG_NORMAL; |
| } |
| |
| static void |
| ensure_icon_size (GtkIconHelper *self, |
| GtkStyleContext *context, |
| gint *width_out, |
| gint *height_out) |
| { |
| gint width, height; |
| GtkSettings *settings; |
| GdkScreen *screen; |
| |
| screen = gtk_style_context_get_screen (context); |
| settings = gtk_settings_get_for_screen (screen); |
| |
| if (self->priv->pixel_size != -1) |
| { |
| width = height = self->priv->pixel_size; |
| } |
| else if (!gtk_icon_size_lookup_for_settings (settings, |
| self->priv->icon_size, |
| &width, &height)) |
| { |
| if (self->priv->icon_size == GTK_ICON_SIZE_INVALID) |
| { |
| width = height = 0; |
| } |
| else |
| { |
| g_warning ("Invalid icon size %d\n", self->priv->icon_size); |
| width = height = 24; |
| } |
| } |
| |
| *width_out = width; |
| *height_out = height; |
| } |
| |
| static GdkPixbuf * |
| ensure_stated_icon_from_info (GtkIconHelper *self, |
| GtkStyleContext *context, |
| GtkIconInfo *info) |
| { |
| GdkPixbuf *destination = NULL; |
| gboolean symbolic; |
| |
| symbolic = FALSE; |
| |
| if (info) |
| destination = |
| gtk_icon_info_load_symbolic_for_context (info, |
| context, |
| &symbolic, |
| NULL); |
| |
| if (destination == NULL) |
| { |
| GtkIconSet *icon_set; |
| icon_set = gtk_style_context_lookup_icon_set (context, GTK_STOCK_MISSING_IMAGE); |
| |
| destination = |
| gtk_icon_set_render_icon_pixbuf (icon_set, context, self->priv->icon_size); |
| } |
| else if (!symbolic) |
| { |
| GtkIconSource *source; |
| GdkPixbuf *rendered; |
| |
| source = gtk_icon_source_new (); |
| gtk_icon_source_set_pixbuf (source, destination); |
| /* The size here is arbitrary; since size isn't |
| * wildcarded in the source, it isn't supposed to be |
| * scaled by the engine function |
| */ |
| gtk_icon_source_set_size (source, |
| GTK_ICON_SIZE_SMALL_TOOLBAR); |
| gtk_icon_source_set_size_wildcarded (source, FALSE); |
| |
| rendered = gtk_render_icon_pixbuf (context, source, (GtkIconSize) -1); |
| gtk_icon_source_free (source); |
| |
| g_object_unref (destination); |
| destination = rendered; |
| } |
| |
| return destination; |
| } |
| |
| static gboolean |
| check_invalidate_pixbuf (GtkIconHelper *self, |
| GtkStyleContext *context) |
| { |
| GtkStateFlags state; |
| |
| state = gtk_style_context_get_state (context); |
| |
| if ((self->priv->rendered_pixbuf != NULL) && |
| (self->priv->last_rendered_state == state)) |
| return FALSE; |
| |
| self->priv->last_rendered_state = state; |
| g_clear_object (&self->priv->rendered_pixbuf); |
| |
| return TRUE; |
| } |
| |
| static GtkIconLookupFlags |
| get_icon_lookup_flags (GtkIconHelper *self) |
| { |
| GtkIconLookupFlags flags = GTK_ICON_LOOKUP_USE_BUILTIN; |
| |
| if (self->priv->use_fallback) |
| flags |= GTK_ICON_LOOKUP_GENERIC_FALLBACK; |
| if (self->priv->pixel_size != -1) |
| flags |= GTK_ICON_LOOKUP_FORCE_SIZE; |
| |
| return flags; |
| } |
| |
| static void |
| ensure_pixbuf_for_icon_name_or_gicon (GtkIconHelper *self, |
| GtkStyleContext *context) |
| { |
| GtkIconTheme *icon_theme; |
| gint width, height; |
| GtkIconInfo *info; |
| GtkIconLookupFlags flags; |
| |
| if (!check_invalidate_pixbuf (self, context)) |
| return; |
| |
| icon_theme = gtk_icon_theme_get_default (); |
| flags = get_icon_lookup_flags (self); |
| |
| ensure_icon_size (self, context, &width, &height); |
| |
| if (self->priv->storage_type == GTK_IMAGE_ICON_NAME && |
| self->priv->icon_name != NULL) |
| { |
| info = gtk_icon_theme_lookup_icon (icon_theme, |
| self->priv->icon_name, |
| MIN (width, height), flags); |
| } |
| else if (self->priv->storage_type == GTK_IMAGE_GICON && |
| self->priv->gicon != NULL) |
| { |
| info = gtk_icon_theme_lookup_by_gicon (icon_theme, |
| self->priv->gicon, |
| MIN (width, height), flags); |
| } |
| else |
| { |
| g_assert_not_reached (); |
| return; |
| } |
| |
| self->priv->rendered_pixbuf = ensure_stated_icon_from_info (self, context, info); |
| |
| if (info) |
| gtk_icon_info_free (info); |
| } |
| |
| static void |
| ensure_pixbuf_for_icon_set (GtkIconHelper *self, |
| GtkStyleContext *context, |
| GtkIconSet *icon_set) |
| { |
| if (!check_invalidate_pixbuf (self, context)) |
| return; |
| |
| self->priv->rendered_pixbuf = |
| gtk_icon_set_render_icon_pixbuf (icon_set, context, self->priv->icon_size); |
| } |
| |
| static void |
| ensure_pixbuf_at_size (GtkIconHelper *self, |
| GtkStyleContext *context) |
| { |
| gint width, height; |
| |
| if (!check_invalidate_pixbuf (self, context)) |
| return; |
| |
| if (self->priv->rendered_pixbuf) |
| return; |
| |
| if (self->priv->pixel_size != -1 || |
| self->priv->icon_size != GTK_ICON_SIZE_INVALID) |
| { |
| ensure_icon_size (self, context, &width, &height); |
| |
| if (width < gdk_pixbuf_get_width (self->priv->orig_pixbuf) || |
| height < gdk_pixbuf_get_height (self->priv->orig_pixbuf)) |
| self->priv->rendered_pixbuf = |
| gdk_pixbuf_scale_simple (self->priv->orig_pixbuf, |
| width, height, |
| GDK_INTERP_BILINEAR); |
| } |
| |
| if (!self->priv->rendered_pixbuf) |
| self->priv->rendered_pixbuf = g_object_ref (self->priv->orig_pixbuf); |
| } |
| |
| GdkPixbuf * |
| _gtk_icon_helper_ensure_pixbuf (GtkIconHelper *self, |
| GtkStyleContext *context) |
| { |
| GdkPixbuf *pixbuf = NULL; |
| GtkIconSet *icon_set; |
| |
| switch (self->priv->storage_type) |
| { |
| case GTK_IMAGE_PIXBUF: |
| if (self->priv->force_scale_pixbuf) |
| ensure_pixbuf_at_size (self, context); |
| else |
| pixbuf = g_object_ref (self->priv->orig_pixbuf); |
| break; |
| |
| case GTK_IMAGE_STOCK: |
| icon_set = gtk_style_context_lookup_icon_set (context, self->priv->stock_id); |
| ensure_pixbuf_for_icon_set (self, context, icon_set); |
| break; |
| |
| case GTK_IMAGE_ICON_SET: |
| icon_set = self->priv->icon_set; |
| ensure_pixbuf_for_icon_set (self, context, icon_set); |
| break; |
| |
| case GTK_IMAGE_ICON_NAME: |
| case GTK_IMAGE_GICON: |
| ensure_pixbuf_for_icon_name_or_gicon (self, context); |
| break; |
| |
| case GTK_IMAGE_ANIMATION: |
| case GTK_IMAGE_EMPTY: |
| default: |
| pixbuf = NULL; |
| break; |
| } |
| |
| if (pixbuf == NULL && |
| self->priv->rendered_pixbuf != NULL) |
| pixbuf = g_object_ref (self->priv->rendered_pixbuf); |
| |
| return pixbuf; |
| } |
| |
| void |
| _gtk_icon_helper_get_size (GtkIconHelper *self, |
| GtkStyleContext *context, |
| gint *width_out, |
| gint *height_out) |
| { |
| GdkPixbuf *pix; |
| gint width, height; |
| |
| width = height = 0; |
| pix = _gtk_icon_helper_ensure_pixbuf (self, context); |
| |
| if (pix != NULL) |
| { |
| width = gdk_pixbuf_get_width (pix); |
| height = gdk_pixbuf_get_height (pix); |
| |
| g_object_unref (pix); |
| } |
| else if (self->priv->storage_type == GTK_IMAGE_ANIMATION) |
| { |
| width = gdk_pixbuf_animation_get_width (self->priv->animation); |
| height = gdk_pixbuf_animation_get_height (self->priv->animation); |
| } |
| else if (self->priv->icon_size != -1) |
| { |
| ensure_icon_size (self, context, &width, &height); |
| } |
| |
| if (width_out) |
| *width_out = width; |
| if (height_out) |
| *height_out = height; |
| } |
| |
| void |
| _gtk_icon_helper_set_gicon (GtkIconHelper *self, |
| GIcon *gicon, |
| GtkIconSize icon_size) |
| { |
| _gtk_icon_helper_clear (self); |
| |
| if (gicon != NULL) |
| { |
| self->priv->storage_type = GTK_IMAGE_GICON; |
| self->priv->gicon = g_object_ref (gicon); |
| _gtk_icon_helper_set_icon_size (self, icon_size); |
| } |
| } |
| |
| void |
| _gtk_icon_helper_set_icon_name (GtkIconHelper *self, |
| const gchar *icon_name, |
| GtkIconSize icon_size) |
| { |
| _gtk_icon_helper_clear (self); |
| |
| if (icon_name != NULL && |
| g_strcmp0 (icon_name, "") != 0) |
| { |
| self->priv->storage_type = GTK_IMAGE_ICON_NAME; |
| self->priv->icon_name = g_strdup (icon_name); |
| _gtk_icon_helper_set_icon_size (self, icon_size); |
| } |
| } |
| |
| void |
| _gtk_icon_helper_set_icon_set (GtkIconHelper *self, |
| GtkIconSet *icon_set, |
| GtkIconSize icon_size) |
| { |
| _gtk_icon_helper_clear (self); |
| |
| if (icon_set != NULL) |
| { |
| self->priv->storage_type = GTK_IMAGE_ICON_SET; |
| self->priv->icon_set = gtk_icon_set_ref (icon_set); |
| _gtk_icon_helper_set_icon_size (self, icon_size); |
| } |
| } |
| |
| void |
| _gtk_icon_helper_set_pixbuf (GtkIconHelper *self, |
| GdkPixbuf *pixbuf) |
| { |
| _gtk_icon_helper_clear (self); |
| |
| if (pixbuf != NULL) |
| { |
| self->priv->storage_type = GTK_IMAGE_PIXBUF; |
| self->priv->orig_pixbuf = g_object_ref (pixbuf); |
| } |
| } |
| |
| void |
| _gtk_icon_helper_set_animation (GtkIconHelper *self, |
| GdkPixbufAnimation *animation) |
| { |
| _gtk_icon_helper_clear (self); |
| |
| if (animation != NULL) |
| { |
| self->priv->storage_type = GTK_IMAGE_ANIMATION; |
| self->priv->animation = g_object_ref (animation); |
| } |
| } |
| |
| void |
| _gtk_icon_helper_set_stock_id (GtkIconHelper *self, |
| const gchar *stock_id, |
| GtkIconSize icon_size) |
| { |
| _gtk_icon_helper_clear (self); |
| |
| if (stock_id != NULL) |
| { |
| self->priv->storage_type = GTK_IMAGE_STOCK; |
| self->priv->stock_id = g_strdup (stock_id); |
| _gtk_icon_helper_set_icon_size (self, icon_size); |
| } |
| } |
| |
| void |
| _gtk_icon_helper_set_icon_size (GtkIconHelper *self, |
| GtkIconSize icon_size) |
| { |
| if (self->priv->icon_size != icon_size) |
| { |
| self->priv->icon_size = icon_size; |
| _gtk_icon_helper_invalidate (self); |
| } |
| } |
| |
| void |
| _gtk_icon_helper_set_pixel_size (GtkIconHelper *self, |
| gint pixel_size) |
| { |
| if (self->priv->pixel_size != pixel_size) |
| { |
| self->priv->pixel_size = pixel_size; |
| _gtk_icon_helper_invalidate (self); |
| } |
| } |
| |
| void |
| _gtk_icon_helper_set_use_fallback (GtkIconHelper *self, |
| gboolean use_fallback) |
| { |
| if (self->priv->use_fallback != use_fallback) |
| { |
| self->priv->use_fallback = use_fallback; |
| _gtk_icon_helper_invalidate (self); |
| } |
| } |
| |
| GtkImageType |
| _gtk_icon_helper_get_storage_type (GtkIconHelper *self) |
| { |
| return self->priv->storage_type; |
| } |
| |
| gboolean |
| _gtk_icon_helper_get_use_fallback (GtkIconHelper *self) |
| { |
| return self->priv->use_fallback; |
| } |
| |
| GtkIconSize |
| _gtk_icon_helper_get_icon_size (GtkIconHelper *self) |
| { |
| return self->priv->icon_size; |
| } |
| |
| gint |
| _gtk_icon_helper_get_pixel_size (GtkIconHelper *self) |
| { |
| return self->priv->pixel_size; |
| } |
| |
| GdkPixbuf * |
| _gtk_icon_helper_peek_pixbuf (GtkIconHelper *self) |
| { |
| return self->priv->orig_pixbuf; |
| } |
| |
| GIcon * |
| _gtk_icon_helper_peek_gicon (GtkIconHelper *self) |
| { |
| return self->priv->gicon; |
| } |
| |
| GdkPixbufAnimation * |
| _gtk_icon_helper_peek_animation (GtkIconHelper *self) |
| { |
| return self->priv->animation; |
| } |
| |
| GtkIconSet * |
| _gtk_icon_helper_peek_icon_set (GtkIconHelper *self) |
| { |
| return self->priv->icon_set; |
| } |
| |
| const gchar * |
| _gtk_icon_helper_get_stock_id (GtkIconHelper *self) |
| { |
| return self->priv->stock_id; |
| } |
| |
| const gchar * |
| _gtk_icon_helper_get_icon_name (GtkIconHelper *self) |
| { |
| return self->priv->icon_name; |
| } |
| |
| GtkIconHelper * |
| _gtk_icon_helper_new (void) |
| { |
| return g_object_new (GTK_TYPE_ICON_HELPER, NULL); |
| } |
| |
| void |
| _gtk_icon_helper_draw (GtkIconHelper *self, |
| GtkStyleContext *context, |
| cairo_t *cr, |
| gdouble x, |
| gdouble y) |
| { |
| GdkPixbuf *pixbuf; |
| |
| pixbuf = _gtk_icon_helper_ensure_pixbuf (self, context); |
| |
| if (pixbuf != NULL) |
| { |
| gtk_render_icon (context, cr, pixbuf, x, y); |
| g_object_unref (pixbuf); |
| } |
| } |
| |
| gboolean |
| _gtk_icon_helper_get_is_empty (GtkIconHelper *self) |
| { |
| return (self->priv->storage_type == GTK_IMAGE_EMPTY); |
| } |
| |
| gboolean |
| _gtk_icon_helper_get_force_scale_pixbuf (GtkIconHelper *self) |
| { |
| return self->priv->force_scale_pixbuf; |
| } |
| |
| void |
| _gtk_icon_helper_set_force_scale_pixbuf (GtkIconHelper *self, |
| gboolean force_scale) |
| { |
| if (self->priv->force_scale_pixbuf != force_scale) |
| { |
| self->priv->force_scale_pixbuf = force_scale; |
| _gtk_icon_helper_invalidate (self); |
| } |
| } |