/* LIBGIMP - The GIMP Library * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball * * gimppreview.c * * 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 3 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 * . */ #include "config.h" #include #include #include "libgimpbase/gimpbase.h" #include "libgimpmath/gimpmath.h" #include "gimpwidgets.h" #include "gimppreview.h" #include "libgimp/libgimp-intl.h" /** * SECTION: gimppreview * @title: GimpPreview * @short_description: A widget providing a #GimpPreviewArea plus * framework to update the preview. * * A widget providing a #GimpPreviewArea plus framework to update the * preview. **/ #define DEFAULT_SIZE 200 #define PREVIEW_TIMEOUT 200 enum { INVALIDATED, LAST_SIGNAL }; enum { PROP_0, PROP_UPDATE }; typedef struct _GimpPreviewPrivate { GtkWidget *area; GtkWidget *grid; GtkWidget *frame; GtkWidget *toggle; GtkWidget *controls; GdkCursor *cursor_busy; GdkCursor *default_cursor; gint xoff, yoff; gint xmin, xmax, ymin, ymax; gint width, height; gboolean update_preview; guint timeout_id; } GimpPreviewPrivate; static void gimp_preview_dispose (GObject *object); static void gimp_preview_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void gimp_preview_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void gimp_preview_direction_changed (GtkWidget *widget, GtkTextDirection prev_dir); static gboolean gimp_preview_popup_menu (GtkWidget *widget); static void gimp_preview_area_realize (GtkWidget *widget, GimpPreview *preview); static void gimp_preview_area_unrealize (GtkWidget *widget, GimpPreview *preview); static void gimp_preview_area_size_allocate (GtkWidget *widget, GtkAllocation *allocation, GimpPreview *preview); static void gimp_preview_area_set_cursor (GimpPreview *preview); static gboolean gimp_preview_area_event (GtkWidget *area, GdkEvent *event, GimpPreview *preview); static void gimp_preview_toggle_callback (GtkWidget *toggle, GimpPreview *preview); static void gimp_preview_notify_checks (GimpPreview *preview); static gboolean gimp_preview_invalidate_now (GimpPreview *preview); static void gimp_preview_real_set_cursor (GimpPreview *preview); static void gimp_preview_real_transform (GimpPreview *preview, gint src_x, gint src_y, gint *dest_x, gint *dest_y); static void gimp_preview_real_untransform (GimpPreview *preview, gint src_x, gint src_y, gint *dest_x, gint *dest_y); G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GimpPreview, gimp_preview, GTK_TYPE_BOX) #define parent_class gimp_preview_parent_class static guint preview_signals[LAST_SIGNAL] = { 0 }; static void gimp_preview_class_init (GimpPreviewClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); parent_class = g_type_class_peek_parent (klass); preview_signals[INVALIDATED] = g_signal_new ("invalidated", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpPreviewClass, invalidated), NULL, NULL, NULL, G_TYPE_NONE, 0); object_class->dispose = gimp_preview_dispose; object_class->get_property = gimp_preview_get_property; object_class->set_property = gimp_preview_set_property; widget_class->direction_changed = gimp_preview_direction_changed; widget_class->popup_menu = gimp_preview_popup_menu; klass->draw = NULL; klass->draw_thumb = NULL; klass->draw_buffer = NULL; klass->set_cursor = gimp_preview_real_set_cursor; klass->transform = gimp_preview_real_transform; klass->untransform = gimp_preview_real_untransform; g_object_class_install_property (object_class, PROP_UPDATE, g_param_spec_boolean ("update", "Update", "Whether the preview should update automatically", TRUE, GIMP_PARAM_READWRITE | G_PARAM_CONSTRUCT)); gtk_widget_class_install_style_property (widget_class, g_param_spec_int ("size", "Size", "The preview's size", 1, 1024, DEFAULT_SIZE, GIMP_PARAM_READABLE)); } static void gimp_preview_init (GimpPreview *preview) { GimpPreviewPrivate *priv; GtkWidget *frame; gdouble xalign = 0.0; priv = gimp_preview_get_instance_private (preview); gtk_orientable_set_orientation (GTK_ORIENTABLE (preview), GTK_ORIENTATION_VERTICAL); gtk_box_set_homogeneous (GTK_BOX (preview), FALSE); gtk_box_set_spacing (GTK_BOX (preview), 6); if (gtk_widget_get_direction (GTK_WIDGET (preview)) == GTK_TEXT_DIR_RTL) xalign = 1.0; priv->frame = gtk_aspect_frame_new (NULL, xalign, 0.0, 1.0, TRUE); gtk_frame_set_shadow_type (GTK_FRAME (priv->frame), GTK_SHADOW_NONE); gtk_box_pack_start (GTK_BOX (preview), priv->frame, TRUE, TRUE, 0); gtk_widget_show (priv->frame); priv->grid = gtk_grid_new (); gtk_container_add (GTK_CONTAINER (priv->frame), priv->grid); gtk_widget_show (priv->grid); priv->timeout_id = 0; priv->xmin = priv->ymin = 0; priv->xmax = priv->ymax = 1; priv->width = priv->xmax - priv->xmin; priv->height = priv->ymax - priv->ymin; priv->xoff = 0; priv->yoff = 0; priv->default_cursor = NULL; /* preview area */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); gtk_widget_set_hexpand (frame, TRUE); gtk_widget_set_vexpand (frame, TRUE); gtk_grid_attach (GTK_GRID (priv->grid), frame, 0, 0, 1, 1); gtk_widget_show (frame); priv->area = gimp_preview_area_new (); gtk_container_add (GTK_CONTAINER (frame), priv->area); gtk_widget_show (priv->area); g_signal_connect_swapped (priv->area, "notify::check-size", G_CALLBACK (gimp_preview_notify_checks), preview); g_signal_connect_swapped (priv->area, "notify::check-type", G_CALLBACK (gimp_preview_notify_checks), preview); g_signal_connect_swapped (priv->area, "notify::check-custom-color1", G_CALLBACK (gimp_preview_notify_checks), preview); g_signal_connect_swapped (priv->area, "notify::check-custom-color2", G_CALLBACK (gimp_preview_notify_checks), preview); gtk_widget_add_events (priv->area, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK); g_signal_connect (priv->area, "event", G_CALLBACK (gimp_preview_area_event), preview); g_signal_connect (priv->area, "realize", G_CALLBACK (gimp_preview_area_realize), preview); g_signal_connect (priv->area, "unrealize", G_CALLBACK (gimp_preview_area_unrealize), preview); g_signal_connect_data (priv->area, "realize", G_CALLBACK (gimp_preview_area_set_cursor), preview, NULL, G_CONNECT_AFTER | G_CONNECT_SWAPPED); g_signal_connect (priv->area, "size-allocate", G_CALLBACK (gimp_preview_area_size_allocate), preview); g_signal_connect_data (priv->area, "size-allocate", G_CALLBACK (gimp_preview_area_set_cursor), preview, NULL, G_CONNECT_AFTER | G_CONNECT_SWAPPED); priv->controls = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_widget_set_margin_top (priv->controls, 3); gtk_grid_attach (GTK_GRID (priv->grid), priv->controls, 0, 2, 2, 1); gtk_widget_show (priv->controls); /* toggle button to (de)activate the instant preview */ priv->toggle = gtk_check_button_new_with_mnemonic (_("_Preview")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->toggle), priv->update_preview); gtk_box_pack_start (GTK_BOX (priv->controls), priv->toggle, TRUE, TRUE, 0); gtk_widget_show (priv->toggle); g_signal_connect (priv->toggle, "toggled", G_CALLBACK (gimp_preview_toggle_callback), preview); } static void gimp_preview_dispose (GObject *object) { GimpPreview *preview = GIMP_PREVIEW (object); GimpPreviewPrivate *priv = gimp_preview_get_instance_private (preview); if (priv->timeout_id) { g_source_remove (priv->timeout_id); priv->timeout_id = 0; } G_OBJECT_CLASS (parent_class)->dispose (object); } static void gimp_preview_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GimpPreview *preview = GIMP_PREVIEW (object); GimpPreviewPrivate *priv = gimp_preview_get_instance_private (preview); switch (property_id) { case PROP_UPDATE: g_value_set_boolean (value, priv->update_preview); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gimp_preview_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GimpPreview *preview = GIMP_PREVIEW (object); GimpPreviewPrivate *priv = gimp_preview_get_instance_private (preview); switch (property_id) { case PROP_UPDATE: gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->toggle), g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gimp_preview_direction_changed (GtkWidget *widget, GtkTextDirection prev_dir) { GimpPreview *preview = GIMP_PREVIEW (widget); GimpPreviewPrivate *priv = gimp_preview_get_instance_private (preview); gdouble xalign = 0.0; if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) xalign = 1.0; gtk_aspect_frame_set (GTK_ASPECT_FRAME (priv->frame), xalign, 0.0, 1.0, TRUE); } static gboolean gimp_preview_popup_menu (GtkWidget *widget) { GimpPreview *preview = GIMP_PREVIEW (widget); GimpPreviewPrivate *priv = gimp_preview_get_instance_private (preview); gimp_preview_area_menu_popup (GIMP_PREVIEW_AREA (priv->area), NULL); return TRUE; } static void gimp_preview_area_realize (GtkWidget *widget, GimpPreview *preview) { GimpPreviewPrivate *priv = gimp_preview_get_instance_private (preview); GdkDisplay *display = gtk_widget_get_display (widget); g_return_if_fail (priv->cursor_busy == NULL); priv->cursor_busy = gdk_cursor_new_for_display (display, GDK_WATCH); } static void gimp_preview_area_unrealize (GtkWidget *widget, GimpPreview *preview) { GimpPreviewPrivate *priv = gimp_preview_get_instance_private (preview); g_clear_object (&priv->cursor_busy); } static void gimp_preview_area_size_allocate (GtkWidget *widget, GtkAllocation *allocation, GimpPreview *preview) { GimpPreviewPrivate *priv = gimp_preview_get_instance_private (preview); gint width = priv->xmax - priv->xmin; gint height = priv->ymax - priv->ymin; priv->width = MIN (width, allocation->width); priv->height = MIN (height, allocation->height); gimp_preview_draw (preview); gimp_preview_invalidate (preview); } static void gimp_preview_area_set_cursor (GimpPreview *preview) { GIMP_PREVIEW_GET_CLASS (preview)->set_cursor (preview); } static gboolean gimp_preview_area_event (GtkWidget *area, GdkEvent *event, GimpPreview *preview) { GdkEventButton *button_event = (GdkEventButton *) event; switch (event->type) { case GDK_BUTTON_PRESS: switch (button_event->button) { case 3: gimp_preview_area_menu_popup (GIMP_PREVIEW_AREA (area), button_event); return TRUE; } break; default: break; } return FALSE; } static void gimp_preview_toggle_callback (GtkWidget *toggle, GimpPreview *preview) { GimpPreviewPrivate *priv = gimp_preview_get_instance_private (preview); if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle))) { priv->update_preview = TRUE; g_object_notify (G_OBJECT (preview), "update"); if (priv->timeout_id) g_source_remove (priv->timeout_id); gimp_preview_invalidate_now (preview); } else { priv->update_preview = FALSE; g_object_notify (G_OBJECT (preview), "update"); gimp_preview_draw (preview); } } static void gimp_preview_notify_checks (GimpPreview *preview) { gimp_preview_draw (preview); gimp_preview_invalidate (preview); } static gboolean gimp_preview_invalidate_now (GimpPreview *preview) { GimpPreviewPrivate *priv = gimp_preview_get_instance_private (preview); GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (preview)); GimpPreviewClass *class = GIMP_PREVIEW_GET_CLASS (preview); gimp_preview_draw (preview); priv->timeout_id = 0; if (toplevel && gtk_widget_get_realized (toplevel)) { gdk_window_set_cursor (gtk_widget_get_window (toplevel), priv->cursor_busy); gdk_window_set_cursor (gtk_widget_get_window (priv->area), priv->cursor_busy); gdk_display_flush (gtk_widget_get_display (toplevel)); g_signal_emit (preview, preview_signals[INVALIDATED], 0); class->set_cursor (preview); gdk_window_set_cursor (gtk_widget_get_window (toplevel), NULL); } else { g_signal_emit (preview, preview_signals[INVALIDATED], 0); } return FALSE; } static void gimp_preview_real_set_cursor (GimpPreview *preview) { GimpPreviewPrivate *priv = gimp_preview_get_instance_private (preview); if (gtk_widget_get_realized (priv->area)) gdk_window_set_cursor (gtk_widget_get_window (priv->area), priv->default_cursor); } static void gimp_preview_real_transform (GimpPreview *preview, gint src_x, gint src_y, gint *dest_x, gint *dest_y) { GimpPreviewPrivate *priv = gimp_preview_get_instance_private (preview); *dest_x = src_x - priv->xoff - priv->xmin; *dest_y = src_y - priv->yoff - priv->ymin; } static void gimp_preview_real_untransform (GimpPreview *preview, gint src_x, gint src_y, gint *dest_x, gint *dest_y) { GimpPreviewPrivate *priv = gimp_preview_get_instance_private (preview); *dest_x = src_x + priv->xoff + priv->xmin; *dest_y = src_y + priv->yoff + priv->ymin; } /** * gimp_preview_set_update: * @preview: a #GimpPreview widget * @update: %TRUE if the preview should invalidate itself when being * scrolled or when gimp_preview_invalidate() is being called * * Sets the state of the "Preview" check button. * * Since: 2.2 **/ void gimp_preview_set_update (GimpPreview *preview, gboolean update) { g_return_if_fail (GIMP_IS_PREVIEW (preview)); g_object_set (preview, "update", update, NULL); } /** * gimp_preview_get_update: * @preview: a #GimpPreview widget * * Returns: the state of the "Preview" check button. * * Since: 2.2 **/ gboolean gimp_preview_get_update (GimpPreview *preview) { GimpPreviewPrivate *priv; g_return_val_if_fail (GIMP_IS_PREVIEW (preview), FALSE); priv = gimp_preview_get_instance_private (preview); return priv->update_preview; } /** * gimp_preview_set_bounds: * @preview: a #GimpPreview widget * @xmin: the minimum X value * @ymin: the minimum Y value * @xmax: the maximum X value * @ymax: the maximum Y value * * Sets the lower and upper limits for the previewed area. The * difference between the upper and lower value is used to set the * maximum size of the #GimpPreviewArea used in the @preview. * * Since: 2.2 **/ void gimp_preview_set_bounds (GimpPreview *preview, gint xmin, gint ymin, gint xmax, gint ymax) { GimpPreviewPrivate *priv; g_return_if_fail (GIMP_IS_PREVIEW (preview)); g_return_if_fail (xmax > xmin); g_return_if_fail (ymax > ymin); priv = gimp_preview_get_instance_private (preview); priv->xmin = xmin; priv->ymin = ymin; priv->xmax = xmax; priv->ymax = ymax; gimp_preview_area_set_max_size (GIMP_PREVIEW_AREA (priv->area), xmax - xmin, ymax - ymin); } /** * gimp_preview_get_bounds: * @preview: A #GimpPreview: * @xmin: (out) (optional): Lower bound for x * @ymin: (out) (optional): Lower bound for y * @xmax: (out) (optional): Higher bound for x * @ymax: (out) (optional): Higher bound for y */ void gimp_preview_get_bounds (GimpPreview *preview, gint *xmin, gint *ymin, gint *xmax, gint *ymax) { GimpPreviewPrivate *priv; g_return_if_fail (GIMP_IS_PREVIEW (preview)); priv = gimp_preview_get_instance_private (preview); if (xmin) *xmin = priv->xmin; if (ymin) *ymin = priv->ymin; if (xmax) *xmax = priv->xmax; if (ymax) *ymax = priv->ymax; } void gimp_preview_set_size (GimpPreview *preview, gint width, gint height) { GimpPreviewPrivate *priv; g_return_if_fail (GIMP_IS_PREVIEW (preview)); priv = gimp_preview_get_instance_private (preview); priv->width = width; priv->height = height; gtk_widget_set_size_request (priv->area, width, height); } /** * gimp_preview_get_size: * @preview: a #GimpPreview widget * @width: (out) (optional): return location for the preview area width * @height: (out) (optional): return location for the preview area height * * Since: 2.2 **/ void gimp_preview_get_size (GimpPreview *preview, gint *width, gint *height) { GimpPreviewPrivate *priv; g_return_if_fail (GIMP_IS_PREVIEW (preview)); priv = gimp_preview_get_instance_private (preview); if (width) *width = priv->width; if (height) *height = priv->height; } void gimp_preview_set_offsets (GimpPreview *preview, gint xoff, gint yoff) { GimpPreviewPrivate *priv; g_return_if_fail (GIMP_IS_PREVIEW (preview)); priv = gimp_preview_get_instance_private (preview); priv->xoff = xoff; priv->yoff = yoff; } /** * gimp_preview_get_offsets: * @preview: A #GimpPreview: * @xoff: (out) (optional): X offset * @yoff: (out) (optional): Y offset */ void gimp_preview_get_offsets (GimpPreview *preview, gint *xoff, gint *yoff) { GimpPreviewPrivate *priv; g_return_if_fail (GIMP_IS_PREVIEW (preview)); priv = gimp_preview_get_instance_private (preview); if (xoff) *xoff = priv->xoff; if (yoff) *yoff = priv->yoff; } /** * gimp_preview_get_position: * @preview: a #GimpPreview widget * @x: (out) (optional): return location for the horizontal offset * @y: (out) (optional): return location for the vertical offset * * Since: 2.2 **/ void gimp_preview_get_position (GimpPreview *preview, gint *x, gint *y) { GimpPreviewPrivate *priv; g_return_if_fail (GIMP_IS_PREVIEW (preview)); priv = gimp_preview_get_instance_private (preview); if (x) *x = priv->xoff + priv->xmin; if (y) *y = priv->yoff + priv->ymin; } /** * gimp_preview_transform: * @preview: a #GimpPreview widget * @src_x: horizontal position on the previewed image * @src_y: vertical position on the previewed image * @dest_x: (out): returns the transformed horizontal position * @dest_y: (out): returns the transformed vertical position * * Transforms from image to widget coordinates. * * Since: 2.4 **/ void gimp_preview_transform (GimpPreview *preview, gint src_x, gint src_y, gint *dest_x, gint *dest_y) { g_return_if_fail (GIMP_IS_PREVIEW (preview)); g_return_if_fail (dest_x != NULL && dest_y != NULL); GIMP_PREVIEW_GET_CLASS (preview)->transform (preview, src_x, src_y, dest_x, dest_y); } /** * gimp_preview_untransform: * @preview: a #GimpPreview widget * @src_x: horizontal position relative to the preview area's origin * @src_y: vertical position relative to preview area's origin * @dest_x: (out): returns the untransformed horizontal position * @dest_y: (out): returns the untransformed vertical position * * Transforms from widget to image coordinates. * * Since: 2.4 **/ void gimp_preview_untransform (GimpPreview *preview, gint src_x, gint src_y, gint *dest_x, gint *dest_y) { g_return_if_fail (GIMP_IS_PREVIEW (preview)); g_return_if_fail (dest_x != NULL && dest_y != NULL); GIMP_PREVIEW_GET_CLASS (preview)->untransform (preview, src_x, src_y, dest_x, dest_y); } /** * gimp_preview_get_frame: * @preview: a #GimpPreview widget * * Returns: (transfer none) (type GtkAspectFrame): a pointer to the * #GtkAspectFrame used in the @preview. * * Since: 3.0 **/ GtkWidget * gimp_preview_get_frame (GimpPreview *preview) { GimpPreviewPrivate *priv; g_return_val_if_fail (GIMP_IS_PREVIEW (preview), NULL); priv = gimp_preview_get_instance_private (preview); return priv->frame; } /** * gimp_preview_get_grid: * @preview: a #GimpPreview widget * * Returns: (transfer none) (type GtkGrid): a pointer to * the #GtkGrid used in the @preview. * * Since: 3.0 **/ GtkWidget * gimp_preview_get_grid (GimpPreview *preview) { GimpPreviewPrivate *priv; g_return_val_if_fail (GIMP_IS_PREVIEW (preview), NULL); priv = gimp_preview_get_instance_private (preview); return priv->grid; } /** * gimp_preview_get_area: * @preview: a #GimpPreview widget * * In most cases, you shouldn't need to access the #GimpPreviewArea * that is being used in the @preview. Sometimes however, you need to. * For example if you want to receive mouse events from the area. In * such cases, use gimp_preview_get_area(). * * Returns: (transfer none) (type GimpPreviewArea): a pointer to the * #GimpPreviewArea used in the @preview. * * Since: 2.4 **/ GtkWidget * gimp_preview_get_area (GimpPreview *preview) { GimpPreviewPrivate *priv; g_return_val_if_fail (GIMP_IS_PREVIEW (preview), NULL); priv = gimp_preview_get_instance_private (preview); return priv->area; } /** * gimp_preview_draw: * @preview: a #GimpPreview widget * * Calls the GimpPreview::draw method. GimpPreview itself doesn't * implement a default draw method so the behavior is determined by * the derived class implementing this method. * * #GimpDrawablePreview implements gimp_preview_draw() by drawing the * original, unmodified drawable to the @preview. * * Since: 2.2 **/ void gimp_preview_draw (GimpPreview *preview) { GimpPreviewClass *class = GIMP_PREVIEW_GET_CLASS (preview); if (class->draw) class->draw (preview); } /** * gimp_preview_draw_buffer: * @preview: a #GimpPreview widget * @buffer: (array): a pixel buffer the size of the preview * @rowstride: the @buffer's rowstride * * Calls the GimpPreview::draw_buffer method. GimpPreview itself * doesn't implement this method so the behavior is determined by the * derived class implementing this method. * * Since: 2.2 **/ void gimp_preview_draw_buffer (GimpPreview *preview, const guchar *buffer, gint rowstride) { GimpPreviewClass *class = GIMP_PREVIEW_GET_CLASS (preview); if (class->draw_buffer) class->draw_buffer (preview, buffer, rowstride); } /** * gimp_preview_invalidate: * @preview: a #GimpPreview widget * * This function starts or renews a short low-priority timeout. When * the timeout expires, the GimpPreview::invalidated signal is emitted * which will usually cause the @preview to be updated. * * This function does nothing unless the "Preview" button is checked. * * During the emission of the signal a busy cursor is set on the * toplevel window containing the @preview and on the preview area * itself. * * Since: 2.2 **/ void gimp_preview_invalidate (GimpPreview *preview) { GimpPreviewPrivate *priv; g_return_if_fail (GIMP_IS_PREVIEW (preview)); priv = gimp_preview_get_instance_private (preview); if (priv->update_preview) { if (priv->timeout_id) g_source_remove (priv->timeout_id); priv->timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, PREVIEW_TIMEOUT, (GSourceFunc) gimp_preview_invalidate_now, preview, NULL); } } /** * gimp_preview_set_default_cursor: * @preview: a #GimpPreview widget * @cursor: a #GdkCursor or %NULL * * Sets the default mouse cursor for the preview. Note that this will * be overridden by a %GDK_FLEUR if the preview has scrollbars, or by a * %GDK_WATCH when the preview is invalidated. * * Since: 2.2 **/ void gimp_preview_set_default_cursor (GimpPreview *preview, GdkCursor *cursor) { GimpPreviewPrivate *priv; g_return_if_fail (GIMP_IS_PREVIEW (preview)); priv = gimp_preview_get_instance_private (preview); g_set_object (&priv->default_cursor, cursor); } /** * gimp_preview_get_default_cursor: * @preview: a #GimpPreview widget * * See gimp_preview_set_default_cursor(): * * Returns: (transfer none): The @preview's default #GdkCursor. * * Since: 3.0 **/ GdkCursor * gimp_preview_get_default_cursor (GimpPreview *preview) { GimpPreviewPrivate *priv; g_return_val_if_fail (GIMP_IS_PREVIEW (preview), NULL); priv = gimp_preview_get_instance_private (preview); return priv->default_cursor; } /** * gimp_preview_get_controls: * @preview: a #GimpPreview widget * * Gives access to the horizontal #GtkBox at the bottom of the preview * that contains the update toggle. Derived widgets can use this function * if they need to add controls to this area. * * Returns: (transfer none) (type GtkBox): the horizontal #GtkBox at the * bottom of the preview. * * Since: 2.4 **/ GtkWidget * gimp_preview_get_controls (GimpPreview *preview) { GimpPreviewPrivate *priv; g_return_val_if_fail (GIMP_IS_PREVIEW (preview), NULL); priv = gimp_preview_get_instance_private (preview); return priv->controls; }