X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkimage.c;h=d373b51b709d81586f61af595b9504d836775b3e;hb=ceeed09d0780e5a2227fcfdb3e13f7530aa5fcf2;hp=184451ef239bbcdffbabea896b42bc57fddca64d;hpb=07fafe7b23028f0ed127efaf9b9a1aab1ef9b4dc;p=~andy%2Fgtk diff --git a/gtk/gtkimage.c b/gtk/gtkimage.c index 184451ef2..d373b51b7 100644 --- a/gtk/gtkimage.c +++ b/gtk/gtkimage.c @@ -12,9 +12,7 @@ * 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, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ /* @@ -25,17 +23,22 @@ */ #include "config.h" + #include #include #include "gtkcontainer.h" -#include "gtkimage.h" +#include "gtkiconhelperprivate.h" +#include "gtkimageprivate.h" #include "gtkiconfactory.h" #include "gtkstock.h" #include "gtkicontheme.h" #include "gtksizerequest.h" #include "gtkintl.h" #include "gtkprivate.h" +#include "gtktypebuiltins.h" + +#include "a11y/gtkimageaccessible.h" /** * SECTION:gtkimage @@ -121,7 +124,7 @@ * * Sometimes an application will want to avoid depending on external data * files, such as image files. GTK+ comes with a program to avoid this, - * called gdk-pixbuf-csource. This program + * called gdk-pixbuf-csource. This library * allows you to convert an image into a C variable declaration, which * can then be loaded into a #GdkPixbuf using * gdk_pixbuf_new_from_inline(). @@ -130,27 +133,13 @@ struct _GtkImagePrivate { - GtkIconSize icon_size; /* Only used with GTK_IMAGE_STOCK, GTK_IMAGE_ICON_SET, GTK_IMAGE_ICON_NAME */ - GtkImageType storage_type; - - union - { - GtkImagePixbufData pixbuf; - GtkImageStockData stock; - GtkImageIconSetData icon_set; - GtkImageAnimationData anim; - GtkImageIconNameData name; - GtkImageGIconData gicon; - } data; - - gboolean was_symbolic; + GtkIconHelper *icon_helper; + + gint animation_timeout; + GdkPixbufAnimationIter *animation_iter; + gchar *filename; /* Only used with GTK_IMAGE_ANIMATION, GTK_IMAGE_PIXBUF */ - gint last_rendered_state; /* a GtkStateType, with -1 meaning an invalid state, - * only used with GTK_IMAGE_GICON, GTK_IMAGE_ICON_NAME */ - gint pixel_size; - guint need_calc_size : 1; - gint required_width; - gint required_height; + gchar *resource_path; /* Only used with GTK_IMAGE_PIXBUF */ }; @@ -166,17 +155,11 @@ static void gtk_image_get_preferred_height (GtkWidget *widget, gint *minimum, gint *natural); -static void gtk_image_style_set (GtkWidget *widget, - GtkStyle *prev_style); +static void gtk_image_style_updated (GtkWidget *widget); static void gtk_image_screen_changed (GtkWidget *widget, GdkScreen *prev_screen); -static void gtk_image_destroy (GtkWidget *widget); +static void gtk_image_finalize (GObject *object); static void gtk_image_reset (GtkImage *image); -static void gtk_image_calc_size (GtkImage *image); - -static void gtk_image_update_size (GtkImage *image, - gint image_width, - gint image_height); static void gtk_image_set_property (GObject *object, guint prop_id, @@ -201,7 +184,9 @@ enum PROP_PIXBUF_ANIMATION, PROP_ICON_NAME, PROP_STORAGE_TYPE, - PROP_GICON + PROP_GICON, + PROP_RESOURCE, + PROP_USE_FALLBACK }; G_DEFINE_TYPE (GtkImage, gtk_image, GTK_TYPE_MISC) @@ -216,15 +201,15 @@ gtk_image_class_init (GtkImageClass *class) gobject_class->set_property = gtk_image_set_property; gobject_class->get_property = gtk_image_get_property; + gobject_class->finalize = gtk_image_finalize; widget_class = GTK_WIDGET_CLASS (class); widget_class->draw = gtk_image_draw; - widget_class->destroy = gtk_image_destroy; widget_class->get_preferred_width = gtk_image_get_preferred_width; widget_class->get_preferred_height = gtk_image_get_preferred_height; widget_class->unmap = gtk_image_unmap; widget_class->unrealize = gtk_image_unrealize; - widget_class->style_set = gtk_image_style_set; + widget_class->style_updated = gtk_image_style_updated; widget_class->screen_changed = gtk_image_screen_changed; g_object_class_install_property (gobject_class, @@ -326,7 +311,22 @@ gtk_image_class_init (GtkImageClass *class) P_("The GIcon being displayed"), G_TYPE_ICON, GTK_PARAM_READWRITE)); - + + /** + * GtkImage:resource: + * + * A path to a resource file to display. + * + * Since: 3.8 + */ + g_object_class_install_property (gobject_class, + PROP_RESOURCE, + g_param_spec_string ("resource", + P_("Resource"), + P_("The resource path being displayed"), + NULL, + GTK_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_STORAGE_TYPE, g_param_spec_enum ("storage-type", @@ -336,7 +336,27 @@ gtk_image_class_init (GtkImageClass *class) GTK_IMAGE_EMPTY, GTK_PARAM_READABLE)); + /** + * GtkImage:use-fallback: + * + * Whether the icon displayed in the GtkImage will use + * standard icon names fallback. The value of this property + * is only relevant for images of type %GTK_IMAGE_ICON_NAME + * and %GTK_IMAGE_GICON. + * + * Since: 3.0 + */ + g_object_class_install_property (gobject_class, + PROP_USE_FALLBACK, + g_param_spec_boolean ("use-fallback", + P_("Use Fallback"), + P_("Whether to use icon names fallback"), + FALSE, + GTK_PARAM_READWRITE)); + g_type_class_add_private (class, sizeof (GtkImagePrivate)); + + gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_IMAGE_ACCESSIBLE); } static void @@ -350,24 +370,22 @@ gtk_image_init (GtkImage *image) priv = image->priv; gtk_widget_set_has_window (GTK_WIDGET (image), FALSE); - - priv->storage_type = GTK_IMAGE_EMPTY; - priv->icon_size = DEFAULT_ICON_SIZE; - - priv->pixel_size = -1; + priv->icon_helper = _gtk_icon_helper_new (); priv->filename = NULL; } static void -gtk_image_destroy (GtkWidget *widget) +gtk_image_finalize (GObject *object) { - GtkImage *image = GTK_IMAGE (widget); + GtkImage *image = GTK_IMAGE (object); - gtk_image_reset (image); + g_clear_object (&image->priv->icon_helper); + + g_free (image->priv->filename); - GTK_WIDGET_CLASS (gtk_image_parent_class)->destroy (widget); -} + G_OBJECT_CLASS (gtk_image_parent_class)->finalize (object); +}; static void gtk_image_set_property (GObject *object, @@ -377,6 +395,10 @@ gtk_image_set_property (GObject *object, { GtkImage *image = GTK_IMAGE (object); GtkImagePrivate *priv = image->priv; + GtkIconSize icon_size = _gtk_icon_helper_get_icon_size (priv->icon_helper); + + if (icon_size == GTK_ICON_SIZE_INVALID) + icon_size = DEFAULT_ICON_SIZE; switch (prop_id) { @@ -389,32 +411,14 @@ gtk_image_set_property (GObject *object, break; case PROP_STOCK: gtk_image_set_from_stock (image, g_value_get_string (value), - priv->icon_size); + icon_size); break; case PROP_ICON_SET: gtk_image_set_from_icon_set (image, g_value_get_boxed (value), - priv->icon_size); + icon_size); break; case PROP_ICON_SIZE: - if (priv->storage_type == GTK_IMAGE_STOCK) - gtk_image_set_from_stock (image, - priv->data.stock.stock_id, - g_value_get_int (value)); - else if (priv->storage_type == GTK_IMAGE_ICON_SET) - gtk_image_set_from_icon_set (image, - priv->data.icon_set.icon_set, - g_value_get_int (value)); - else if (priv->storage_type == GTK_IMAGE_ICON_NAME) - gtk_image_set_from_icon_name (image, - priv->data.name.icon_name, - g_value_get_int (value)); - else if (priv->storage_type == GTK_IMAGE_GICON) - gtk_image_set_from_gicon (image, - priv->data.gicon.icon, - g_value_get_int (value)); - else - /* Save to be used when STOCK, ICON_SET, ICON_NAME or GICON property comes in */ - priv->icon_size = g_value_get_int (value); + _gtk_icon_helper_set_icon_size (priv->icon_helper, g_value_get_int (value)); break; case PROP_PIXEL_SIZE: gtk_image_set_pixel_size (image, g_value_get_int (value)); @@ -425,11 +429,18 @@ gtk_image_set_property (GObject *object, break; case PROP_ICON_NAME: gtk_image_set_from_icon_name (image, g_value_get_string (value), - priv->icon_size); + icon_size); break; case PROP_GICON: gtk_image_set_from_gicon (image, g_value_get_object (value), - priv->icon_size); + icon_size); + break; + case PROP_RESOURCE: + gtk_image_set_from_resource (image, g_value_get_string (value)); + break; + + case PROP_USE_FALLBACK: + _gtk_icon_helper_set_use_fallback (priv->icon_helper, g_value_get_boolean (value)); break; default: @@ -447,69 +458,44 @@ gtk_image_get_property (GObject *object, GtkImage *image = GTK_IMAGE (object); GtkImagePrivate *priv = image->priv; - /* The "getter" functions whine if you try to get the wrong - * storage type. This function is instead robust against that, - * so that GUI builders don't have to jump through hoops - * to avoid g_warning - */ - switch (prop_id) { case PROP_PIXBUF: - if (priv->storage_type != GTK_IMAGE_PIXBUF) - g_value_set_object (value, NULL); - else - g_value_set_object (value, - gtk_image_get_pixbuf (image)); + g_value_set_object (value, _gtk_icon_helper_peek_pixbuf (priv->icon_helper)); break; case PROP_FILE: g_value_set_string (value, priv->filename); break; case PROP_STOCK: - if (priv->storage_type != GTK_IMAGE_STOCK) - g_value_set_string (value, NULL); - else - g_value_set_string (value, - priv->data.stock.stock_id); + g_value_set_string (value, _gtk_icon_helper_get_stock_id (priv->icon_helper)); break; case PROP_ICON_SET: - if (priv->storage_type != GTK_IMAGE_ICON_SET) - g_value_set_boxed (value, NULL); - else - g_value_set_boxed (value, - priv->data.icon_set.icon_set); + g_value_set_boxed (value, _gtk_icon_helper_peek_icon_set (priv->icon_helper)); break; case PROP_ICON_SIZE: - g_value_set_int (value, priv->icon_size); + g_value_set_int (value, _gtk_icon_helper_get_icon_size (priv->icon_helper)); break; case PROP_PIXEL_SIZE: - g_value_set_int (value, priv->pixel_size); + g_value_set_int (value, _gtk_icon_helper_get_pixel_size (priv->icon_helper)); break; case PROP_PIXBUF_ANIMATION: - if (priv->storage_type != GTK_IMAGE_ANIMATION) - g_value_set_object (value, NULL); - else - g_value_set_object (value, - priv->data.anim.anim); + g_value_set_object (value, _gtk_icon_helper_peek_animation (priv->icon_helper)); break; case PROP_ICON_NAME: - if (priv->storage_type != GTK_IMAGE_ICON_NAME) - g_value_set_string (value, NULL); - else - g_value_set_string (value, - priv->data.name.icon_name); + g_value_set_string (value, _gtk_icon_helper_get_icon_name (priv->icon_helper)); break; case PROP_GICON: - if (priv->storage_type != GTK_IMAGE_GICON) - g_value_set_object (value, NULL); - else - g_value_set_object (value, - priv->data.gicon.icon); + g_value_set_object (value, _gtk_icon_helper_peek_gicon (priv->icon_helper)); + break; + case PROP_RESOURCE: + g_value_set_string (value, priv->resource_path); + break; + case PROP_USE_FALLBACK: + g_value_set_boolean (value, _gtk_icon_helper_get_use_fallback (priv->icon_helper)); break; case PROP_STORAGE_TYPE: - g_value_set_enum (value, priv->storage_type); + g_value_set_enum (value, _gtk_icon_helper_get_storage_type (priv->icon_helper)); break; - default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -519,7 +505,7 @@ gtk_image_get_property (GObject *object, /** * gtk_image_new_from_file: - * @filename: a filename + * @filename: (type filename): a filename * * Creates a new #GtkImage displaying the file @filename. If the file * isn't found or can't be loaded, the resulting #GtkImage will @@ -552,6 +538,43 @@ gtk_image_new_from_file (const gchar *filename) return GTK_WIDGET (image); } +/** + * gtk_image_new_from_resource: + * @resource_path: a resource path + * + * Creates a new #GtkImage displaying the resource file @resource_path. If the file + * isn't found or can't be loaded, the resulting #GtkImage will + * display a "broken image" icon. This function never returns %NULL, + * it always returns a valid #GtkImage widget. + * + * If the file contains an animation, the image will contain an + * animation. + * + * If you need to detect failures to load the file, use + * gdk_pixbuf_new_from_file() to load the file yourself, then create + * the #GtkImage from the pixbuf. (Or for animations, use + * gdk_pixbuf_animation_new_from_file()). + * + * The storage type (gtk_image_get_storage_type()) of the returned + * image is not defined, it will be whatever is appropriate for + * displaying the file. + * + * Return value: a new #GtkImage + * + * Since: 3.4 + **/ +GtkWidget* +gtk_image_new_from_resource (const gchar *resource_path) +{ + GtkImage *image; + + image = g_object_new (GTK_TYPE_IMAGE, NULL); + + gtk_image_set_from_resource (image, resource_path); + + return GTK_WIDGET (image); +} + /** * gtk_image_new_from_pixbuf: * @pixbuf: (allow-none): a #GdkPixbuf, or %NULL @@ -725,7 +748,7 @@ gtk_image_new_from_gicon (GIcon *icon, /** * gtk_image_set_from_file: * @image: a #GtkImage - * @filename: (allow-none): a filename or %NULL + * @filename: (type filename) (allow-none): a filename or %NULL * * See gtk_image_new_from_file() for details. **/ @@ -757,7 +780,7 @@ gtk_image_set_from_file (GtkImage *image, { gtk_image_set_from_stock (image, GTK_STOCK_MISSING_IMAGE, - GTK_ICON_SIZE_BUTTON); + DEFAULT_ICON_SIZE); g_object_thaw_notify (G_OBJECT (image)); return; } @@ -780,6 +803,60 @@ gtk_image_set_from_file (GtkImage *image, g_object_thaw_notify (G_OBJECT (image)); } +/** + * gtk_image_set_from_resource: + * @image: a #GtkImage + * @resource_path: (allow-none): a resource path or %NULL + * + * See gtk_image_new_from_resource() for details. + **/ +void +gtk_image_set_from_resource (GtkImage *image, + const gchar *resource_path) +{ + GtkImagePrivate *priv; + GdkPixbufAnimation *animation; + + g_return_if_fail (GTK_IS_IMAGE (image)); + + priv = image->priv; + + g_object_freeze_notify (G_OBJECT (image)); + + gtk_image_clear (image); + + if (resource_path == NULL) + { + g_object_thaw_notify (G_OBJECT (image)); + return; + } + + animation = gdk_pixbuf_animation_new_from_resource (resource_path, NULL); + + if (animation == NULL) + { + gtk_image_set_from_stock (image, + GTK_STOCK_MISSING_IMAGE, + DEFAULT_ICON_SIZE); + g_object_thaw_notify (G_OBJECT (image)); + return; + } + + priv->resource_path = g_strdup (resource_path); + + if (gdk_pixbuf_animation_is_static_image (animation)) + gtk_image_set_from_pixbuf (image, gdk_pixbuf_animation_get_static_image (animation)); + else + gtk_image_set_from_animation (image, animation); + + g_object_notify (G_OBJECT (image), "resource"); + + g_object_unref (animation); + + g_object_thaw_notify (G_OBJECT (image)); +} + + /** * gtk_image_set_from_pixbuf: * @image: a #GtkImage @@ -801,21 +878,10 @@ gtk_image_set_from_pixbuf (GtkImage *image, g_object_freeze_notify (G_OBJECT (image)); - if (pixbuf) - g_object_ref (pixbuf); - gtk_image_clear (image); if (pixbuf != NULL) - { - priv->storage_type = GTK_IMAGE_PIXBUF; - - priv->data.pixbuf.pixbuf = pixbuf; - - gtk_image_update_size (image, - gdk_pixbuf_get_width (pixbuf), - gdk_pixbuf_get_height (pixbuf)); - } + _gtk_icon_helper_set_pixbuf (priv->icon_helper, pixbuf); g_object_notify (G_OBJECT (image), "pixbuf"); @@ -844,22 +910,13 @@ gtk_image_set_from_stock (GtkImage *image, g_object_freeze_notify (G_OBJECT (image)); - /* in case stock_id == priv->data.stock.stock_id */ new_id = g_strdup (stock_id); - gtk_image_clear (image); if (new_id) { - priv->storage_type = GTK_IMAGE_STOCK; - - priv->data.stock.stock_id = new_id; - priv->icon_size = size; - - /* Size is demand-computed in size request method - * if we're a stock image, since changing the - * style impacts the size request - */ + _gtk_icon_helper_set_stock_id (priv->icon_helper, new_id, size); + g_free (new_id); } g_object_notify (G_OBJECT (image), "stock"); @@ -888,7 +945,7 @@ gtk_image_set_from_icon_set (GtkImage *image, priv = image->priv; g_object_freeze_notify (G_OBJECT (image)); - + if (icon_set) gtk_icon_set_ref (icon_set); @@ -896,14 +953,8 @@ gtk_image_set_from_icon_set (GtkImage *image, if (icon_set) { - priv->storage_type = GTK_IMAGE_ICON_SET; - - priv->data.icon_set.icon_set = icon_set; - priv->icon_size = size; - - /* Size is demand-computed in size request method - * if we're an icon set - */ + _gtk_icon_helper_set_icon_set (priv->icon_helper, icon_set, size); + gtk_icon_set_unref (icon_set); } g_object_notify (G_OBJECT (image), "icon-set"); @@ -941,15 +992,8 @@ gtk_image_set_from_animation (GtkImage *image, if (animation != NULL) { - priv->storage_type = GTK_IMAGE_ANIMATION; - - priv->data.anim.anim = animation; - priv->data.anim.frame_timeout = 0; - priv->data.anim.iter = NULL; - - gtk_image_update_size (image, - gdk_pixbuf_animation_get_width (animation), - gdk_pixbuf_animation_get_height (animation)); + _gtk_icon_helper_set_animation (priv->icon_helper, animation); + g_object_unref (animation); } g_object_notify (G_OBJECT (image), "pixbuf-animation"); @@ -972,8 +1016,8 @@ gtk_image_set_from_icon_name (GtkImage *image, const gchar *icon_name, GtkIconSize size) { - gchar *new_name; GtkImagePrivate *priv; + gchar *new_name; g_return_if_fail (GTK_IS_IMAGE (image)); @@ -981,22 +1025,13 @@ gtk_image_set_from_icon_name (GtkImage *image, g_object_freeze_notify (G_OBJECT (image)); - /* in case icon_name == priv->data.name.icon_name */ new_name = g_strdup (icon_name); - gtk_image_clear (image); if (new_name) { - priv->storage_type = GTK_IMAGE_ICON_NAME; - - priv->data.name.icon_name = new_name; - priv->icon_size = size; - - /* Size is demand-computed in size request method - * if we're a icon theme image, since changing the - * style impacts the size request - */ + _gtk_icon_helper_set_icon_name (priv->icon_helper, new_name, size); + g_free (new_name); } g_object_notify (G_OBJECT (image), "icon-name"); @@ -1028,23 +1063,15 @@ gtk_image_set_from_gicon (GtkImage *image, g_object_freeze_notify (G_OBJECT (image)); - /* in case icon == priv->data.gicon.icon */ if (icon) g_object_ref (icon); - + gtk_image_clear (image); if (icon) { - priv->storage_type = GTK_IMAGE_GICON; - - priv->data.gicon.icon = icon; - priv->icon_size = size; - - /* Size is demand-computed in size request method - * if we're a icon theme image, since changing the - * style impacts the size request - */ + _gtk_icon_helper_set_gicon (priv->icon_helper, icon, size); + g_object_unref (icon); } g_object_notify (G_OBJECT (image), "gicon"); @@ -1068,7 +1095,7 @@ gtk_image_get_storage_type (GtkImage *image) { g_return_val_if_fail (GTK_IS_IMAGE (image), GTK_IMAGE_EMPTY); - return image->priv->storage_type; + return _gtk_icon_helper_get_storage_type (image->priv->icon_helper); } /** @@ -1087,19 +1114,9 @@ gtk_image_get_storage_type (GtkImage *image) GdkPixbuf* gtk_image_get_pixbuf (GtkImage *image) { - GtkImagePrivate *priv; - g_return_val_if_fail (GTK_IS_IMAGE (image), NULL); - priv = image->priv; - - g_return_val_if_fail (priv->storage_type == GTK_IMAGE_PIXBUF || - priv->storage_type == GTK_IMAGE_EMPTY, NULL); - - if (priv->storage_type == GTK_IMAGE_EMPTY) - priv->data.pixbuf.pixbuf = NULL; - - return priv->data.pixbuf.pixbuf; + return _gtk_icon_helper_peek_pixbuf (image->priv->icon_helper); } /** @@ -1127,17 +1144,11 @@ gtk_image_get_stock (GtkImage *image, priv = image->priv; - g_return_if_fail (priv->storage_type == GTK_IMAGE_STOCK || - priv->storage_type == GTK_IMAGE_EMPTY); - - if (priv->storage_type == GTK_IMAGE_EMPTY) - priv->data.stock.stock_id = NULL; - if (stock_id) - *stock_id = priv->data.stock.stock_id; + *stock_id = (gchar *) _gtk_icon_helper_get_stock_id (priv->icon_helper); if (size) - *size = priv->icon_size; + *size = _gtk_icon_helper_get_icon_size (priv->icon_helper); } /** @@ -1163,14 +1174,11 @@ gtk_image_get_icon_set (GtkImage *image, priv = image->priv; - g_return_if_fail (priv->storage_type == GTK_IMAGE_ICON_SET || - priv->storage_type == GTK_IMAGE_EMPTY); - if (icon_set) - *icon_set = priv->data.icon_set.icon_set; + *icon_set = _gtk_icon_helper_peek_icon_set (priv->icon_helper); if (size) - *size = priv->icon_size; + *size = _gtk_icon_helper_get_icon_size (priv->icon_helper); } /** @@ -1195,14 +1203,7 @@ gtk_image_get_animation (GtkImage *image) priv = image->priv; - g_return_val_if_fail (priv->storage_type == GTK_IMAGE_ANIMATION || - priv->storage_type == GTK_IMAGE_EMPTY, - NULL); - - if (priv->storage_type == GTK_IMAGE_EMPTY) - priv->data.anim.anim = NULL; - - return priv->data.anim.anim; + return _gtk_icon_helper_peek_animation (priv->icon_helper); } /** @@ -1222,9 +1223,9 @@ gtk_image_get_animation (GtkImage *image) * Since: 2.6 **/ void -gtk_image_get_icon_name (GtkImage *image, - G_CONST_RETURN gchar **icon_name, - GtkIconSize *size) +gtk_image_get_icon_name (GtkImage *image, + const gchar **icon_name, + GtkIconSize *size) { GtkImagePrivate *priv; @@ -1232,17 +1233,11 @@ gtk_image_get_icon_name (GtkImage *image, priv = image->priv; - g_return_if_fail (priv->storage_type == GTK_IMAGE_ICON_NAME || - priv->storage_type == GTK_IMAGE_EMPTY); - - if (priv->storage_type == GTK_IMAGE_EMPTY) - priv->data.name.icon_name = NULL; - if (icon_name) - *icon_name = priv->data.name.icon_name; + *icon_name = _gtk_icon_helper_get_icon_name (priv->icon_helper); if (size) - *size = priv->icon_size; + *size = _gtk_icon_helper_get_icon_size (priv->icon_helper); } /** @@ -1272,17 +1267,11 @@ gtk_image_get_gicon (GtkImage *image, priv = image->priv; - g_return_if_fail (priv->storage_type == GTK_IMAGE_GICON || - priv->storage_type == GTK_IMAGE_EMPTY); - - if (priv->storage_type == GTK_IMAGE_EMPTY) - priv->data.gicon.icon = NULL; - if (gicon) - *gicon = priv->data.gicon.icon; + *gicon = _gtk_icon_helper_peek_gicon (priv->icon_helper); if (size) - *size = priv->icon_size; + *size = _gtk_icon_helper_get_icon_size (priv->icon_helper); } /** @@ -1303,21 +1292,16 @@ gtk_image_reset_anim_iter (GtkImage *image) { GtkImagePrivate *priv = image->priv; - if (priv->storage_type == GTK_IMAGE_ANIMATION) + if (gtk_image_get_storage_type (image) == GTK_IMAGE_ANIMATION) { /* Reset the animation */ - - if (priv->data.anim.frame_timeout) + if (priv->animation_timeout) { - g_source_remove (priv->data.anim.frame_timeout); - priv->data.anim.frame_timeout = 0; + g_source_remove (priv->animation_timeout); + priv->animation_timeout = 0; } - if (priv->data.anim.iter) - { - g_object_unref (priv->data.anim.iter); - priv->data.anim.iter = NULL; - } + g_clear_object (&priv->animation_iter); } } @@ -1344,229 +1328,69 @@ animation_timeout (gpointer data) GtkImagePrivate *priv = image->priv; int delay; - priv->data.anim.frame_timeout = 0; + priv->animation_timeout = 0; - gdk_pixbuf_animation_iter_advance (priv->data.anim.iter, NULL); + gdk_pixbuf_animation_iter_advance (priv->animation_iter, NULL); - delay = gdk_pixbuf_animation_iter_get_delay_time (priv->data.anim.iter); + delay = gdk_pixbuf_animation_iter_get_delay_time (priv->animation_iter); if (delay >= 0) { GtkWidget *widget = GTK_WIDGET (image); - priv->data.anim.frame_timeout = + priv->animation_timeout = gdk_threads_add_timeout (delay, animation_timeout, image); gtk_widget_queue_draw (widget); - - if (gtk_widget_is_drawable (widget)) - gdk_window_process_updates (gtk_widget_get_window (widget), TRUE); } return FALSE; } -static void -icon_theme_changed (GtkImage *image) +static GdkPixbuf * +get_animation_frame (GtkImage *image) { GtkImagePrivate *priv = image->priv; - if (priv->storage_type == GTK_IMAGE_ICON_NAME) + if (priv->animation_iter == NULL) { - if (priv->data.name.pixbuf) - g_object_unref (priv->data.name.pixbuf); - priv->data.name.pixbuf = NULL; + int delay; - gtk_widget_queue_draw (GTK_WIDGET (image)); - } - if (priv->storage_type == GTK_IMAGE_GICON) - { - if (priv->data.gicon.pixbuf) - g_object_unref (priv->data.gicon.pixbuf); - priv->data.gicon.pixbuf = NULL; + priv->animation_iter = + gdk_pixbuf_animation_get_iter (_gtk_icon_helper_peek_animation (priv->icon_helper), NULL); - gtk_widget_queue_draw (GTK_WIDGET (image)); + delay = gdk_pixbuf_animation_iter_get_delay_time (priv->animation_iter); + if (delay >= 0) + priv->animation_timeout = + gdk_threads_add_timeout (delay, animation_timeout, image); } -} -static void -ensure_pixbuf_for_icon_name (GtkImage *image, - GtkStateType state) -{ - GtkImagePrivate *priv = image->priv; - GdkScreen *screen; - GtkIconTheme *icon_theme; - GtkSettings *settings; - gint width, height; - gint *sizes, *s, dist; - GtkIconInfo *info; - GtkIconLookupFlags flags; - - g_return_if_fail (priv->storage_type == GTK_IMAGE_ICON_NAME); - - screen = gtk_widget_get_screen (GTK_WIDGET (image)); - icon_theme = gtk_icon_theme_get_for_screen (screen); - settings = gtk_settings_get_for_screen (screen); - flags = GTK_ICON_LOOKUP_USE_BUILTIN; - if (priv->data.name.pixbuf == NULL || - (priv->was_symbolic && priv->last_rendered_state != state)) - { - priv->last_rendered_state = state; - if (priv->data.name.pixbuf) - { - g_object_unref (priv->data.name.pixbuf); - priv->data.name.pixbuf = NULL; - } - if (priv->pixel_size != -1) - { - width = height = priv->pixel_size; - flags |= GTK_ICON_LOOKUP_FORCE_SIZE; - } - else if (!gtk_icon_size_lookup_for_settings (settings, - priv->icon_size, - &width, &height)) - { - if (priv->icon_size == -1) - { - /* Find an available size close to 48 */ - sizes = gtk_icon_theme_get_icon_sizes (icon_theme, priv->data.name.icon_name); - dist = 100; - width = height = 48; - for (s = sizes; *s; s++) - { - if (*s == -1) - { - width = height = 48; - break; - } - if (*s < 48) - { - if (48 - *s < dist) - { - width = height = *s; - dist = 48 - *s; - } - } - else - { - if (*s - 48 < dist) - { - width = height = *s; - dist = *s - 48; - } - } - } - g_free (sizes); - } - else - { - g_warning ("Invalid icon size %d\n", priv->icon_size); - width = height = 24; - } - } - - info = gtk_icon_theme_lookup_icon (icon_theme, - priv->data.name.icon_name, - MIN (width, height), flags); - if (info) - { - GtkStyleContext *context; - gboolean was_symbolic; - - context = gtk_widget_get_style_context (GTK_WIDGET (image)); - priv->data.name.pixbuf = - gtk_icon_info_load_symbolic_for_context (info, - context, - &was_symbolic, - NULL); - priv->was_symbolic = was_symbolic; - gtk_icon_info_free (info); - } - - if (priv->data.name.pixbuf == NULL) - { - priv->data.name.pixbuf = - gtk_widget_render_icon (GTK_WIDGET (image), - GTK_STOCK_MISSING_IMAGE, - priv->icon_size, - NULL); - priv->was_symbolic = FALSE; - } - } + /* don't advance the anim iter here, or we could get frame changes between two + * exposes of different areas. + */ + return g_object_ref (gdk_pixbuf_animation_iter_get_pixbuf (priv->animation_iter)); } static void -ensure_pixbuf_for_gicon (GtkImage *image, - GtkStateType state) +gtk_image_get_preferred_size (GtkImage *image, + gint *width_out, + gint *height_out) { GtkImagePrivate *priv = image->priv; - GdkScreen *screen; - GtkIconTheme *icon_theme; - GtkSettings *settings; gint width, height; - GtkIconInfo *info; - GtkIconLookupFlags flags; + GtkBorder border; + GtkStyleContext *context; - g_return_if_fail (priv->storage_type == GTK_IMAGE_GICON); + context = gtk_widget_get_style_context (GTK_WIDGET (image)); + _gtk_icon_helper_get_size (priv->icon_helper, context, &width, &height); + _gtk_misc_get_padding_and_border (GTK_MISC (image), &border); - screen = gtk_widget_get_screen (GTK_WIDGET (image)); - icon_theme = gtk_icon_theme_get_for_screen (screen); - settings = gtk_settings_get_for_screen (screen); - flags = GTK_ICON_LOOKUP_USE_BUILTIN; - if (priv->data.gicon.pixbuf == NULL || - (priv->was_symbolic && priv->last_rendered_state != state)) - { - priv->last_rendered_state = state; - if (priv->data.gicon.pixbuf) - { - g_object_unref (priv->data.gicon.pixbuf); - priv->data.gicon.pixbuf = NULL; - } - if (priv->pixel_size != -1) - { - width = height = priv->pixel_size; - flags |= GTK_ICON_LOOKUP_FORCE_SIZE; - } - else if (!gtk_icon_size_lookup_for_settings (settings, - priv->icon_size, - &width, &height)) - { - if (priv->icon_size == -1) - width = height = 48; - else - { - g_warning ("Invalid icon size %d\n", priv->icon_size); - width = height = 24; - } - } - - info = gtk_icon_theme_lookup_by_gicon (icon_theme, - priv->data.gicon.icon, - MIN (width, height), flags); - if (info) - { - GtkStyleContext *context; - gboolean was_symbolic; - - context = gtk_widget_get_style_context (GTK_WIDGET (image)); - priv->data.gicon.pixbuf = - gtk_icon_info_load_symbolic_for_context (info, - context, - &was_symbolic, - NULL); - priv->was_symbolic = was_symbolic; - gtk_icon_info_free (info); - } + width += border.left + border.right; + height += border.top + border.bottom; - if (priv->data.gicon.pixbuf == NULL) - { - priv->data.gicon.pixbuf = - gtk_widget_render_icon (GTK_WIDGET (image), - GTK_STOCK_MISSING_IMAGE, - priv->icon_size, - NULL); - priv->was_symbolic = FALSE; - } - } + if (width_out) + *width_out = width; + if (height_out) + *height_out = height; } static gint @@ -1575,181 +1399,50 @@ gtk_image_draw (GtkWidget *widget, { GtkImage *image; GtkImagePrivate *priv; + GtkMisc *misc; + GtkStyleContext *context; + gint x, y, width, height; + gfloat xalign, yalign; + GtkBorder border; g_return_val_if_fail (GTK_IS_IMAGE (widget), FALSE); image = GTK_IMAGE (widget); + misc = GTK_MISC (image); priv = image->priv; - - if (priv->storage_type != GTK_IMAGE_EMPTY) - { - GtkMisc *misc; - gint x, y; - gint xpad, ypad; - gfloat xalign, yalign; - GdkPixbuf *pixbuf; - GtkStateType state; - gboolean needs_state_transform; - - misc = GTK_MISC (widget); - - /* For stock items and icon sets, we lazily calculate - * the size; we might get here between a queue_resize() - * and size_request() if something explicitely forces - * a redraw. - */ - if (priv->need_calc_size) - gtk_image_calc_size (image); - - gtk_misc_get_alignment (misc, &xalign, &yalign); - gtk_misc_get_padding (misc, &xpad, &ypad); - - if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR) - xalign = 1.0 - xalign; - - x = floor (xpad + ((gtk_widget_get_allocated_width (widget) - priv->required_width) * xalign)); - y = floor (ypad + ((gtk_widget_get_allocated_height (widget) - priv->required_height) * yalign)); - - needs_state_transform = gtk_widget_get_state (widget) != GTK_STATE_NORMAL; - - switch (priv->storage_type) - { - case GTK_IMAGE_PIXBUF: - pixbuf = priv->data.pixbuf.pixbuf; - g_object_ref (pixbuf); - break; - - case GTK_IMAGE_STOCK: - pixbuf = gtk_widget_render_icon (widget, - priv->data.stock.stock_id, - priv->icon_size, - NULL); - - /* already done */ - needs_state_transform = FALSE; - break; - - case GTK_IMAGE_ICON_SET: - pixbuf = - gtk_icon_set_render_icon (priv->data.icon_set.icon_set, - gtk_widget_get_style (widget), - gtk_widget_get_direction (widget), - gtk_widget_get_state (widget), - priv->icon_size, - widget, - NULL); - - /* already done */ - needs_state_transform = FALSE; - break; - - case GTK_IMAGE_ANIMATION: - { - if (priv->data.anim.iter == NULL) - { - priv->data.anim.iter = gdk_pixbuf_animation_get_iter (priv->data.anim.anim, NULL); - - if (gdk_pixbuf_animation_iter_get_delay_time (priv->data.anim.iter) >= 0) - priv->data.anim.frame_timeout = - gdk_threads_add_timeout (gdk_pixbuf_animation_iter_get_delay_time (priv->data.anim.iter), - animation_timeout, - image); - } - - /* don't advance the anim iter here, or we could get frame changes between two - * exposes of different areas. - */ - - pixbuf = gdk_pixbuf_animation_iter_get_pixbuf (priv->data.anim.iter); - g_object_ref (pixbuf); - } - break; - - case GTK_IMAGE_ICON_NAME: - state = gtk_widget_get_state (widget); - if (state == GTK_STATE_INSENSITIVE) - { - ensure_pixbuf_for_icon_name (image, GTK_STATE_NORMAL); - } - else - { - ensure_pixbuf_for_icon_name (image, state); - /* Already done by the loading function? */ - if (priv->was_symbolic) - needs_state_transform = FALSE; - } - pixbuf = priv->data.name.pixbuf; - if (pixbuf) - { - g_object_ref (pixbuf); - } - break; - - case GTK_IMAGE_GICON: - state = gtk_widget_get_state (widget); - if (state == GTK_STATE_INSENSITIVE) - { - ensure_pixbuf_for_gicon (image, GTK_STATE_NORMAL); - } - else - { - ensure_pixbuf_for_gicon (image, state); - /* Already done by the loading function? */ - if (priv->was_symbolic) - needs_state_transform = FALSE; - } - pixbuf = priv->data.gicon.pixbuf; - if (pixbuf) - { - g_object_ref (pixbuf); - } - break; - - case GTK_IMAGE_EMPTY: - default: - g_assert_not_reached (); - pixbuf = NULL; - break; - } + context = gtk_widget_get_style_context (widget); - if (pixbuf) - { - if (needs_state_transform) - { - GtkIconSource *source; - GdkPixbuf *rendered; - - source = gtk_icon_source_new (); - gtk_icon_source_set_pixbuf (source, pixbuf); - /* The size here is arbitrary; since size isn't - * wildcarded in the souce, 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_style_render_icon (gtk_widget_get_style (widget), - source, - gtk_widget_get_direction (widget), - gtk_widget_get_state (widget), - /* arbitrary */ - (GtkIconSize)-1, - widget, - "gtk-image"); - - gtk_icon_source_free (source); - - g_object_unref (pixbuf); - pixbuf = rendered; - } - - gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y); - cairo_paint (cr); - - g_object_unref (pixbuf); - } + gtk_render_background (context, cr, + 0, 0, + gtk_widget_get_allocated_width (widget), gtk_widget_get_allocated_height (widget)); + gtk_render_frame (context, cr, + 0, 0, + gtk_widget_get_allocated_width (widget), gtk_widget_get_allocated_height (widget)); + + gtk_misc_get_alignment (misc, &xalign, &yalign); + gtk_image_get_preferred_size (image, &width, &height); + _gtk_misc_get_padding_and_border (misc, &border); + + if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR) + xalign = 1.0 - xalign; + + x = floor ((gtk_widget_get_allocated_width (widget) - width) * xalign) + border.left; + y = floor ((gtk_widget_get_allocated_height (widget) - height) * yalign) + border.top; + + if (gtk_image_get_storage_type (image) == GTK_IMAGE_ANIMATION) + { + GdkPixbuf *pixbuf = get_animation_frame (image); + gtk_render_icon (context, cr, + pixbuf, + x, y); + g_object_unref (pixbuf); + } + else + { + _gtk_icon_helper_draw (priv->icon_helper, + context, cr, + x, y); } return FALSE; @@ -1759,85 +1452,40 @@ static void gtk_image_reset (GtkImage *image) { GtkImagePrivate *priv = image->priv; + GtkImageType storage_type; g_object_freeze_notify (G_OBJECT (image)); + storage_type = gtk_image_get_storage_type (image); - if (priv->storage_type != GTK_IMAGE_EMPTY) + if (storage_type != GTK_IMAGE_EMPTY) g_object_notify (G_OBJECT (image), "storage-type"); - if (priv->icon_size != DEFAULT_ICON_SIZE) - { - priv->icon_size = DEFAULT_ICON_SIZE; - g_object_notify (G_OBJECT (image), "icon-size"); - } + g_object_notify (G_OBJECT (image), "icon-size"); - switch (priv->storage_type) + switch (storage_type) { - case GTK_IMAGE_PIXBUF: - - if (priv->data.pixbuf.pixbuf) - g_object_unref (priv->data.pixbuf.pixbuf); - g_object_notify (G_OBJECT (image), "pixbuf"); - break; - case GTK_IMAGE_STOCK: - - g_free (priv->data.stock.stock_id); - - priv->data.stock.stock_id = NULL; - g_object_notify (G_OBJECT (image), "stock"); break; - case GTK_IMAGE_ICON_SET: - if (priv->data.icon_set.icon_set) - gtk_icon_set_unref (priv->data.icon_set.icon_set); - priv->data.icon_set.icon_set = NULL; - g_object_notify (G_OBJECT (image), "icon-set"); break; - case GTK_IMAGE_ANIMATION: - gtk_image_reset_anim_iter (image); - - if (priv->data.anim.anim) - g_object_unref (priv->data.anim.anim); - priv->data.anim.anim = NULL; - + gtk_image_reset_anim_iter (image); g_object_notify (G_OBJECT (image), "pixbuf-animation"); - break; - case GTK_IMAGE_ICON_NAME: - g_free (priv->data.name.icon_name); - priv->data.name.icon_name = NULL; - if (priv->data.name.pixbuf) - g_object_unref (priv->data.name.pixbuf); - priv->data.name.pixbuf = NULL; - g_object_notify (G_OBJECT (image), "icon-name"); - break; - case GTK_IMAGE_GICON: - if (priv->data.gicon.icon) - g_object_unref (priv->data.gicon.icon); - priv->data.gicon.icon = NULL; - if (priv->data.gicon.pixbuf) - g_object_unref (priv->data.gicon.pixbuf); - priv->data.gicon.pixbuf = NULL; - g_object_notify (G_OBJECT (image), "gicon"); - break; - case GTK_IMAGE_EMPTY: default: break; - } if (priv->filename) @@ -1847,9 +1495,14 @@ gtk_image_reset (GtkImage *image) g_object_notify (G_OBJECT (image), "file"); } - priv->storage_type = GTK_IMAGE_EMPTY; + if (priv->resource_path) + { + g_free (priv->resource_path); + priv->resource_path = NULL; + g_object_notify (G_OBJECT (image), "resource"); + } - memset (&priv->data, '\0', sizeof (priv->data)); + _gtk_icon_helper_clear (priv->icon_helper); g_object_thaw_notify (G_OBJECT (image)); } @@ -1865,72 +1518,10 @@ gtk_image_reset (GtkImage *image) void gtk_image_clear (GtkImage *image) { - GtkImagePrivate *priv = image->priv; - - priv->need_calc_size = 1; - gtk_image_reset (image); - gtk_image_update_size (image, 0, 0); -} - -static void -gtk_image_calc_size (GtkImage *image) -{ - GtkWidget *widget = GTK_WIDGET (image); - GtkImagePrivate *priv = image->priv; - GdkPixbuf *pixbuf = NULL; - - priv->need_calc_size = 0; - - /* We update stock/icon set on every size request, because - * the theme could have affected the size; for other kinds of - * image, we just update the required width/height when the image data - * is set. - */ - switch (priv->storage_type) - { - case GTK_IMAGE_STOCK: - pixbuf = gtk_widget_render_icon (widget, - priv->data.stock.stock_id, - priv->icon_size, - NULL); - break; - - case GTK_IMAGE_ICON_SET: - pixbuf = gtk_icon_set_render_icon (priv->data.icon_set.icon_set, - gtk_widget_get_style (widget), - gtk_widget_get_direction (widget), - gtk_widget_get_state (widget), - priv->icon_size, - widget, - NULL); - break; - case GTK_IMAGE_ICON_NAME: - ensure_pixbuf_for_icon_name (image, GTK_STATE_NORMAL); - pixbuf = priv->data.name.pixbuf; - if (pixbuf) g_object_ref (pixbuf); - break; - case GTK_IMAGE_GICON: - ensure_pixbuf_for_gicon (image, GTK_STATE_NORMAL); - pixbuf = priv->data.gicon.pixbuf; - if (pixbuf) - g_object_ref (pixbuf); - break; - default: - break; - } - if (pixbuf) - { - gint xpad, ypad; - - gtk_misc_get_padding (GTK_MISC (image), &xpad, &ypad); - - priv->required_width = gdk_pixbuf_get_width (pixbuf) + xpad * 2; - priv->required_height = gdk_pixbuf_get_height (pixbuf) + ypad * 2; - - g_object_unref (pixbuf); - } + if (gtk_widget_get_visible (GTK_WIDGET (image))) + gtk_widget_queue_resize (GTK_WIDGET (image)); } static void @@ -1938,44 +1529,38 @@ gtk_image_get_preferred_width (GtkWidget *widget, gint *minimum, gint *natural) { - GtkImage *image; - GtkImagePrivate *priv; + gint width; - image = GTK_IMAGE (widget); - priv = image->priv; - - gtk_image_calc_size (image); - - *minimum = *natural = priv->required_width; -} + gtk_image_get_preferred_size (GTK_IMAGE (widget), &width, NULL); + *minimum = *natural = width; +} static void gtk_image_get_preferred_height (GtkWidget *widget, gint *minimum, gint *natural) { - GtkImage *image; - GtkImagePrivate *priv; - - image = GTK_IMAGE (widget); - priv = image->priv; + gint height; - gtk_image_calc_size (image); - - *minimum = *natural = priv->required_height; + gtk_image_get_preferred_size (GTK_IMAGE (widget), NULL, &height); + *minimum = *natural = height; } static void -gtk_image_style_set (GtkWidget *widget, - GtkStyle *prev_style) +icon_theme_changed (GtkImage *image) { - GtkImage *image; + GtkImagePrivate *priv = image->priv; - image = GTK_IMAGE (widget); + _gtk_icon_helper_invalidate (priv->icon_helper); + gtk_widget_queue_draw (GTK_WIDGET (image)); +} - GTK_WIDGET_CLASS (gtk_image_parent_class)->style_set (widget, prev_style); +static void +gtk_image_style_updated (GtkWidget *widget) +{ + GTK_WIDGET_CLASS (gtk_image_parent_class)->style_updated (widget); - icon_theme_changed (image); + icon_theme_changed (GTK_IMAGE (widget)); } static void @@ -1992,26 +1577,21 @@ gtk_image_screen_changed (GtkWidget *widget, icon_theme_changed (image); } - -static void -gtk_image_update_size (GtkImage *image, - gint image_width, - gint image_height) +void +_gtk_image_gicon_data_clear (GtkImageGIconData *data) { - GtkWidget *widget = GTK_WIDGET (image); - GtkImagePrivate *priv = image->priv; - gint xpad, ypad; - - gtk_misc_get_padding (GTK_MISC (image), &xpad, &ypad); - - priv->required_width = image_width + xpad * 2; - priv->required_height = image_height + ypad * 2; - - if (gtk_widget_get_visible (widget)) - gtk_widget_queue_resize (widget); + if (data->pixbuf) + { + g_object_unref (data->pixbuf); + data->pixbuf = NULL; + } + if (data->icon) + { + g_object_unref (data->icon); + data->icon = NULL; + } } - /** * gtk_image_set_pixel_size: * @image: a #GtkImage @@ -2028,37 +1608,20 @@ gtk_image_set_pixel_size (GtkImage *image, gint pixel_size) { GtkImagePrivate *priv; + gint old_pixel_size; g_return_if_fail (GTK_IS_IMAGE (image)); priv = image->priv; + old_pixel_size = gtk_image_get_pixel_size (image); - if (priv->pixel_size != pixel_size) + if (pixel_size != old_pixel_size) { - priv->pixel_size = pixel_size; - - if (priv->storage_type == GTK_IMAGE_ICON_NAME) - { - if (priv->data.name.pixbuf) - { - g_object_unref (priv->data.name.pixbuf); - priv->data.name.pixbuf = NULL; - } - - gtk_image_update_size (image, pixel_size, pixel_size); - } - - if (priv->storage_type == GTK_IMAGE_GICON) - { - if (priv->data.gicon.pixbuf) - { - g_object_unref (priv->data.gicon.pixbuf); - priv->data.gicon.pixbuf = NULL; - } - - gtk_image_update_size (image, pixel_size, pixel_size); - } - + _gtk_icon_helper_set_pixel_size (priv->icon_helper, pixel_size); + + if (gtk_widget_get_visible (GTK_WIDGET (image))) + gtk_widget_queue_resize (GTK_WIDGET (image)); + g_object_notify (G_OBJECT (image), "pixel-size"); } } @@ -2078,5 +1641,5 @@ gtk_image_get_pixel_size (GtkImage *image) { g_return_val_if_fail (GTK_IS_IMAGE (image), -1); - return image->priv->pixel_size; + return _gtk_icon_helper_get_pixel_size (image->priv->icon_helper); }