This also depends on the same settings to render brushes with theme colors, because this is data, not user interface. Therefore following theme colors is not necessarily what creators would expect.
854 lines
26 KiB
C
854 lines
26 KiB
C
/* GIMP - The GNU Image Manipulation Program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* gimpview.c
|
|
* Copyright (C) 2001-2006 Michael Natterer <mitch@gimp.org>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <gegl.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "libgimpcolor/gimpcolor.h"
|
|
#include "libgimpmath/gimpmath.h"
|
|
#include "libgimpbase/gimpbase.h"
|
|
#include "libgimpwidgets/gimpwidgets.h"
|
|
|
|
#include "widgets-types.h"
|
|
|
|
#include "core/gimpcontext.h"
|
|
#include "core/gimpmarshal.h"
|
|
#include "core/gimpviewable.h"
|
|
|
|
#include "gimpdnd.h"
|
|
#include "gimpview.h"
|
|
#include "gimpview-popup.h"
|
|
#include "gimpviewrenderer.h"
|
|
#include "gimpviewrenderer-utils.h"
|
|
|
|
|
|
enum
|
|
{
|
|
SET_VIEWABLE,
|
|
CLICKED,
|
|
DOUBLE_CLICKED,
|
|
CONTEXT,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
|
|
static void gimp_view_dispose (GObject *object);
|
|
|
|
static void gimp_view_realize (GtkWidget *widget);
|
|
static void gimp_view_unrealize (GtkWidget *widget);
|
|
static void gimp_view_map (GtkWidget *widget);
|
|
static void gimp_view_unmap (GtkWidget *widget);
|
|
static void gimp_view_get_preferred_width (GtkWidget *widget,
|
|
gint *minimum_width,
|
|
gint *natural_width);
|
|
static void gimp_view_get_preferred_height (GtkWidget *widget,
|
|
gint *minimum_height,
|
|
gint *natural_height);
|
|
static void gimp_view_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation);
|
|
static void gimp_view_style_updated (GtkWidget *widget);
|
|
static gboolean gimp_view_draw (GtkWidget *widget,
|
|
cairo_t *cr);
|
|
static gboolean gimp_view_button_press_event (GtkWidget *widget,
|
|
GdkEventButton *bevent);
|
|
static gboolean gimp_view_button_release_event (GtkWidget *widget,
|
|
GdkEventButton *bevent);
|
|
static gboolean gimp_view_enter_notify_event (GtkWidget *widget,
|
|
GdkEventCrossing *event);
|
|
static gboolean gimp_view_leave_notify_event (GtkWidget *widget,
|
|
GdkEventCrossing *event);
|
|
|
|
static void gimp_view_real_set_viewable (GimpView *view,
|
|
GimpViewable *old,
|
|
GimpViewable *viewable);
|
|
|
|
static void gimp_view_update_callback (GimpViewRenderer *renderer,
|
|
GimpView *view);
|
|
|
|
static void gimp_view_monitor_changed (GimpView *view);
|
|
|
|
static GimpViewable * gimp_view_drag_viewable (GtkWidget *widget,
|
|
GimpContext **context,
|
|
gpointer data);
|
|
static GdkPixbuf * gimp_view_drag_pixbuf (GtkWidget *widget,
|
|
gpointer data);
|
|
|
|
|
|
G_DEFINE_TYPE (GimpView, gimp_view, GTK_TYPE_WIDGET)
|
|
|
|
#define parent_class gimp_view_parent_class
|
|
|
|
static guint view_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
|
|
static void
|
|
gimp_view_class_init (GimpViewClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
view_signals[SET_VIEWABLE] =
|
|
g_signal_new ("set-viewable",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpViewClass, set_viewable),
|
|
NULL, NULL,
|
|
gimp_marshal_VOID__OBJECT_OBJECT,
|
|
G_TYPE_NONE, 2,
|
|
GIMP_TYPE_VIEWABLE, GIMP_TYPE_VIEWABLE);
|
|
|
|
view_signals[CLICKED] =
|
|
g_signal_new ("clicked",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpViewClass, clicked),
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 1,
|
|
GDK_TYPE_MODIFIER_TYPE);
|
|
|
|
view_signals[DOUBLE_CLICKED] =
|
|
g_signal_new ("double-clicked",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpViewClass, double_clicked),
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
|
|
view_signals[CONTEXT] =
|
|
g_signal_new ("context",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpViewClass, context),
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
|
|
object_class->dispose = gimp_view_dispose;
|
|
|
|
widget_class->activate_signal = view_signals[CLICKED];
|
|
widget_class->realize = gimp_view_realize;
|
|
widget_class->unrealize = gimp_view_unrealize;
|
|
widget_class->map = gimp_view_map;
|
|
widget_class->unmap = gimp_view_unmap;
|
|
widget_class->get_preferred_width = gimp_view_get_preferred_width;
|
|
widget_class->get_preferred_height = gimp_view_get_preferred_height;
|
|
widget_class->size_allocate = gimp_view_size_allocate;
|
|
widget_class->style_updated = gimp_view_style_updated;
|
|
widget_class->draw = gimp_view_draw;
|
|
widget_class->button_press_event = gimp_view_button_press_event;
|
|
widget_class->button_release_event = gimp_view_button_release_event;
|
|
widget_class->enter_notify_event = gimp_view_enter_notify_event;
|
|
widget_class->leave_notify_event = gimp_view_leave_notify_event;
|
|
|
|
klass->set_viewable = gimp_view_real_set_viewable;
|
|
klass->clicked = NULL;
|
|
klass->double_clicked = NULL;
|
|
klass->context = NULL;
|
|
|
|
gtk_widget_class_set_css_name (widget_class, "GimpView");
|
|
}
|
|
|
|
static void
|
|
gimp_view_init (GimpView *view)
|
|
{
|
|
gtk_widget_set_has_window (GTK_WIDGET (view), FALSE);
|
|
gtk_widget_add_events (GTK_WIDGET (view),
|
|
GDK_BUTTON_PRESS_MASK |
|
|
GDK_BUTTON_RELEASE_MASK |
|
|
GDK_ENTER_NOTIFY_MASK |
|
|
GDK_LEAVE_NOTIFY_MASK);
|
|
|
|
view->clickable = FALSE;
|
|
view->eat_button_events = TRUE;
|
|
view->show_popup = FALSE;
|
|
view->expand = FALSE;
|
|
|
|
view->in_button = FALSE;
|
|
view->has_grab = FALSE;
|
|
view->press_state = 0;
|
|
|
|
gimp_widget_track_monitor (GTK_WIDGET (view),
|
|
G_CALLBACK (gimp_view_monitor_changed),
|
|
NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
gimp_view_dispose (GObject *object)
|
|
{
|
|
GimpView *view = GIMP_VIEW (object);
|
|
|
|
if (view->viewable)
|
|
gimp_view_set_viewable (view, NULL);
|
|
|
|
g_clear_object (&view->renderer);
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gimp_view_realize (GtkWidget *widget)
|
|
{
|
|
GimpView *view = GIMP_VIEW (widget);
|
|
GtkAllocation allocation;
|
|
GdkWindowAttr attributes;
|
|
gint attributes_mask;
|
|
|
|
GTK_WIDGET_CLASS (parent_class)->realize (widget);
|
|
|
|
gtk_widget_get_allocation (widget, &allocation);
|
|
|
|
attributes.window_type = GDK_WINDOW_CHILD;
|
|
attributes.x = allocation.x;
|
|
attributes.y = allocation.y;
|
|
attributes.width = allocation.width;
|
|
attributes.height = allocation.height;
|
|
attributes.wclass = GDK_INPUT_ONLY;
|
|
attributes.event_mask = gtk_widget_get_events (widget);
|
|
|
|
attributes_mask = GDK_WA_X | GDK_WA_Y;
|
|
|
|
view->event_window = gdk_window_new (gtk_widget_get_window (widget),
|
|
&attributes, attributes_mask);
|
|
gdk_window_set_user_data (view->event_window, view);
|
|
}
|
|
|
|
static void
|
|
gimp_view_unrealize (GtkWidget *widget)
|
|
{
|
|
GimpView *view = GIMP_VIEW (widget);
|
|
|
|
if (view->event_window)
|
|
{
|
|
gdk_window_set_user_data (view->event_window, NULL);
|
|
gdk_window_destroy (view->event_window);
|
|
view->event_window = NULL;
|
|
}
|
|
|
|
GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
|
|
}
|
|
|
|
static void
|
|
gimp_view_map (GtkWidget *widget)
|
|
{
|
|
GimpView *view = GIMP_VIEW (widget);
|
|
|
|
GTK_WIDGET_CLASS (parent_class)->map (widget);
|
|
|
|
if (view->event_window)
|
|
gdk_window_show (view->event_window);
|
|
}
|
|
|
|
static void
|
|
gimp_view_unmap (GtkWidget *widget)
|
|
{
|
|
GimpView *view = GIMP_VIEW (widget);
|
|
|
|
if (view->has_grab)
|
|
{
|
|
gtk_grab_remove (widget);
|
|
view->has_grab = FALSE;
|
|
}
|
|
|
|
if (view->event_window)
|
|
gdk_window_hide (view->event_window);
|
|
|
|
GTK_WIDGET_CLASS (parent_class)->unmap (widget);
|
|
}
|
|
|
|
static void
|
|
gimp_view_get_preferred_width (GtkWidget *widget,
|
|
gint *minimum_width,
|
|
gint *natural_width)
|
|
{
|
|
GimpView *view = GIMP_VIEW (widget);
|
|
|
|
if (view->expand)
|
|
{
|
|
*minimum_width = *natural_width = 2 * view->renderer->border_width + 1;
|
|
}
|
|
else
|
|
{
|
|
*minimum_width = *natural_width = (view->renderer->width +
|
|
2 * view->renderer->border_width);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_view_get_preferred_height (GtkWidget *widget,
|
|
gint *minimum_height,
|
|
gint *natural_height)
|
|
{
|
|
GimpView *view = GIMP_VIEW (widget);
|
|
|
|
if (view->expand)
|
|
{
|
|
*minimum_height = *natural_height = 2 * view->renderer->border_width + 1;
|
|
}
|
|
else
|
|
{
|
|
*minimum_height = *natural_height = (view->renderer->height +
|
|
2 * view->renderer->border_width);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_view_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation)
|
|
{
|
|
GimpView *view = GIMP_VIEW (widget);
|
|
gint width;
|
|
gint height;
|
|
|
|
if (view->expand)
|
|
{
|
|
width = MIN (GIMP_VIEWABLE_MAX_PREVIEW_SIZE,
|
|
allocation->width - 2 * view->renderer->border_width);
|
|
height = MIN (GIMP_VIEWABLE_MAX_PREVIEW_SIZE,
|
|
allocation->height - 2 * view->renderer->border_width);
|
|
|
|
if (view->renderer->width != width ||
|
|
view->renderer->height != height)
|
|
{
|
|
gint border_width = view->renderer->border_width;
|
|
|
|
if (view->renderer->size != -1 && view->renderer->viewable)
|
|
{
|
|
gint view_width;
|
|
gint view_height;
|
|
gint scaled_width;
|
|
gint scaled_height;
|
|
|
|
gimp_viewable_get_preview_size (view->renderer->viewable,
|
|
GIMP_VIEWABLE_MAX_PREVIEW_SIZE,
|
|
view->renderer->is_popup,
|
|
view->renderer->dot_for_dot,
|
|
&view_width,
|
|
&view_height);
|
|
|
|
gimp_viewable_calc_preview_size (view_width, view_height,
|
|
width, height,
|
|
TRUE, 1.0, 1.0,
|
|
&scaled_width, &scaled_height,
|
|
NULL);
|
|
|
|
if (scaled_width > width)
|
|
{
|
|
scaled_height = scaled_height * width / scaled_width;
|
|
scaled_width = scaled_width * width / scaled_width;
|
|
}
|
|
else if (scaled_height > height)
|
|
{
|
|
scaled_width = scaled_width * height / scaled_height;
|
|
scaled_height = scaled_height * height / scaled_height;
|
|
}
|
|
|
|
gimp_view_renderer_set_size (view->renderer,
|
|
MAX (scaled_width, scaled_height),
|
|
border_width);
|
|
}
|
|
else
|
|
{
|
|
gimp_view_renderer_set_size_full (view->renderer,
|
|
width, height,
|
|
border_width);
|
|
}
|
|
|
|
gimp_view_renderer_remove_idle (view->renderer);
|
|
}
|
|
}
|
|
|
|
width = (view->renderer->width +
|
|
2 * view->renderer->border_width);
|
|
height = (view->renderer->height +
|
|
2 * view->renderer->border_width);
|
|
|
|
if (allocation->width > width)
|
|
allocation->x += (allocation->width - width) / 2;
|
|
|
|
if (allocation->height > height)
|
|
allocation->y += (allocation->height - height) / 2;
|
|
|
|
allocation->width = width;
|
|
allocation->height = height;
|
|
|
|
GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
|
|
|
|
if (gtk_widget_get_realized (widget))
|
|
gdk_window_move_resize (view->event_window,
|
|
allocation->x,
|
|
allocation->y,
|
|
allocation->width,
|
|
allocation->height);
|
|
}
|
|
|
|
static void
|
|
gimp_view_style_updated (GtkWidget *widget)
|
|
{
|
|
GimpView *view = GIMP_VIEW (widget);
|
|
|
|
GTK_WIDGET_CLASS (parent_class)->style_updated (widget);
|
|
|
|
gimp_view_renderer_invalidate (view->renderer);
|
|
}
|
|
|
|
static gboolean
|
|
gimp_view_draw (GtkWidget *widget,
|
|
cairo_t *cr)
|
|
{
|
|
GtkAllocation allocation;
|
|
|
|
gtk_widget_get_allocation (widget, &allocation);
|
|
|
|
gimp_view_renderer_draw (GIMP_VIEW (widget)->renderer,
|
|
widget, cr,
|
|
allocation.width,
|
|
allocation.height);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
#define DEBUG_MEMSIZE 1
|
|
|
|
#ifdef DEBUG_MEMSIZE
|
|
extern gboolean gimp_debug_memsize;
|
|
#endif
|
|
|
|
static gboolean
|
|
gimp_view_button_press_event (GtkWidget *widget,
|
|
GdkEventButton *bevent)
|
|
{
|
|
GimpView *view = GIMP_VIEW (widget);
|
|
|
|
#ifdef DEBUG_MEMSIZE
|
|
if (bevent->type == GDK_BUTTON_PRESS && bevent->button == 2)
|
|
{
|
|
gimp_debug_memsize = TRUE;
|
|
|
|
gimp_object_get_memsize (GIMP_OBJECT (view->viewable), NULL);
|
|
|
|
gimp_debug_memsize = FALSE;
|
|
}
|
|
#endif /* DEBUG_MEMSIZE */
|
|
|
|
if (! view->clickable &&
|
|
! view->show_popup)
|
|
return FALSE;
|
|
|
|
if (! gtk_widget_get_realized (widget))
|
|
return FALSE;
|
|
|
|
if (bevent->type == GDK_BUTTON_PRESS)
|
|
{
|
|
if (gdk_event_triggers_context_menu ((GdkEvent *) bevent))
|
|
{
|
|
view->press_state = 0;
|
|
|
|
g_signal_emit (widget, view_signals[CONTEXT], 0);
|
|
}
|
|
else if (bevent->button == 1)
|
|
{
|
|
gtk_grab_add (widget);
|
|
|
|
view->has_grab = TRUE;
|
|
view->press_state = bevent->state;
|
|
|
|
if (view->show_popup && view->viewable)
|
|
{
|
|
gimp_view_popup_show (widget, bevent,
|
|
view->renderer->context,
|
|
view->viewable,
|
|
view->renderer->width,
|
|
view->renderer->height,
|
|
view->renderer->dot_for_dot);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
view->press_state = 0;
|
|
|
|
if (bevent->button == 2)
|
|
gimp_view_popup_show (widget, bevent,
|
|
view->renderer->context,
|
|
view->viewable,
|
|
view->renderer->width,
|
|
view->renderer->height,
|
|
view->renderer->dot_for_dot);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (bevent->type == GDK_2BUTTON_PRESS)
|
|
{
|
|
if (bevent->button == 1)
|
|
g_signal_emit (widget, view_signals[DOUBLE_CLICKED], 0);
|
|
}
|
|
|
|
return view->eat_button_events ? TRUE : FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
gimp_view_button_release_event (GtkWidget *widget,
|
|
GdkEventButton *bevent)
|
|
{
|
|
GimpView *view = GIMP_VIEW (widget);
|
|
|
|
if (! view->clickable &&
|
|
! view->show_popup)
|
|
return FALSE;
|
|
|
|
if (bevent->button == 1)
|
|
{
|
|
gtk_grab_remove (widget);
|
|
view->has_grab = FALSE;
|
|
|
|
if (view->clickable && view->in_button)
|
|
{
|
|
g_signal_emit (widget, view_signals[CLICKED], 0, view->press_state);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return view->eat_button_events ? TRUE : FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
gimp_view_enter_notify_event (GtkWidget *widget,
|
|
GdkEventCrossing *event)
|
|
{
|
|
GimpView *view = GIMP_VIEW (widget);
|
|
GtkWidget *event_widget = gtk_get_event_widget ((GdkEvent *) event);
|
|
|
|
if ((event_widget == widget) &&
|
|
(event->detail != GDK_NOTIFY_INFERIOR))
|
|
{
|
|
view->in_button = TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
gimp_view_leave_notify_event (GtkWidget *widget,
|
|
GdkEventCrossing *event)
|
|
{
|
|
GimpView *view = GIMP_VIEW (widget);
|
|
GtkWidget *event_widget = gtk_get_event_widget ((GdkEvent *) event);
|
|
|
|
if ((event_widget == widget) &&
|
|
(event->detail != GDK_NOTIFY_INFERIOR))
|
|
{
|
|
view->in_button = FALSE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gimp_view_real_set_viewable (GimpView *view,
|
|
GimpViewable *old,
|
|
GimpViewable *viewable)
|
|
{
|
|
GType viewable_type = G_TYPE_NONE;
|
|
|
|
if (viewable == view->viewable)
|
|
return;
|
|
|
|
if (viewable)
|
|
{
|
|
viewable_type = G_TYPE_FROM_INSTANCE (viewable);
|
|
|
|
g_return_if_fail (g_type_is_a (viewable_type,
|
|
view->renderer->viewable_type));
|
|
}
|
|
|
|
if (view->viewable)
|
|
{
|
|
if (! viewable && ! view->renderer->is_popup)
|
|
{
|
|
if (gimp_dnd_viewable_source_remove (GTK_WIDGET (view),
|
|
G_TYPE_FROM_INSTANCE (view->viewable)))
|
|
{
|
|
if (gimp_viewable_get_size (view->viewable, NULL, NULL))
|
|
gimp_dnd_pixbuf_source_remove (GTK_WIDGET (view));
|
|
|
|
gtk_drag_source_unset (GTK_WIDGET (view));
|
|
}
|
|
}
|
|
}
|
|
else if (viewable && ! view->renderer->is_popup)
|
|
{
|
|
if (gimp_dnd_drag_source_set_by_type (GTK_WIDGET (view),
|
|
GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
|
|
viewable_type,
|
|
GDK_ACTION_COPY))
|
|
{
|
|
gimp_dnd_viewable_source_add (GTK_WIDGET (view),
|
|
viewable_type,
|
|
gimp_view_drag_viewable,
|
|
NULL);
|
|
|
|
if (gimp_viewable_get_size (viewable, NULL, NULL))
|
|
gimp_dnd_pixbuf_source_add (GTK_WIDGET (view),
|
|
gimp_view_drag_pixbuf,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
gimp_view_renderer_set_viewable (view->renderer, viewable);
|
|
|
|
g_set_weak_pointer (&view->viewable, viewable);
|
|
}
|
|
|
|
/* public functions */
|
|
|
|
GtkWidget *
|
|
gimp_view_new (GimpContext *context,
|
|
GimpViewable *viewable,
|
|
gint size,
|
|
gint border_width,
|
|
gboolean is_popup)
|
|
{
|
|
GtkWidget *view;
|
|
|
|
g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
|
|
g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
|
|
|
|
view = gimp_view_new_by_types (context,
|
|
GIMP_TYPE_VIEW,
|
|
G_TYPE_FROM_INSTANCE (viewable),
|
|
size, border_width, is_popup);
|
|
|
|
if (view)
|
|
gimp_view_set_viewable (GIMP_VIEW (view), viewable);
|
|
|
|
gimp_view_renderer_remove_idle (GIMP_VIEW (view)->renderer);
|
|
|
|
return view;
|
|
}
|
|
|
|
GtkWidget *
|
|
gimp_view_new_full (GimpContext *context,
|
|
GimpViewable *viewable,
|
|
gint width,
|
|
gint height,
|
|
gint border_width,
|
|
gboolean is_popup,
|
|
gboolean clickable,
|
|
gboolean show_popup)
|
|
{
|
|
GtkWidget *view;
|
|
|
|
g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
|
|
g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
|
|
|
|
view = gimp_view_new_full_by_types (context,
|
|
GIMP_TYPE_VIEW,
|
|
G_TYPE_FROM_INSTANCE (viewable),
|
|
width, height, border_width,
|
|
is_popup, clickable, show_popup);
|
|
|
|
if (view)
|
|
gimp_view_set_viewable (GIMP_VIEW (view), viewable);
|
|
|
|
gimp_view_renderer_remove_idle (GIMP_VIEW (view)->renderer);
|
|
|
|
return view;
|
|
}
|
|
|
|
GtkWidget *
|
|
gimp_view_new_by_types (GimpContext *context,
|
|
GType view_type,
|
|
GType viewable_type,
|
|
gint size,
|
|
gint border_width,
|
|
gboolean is_popup)
|
|
{
|
|
GimpViewRenderer *renderer;
|
|
GimpView *view;
|
|
|
|
g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
|
|
g_return_val_if_fail (g_type_is_a (view_type, GIMP_TYPE_VIEW), NULL);
|
|
g_return_val_if_fail (g_type_is_a (viewable_type, GIMP_TYPE_VIEWABLE), NULL);
|
|
g_return_val_if_fail (size > 0 &&
|
|
size <= GIMP_VIEWABLE_MAX_PREVIEW_SIZE, NULL);
|
|
g_return_val_if_fail (border_width >= 0 &&
|
|
border_width <= GIMP_VIEW_MAX_BORDER_WIDTH, NULL);
|
|
|
|
renderer = gimp_view_renderer_new (context, viewable_type, size,
|
|
border_width, is_popup);
|
|
|
|
g_return_val_if_fail (renderer != NULL, NULL);
|
|
|
|
view = g_object_new (view_type, NULL);
|
|
|
|
g_signal_connect (renderer, "update",
|
|
G_CALLBACK (gimp_view_update_callback),
|
|
view);
|
|
|
|
view->renderer = renderer;
|
|
|
|
return GTK_WIDGET (view);
|
|
}
|
|
|
|
GtkWidget *
|
|
gimp_view_new_full_by_types (GimpContext *context,
|
|
GType view_type,
|
|
GType viewable_type,
|
|
gint width,
|
|
gint height,
|
|
gint border_width,
|
|
gboolean is_popup,
|
|
gboolean clickable,
|
|
gboolean show_popup)
|
|
{
|
|
GimpViewRenderer *renderer;
|
|
GimpView *view;
|
|
|
|
g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
|
|
g_return_val_if_fail (g_type_is_a (view_type, GIMP_TYPE_VIEW), NULL);
|
|
g_return_val_if_fail (g_type_is_a (viewable_type, GIMP_TYPE_VIEWABLE), NULL);
|
|
g_return_val_if_fail (width > 0 &&
|
|
width <= GIMP_VIEWABLE_MAX_PREVIEW_SIZE, NULL);
|
|
g_return_val_if_fail (height > 0 &&
|
|
height <= GIMP_VIEWABLE_MAX_PREVIEW_SIZE, NULL);
|
|
g_return_val_if_fail (border_width >= 0 &&
|
|
border_width <= GIMP_VIEW_MAX_BORDER_WIDTH, NULL);
|
|
|
|
renderer = gimp_view_renderer_new_full (context, viewable_type,
|
|
width, height, border_width,
|
|
is_popup);
|
|
|
|
g_return_val_if_fail (renderer != NULL, NULL);
|
|
|
|
view = g_object_new (view_type, NULL);
|
|
|
|
g_signal_connect (renderer, "update",
|
|
G_CALLBACK (gimp_view_update_callback),
|
|
view);
|
|
|
|
view->renderer = renderer;
|
|
view->clickable = clickable ? TRUE : FALSE;
|
|
view->show_popup = show_popup ? TRUE : FALSE;
|
|
|
|
return GTK_WIDGET (view);
|
|
}
|
|
|
|
GimpViewable *
|
|
gimp_view_get_viewable (GimpView *view)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_VIEW (view), NULL);
|
|
|
|
return view->viewable;
|
|
}
|
|
|
|
void
|
|
gimp_view_set_viewable (GimpView *view,
|
|
GimpViewable *viewable)
|
|
{
|
|
g_return_if_fail (GIMP_IS_VIEW (view));
|
|
g_return_if_fail (viewable == NULL || GIMP_IS_VIEWABLE (viewable));
|
|
|
|
if (viewable == view->viewable)
|
|
return;
|
|
|
|
g_signal_emit (view, view_signals[SET_VIEWABLE], 0, view->viewable, viewable);
|
|
}
|
|
|
|
void
|
|
gimp_view_set_expand (GimpView *view,
|
|
gboolean expand)
|
|
{
|
|
g_return_if_fail (GIMP_IS_VIEW (view));
|
|
|
|
if (view->expand != expand)
|
|
{
|
|
view->expand = expand ? TRUE : FALSE;
|
|
gtk_widget_queue_resize (GTK_WIDGET (view));
|
|
}
|
|
}
|
|
|
|
|
|
/* private functions */
|
|
|
|
static void
|
|
gimp_view_update_callback (GimpViewRenderer *renderer,
|
|
GimpView *view)
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET (view);
|
|
GtkRequisition requisition;
|
|
gint width;
|
|
gint height;
|
|
|
|
gtk_widget_get_preferred_size (widget, &requisition, NULL);
|
|
|
|
width = renderer->width + 2 * renderer->border_width;
|
|
height = renderer->height + 2 * renderer->border_width;
|
|
|
|
if (width != requisition.width ||
|
|
height != requisition.height)
|
|
{
|
|
gtk_widget_queue_resize (widget);
|
|
}
|
|
else
|
|
{
|
|
gtk_widget_queue_draw (widget);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_view_monitor_changed (GimpView *view)
|
|
{
|
|
if (view->renderer)
|
|
gimp_view_renderer_free_color_transform (view->renderer);
|
|
}
|
|
|
|
static GimpViewable *
|
|
gimp_view_drag_viewable (GtkWidget *widget,
|
|
GimpContext **context,
|
|
gpointer data)
|
|
{
|
|
if (context)
|
|
*context = GIMP_VIEW (widget)->renderer->context;
|
|
|
|
return GIMP_VIEW (widget)->viewable;
|
|
}
|
|
|
|
static GdkPixbuf *
|
|
gimp_view_drag_pixbuf (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
GimpView *view = GIMP_VIEW (widget);
|
|
GimpViewable *viewable = view->viewable;
|
|
gint width;
|
|
gint height;
|
|
|
|
if (viewable && gimp_viewable_get_size (viewable, &width, &height))
|
|
return gimp_viewable_get_new_pixbuf (viewable, view->renderer->context,
|
|
width, height, NULL, NULL);
|
|
|
|
return NULL;
|
|
}
|