gimp/app/tools/gimprectangletool.c

2204 lines
67 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <string.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpwidgets/gimpwidgets.h"
#include "tools-types.h"
#include "core/gimpchannel.h"
#include "core/gimpimage.h"
#include "core/gimpimage-crop.h"
#include "core/gimppickable.h"
#include "core/gimpmarshal.h"
#include "display/gimpdisplay.h"
#include "display/gimpdisplayshell.h"
#include "display/gimpdisplayshell-transform.h"
#include "gimpdrawtool.h"
#include "gimprectangleoptions.h"
#include "gimprectangletool.h"
#include "gimptoolcontrol.h"
#include "gimp-intl.h"
enum
{
RECTANGLE_CHANGED,
LAST_SIGNAL
};
/* speed of key movement */
#define ARROW_VELOCITY 25
#define HANDLE_SIZE 50
#define MIN_HANDLE_SIZE 6
#define GIMP_RECTANGLE_TOOL_GET_PRIVATE(obj) \
(gimp_rectangle_tool_get_private (GIMP_RECTANGLE_TOOL (obj)))
typedef struct _GimpRectangleToolPrivate GimpRectangleToolPrivate;
struct _GimpRectangleToolPrivate
{
gint pressx; /* x where button pressed */
gint pressy; /* y where button pressed */
gint x1, y1; /* upper left hand coordinate */
gint x2, y2; /* lower right hand coords */
guint function; /* moving or resizing */
GimpRectangleConstraint constraint; /* how to constrain rectangle */
/* Internal state */
gint startx; /* starting x coord */
gint starty; /* starting y coord */
gint lastx; /* previous x coord */
gint lasty; /* previous y coord */
gint handle_w; /* handle width */
gint handle_h; /* handle height */
gint saved_x1; /* for saving in case action */
gint saved_y1; /* is canceled */
gint saved_x2;
gint saved_y2;
gdouble saved_center_x;
gdouble saved_center_y;
gint suppress_updates;
GimpRectangleGuide guide; /* synced with options->guide, only exists for drawing */
};
static void gimp_rectangle_tool_iface_base_init (GimpRectangleToolInterface *iface);
static GimpRectangleToolPrivate *
gimp_rectangle_tool_get_private (GimpRectangleTool *rectangle);
GimpRectangleConstraint
gimp_rectangle_tool_get_constraint (GimpRectangleTool *rectangle);
/* Rectangle helper functions */
static void gimp_rectangle_tool_start (GimpRectangleTool *rectangle,
GimpDisplay *display);
static void gimp_rectangle_tool_halt (GimpRectangleTool *rectangle);
static void gimp_rectangle_tool_draw_guides (GimpDrawTool *draw_tool);
/* Rectangle dialog functions */
static void gimp_rectangle_tool_update_options (GimpRectangleTool *rectangle,
GimpDisplay *display);
static void gimp_rectangle_tool_options_notify (GimpRectangleOptions *options,
GParamSpec *pspec,
GimpRectangleTool *rectangle);
static void gimp_rectangle_tool_check_function (GimpRectangleTool *rectangle,
gint *x1,
gint *y1,
gint *x2,
gint *y2);
static void gimp_rectangle_tool_rectangle_changed (GimpRectangleTool *rectangle);
static void gimp_rectangle_tool_constrain (GimpRectangleTool *rectangle,
gint *x1,
gint *y1,
gint *x2,
gint *y2);
static void gimp_rectangle_tool_auto_shrink (GimpRectangleTool *rectangle);
static GtkAnchorType gimp_rectangle_tool_get_anchor (GimpRectangleToolPrivate *private,
gint *w,
gint *h);
static void gimp_rectangle_tool_set_highlight (GimpRectangleTool *rectangle);
static guint gimp_rectangle_tool_signals[LAST_SIGNAL] = { 0 };
GType
gimp_rectangle_tool_interface_get_type (void)
{
static GType iface_type = 0;
if (! iface_type)
{
const GTypeInfo iface_info =
{
sizeof (GimpRectangleToolInterface),
(GBaseInitFunc) gimp_rectangle_tool_iface_base_init,
(GBaseFinalizeFunc) NULL,
};
iface_type = g_type_register_static (G_TYPE_INTERFACE,
"GimpRectangleToolInterface",
&iface_info, 0);
g_type_interface_add_prerequisite (iface_type, GIMP_TYPE_DRAW_TOOL);
}
return iface_type;
}
static void
gimp_rectangle_tool_iface_base_init (GimpRectangleToolInterface *iface)
{
static gboolean initialized = FALSE;
if (! initialized)
{
gimp_rectangle_tool_signals[RECTANGLE_CHANGED] =
g_signal_new ("rectangle-changed",
G_TYPE_FROM_INTERFACE (iface),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpRectangleToolInterface,
rectangle_changed),
NULL, NULL,
gimp_marshal_VOID__VOID,
G_TYPE_NONE, 0);
g_object_interface_install_property (iface,
g_param_spec_int ("x1",
NULL, NULL,
-GIMP_MAX_IMAGE_SIZE,
GIMP_MAX_IMAGE_SIZE,
0,
GIMP_PARAM_READWRITE));
g_object_interface_install_property (iface,
g_param_spec_int ("y1",
NULL, NULL,
-GIMP_MAX_IMAGE_SIZE,
GIMP_MAX_IMAGE_SIZE,
0,
GIMP_PARAM_READWRITE));
g_object_interface_install_property (iface,
g_param_spec_int ("x2",
NULL, NULL,
-GIMP_MAX_IMAGE_SIZE,
GIMP_MAX_IMAGE_SIZE,
0,
GIMP_PARAM_READWRITE));
g_object_interface_install_property (iface,
g_param_spec_int ("y2",
NULL, NULL,
-GIMP_MAX_IMAGE_SIZE,
GIMP_MAX_IMAGE_SIZE,
0,
GIMP_PARAM_READWRITE));
g_object_interface_install_property (iface,
g_param_spec_enum ("constraint",
NULL, NULL,
GIMP_TYPE_RECTANGLE_CONSTRAINT,
GIMP_RECTANGLE_CONSTRAIN_NONE,
GIMP_PARAM_READWRITE));
iface->execute = NULL;
iface->cancel = NULL;
iface->rectangle_changed = NULL;
initialized = TRUE;
}
}
static void
gimp_rectangle_tool_private_finalize (GimpRectangleToolPrivate *private)
{
g_free (private);
}
static GimpRectangleToolPrivate *
gimp_rectangle_tool_get_private (GimpRectangleTool *tool)
{
static GQuark private_key = 0;
GimpRectangleToolPrivate *private;
if (G_UNLIKELY (private_key == 0))
private_key = g_quark_from_static_string ("gimp-rectangle-tool-private");
private = g_object_get_qdata (G_OBJECT (tool), private_key);
if (! private)
{
private = g_new0 (GimpRectangleToolPrivate, 1);
g_object_set_qdata_full (G_OBJECT (tool), private_key, private,
(GDestroyNotify)
gimp_rectangle_tool_private_finalize);
}
return private;
}
/**
* gimp_rectangle_tool_install_properties:
* @klass: the class structure for a type deriving from #GObject
*
* Installs the necessary properties for a class implementing
* #GimpToolOptions. A #GimpRectangleToolProp property is installed
* for each property, using the values from the #GimpRectangleToolProp
* enumeration. The caller must make sure itself that the enumeration
* values don't collide with some other property values they
* are using (that's what %GIMP_RECTANGLE_TOOL_PROP_LAST is good for).
**/
void
gimp_rectangle_tool_install_properties (GObjectClass *klass)
{
g_object_class_override_property (klass,
GIMP_RECTANGLE_TOOL_PROP_X1,
"x1");
g_object_class_override_property (klass,
GIMP_RECTANGLE_TOOL_PROP_Y1,
"y1");
g_object_class_override_property (klass,
GIMP_RECTANGLE_TOOL_PROP_X2,
"x2");
g_object_class_override_property (klass,
GIMP_RECTANGLE_TOOL_PROP_Y2,
"y2");
g_object_class_override_property (klass,
GIMP_RECTANGLE_TOOL_PROP_CONSTRAINT,
"constraint");
}
void
gimp_rectangle_tool_set_constraint (GimpRectangleTool *tool,
GimpRectangleConstraint constraint)
{
GimpRectangleToolPrivate *private;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (tool));
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
private->constraint = constraint;
g_object_notify (G_OBJECT (tool), "constraint");
}
GimpRectangleConstraint
gimp_rectangle_tool_get_constraint (GimpRectangleTool *tool)
{
GimpRectangleToolPrivate *private;
g_return_val_if_fail (GIMP_IS_RECTANGLE_TOOL (tool), 0);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
return private->constraint;
}
void
gimp_rectangle_tool_get_press_coords (GimpRectangleTool *rectangle,
gint *pressx_ptr,
gint *pressy_ptr)
{
GimpRectangleToolPrivate *private;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rectangle);
*pressx_ptr = private->pressx;
*pressy_ptr = private->pressy;
}
void
gimp_rectangle_tool_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpRectangleTool *rectangle = GIMP_RECTANGLE_TOOL (object);
GimpRectangleToolPrivate *private;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rectangle);
switch (property_id)
{
case GIMP_RECTANGLE_TOOL_PROP_X1:
private->x1 = g_value_get_int (value);
break;
case GIMP_RECTANGLE_TOOL_PROP_Y1:
private->y1 = g_value_get_int (value);
break;
case GIMP_RECTANGLE_TOOL_PROP_X2:
private->x2 = g_value_get_int (value);
break;
case GIMP_RECTANGLE_TOOL_PROP_Y2:
private->y2 = g_value_get_int (value);
break;
case GIMP_RECTANGLE_TOOL_PROP_CONSTRAINT:
gimp_rectangle_tool_set_constraint (rectangle, g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
void
gimp_rectangle_tool_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpRectangleTool *rectangle = GIMP_RECTANGLE_TOOL (object);
GimpRectangleToolPrivate *private;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rectangle);
switch (property_id)
{
case GIMP_RECTANGLE_TOOL_PROP_X1:
g_value_set_int (value, private->x1);
break;
case GIMP_RECTANGLE_TOOL_PROP_Y1:
g_value_set_int (value, private->y1);
break;
case GIMP_RECTANGLE_TOOL_PROP_X2:
g_value_set_int (value, private->x2);
break;
case GIMP_RECTANGLE_TOOL_PROP_Y2:
g_value_set_int (value, private->y2);
break;
case GIMP_RECTANGLE_TOOL_PROP_CONSTRAINT:
g_value_set_enum (value, gimp_rectangle_tool_get_constraint (rectangle));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
void
gimp_rectangle_tool_constructor (GObject *object)
{
GimpRectangleTool *rectangle = GIMP_RECTANGLE_TOOL (object);
GimpRectangleToolPrivate *private;
GimpRectangleOptions *options;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (object);
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (object);
g_object_get (options,
"guide", &private->guide,
NULL);
g_signal_connect_object (options, "notify",
G_CALLBACK (gimp_rectangle_tool_options_notify),
rectangle, 0);
}
void
gimp_rectangle_tool_control (GimpTool *tool,
GimpToolAction action,
GimpDisplay *display)
{
GimpRectangleTool *rectangle = GIMP_RECTANGLE_TOOL (tool);
switch (action)
{
case GIMP_TOOL_ACTION_PAUSE:
break;
case GIMP_TOOL_ACTION_RESUME:
gimp_rectangle_tool_configure (rectangle);
break;
case GIMP_TOOL_ACTION_HALT:
gimp_rectangle_tool_halt (rectangle);
break;
default:
break;
}
}
void
gimp_rectangle_tool_button_press (GimpTool *tool,
GimpCoords *coords,
guint32 time,
GdkModifierType state,
GimpDisplay *display)
{
GimpRectangleTool *rectangle;
GimpDrawTool *draw_tool;
GimpRectangleToolPrivate *private;
GimpRectangleOptions *options;
gint x, y;
gint snap_x, snap_y;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (tool));
rectangle = GIMP_RECTANGLE_TOOL (tool);
draw_tool = GIMP_DRAW_TOOL (tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (tool);
x = ROUND (coords->x);
y = ROUND (coords->y);
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
if (display != tool->display)
{
if (gimp_draw_tool_is_active (draw_tool))
gimp_draw_tool_stop (draw_tool);
gimp_rectangle_tool_set_function (rectangle, RECT_CREATING);
g_object_set (rectangle,
"x1", x,
"y1", y,
"x2", x,
"y2", y,
NULL);
gimp_rectangle_tool_start (rectangle, display);
}
/* save existing shape in case of cancellation */
private->saved_x1 = private->x1;
private->saved_y1 = private->y1;
private->saved_x2 = private->x2;
private->saved_y2 = private->y2;
g_object_get (options,
"center-x", &private->saved_center_x,
"center-y", &private->saved_center_y,
NULL);
switch (private->function)
{
case RECT_CREATING:
g_object_set (options,
"center-x", (gdouble) x,
"center-y", (gdouble) y,
NULL);
g_object_set (rectangle,
"x1", x,
"y1", y,
"x2", x,
"y2", y,
NULL);
gimp_tool_control_set_snap_offsets (tool->control, 0, 0, 0, 0);
break;
case RECT_RESIZING_UPPER_LEFT:
gimp_tool_control_set_snap_offsets (tool->control,
private->x1 - coords->x,
private->y1 - coords->y,
0, 0);
break;
case RECT_RESIZING_UPPER_RIGHT:
gimp_tool_control_set_snap_offsets (tool->control,
private->x2 - coords->x,
private->y1 - coords->y,
0, 0);
break;
case RECT_RESIZING_LOWER_LEFT:
gimp_tool_control_set_snap_offsets (tool->control,
private->x1 - coords->x,
private->y2 - coords->y,
0, 0);
break;
case RECT_RESIZING_LOWER_RIGHT:
gimp_tool_control_set_snap_offsets (tool->control,
private->x2 - coords->x,
private->y2 - coords->y,
0, 0);
break;
case RECT_RESIZING_LEFT:
gimp_tool_control_set_snap_offsets (tool->control,
private->x1 - coords->x, 0,
0, 0);
break;
case RECT_RESIZING_RIGHT:
gimp_tool_control_set_snap_offsets (tool->control,
private->x2 - coords->x, 0,
0, 0);
break;
case RECT_RESIZING_TOP:
gimp_tool_control_set_snap_offsets (tool->control,
0, private->y1 - coords->y,
0, 0);
break;
case RECT_RESIZING_BOTTOM:
gimp_tool_control_set_snap_offsets (tool->control,
0, private->y2 - coords->y,
0, 0);
break;
case RECT_MOVING:
gimp_tool_control_set_snap_offsets (tool->control,
private->x1 - coords->x,
private->y1 - coords->y,
private->x2 - private->x1,
private->y2 - private->y1);
break;
default:
break;
}
gimp_tool_control_get_snap_offsets (tool->control,
&snap_x, &snap_y, NULL, NULL);
x += snap_x;
y += snap_y;
private->pressx = x;
private->pressy = y;
private->startx = x;
private->starty = y;
private->lastx = x;
private->lasty = y;
gimp_tool_control_activate (tool->control);
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
}
void
gimp_rectangle_tool_button_release (GimpTool *tool,
GimpCoords *coords,
guint32 time,
GdkModifierType state,
GimpButtonReleaseType release_type,
GimpDisplay *display)
{
GimpRectangleTool *rectangle;
GimpRectangleToolPrivate *private;
GimpRectangleOptions *options;
gboolean auto_shrink;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (tool));
rectangle = GIMP_RECTANGLE_TOOL (tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (tool);
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
g_object_get (options,
"auto-shrink", &auto_shrink,
NULL);
if (auto_shrink)
gimp_rectangle_tool_auto_shrink (rectangle);
gimp_tool_control_halt (tool->control);
if (private->function == RECT_EXECUTING)
gimp_tool_pop_status (tool, display);
switch (release_type)
{
case GIMP_BUTTON_RELEASE_NORMAL:
gimp_rectangle_tool_rectangle_changed (rectangle);
break;
case GIMP_BUTTON_RELEASE_CANCEL:
g_object_set (options,
"center-x", private->saved_center_x,
"center-y", private->saved_center_y,
NULL);
g_object_set (rectangle,
"x1", private->saved_x1,
"y1", private->saved_y1,
"x2", private->saved_x2,
"y2", private->saved_y2,
NULL);
break;
case GIMP_BUTTON_RELEASE_CLICK:
if (gimp_rectangle_tool_execute (rectangle))
gimp_rectangle_tool_halt (rectangle);
break;
case GIMP_BUTTON_RELEASE_NO_MOTION:
break;
}
gimp_tool_control_set_snap_offsets (tool->control, 0, 0, 0, 0);
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
}
void
gimp_rectangle_tool_motion (GimpTool *tool,
GimpCoords *coords,
guint32 time,
GdkModifierType state,
GimpDisplay *display)
{
GimpRectangleTool *rectangle;
GimpRectangleToolPrivate *private;
GimpRectangleOptions *options;
gint x1, y1, x2, y2;
gint curx, cury;
gint snap_x, snap_y;
gint inc_x, inc_y;
gboolean fixed_width;
gboolean fixed_height;
gboolean fixed_aspect;
gboolean fixed_center;
gdouble width, height;
gdouble center_x, center_y;
gboolean created_now = FALSE;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (tool));
rectangle = GIMP_RECTANGLE_TOOL (tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (tool);
/* This is the only case when the motion events should be ignored --
* we're just waiting for the button release event to execute.
*/
if (private->function == RECT_EXECUTING)
return;
curx = ROUND (coords->x);
cury = ROUND (coords->y);
gimp_tool_control_get_snap_offsets (tool->control,
&snap_x, &snap_y, NULL, NULL);
curx += snap_x;
cury += snap_y;
/* If there have been no changes... return */
if (private->lastx == curx && private->lasty == cury)
return;
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
inc_x = curx - private->startx;
inc_y = cury - private->starty;
g_object_get (options,
"fixed-width", &fixed_width,
"fixed-height", &fixed_height,
"fixed-aspect", &fixed_aspect,
"fixed-center", &fixed_center,
"width", &width,
"height", &height,
"center-x", &center_x,
"center-y", &center_y,
NULL);
x1 = private->x1;
y1 = private->y1;
x2 = private->x2;
y2 = private->y2;
switch (private->function)
{
case RECT_INACTIVE:
g_warning ("function is RECT_INACTIVE while mouse is moving");
break;
case RECT_CREATING:
break;
case RECT_RESIZING_UPPER_LEFT:
case RECT_RESIZING_LOWER_LEFT:
case RECT_RESIZING_LEFT:
x1 = private->x1 + inc_x;
if (fixed_width)
x2 = x1 + width;
else if (fixed_center)
x2 = x1 + 2 * (center_x - x1);
break;
case RECT_RESIZING_UPPER_RIGHT:
case RECT_RESIZING_LOWER_RIGHT:
case RECT_RESIZING_RIGHT:
x2 = private->x2 + inc_x;
if (fixed_width)
x1 = x2 - width;
else if (fixed_center)
x1 = x2 - 2 * (x2 - center_x);
break;
case RECT_RESIZING_BOTTOM:
case RECT_RESIZING_TOP:
x1 = private->x1;
x2 = private->x2;
break;
case RECT_MOVING:
x1 = private->x1 + inc_x;
x2 = private->x2 + inc_x;
break;
}
switch (private->function)
{
case RECT_CREATING:
break;
case RECT_RESIZING_UPPER_LEFT:
case RECT_RESIZING_UPPER_RIGHT:
case RECT_RESIZING_TOP:
y1 = private->y1 + inc_y;
if (fixed_height)
y2 = y1 + height;
else if (fixed_center)
y2 = y1 + 2 * (center_y - y1);
break;
case RECT_RESIZING_LOWER_LEFT:
case RECT_RESIZING_LOWER_RIGHT:
case RECT_RESIZING_BOTTOM:
y2 = private->y2 + inc_y;
if (fixed_height)
y1 = y2 - height;
else if (fixed_center)
y1 = y2 - 2 * (y2 - center_y);
break;
case RECT_RESIZING_RIGHT:
case RECT_RESIZING_LEFT:
y1 = private->y1;
y2 = private->y2;
break;
case RECT_MOVING:
y1 = private->y1 + inc_y;
y2 = private->y2 + inc_y;
break;
}
if (fixed_aspect)
{
gdouble aspect;
gdouble numerator, denominator;
g_object_get (options,
"aspect-numerator", &numerator,
"aspect-denominator", &denominator,
NULL);
aspect = CLAMP (numerator / denominator,
1.0 / display->image->height,
display->image->width);
switch (private->function)
{
case RECT_RESIZING_UPPER_LEFT:
/* The same basically happens for each corner, just with a
* different fixed corner. To keep within aspect ratio and
* at the same time keep the cursor on one edge if not the
* corner itself: - calculate the two positions of the
* corner in question on the base of the current mouse
* cursor position and the fixed corner opposite the one
* selected. - decide on which egde we are inside the
* rectangle dimension - if we are on the inside of the
* vertical edge then we use the x position of the cursor,
* otherwise we are on the inside (or close enough) of the
* horizontal edge and then we use the y position of the
* cursor for the base of our new corner.
*/
x1 = private->x2 - (private->y2 - cury) * aspect + 0.5;
y1 = private->y2 - (private->x2 - curx) / aspect + 0.5;
if ((y1 < cury) && (cury < y2))
x1 = curx;
else
y1 = cury;
if (fixed_center)
{
x2 = x1 + 2 * (center_x - x1);
y2 = y1 + 2 * (center_y - y1);
}
break;
case RECT_RESIZING_UPPER_RIGHT:
x2 = private->x1 + (private->y2 - cury) * aspect + 0.5;
y1 = private->y2 - (curx - private->x1) / aspect + 0.5;
if ((y1 < cury) && (cury < y2))
x2 = curx;
else
y1 = cury;
if (fixed_center)
{
x1 = x2 - 2 * (x2 - center_x);
y2 = y1 + 2 * (center_y - y1);
}
break;
case RECT_RESIZING_LOWER_LEFT:
x1 = private->x2 - (cury - private->y1) * aspect + 0.5;
y2 = private->y1 + (private->x2 - curx) / aspect + 0.5;
if ((y1 < cury) && (cury < y2))
x1 = curx;
else
y2 = cury;
if (fixed_center)
{
x2 = x1 + 2 * (center_x - x1);
y1 = y2 - 2 * (y2 - center_y);
}
break;
case RECT_RESIZING_LOWER_RIGHT:
x2 = private->x1 + (cury - private->y1) * aspect + 0.5;
y2 = private->y1 + (curx - private->x1) / aspect + 0.5;
if ((y1 < cury) && (cury < y2))
x2 = curx;
else
y2 = cury;
if (fixed_center)
{
x1 = x2 - 2 * (x2 - center_x);
y1 = y2 - 2 * (y2 - center_y);
}
break;
case RECT_RESIZING_TOP:
x2 = private->x1 + (private->y2 - y1) * aspect + 0.5;
if (fixed_center)
x1 = x2 - 2 * (x2 - center_x);
break;
case RECT_RESIZING_LEFT:
/* When resizing the left hand delimiter then the aspect
* dictates the height of the result, any inc_y is redundant
* and not relevant to the result
*/
y2 = private->y1 + (private->x2 - x1) / aspect + 0.5;
if (fixed_center)
y1 = y2 - 2 * (y2 - center_y);
break;
case RECT_RESIZING_BOTTOM:
x2 = private->x1 + (y2 - private->y1) * aspect + 0.5;
if (fixed_center)
x1 = x2 - 2 * (x2 - center_x);
break;
case RECT_RESIZING_RIGHT:
/* When resizing the right hand delimiter then the aspect
* dictates the height of the result, any inc_y is redundant
* and not relevant to the result
*/
y2 = private->y1 + (x2 - private->x1) / aspect + 0.5;
if (fixed_center)
y1 = y2 - 2 * (y2 - center_y);
break;
default:
break;
}
}
private->lastx = curx;
private->lasty = cury;
/* Check to see whether the new rectangle obeys the boundary
* constraints, if any, and constrain it.
*/
gimp_rectangle_tool_constrain (rectangle, &x1, &y1, &x2, &y2);
/* set startx, starty according to function, to keep rect on cursor */
switch (private->function)
{
case RECT_RESIZING_UPPER_LEFT:
private->startx = x1;
private->starty = y1;
break;
case RECT_RESIZING_UPPER_RIGHT:
private->startx = x2;
private->starty = y1;
break;
case RECT_RESIZING_LOWER_LEFT:
private->startx = x1;
private->starty = y2;
break;
case RECT_RESIZING_LOWER_RIGHT:
private->startx = x2;
private->starty = y2;
break;
case RECT_RESIZING_TOP:
private->startx = curx;
private->starty = y1;
break;
case RECT_RESIZING_LEFT:
private->startx = x1;
private->starty = cury;
break;
case RECT_RESIZING_BOTTOM:
private->startx = curx;
private->starty = y2;
break;
case RECT_RESIZING_RIGHT:
private->startx = x2;
private->starty = cury;
break;
case RECT_MOVING:
private->startx = curx;
private->starty = cury;
break;
default:
break;
}
/* fix function if user has "flipped" the rectangle */
if (! created_now)
gimp_rectangle_tool_check_function (rectangle,
&x1, &y1,
&x2, &y2);
/* make sure that the coords are in bounds */
g_object_set (rectangle,
"x1", MIN (x1, x2),
"y1", MIN (y1, y2),
"x2", MAX (x1, x2),
"y2", MAX (y1, y2),
NULL);
/* recalculate the coordinates for rectangle_draw based on the new values */
gimp_rectangle_tool_configure (rectangle);
gimp_rectangle_tool_update_options (rectangle, display);
if (private->function != RECT_MOVING &&
private->function != RECT_EXECUTING)
{
gint w, h;
gimp_tool_pop_status (tool, display);
w = private->x2 - private->x1;
h = private->y2 - private->y1;
if (w > 0 && h > 0)
gimp_tool_push_status_coords (tool, display,
_("Rectangle: "), w, " × ", h, NULL);
}
if (private->function == RECT_CREATING)
{
GimpRectangleFunction function = RECT_CREATING;
if (inc_x < 0 && inc_y < 0)
function = RECT_RESIZING_UPPER_LEFT;
else if (inc_x < 0 && inc_y > 0)
function = RECT_RESIZING_LOWER_LEFT;
else if (inc_x > 0 && inc_y < 0)
function = RECT_RESIZING_UPPER_RIGHT;
else if (inc_x > 0 && inc_y > 0)
function = RECT_RESIZING_LOWER_RIGHT;
created_now = TRUE;
gimp_rectangle_tool_set_function (rectangle, function);
}
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
}
void
gimp_rectangle_tool_active_modifier_key (GimpTool *tool,
GdkModifierType key,
gboolean press,
GdkModifierType state,
GimpDisplay *display)
{
GimpRectangleTool *rectangle;
GimpRectangleOptions *options;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (tool));
rectangle = GIMP_RECTANGLE_TOOL (tool);
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (tool);
if (key == GDK_SHIFT_MASK)
{
gboolean fixed_aspect;
g_object_get (options,
"fixed-aspect", &fixed_aspect,
NULL);
g_object_set (options,
"fixed-aspect", ! fixed_aspect,
NULL);
}
if (key == GDK_CONTROL_MASK)
{
gboolean fixed_center;
g_object_get (options,
"fixed-center", &fixed_center,
NULL);
g_object_set (options,
"fixed-center", ! fixed_center,
NULL);
if (! fixed_center)
{
gint pressx, pressy;
gdouble center_x, center_y;
gimp_rectangle_tool_get_press_coords (rectangle, &pressx, &pressy);
center_x = pressx;
center_y = pressy;
g_object_set (options,
"center-x", center_x,
"center-y", center_y,
NULL);
}
}
}
static void swap_ints (gint *i,
gint *j)
{
gint tmp;
tmp = *i;
*i = *j;
*j = tmp;
}
/*
* gimp_rectangle_tool_check_function() is needed to deal with
* situations where the user drags a corner or edge across one of the
* existing edges, thereby changing its function. Ugh.
*/
static void
gimp_rectangle_tool_check_function (GimpRectangleTool *rectangle,
gint *x1, gint *y1,
gint *x2, gint *y2)
{
GimpRectangleToolPrivate *private;
GimpRectangleFunction function;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rectangle);
function = private->function;
if (*x2 < *x1)
{
swap_ints (x1, x2);
switch (function)
{
case RECT_RESIZING_UPPER_LEFT:
function = RECT_RESIZING_UPPER_RIGHT;
break;
case RECT_RESIZING_UPPER_RIGHT:
function = RECT_RESIZING_UPPER_LEFT;
break;
case RECT_RESIZING_LOWER_LEFT:
function = RECT_RESIZING_LOWER_RIGHT;
break;
case RECT_RESIZING_LOWER_RIGHT:
function = RECT_RESIZING_LOWER_LEFT;
break;
case RECT_RESIZING_LEFT:
function = RECT_RESIZING_RIGHT;
break;
case RECT_RESIZING_RIGHT:
function = RECT_RESIZING_LEFT;
break;
/* avoid annoying warnings about unhandled enums */
default:
break;
}
}
if (*y2 < *y1)
{
swap_ints (y1, y2);
switch (function)
{
case RECT_RESIZING_UPPER_LEFT:
function = RECT_RESIZING_LOWER_LEFT;
break;
case RECT_RESIZING_UPPER_RIGHT:
function = RECT_RESIZING_LOWER_RIGHT;
break;
case RECT_RESIZING_LOWER_LEFT:
function = RECT_RESIZING_UPPER_LEFT;
break;
case RECT_RESIZING_LOWER_RIGHT:
function = RECT_RESIZING_UPPER_RIGHT;
break;
case RECT_RESIZING_TOP:
function = RECT_RESIZING_BOTTOM;
break;
case RECT_RESIZING_BOTTOM:
function = RECT_RESIZING_TOP;
break;
default:
break;
}
}
gimp_rectangle_tool_set_function (rectangle, function);
#undef SWAP
}
gboolean
gimp_rectangle_tool_key_press (GimpTool *tool,
GdkEventKey *kevent,
GimpDisplay *display)
{
GimpRectangleTool *rectangle;
GimpRectangleToolPrivate *private;
gint dx = 0;
gint dy = 0;
gint inc_x1 = 0;
gint inc_y1 = 0;
gint inc_x2 = 0;
gint inc_y2 = 0;
gint x1, y1;
gint x2, y2;
g_return_val_if_fail (GIMP_IS_RECTANGLE_TOOL (tool), FALSE);
if (display != tool->display)
return FALSE;
rectangle = GIMP_RECTANGLE_TOOL (tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
switch (kevent->keyval)
{
case GDK_Up:
dy = -1;
break;
case GDK_Left:
dx = -1;
break;
case GDK_Right:
dx = 1;
break;
case GDK_Down:
dy = 1;
break;
case GDK_KP_Enter:
case GDK_Return:
if (gimp_rectangle_tool_execute (rectangle))
gimp_rectangle_tool_halt (rectangle);
return TRUE;
case GDK_Escape:
gimp_rectangle_tool_cancel (rectangle);
gimp_rectangle_tool_halt (rectangle);
return TRUE;
default:
return FALSE;
}
/* If the shift key is down, move by an accelerated increment */
if (kevent->state & GDK_SHIFT_MASK)
{
dx *= ARROW_VELOCITY;
dy *= ARROW_VELOCITY;
}
/* Resize the rectangle if the mouse is over a handle, otherwise move it */
switch (private->function)
{
case RECT_RESIZING_UPPER_LEFT:
inc_x1 = dx;
inc_y1 = dy;
break;
case RECT_RESIZING_UPPER_RIGHT:
inc_x2 = dx;
inc_y1 = dy;
break;
case RECT_RESIZING_LOWER_LEFT:
inc_x1 = dx;
inc_y2 = dy;
break;
case RECT_RESIZING_LOWER_RIGHT:
inc_x2 = dx;
inc_y2 = dy;
break;
case RECT_RESIZING_LEFT:
inc_x1 = dx;
break;
case RECT_RESIZING_RIGHT:
inc_x2 = dx;
break;
case RECT_RESIZING_TOP:
inc_y1 = dy;
break;
case RECT_RESIZING_BOTTOM:
inc_y2 = dy;
break;
default:
inc_x1 = inc_x2 = dx;
inc_y1 = inc_y2 = dy;
break;
}
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
x1 = private->x1 + inc_x1;
y1 = private->y1 + inc_y1;
x2 = private->x2 + inc_x2;
y2 = private->y2 + inc_y2;
gimp_rectangle_tool_check_function (rectangle, &x1, &y1, &x2, &y2);
g_object_set (rectangle,
"x1", x1,
"y1", y1,
"x2", x2,
"y2", y2,
NULL);
gimp_rectangle_tool_configure (rectangle);
gimp_rectangle_tool_update_options (rectangle, display);
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
gimp_rectangle_tool_rectangle_changed (rectangle);
/* Evil hack to suppress oper updates. We do this because we don't
* want the rectangle tool to change function while the rectangle
* is being resized or moved using the keyboard.
*/
private->suppress_updates = 2;
return TRUE;
}
void
gimp_rectangle_tool_oper_update (GimpTool *tool,
GimpCoords *coords,
GdkModifierType state,
gboolean proximity,
GimpDisplay *display)
{
GimpRectangleToolPrivate *private;
GimpDrawTool *draw_tool;
gint function;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (tool));
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
draw_tool = GIMP_DRAW_TOOL (tool);
if (tool->display != display)
return;
if (private->suppress_updates)
{
private->suppress_updates--;
return;
}
if (coords->x > private->x1 && coords->x < private->x2 &&
coords->y > private->y1 && coords->y < private->y2)
{
GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (tool->display->shell);
gdouble handle_w = private->handle_w / shell->scale_x;
gdouble handle_h = private->handle_h / shell->scale_y;
if (gimp_draw_tool_on_handle (draw_tool, display,
coords->x, coords->y,
GIMP_HANDLE_SQUARE,
private->x1, private->y1,
private->handle_w, private->handle_h,
GTK_ANCHOR_NORTH_WEST,
FALSE))
{
function = RECT_RESIZING_UPPER_LEFT;
}
else if (gimp_draw_tool_on_handle (draw_tool, display,
coords->x, coords->y,
GIMP_HANDLE_SQUARE,
private->x2, private->y2,
private->handle_w, private->handle_h,
GTK_ANCHOR_SOUTH_EAST,
FALSE))
{
function = RECT_RESIZING_LOWER_RIGHT;
}
else if (gimp_draw_tool_on_handle (draw_tool, display,
coords->x, coords->y,
GIMP_HANDLE_SQUARE,
private->x2, private->y1,
private->handle_w, private->handle_h,
GTK_ANCHOR_NORTH_EAST,
FALSE))
{
function = RECT_RESIZING_UPPER_RIGHT;
}
else if (gimp_draw_tool_on_handle (draw_tool, display,
coords->x, coords->y,
GIMP_HANDLE_SQUARE,
private->x1, private->y2,
private->handle_w, private->handle_h,
GTK_ANCHOR_SOUTH_WEST,
FALSE))
{
function = RECT_RESIZING_LOWER_LEFT;
}
else if ((fabs (coords->x - private->x1) < (handle_w * 2) / 3))
{
function = RECT_RESIZING_LEFT;
}
else if ((fabs (coords->x - private->x2) < (handle_w * 2) / 3))
{
function = RECT_RESIZING_RIGHT;
}
else if ((fabs (coords->y - private->y1) < (handle_h * 2) / 3))
{
function = RECT_RESIZING_TOP;
}
else if ((fabs (coords->y - private->y2) < (handle_h * 2) / 3))
{
function = RECT_RESIZING_BOTTOM;
}
else
{
function = RECT_MOVING;
}
}
else
{
/* otherwise, the new function will be creating, since we want
* to start a new rectangle
*/
function = RECT_CREATING;
}
gimp_rectangle_tool_set_function (GIMP_RECTANGLE_TOOL (tool), function);
}
void
gimp_rectangle_tool_cursor_update (GimpTool *tool,
GimpCoords *coords,
GdkModifierType state,
GimpDisplay *display)
{
GimpRectangleTool *rectangle;
GimpRectangleToolPrivate *private;
GimpCursorType cursor = GIMP_CURSOR_CROSSHAIR_SMALL;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (tool));
rectangle = GIMP_RECTANGLE_TOOL (tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
if (tool->display == display)
{
switch (private->function)
{
case RECT_CREATING:
cursor = GIMP_CURSOR_CROSSHAIR_SMALL;
break;
case RECT_MOVING:
cursor = GIMP_CURSOR_MOVE;
break;
case RECT_RESIZING_UPPER_LEFT:
cursor = GIMP_CURSOR_CORNER_TOP_LEFT;
break;
case RECT_RESIZING_UPPER_RIGHT:
cursor = GIMP_CURSOR_CORNER_TOP_RIGHT;
break;
case RECT_RESIZING_LOWER_LEFT:
cursor = GIMP_CURSOR_CORNER_BOTTOM_LEFT;
break;
case RECT_RESIZING_LOWER_RIGHT:
cursor = GIMP_CURSOR_CORNER_BOTTOM_RIGHT;
break;
case RECT_RESIZING_LEFT:
cursor = GIMP_CURSOR_SIDE_LEFT;
break;
case RECT_RESIZING_RIGHT:
cursor = GIMP_CURSOR_SIDE_RIGHT;
break;
case RECT_RESIZING_TOP:
cursor = GIMP_CURSOR_SIDE_TOP;
break;
case RECT_RESIZING_BOTTOM:
cursor = GIMP_CURSOR_SIDE_BOTTOM;
break;
default:
break;
}
}
gimp_tool_control_set_cursor (tool->control, cursor);
}
void
gimp_rectangle_tool_draw (GimpDrawTool *draw_tool)
{
GimpTool *tool;
GimpRectangleToolPrivate *private;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (draw_tool));
tool = GIMP_TOOL (draw_tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
if (private->function == RECT_INACTIVE)
return;
gimp_draw_tool_draw_rectangle (draw_tool, FALSE,
private->x1,
private->y1,
private->x2 - private->x1,
private->y2 - private->y1,
FALSE);
switch (private->function)
{
case RECT_MOVING:
if (gimp_tool_control_is_active (tool->control))
break;
/* else fallthrough */
case RECT_CREATING:
gimp_draw_tool_draw_corner (draw_tool, FALSE,
private->x1, private->y1,
private->x2, private->y2,
private->handle_w, private->handle_h,
GTK_ANCHOR_NORTH_WEST, FALSE);
gimp_draw_tool_draw_corner (draw_tool, FALSE,
private->x1, private->y1,
private->x2, private->y2,
private->handle_w, private->handle_h,
GTK_ANCHOR_NORTH_EAST, FALSE);
gimp_draw_tool_draw_corner (draw_tool, FALSE,
private->x1, private->y1,
private->x2, private->y2,
private->handle_w, private->handle_h,
GTK_ANCHOR_SOUTH_WEST, FALSE);
gimp_draw_tool_draw_corner (draw_tool, FALSE,
private->x1, private->y1,
private->x2, private->y2,
private->handle_w, private->handle_h,
GTK_ANCHOR_SOUTH_EAST, FALSE);
break;
default:
{
GtkAnchorType anchor;
gint w, h;
anchor = gimp_rectangle_tool_get_anchor (private, &w, &h);
gimp_draw_tool_draw_corner (draw_tool,
! gimp_tool_control_is_active (tool->control),
private->x1, private->y1,
private->x2, private->y2,
w, h,
anchor, FALSE);
}
break;
}
gimp_rectangle_tool_draw_guides (draw_tool);
}
static void
gimp_rectangle_tool_draw_guides (GimpDrawTool *draw_tool)
{
GimpTool *tool;
GimpRectangleToolPrivate *private;
gint x1, x2, y1, y2;
tool = GIMP_TOOL (draw_tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (draw_tool);
x1 = private->x1;
x2 = private->x2;
y1 = private->y1;
y2 = private->y2;
switch (private->guide)
{
case GIMP_RECTANGLE_GUIDE_NONE:
break;
case GIMP_RECTANGLE_GUIDE_CENTER_LINES:
gimp_draw_tool_draw_line (draw_tool,
x1, (y1 + y2) / 2,
x2, (y1 + y2) / 2, FALSE);
gimp_draw_tool_draw_line (draw_tool,
(x1 + x2) / 2, y1,
(x1 + x2) / 2, y2, FALSE);
break;
case GIMP_RECTANGLE_GUIDE_THIRDS:
gimp_draw_tool_draw_line (draw_tool,
x1, (2 * y1 + y2) / 3,
x2, (2 * y1 + y2) / 3, FALSE);
gimp_draw_tool_draw_line (draw_tool,
x1, (y1 + 2 * y2) / 3,
x2, (y1 + 2 * y2) / 3, FALSE);
gimp_draw_tool_draw_line (draw_tool,
(2 * x1 + x2) / 3, y1,
(2 * x1 + x2) / 3, y2, FALSE);
gimp_draw_tool_draw_line (draw_tool,
(x1 + 2 * x2) / 3, y1,
(x1 + 2 * x2) / 3, y2, FALSE);
break;
case GIMP_RECTANGLE_GUIDE_GOLDEN:
gimp_draw_tool_draw_line (draw_tool,
x1,
(2 * y1 + (1 + sqrt(5)) * y2) / (3 + sqrt(5)),
x2,
(2 * y1 + (1 + sqrt(5)) * y2) / (3 + sqrt(5)),
FALSE);
gimp_draw_tool_draw_line (draw_tool,
x1,
((1 + sqrt(5)) * y1 + 2 * y2) / (3 + sqrt(5)),
x2,
((1 + sqrt(5)) * y1 + 2 * y2) / (3 + sqrt(5)),
FALSE);
gimp_draw_tool_draw_line (draw_tool,
(2 * x1 + (1 + sqrt(5)) * x2) / (3 + sqrt(5)),
y1,
(2 * x1 + (1 + sqrt(5)) * x2) / (3 + sqrt(5)),
y2,
FALSE);
gimp_draw_tool_draw_line (draw_tool,
((1 + sqrt(5)) * x1 + 2 * x2) / (3 + sqrt(5)),
y1,
((1 + sqrt(5)) * x1 + 2 * x2) / (3 + sqrt(5)),
y2, FALSE);
break;
}
}
void
gimp_rectangle_tool_configure (GimpRectangleTool *rectangle)
{
GimpTool *tool = GIMP_TOOL (rectangle);
GimpRectangleToolPrivate *private;
GimpRectangleOptions *options;
GimpDisplayShell *shell;
gint dx1, dx2;
gint dy1, dy2;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (tool);
if (! tool->display)
return;
gimp_rectangle_tool_set_highlight (rectangle);
shell = GIMP_DISPLAY_SHELL (tool->display->shell);
gimp_display_shell_transform_xy (shell,
private->x1, private->y1,
&dx1, &dy1,
FALSE);
gimp_display_shell_transform_xy (shell,
private->x2, private->y2,
&dx2, &dy2,
FALSE);
private->handle_w = (dx2 - dx1) / 3;
private->handle_h = (dy2 - dy1) / 3;
private->handle_w = CLAMP (private->handle_w, MIN_HANDLE_SIZE, HANDLE_SIZE);
private->handle_h = CLAMP (private->handle_h, MIN_HANDLE_SIZE, HANDLE_SIZE);
}
static void
gimp_rectangle_tool_start (GimpRectangleTool *rectangle,
GimpDisplay *display)
{
GimpTool *tool = GIMP_TOOL (rectangle);
tool->display = display;
gimp_rectangle_tool_configure (rectangle);
/* initialize the statusbar display */
gimp_tool_push_status_coords (tool, tool->display,
_("Rectangle: "), 0, " x ", 0, NULL);
gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), tool->display);
}
static void
gimp_rectangle_tool_halt (GimpRectangleTool *rectangle)
{
GimpTool *tool = GIMP_TOOL (rectangle);
if (tool->display)
gimp_display_shell_set_highlight (GIMP_DISPLAY_SHELL (tool->display->shell),
NULL);
if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (rectangle)))
gimp_draw_tool_stop (GIMP_DRAW_TOOL (rectangle));
if (gimp_tool_control_is_active (tool->control))
gimp_tool_control_halt (tool->control);
tool->display = NULL;
tool->drawable = NULL;
gimp_rectangle_tool_set_function (rectangle, RECT_INACTIVE);
}
gboolean
gimp_rectangle_tool_execute (GimpRectangleTool *rectangle)
{
GimpRectangleToolInterface *iface;
gboolean retval = FALSE;
iface = GIMP_RECTANGLE_TOOL_GET_INTERFACE (rectangle);
if (iface->execute)
{
GimpRectangleToolPrivate *private;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rectangle);
gimp_draw_tool_pause (GIMP_DRAW_TOOL (rectangle));
retval = iface->execute (rectangle,
private->x1,
private->y1,
private->x2 - private->x1,
private->y2 - private->y1);
gimp_rectangle_tool_configure (rectangle);
gimp_draw_tool_resume (GIMP_DRAW_TOOL (rectangle));
}
return retval;
}
void
gimp_rectangle_tool_cancel (GimpRectangleTool *rectangle)
{
GimpRectangleToolInterface *iface;
iface = GIMP_RECTANGLE_TOOL_GET_INTERFACE (rectangle);
if (iface->cancel)
iface->cancel (rectangle);
}
static void
gimp_rectangle_tool_update_options (GimpRectangleTool *rectangle,
GimpDisplay *display)
{
GimpRectangleToolPrivate *private;
GimpRectangleOptions *options;
gdouble x;
gdouble y;
gdouble width;
gdouble height;
gdouble center_x, center_y;
gboolean fixed_width;
gboolean fixed_height;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rectangle);
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (rectangle);
g_object_get (options,
"fixed-width", &fixed_width,
"fixed-height", &fixed_height,
NULL);
x = private->x1;
y = private->y1;
width = private->x2 - private->x1;
height = private->y2 - private->y1;
center_x = (private->x1 + private->x2) / 2.0;
center_y = (private->y1 + private->y2) / 2.0;
g_signal_handlers_block_by_func (options,
gimp_rectangle_tool_options_notify,
rectangle);
g_object_set (options,
"x0", x,
"y0", y,
NULL);
if (! fixed_width)
g_object_set (options,
"width", width,
NULL);
if (! fixed_height)
g_object_set (options,
"height", height,
NULL);
g_signal_handlers_unblock_by_func (options,
gimp_rectangle_tool_options_notify,
rectangle);
g_object_set (options,
"center-x", center_x,
"center-y", center_y,
NULL);
}
static void
gimp_rectangle_tool_synthesize_motion (GimpTool *tool,
gint function,
gint startx,
gint starty,
GimpCoords *coords)
{
GimpRectangleToolPrivate *private;
GimpRectangleFunction old_function;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
private->startx = startx;
private->starty = starty;
old_function = private->function;
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
gimp_rectangle_tool_set_function (GIMP_RECTANGLE_TOOL (tool), function);
gimp_rectangle_tool_motion (tool, coords, 0, 0, tool->display);
gimp_rectangle_tool_set_function (GIMP_RECTANGLE_TOOL (tool), old_function);
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
gimp_rectangle_tool_rectangle_changed (GIMP_RECTANGLE_TOOL (tool));
}
static void
gimp_rectangle_tool_options_notify (GimpRectangleOptions *options,
GParamSpec *pspec,
GimpRectangleTool *rectangle)
{
GimpTool *tool = GIMP_TOOL (rectangle);
GimpRectangleToolPrivate *private;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rectangle);
if (! strcmp (pspec->name, "guide"))
{
gimp_draw_tool_pause (GIMP_DRAW_TOOL (rectangle));
g_object_get (options,
"guide", &private->guide,
NULL);
gimp_draw_tool_resume (GIMP_DRAW_TOOL (rectangle));
return;
}
if (! tool->display)
return;
if (! strcmp (pspec->name, "x0"))
{
GimpCoords coords;
gdouble x;
g_object_get (options, "x0", &x, NULL);
coords.x = x;
coords.y = private->y1;
gimp_rectangle_tool_synthesize_motion (tool,
RECT_RESIZING_LEFT,
private->x1,
private->y1,
&coords);
}
else if (! strcmp (pspec->name, "y0"))
{
GimpCoords coords;
gdouble y;
g_object_get (options, "y0", &y, NULL);
coords.x = private->x1;
coords.y = y;
gimp_rectangle_tool_synthesize_motion (tool,
RECT_RESIZING_TOP,
private->x1,
private->y1,
&coords);
}
else if (! strcmp (pspec->name, "width"))
{
GimpCoords coords;
gdouble width;
g_object_get (options, "width", &width, NULL);
coords.x = private->x1 + width;
coords.y = private->y2;
gimp_rectangle_tool_synthesize_motion (tool,
RECT_RESIZING_RIGHT,
private->x2,
private->y2,
&coords);
}
else if (! strcmp (pspec->name, "height"))
{
GimpCoords coords;
gdouble height;
g_object_get (options, "height", &height, NULL);
coords.x = private->x2;
coords.y = private->y1 + height;
gimp_rectangle_tool_synthesize_motion (tool,
RECT_RESIZING_BOTTOM,
private->x2,
private->y2,
&coords);
}
else if (! strcmp (pspec->name, "fixed-aspect") ||
! strcmp (pspec->name, "aspect-numerator") ||
! strcmp (pspec->name, "aspect-denominator"))
{
gdouble numerator, denominator;
gboolean fixed_aspect;
g_object_get (options,
"aspect-numerator", &numerator,
"aspect-denominator", &denominator,
"fixed-aspect", &fixed_aspect,
NULL);
if (fixed_aspect)
{
GimpCoords coords;
gdouble aspect;
aspect = numerator / denominator;
coords.x = private->x2;
coords.y = private->y1 + (private->x2 - private->x1) / aspect;
gimp_rectangle_tool_synthesize_motion (tool,
RECT_RESIZING_BOTTOM,
private->x2,
private->y2,
&coords);
}
}
else if (! strcmp (pspec->name, "highlight"))
{
gimp_rectangle_tool_set_highlight (rectangle);
}
}
GimpRectangleFunction
gimp_rectangle_tool_get_function (GimpRectangleTool *rectangle)
{
g_return_val_if_fail (GIMP_IS_RECTANGLE_TOOL (rectangle), RECT_INACTIVE);
return GIMP_RECTANGLE_TOOL_GET_PRIVATE (rectangle)->function;
}
void
gimp_rectangle_tool_set_function (GimpRectangleTool *rectangle,
GimpRectangleFunction function)
{
GimpRectangleToolPrivate *private;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (rectangle));
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rectangle);
/* redraw the tool when the function changes */
/* FIXME: should also update the cursor */
if (private->function != function)
{
GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (rectangle);
gimp_draw_tool_pause (draw_tool);
private->function = function;
gimp_draw_tool_resume (draw_tool);
}
}
static void
gimp_rectangle_tool_rectangle_changed (GimpRectangleTool *rectangle)
{
g_signal_emit (rectangle,
gimp_rectangle_tool_signals[RECTANGLE_CHANGED], 0);
}
/*
* check whether the coordinates extend outside the bounds of the image
* or active drawable, if it is constrained not to. If it does,truncates
* the corrners to the constraints.
*/
void
gimp_rectangle_tool_constrain (GimpRectangleTool *rectangle,
gint *x1,
gint *y1,
gint *x2,
gint *y2)
{
GimpTool *tool = GIMP_TOOL (rectangle);
GimpRectangleToolPrivate *private;
GimpRectangleConstraint constraint;
GimpImage *image;
gint min_x, min_y;
gint max_x, max_y;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rectangle);
constraint = gimp_rectangle_tool_get_constraint (rectangle);
image = tool->display->image;
switch (constraint)
{
case GIMP_RECTANGLE_CONSTRAIN_NONE:
return ;
case GIMP_RECTANGLE_CONSTRAIN_IMAGE:
min_x = 0;
min_y = 0;
max_x = image->width;
max_y = image->height;
break;
case GIMP_RECTANGLE_CONSTRAIN_DRAWABLE:
{
GimpItem *item = GIMP_ITEM (tool->drawable);
gimp_item_offsets (item, &min_x, &min_y);
max_x = min_x + gimp_item_width (item);
max_y = min_y + gimp_item_height (item);
}
break;
default:
g_warning ("Invalid rectangle constraint.\n");
return;
}
if (*x1 < min_x)
{
*x1 = min_x;
}
if (*x2 > max_x)
{
*x2 = max_x;
}
if (*y1 < min_y)
{
*y1 = min_y;
}
if (*y2 > max_y)
{
*y2 = max_y;
}
}
static void
gimp_rectangle_tool_auto_shrink (GimpRectangleTool *rectangle)
{
GimpTool *tool = GIMP_TOOL (rectangle);
GimpRectangleToolPrivate *private;
GimpDisplay *display = tool->display;
gint width;
gint height;
gint offset_x = 0;
gint offset_y = 0;
gint x1, y1;
gint x2, y2;
gint shrunk_x1;
gint shrunk_y1;
gint shrunk_x2;
gint shrunk_y2;
gboolean shrink_merged;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rectangle);
if (! display)
return;
width = display->image->width;
height = display->image->height;
g_object_get (gimp_tool_get_options (tool),
"shrink-merged", &shrink_merged,
NULL);
x1 = private->x1 - offset_x > 0 ? private->x1 - offset_x : 0;
x2 = private->x2 - offset_x < width ? private->x2 - offset_x : width;
y1 = private->y1 - offset_y > 0 ? private->y1 - offset_y : 0;
y2 = private->y2 - offset_y < height ? private->y2 - offset_y : height;
if (gimp_image_crop_auto_shrink (display->image,
x1, y1, x2, y2,
! shrink_merged,
&shrunk_x1,
&shrunk_y1,
&shrunk_x2,
&shrunk_y2))
{
gimp_draw_tool_pause (GIMP_DRAW_TOOL (rectangle));
g_object_set (rectangle,
"x1", offset_x + shrunk_x1,
"y1", offset_y + shrunk_y1,
"x2", offset_x + shrunk_x2,
"y2", offset_y + shrunk_y2,
NULL);
gimp_rectangle_tool_configure (rectangle);
gimp_draw_tool_resume (GIMP_DRAW_TOOL (rectangle));
}
}
static GtkAnchorType
gimp_rectangle_tool_get_anchor (GimpRectangleToolPrivate *private,
gint *w,
gint *h)
{
*w = private->handle_w;
*h = private->handle_h;
switch (private->function)
{
case RECT_RESIZING_UPPER_LEFT:
return GTK_ANCHOR_NORTH_WEST;
case RECT_RESIZING_UPPER_RIGHT:
return GTK_ANCHOR_NORTH_EAST;
case RECT_RESIZING_LOWER_LEFT:
return GTK_ANCHOR_SOUTH_WEST;
case RECT_RESIZING_LOWER_RIGHT:
return GTK_ANCHOR_SOUTH_EAST;
case RECT_RESIZING_LEFT:
*w = (*w * 2) / 3;
return GTK_ANCHOR_WEST;
case RECT_RESIZING_RIGHT:
*w = (*w * 2) / 3;
return GTK_ANCHOR_EAST;
case RECT_RESIZING_TOP:
*h = (*h * 2) / 3;
return GTK_ANCHOR_NORTH;
case RECT_RESIZING_BOTTOM:
*h = (*h * 2) / 3;
return GTK_ANCHOR_SOUTH;
default:
return GTK_ANCHOR_CENTER;
}
}
static void
gimp_rectangle_tool_set_highlight (GimpRectangleTool *rectangle)
{
GimpTool *tool = GIMP_TOOL (rectangle);
GimpRectangleOptions *options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (tool);
GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (tool->display->shell);
gboolean highlight = FALSE;
g_object_get (options, "highlight", &highlight, NULL);
if (highlight)
{
GimpRectangleToolPrivate *private;
GdkRectangle rect;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
rect.x = private->x1;
rect.y = private->y1;
rect.width = private->x2 - private->x1;
rect.height = private->y2 - private->y1;
gimp_display_shell_set_highlight (shell, &rect);
}
else
{
gimp_display_shell_set_highlight (shell, NULL);
}
}