/* LIBGIMP - The GIMP Library * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball * * gimpoffsetarea.c * Copyright (C) 2001 Sven Neumann * * 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, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include "gimpwidgetstypes.h" #include "gimpoffsetarea.h" #define DRAWING_AREA_SIZE 200 enum { OFFSETS_CHANGED, LAST_SIGNAL }; static void gimp_offset_area_destroy (GtkObject *object); static void gimp_offset_area_resize (GimpOffsetArea *offset_area); static gboolean gimp_offset_area_event (GtkWidget *widget, GdkEvent *event); static void gimp_offset_area_draw (GimpOffsetArea *offset_area); static guint gimp_offset_area_signals[LAST_SIGNAL] = { 0 }; static GtkDrawingAreaClass *parent_class = NULL; static void gimp_offset_area_destroy (GtkObject *object) { GimpOffsetArea *offset_area = GIMP_OFFSET_AREA (object); g_return_if_fail (offset_area != NULL); if (GTK_OBJECT_CLASS (parent_class)->destroy) GTK_OBJECT_CLASS (parent_class)->destroy (object); } static void gimp_offset_area_class_init (GimpOffsetAreaClass *klass) { GtkObjectClass *object_class; GtkWidgetClass *widget_class; object_class = (GtkObjectClass *) klass; widget_class = (GtkWidgetClass *) klass; parent_class = g_type_class_peek_parent (klass); gimp_offset_area_signals[OFFSETS_CHANGED] = g_signal_new ("offsets_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpOffsetAreaClass, offsets_changed), NULL, NULL, gtk_marshal_VOID__INT_INT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); object_class->destroy = gimp_offset_area_destroy; widget_class->event = gimp_offset_area_event; } static void gimp_offset_area_init (GimpOffsetArea *offset_area) { offset_area->orig_width = 0; offset_area->orig_height = 0; offset_area->width = 0; offset_area->height = 0; offset_area->offset_x = 0; offset_area->offset_y = 0; offset_area->display_ratio_x = 1.0; offset_area->display_ratio_y = 1.0; gtk_widget_add_events (GTK_WIDGET (offset_area), GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK); } GtkType gimp_offset_area_get_type (void) { static GtkType offset_area_type = 0; if (!offset_area_type) { GtkTypeInfo offset_area_info = { "GimpOffsetArea", sizeof (GimpOffsetArea), sizeof (GimpOffsetAreaClass), (GtkClassInitFunc) gimp_offset_area_class_init, (GtkObjectInitFunc) gimp_offset_area_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL }; offset_area_type = gtk_type_unique (gtk_drawing_area_get_type (), &offset_area_info); } return offset_area_type; } /** * gimp_offset_area_new: * @orig_width: the original width * @orig_height: the original height * * Creates a new #GimpOffsetArea widget. A #GimpOffsetArea can be used * when resizing an image or a drawable to allow the user to interactively * specify the new offsets. * * Return value: the new #GimpOffsetArea widget. **/ GtkWidget * gimp_offset_area_new (gint orig_width, gint orig_height) { GimpOffsetArea *offset_area; g_return_val_if_fail (orig_width > 0, NULL); g_return_val_if_fail (orig_height > 0, NULL); offset_area = g_object_new (GIMP_TYPE_OFFSET_AREA, NULL); offset_area->orig_width = offset_area->width = orig_width; offset_area->orig_height = offset_area->height = orig_height; gimp_offset_area_resize (offset_area); return GTK_WIDGET (offset_area); } /** * gimp_offset_area_set_size: * @offset_area: a #GimpOffsetArea. * @width: the new width * @height: the new height * * Sets the size of the image/drawable displayed by the #GimpOffsetArea. * If the offsets change as a result of this change, the %offsets_changed * signal is emitted. **/ void gimp_offset_area_set_size (GimpOffsetArea *offset_area, gint width, gint height) { gint offset_x; gint offset_y; g_return_if_fail (offset_area != NULL); g_return_if_fail (GIMP_IS_OFFSET_AREA (offset_area)); g_return_if_fail (width > 0 && height > 0); if (offset_area->width != width || offset_area->height != height) { offset_area->width = width; offset_area->height = height; if (offset_area->orig_width <= offset_area->width) offset_x = CLAMP (offset_area->offset_x, 0, offset_area->width - offset_area->orig_width); else offset_x = CLAMP (offset_area->offset_x, offset_area->width - offset_area->orig_width, 0); if (offset_area->orig_height <= offset_area->height) offset_y = CLAMP (offset_area->offset_y, 0, offset_area->height - offset_area->orig_height); else offset_y = CLAMP (offset_area->offset_y, offset_area->height - offset_area->orig_height, 0); offset_area->offset_x = offset_x; offset_area->offset_y = offset_y; gimp_offset_area_resize (offset_area); gtk_widget_queue_draw (GTK_WIDGET (offset_area)); if (offset_x != offset_area->offset_x || offset_y != offset_area->offset_y) { g_signal_emit (G_OBJECT (offset_area), gimp_offset_area_signals[OFFSETS_CHANGED], 0, offset_area->offset_x, offset_area->offset_y); } } } /** * gimp_offset_area_set_size: * @offset_area: a #GimpOffsetArea. * @width: the X offset * @height: the Y offset * * Sets the offsets of the image/drawable displayed by the #GimpOffsetArea. * It does not emit the %offsets_changed signal. **/ void gimp_offset_area_set_offsets (GimpOffsetArea *offset_area, gint offset_x, gint offset_y) { g_return_if_fail (offset_area != NULL); g_return_if_fail (GIMP_IS_OFFSET_AREA (offset_area)); if (offset_area->offset_x != offset_x || offset_area->offset_y != offset_y) { if (offset_area->orig_width <= offset_area->width) offset_area->offset_x = CLAMP (offset_x, 0, offset_area->width - offset_area->orig_width); else offset_area->offset_x = CLAMP (offset_x, offset_area->width - offset_area->orig_width, 0); if (offset_area->orig_height <= offset_area->height) offset_area->offset_y = CLAMP (offset_y, 0, offset_area->height - offset_area->orig_height); else offset_area->offset_y = CLAMP (offset_y, offset_area->height - offset_area->orig_height, 0); gtk_widget_queue_draw (GTK_WIDGET (offset_area)); } } static void gimp_offset_area_resize (GimpOffsetArea *offset_area) { gint width; gint height; gdouble ratio; if (offset_area->orig_width == 0 || offset_area->orig_height == 0) return; if (offset_area->orig_width <= offset_area->width) width = offset_area->width; else width = offset_area->orig_width * 2 - offset_area->width; if (offset_area->orig_height <= offset_area->height) height = offset_area->height; else height = offset_area->orig_height * 2 - offset_area->height; ratio = (gdouble) DRAWING_AREA_SIZE / (gdouble) MAX (width, height); width = ratio * (gdouble) width; height = ratio * (gdouble) height; gtk_drawing_area_size (GTK_DRAWING_AREA (offset_area), width, height); } static gboolean gimp_offset_area_event (GtkWidget *widget, GdkEvent *event) { static gint orig_offset_x = 0; static gint orig_offset_y = 0; static gint start_x = 0; static gint start_y = 0; GimpOffsetArea *offset_area = GIMP_OFFSET_AREA (widget); GdkEventConfigure *conf_event; gint offset_x; gint offset_y; if (offset_area->orig_width == 0 || offset_area->orig_height == 0) return FALSE; switch (event->type) { case GDK_CONFIGURE: conf_event = (GdkEventConfigure *) event; offset_area->display_ratio_x = (gdouble) conf_event->width / ((offset_area->orig_width <= offset_area->width) ? offset_area->width : offset_area->orig_width * 2 - offset_area->width); offset_area->display_ratio_y = (gdouble) conf_event->height / ((offset_area->orig_height <= offset_area->height) ? offset_area->height : offset_area->orig_height * 2 - offset_area->height); break; case GDK_EXPOSE: gimp_offset_area_draw (offset_area); break; case GDK_BUTTON_PRESS: gdk_pointer_grab (widget->window, FALSE, (GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK), NULL, NULL, event->button.time); orig_offset_x = offset_area->offset_x; orig_offset_y = offset_area->offset_y; start_x = event->button.x; start_y = event->button.y; break; case GDK_MOTION_NOTIFY: offset_x = orig_offset_x + (event->motion.x - start_x) / offset_area->display_ratio_x; offset_y = orig_offset_y + (event->motion.y - start_y) / offset_area->display_ratio_y; if (offset_area->offset_x != offset_x || offset_area->offset_y != offset_y) { gimp_offset_area_set_offsets (offset_area, offset_x, offset_y); g_signal_emit (G_OBJECT (offset_area), gimp_offset_area_signals[OFFSETS_CHANGED], 0, offset_area->offset_x, offset_area->offset_y); } break; case GDK_BUTTON_RELEASE: gdk_pointer_ungrab (event->button.time); start_x = start_y = 0; break; default: break; } return FALSE; } static void gimp_offset_area_draw (GimpOffsetArea *offset_area) { GtkWidget *widget = GTK_WIDGET (offset_area); gint w, h; gint x, y; gdk_window_clear (widget->window); x = offset_area->display_ratio_x * ((offset_area->orig_width <= offset_area->width) ? offset_area->offset_x : offset_area->offset_x + offset_area->orig_width - offset_area->width); y = offset_area->display_ratio_y * ((offset_area->orig_height <= offset_area->height) ? offset_area->offset_y : offset_area->offset_y + offset_area->orig_height - offset_area->height); w = offset_area->display_ratio_x * offset_area->orig_width; h = offset_area->display_ratio_y * offset_area->orig_height; gtk_draw_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, x, y, w, h); if (offset_area->orig_width > offset_area->width || offset_area->orig_height > offset_area->height) { if (offset_area->orig_width > offset_area->width) { x = offset_area->display_ratio_x * (offset_area->orig_width - offset_area->width); w = offset_area->display_ratio_x * offset_area->width; } else { x = -1; w = widget->allocation.width + 2; } if (offset_area->orig_height > offset_area->height) { y = offset_area->display_ratio_y * (offset_area->orig_height - offset_area->height); h = offset_area->display_ratio_y * offset_area->height; } else { y = -1; h = widget->allocation.height + 2; } gdk_draw_rectangle (widget->window, widget->style->black_gc, 0, x, y, w, h); } }