gimp/app/tools/gimprectangletool.c

4205 lines
142 KiB
C
Raw Normal View History

/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
* Copyright (C) 2007 Martin Nordholts
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gegl.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/gimp-utils.h"
#include "core/gimp.h"
#include "core/gimpchannel.h"
#include "core/gimpcontext.h"
#include "core/gimpimage-crop.h"
#include "core/gimpimage.h"
#include "core/gimpmarshal.h"
#include "core/gimppickable.h"
#include "core/gimptoolinfo.h"
#include "widgets/gimpwidgets-utils.h"
#include "display/gimpcanvasgroup.h"
#include "display/gimpdisplay.h"
#include "display/gimpdisplayshell.h"
#include "display/gimpdisplayshell-scroll.h"
#include "display/gimpdisplayshell-transform.h"
#include "gimpdrawtool.h"
#include "gimprectangleoptions.h"
#include "gimprectangletool.h"
#include "gimptoolcontrol.h"
#include "gimp-log.h"
#include "gimp-intl.h"
enum
{
RECTANGLE_CHANGE_COMPLETE,
LAST_SIGNAL
};
/* speed of key movement */
#define ARROW_VELOCITY 25
#define MAX_HANDLE_SIZE 50
#define MIN_HANDLE_SIZE 15
#define NARROW_MODE_HANDLE_SIZE 15
#define NARROW_MODE_THRESHOLD 45
typedef enum
{
CLAMPED_NONE = 0,
CLAMPED_LEFT = 1 << 0,
CLAMPED_RIGHT = 1 << 1,
CLAMPED_TOP = 1 << 2,
CLAMPED_BOTTOM = 1 << 3
} ClampedSide;
typedef enum
{
SIDE_TO_RESIZE_NONE,
SIDE_TO_RESIZE_LEFT,
SIDE_TO_RESIZE_RIGHT,
SIDE_TO_RESIZE_TOP,
SIDE_TO_RESIZE_BOTTOM,
SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY,
SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY,
} SideToResize;
#define FEQUAL(a,b) (fabs ((a) - (b)) < 0.0001)
#define PIXEL_FEQUAL(a,b) (fabs ((a) - (b)) < 0.5)
#define GIMP_RECTANGLE_TOOL_GET_PRIVATE(obj) \
(gimp_rectangle_tool_get_private (GIMP_RECTANGLE_TOOL (obj)))
typedef struct _GimpRectangleToolPrivate GimpRectangleToolPrivate;
struct _GimpRectangleToolPrivate
{
/* The following members are "constants", that is, variables that are setup
* during gimp_rectangle_tool_button_press and then only read.
*/
/* Wether or not the rectangle currently being rubber-banded was
* created from scatch.
*/
gboolean is_new;
/* Holds the coordinate that should be used as the "other side" when
* fixed-center is turned off.
*/
gdouble other_side_x;
gdouble other_side_y;
/* Holds the coordinate to be used as center when fixed-center is used. */
gdouble center_x_on_fixed_center;
gdouble center_y_on_fixed_center;
/* True when the rectangle is being adjusted (moved or
* rubber-banded).
*/
gboolean rect_adjusting;
/* The rest of the members are internal state variables, that is, variables
* that might change during the manipulation session of the rectangle. Make
* sure these variables are in consistent states.
*/
/* Coordinates of upper left and lower right rectangle corners. */
gdouble x1, y1;
gdouble x2, y2;
/* Integer coordinats of upper left corner and size. We must
* calculate this separately from the gdouble ones because sometimes
* we don't want to affect the integer size (e.g. when moving the
* rectangle), but that will be the case if we always calculate the
* integer coordinates based on rounded values of the gdouble
* coordinates even if the gdouble width remains constant.
*
* TODO: Change the internal double-representation of the rectangle
* to x,y width,height instead of x1,y1 x2,y2. That way we don't
* need to keep a separate representation of the integer version of
* the rectangle; rounding width an height will yield consistant
* results and not depend on position of the rectangle.
*/
gint x1_int, y1_int;
gint width_int, height_int;
/* What modification state the rectangle is in. What corner are we resizing,
* or are we moving the rectangle? etc.
*/
guint function;
/* How to constrain the rectangle. */
GimpRectangleConstraint constraint;
/* What precision the rectangle will apear to have externally (it
* will always be double internally)
*/
GimpRectanglePrecision precision;
/* Previous coordinate applied to the rectangle. */
gdouble lastx;
gdouble lasty;
/* Width and height of corner handles. */
gint corner_handle_w;
gint corner_handle_h;
/* Width and height of side handles. */
gint top_and_bottom_handle_w;
gint left_and_right_handle_h;
/* Wether or not the rectangle is in a 'narrow situation' i.e. it is
* too small for reasonable sized handle to be inside. In this case
* we put handles on the outside.
*/
gboolean narrow_mode;
/* For what scale the handle sizes is calculated. We must cache this
* so that we can differentiate between when the tool is resumed
* because of zoom level just has changed or because the highlight
* has just been updated.
*/
gdouble scale_x_used_for_handle_size_calculations;
gdouble scale_y_used_for_handle_size_calculations;
/* For saving in case of cancelation. */
gdouble saved_x1;
gdouble saved_y1;
gdouble saved_x2;
gdouble saved_y2;
gint suppress_updates;
};
static void gimp_rectangle_tool_iface_base_init (GimpRectangleToolInterface *iface);
static GimpRectangleToolPrivate *
gimp_rectangle_tool_get_private (GimpRectangleTool *rect_tool);
static void gimp_rectangle_tool_start (GimpRectangleTool *rect_tool,
GimpDisplay *display);
static void gimp_rectangle_tool_halt (GimpRectangleTool *rect_tool);
static void gimp_rectangle_tool_update_options (GimpRectangleTool *rect_tool,
GimpDisplay *display);
static void gimp_rectangle_tool_options_notify (GimpRectangleOptions *options,
GParamSpec *pspec,
GimpRectangleTool *rect_tool);
static void gimp_rectangle_tool_shell_scrolled (GimpDisplayShell *options,
GimpRectangleTool *rect_tool);
static void gimp_rectangle_tool_check_function (GimpRectangleTool *rect_tool);
static void gimp_rectangle_tool_rectangle_change_complete
(GimpRectangleTool *rect_tool);
static void gimp_rectangle_tool_auto_shrink (GimpRectangleTool *rect_tool);
static gboolean gimp_rectangle_tool_coord_outside (GimpRectangleTool *rect_tool,
const GimpCoords *coords);
static gboolean gimp_rectangle_tool_coord_on_handle (GimpRectangleTool *rect_tool,
const GimpCoords *coords,
GimpHandleAnchor anchor);
static GimpHandleAnchor gimp_rectangle_tool_get_anchor (GimpRectangleToolPrivate *private);
static void gimp_rectangle_tool_update_highlight (GimpRectangleTool *rect_tool);
static gboolean gimp_rectangle_tool_rect_rubber_banding_func
(GimpRectangleTool *rect_tool);
static gboolean gimp_rectangle_tool_rect_adjusting_func (GimpRectangleTool *rect_tool);
static void gimp_rectangle_tool_update_handle_sizes (GimpRectangleTool *rect_tool);
static gboolean gimp_rectangle_tool_scale_has_changed (GimpRectangleTool *rect_tool);
static void gimp_rectangle_tool_get_other_side (GimpRectangleTool *rect_tool,
gdouble **other_x,
gdouble **other_y);
static void gimp_rectangle_tool_get_other_side_coord (GimpRectangleTool *rect_tool,
gdouble *other_side_x,
gdouble *other_side_y);
static void gimp_rectangle_tool_set_other_side_coord (GimpRectangleTool *rect_tool,
gdouble other_side_x,
gdouble other_side_y);
static void gimp_rectangle_tool_apply_coord (GimpRectangleTool *rect_tool,
gdouble coord_x,
gdouble coord_y);
static void gimp_rectangle_tool_setup_snap_offsets (GimpRectangleTool *rect_tool,
const GimpCoords *coords);
static void gimp_rectangle_tool_clamp (GimpRectangleTool *rect_tool,
ClampedSide *clamped_sides,
GimpRectangleConstraint constraint,
gboolean symmetrically);
static void gimp_rectangle_tool_clamp_width (GimpRectangleTool *rect_tool,
ClampedSide *clamped_sides,
GimpRectangleConstraint constraint,
gboolean symmetrically);
static void gimp_rectangle_tool_clamp_height (GimpRectangleTool *rect_tool,
ClampedSide *clamped_sides,
GimpRectangleConstraint constraint,
gboolean symmetrically);
static void gimp_rectangle_tool_keep_inside (GimpRectangleTool *rect_tool,
GimpRectangleConstraint constraint);
static void gimp_rectangle_tool_keep_inside_horizontally
(GimpRectangleTool *rect_tool,
GimpRectangleConstraint constraint);
static void gimp_rectangle_tool_keep_inside_vertically
(GimpRectangleTool *rect_tool,
GimpRectangleConstraint constraint);
static void gimp_rectangle_tool_apply_fixed_width (GimpRectangleTool *rect_tool,
GimpRectangleConstraint constraint,
gdouble width);
static void gimp_rectangle_tool_apply_fixed_height (GimpRectangleTool *rect_tool,
GimpRectangleConstraint constraint,
gdouble height);
static void gimp_rectangle_tool_apply_aspect (GimpRectangleTool *rect_tool,
gdouble aspect,
gint clamped_sides);
static void gimp_rectangle_tool_update_with_coord (GimpRectangleTool *rect_tool,
gdouble new_x,
gdouble new_y);
static void gimp_rectangle_tool_apply_fixed_rule (GimpRectangleTool *rect_tool);
static void gimp_rectangle_tool_get_constraints (GimpRectangleTool *rect_tool,
gint *min_x,
gint *min_y,
gint *max_x,
gint *max_y,
GimpRectangleConstraint constraint);
static void gimp_rectangle_tool_handle_general_clamping
(GimpRectangleTool *rect_tool);
static void gimp_rectangle_tool_update_int_rect (GimpRectangleTool *rect_tool);
static void gimp_rectangle_tool_get_public_rect (GimpRectangleTool *rect_tool,
gdouble *pub_x1,
gdouble *pub_y1,
gdouble *pub_x2,
gdouble *pub_y2);
static void gimp_rectangle_tool_adjust_coord (GimpRectangleTool *rect_tool,
gdouble coord_x_input,
gdouble coord_y_input,
gdouble *coord_x_output,
gdouble *coord_y_output);
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_CHANGE_COMPLETE] =
g_signal_new ("rectangle-change-complete",
G_TYPE_FROM_INTERFACE (iface),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpRectangleToolInterface,
rectangle_change_complete),
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));
g_object_interface_install_property (iface,
g_param_spec_enum ("precision",
NULL, NULL,
GIMP_TYPE_RECTANGLE_PRECISION,
GIMP_RECTANGLE_PRECISION_INT,
GIMP_PARAM_READWRITE));
g_object_interface_install_property (iface,
g_param_spec_boolean ("narrow-mode",
NULL, NULL,
FALSE,
GIMP_PARAM_READWRITE));
iface->execute = NULL;
iface->cancel = NULL;
iface->rectangle_change_complete = NULL;
initialized = TRUE;
}
}
static void
gimp_rectangle_tool_private_finalize (GimpRectangleToolPrivate *private)
{
g_slice_free (GimpRectangleToolPrivate, 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_slice_new0 (GimpRectangleToolPrivate);
g_object_set_qdata_full (G_OBJECT (tool), private_key, private,
(GDestroyNotify)
gimp_rectangle_tool_private_finalize);
}
return private;
}
/**
* gimp_rectangle_tool_init:
* @rect_tool:
*
* Initializes the GimpRectangleTool.
**/
void
gimp_rectangle_tool_init (GimpRectangleTool *rect_tool)
{
/* No need to initialize anything yet. */
}
/**
* 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");
g_object_class_override_property (klass,
GIMP_RECTANGLE_TOOL_PROP_PRECISION,
"precision");
g_object_class_override_property (klass,
GIMP_RECTANGLE_TOOL_PROP_NARROW_MODE,
"narrow-mode");
}
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;
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
gimp_rectangle_tool_clamp (tool,
NULL,
constraint,
FALSE);
gimp_rectangle_tool_update_highlight (tool);
gimp_rectangle_tool_update_handle_sizes (tool);
gimp_rectangle_tool_rectangle_change_complete (tool);
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
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;
}
/**
* gimp_rectangle_tool_pending_size_set:
* @width_property: Option property to set to pending rectangle width.
* @height_property: Option property to set to pending rectangle height.
*
* Sets specified rectangle tool options properties to the width and
* height of the current pending rectangle.
*/
void
gimp_rectangle_tool_pending_size_set (GimpRectangleTool *rect_tool,
GObject *object,
const gchar *width_property,
const gchar *height_property)
{
GimpRectangleToolPrivate *private;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (rect_tool));
g_return_if_fail (width_property != NULL);
g_return_if_fail (height_property != NULL);
private = gimp_rectangle_tool_get_private (rect_tool);
g_object_set (object,
width_property, MAX (private->x2 - private->x1, 1.0),
height_property, MAX (private->y2 - private->y1, 1.0),
NULL);
}
/**
* gimp_rectangle_tool_constraint_size_set:
* @width_property: Option property to set to current constraint width.
* @height_property: Option property to set to current constraint height.
*
* Sets specified rectangle tool options properties to the width and
* height of the current contraint size.
*/
void
gimp_rectangle_tool_constraint_size_set (GimpRectangleTool *rect_tool,
GObject *object,
const gchar *width_property,
const gchar *height_property)
{
GimpTool *tool;
GimpContext *context;
GimpImage *image;
gdouble width;
gdouble height;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (rect_tool));
tool = GIMP_TOOL (rect_tool);
context = gimp_get_user_context (tool->tool_info->gimp);
image = gimp_context_get_image (context);
if (! image)
{
width = 1.0;
height = 1.0;
}
else
{
GimpRectangleConstraint constraint;
constraint = gimp_rectangle_tool_get_constraint (rect_tool);
switch (constraint)
{
case GIMP_RECTANGLE_CONSTRAIN_DRAWABLE:
{
GimpItem *item = GIMP_ITEM (gimp_image_get_active_layer (image));
if (! item)
{
width = 1.0;
height = 1.0;
}
else
{
width = gimp_item_get_width (item);
height = gimp_item_get_height (item);
}
}
break;
case GIMP_RECTANGLE_CONSTRAIN_IMAGE:
default:
{
app/actions/channels-commands.c app/actions/colormap-actions.c 2007-12-25 Michael Natterer <mitch@gimp.org> * app/actions/channels-commands.c * app/actions/colormap-actions.c * app/actions/colormap-commands.c * app/actions/image-commands.c * app/core/gimp-edit.c * app/core/gimpdrawable-preview.c * app/core/gimpimage-colorhash.c * app/core/gimpimage-colormap.c * app/core/gimpimage-convert.c * app/core/gimpimage-crop.c * app/core/gimpimage-duplicate.c * app/core/gimpimage-flip.c * app/core/gimpimage-guides.c * app/core/gimpimage-merge.c * app/core/gimpimage-preview.c * app/core/gimpimage-quick-mask.c * app/core/gimpimage-resize.c * app/core/gimpimage-rotate.c * app/core/gimpimage-sample-points.c * app/core/gimpimage-scale.c * app/core/gimpimage-snap.c * app/core/gimpimage.c * app/core/gimpimagefile.c * app/core/gimpimageundo.c * app/core/gimpitem-preview.c * app/core/gimpitem.c * app/core/gimplayer.c * app/core/gimppalette-import.c * app/core/gimpprojection-construct.c * app/core/gimpprojection.c * app/core/gimpselection.c * app/core/gimpundo.c * app/dialogs/layer-options-dialog.c * app/dialogs/print-size-dialog.c * app/display/gimpdisplay.c * app/display/gimpdisplayshell-draw.c * app/display/gimpdisplayshell-scale.c * app/display/gimpdisplayshell-scroll.c * app/display/gimpdisplayshell-title.c * app/display/gimpdisplayshell-transform.c * app/display/gimpdisplayshell.c * app/display/gimpstatusbar.c * app/file/file-open.c * app/paint/gimppaintoptions.c * app/tools/gimpaligntool.c * app/tools/gimpcolortool.c * app/tools/gimpeditselectiontool.c * app/tools/gimpiscissorstool.c * app/tools/gimpmeasuretool.c * app/tools/gimpmovetool.c * app/tools/gimpperspectiveclonetool.c * app/tools/gimprectangleselecttool.c * app/tools/gimprectangletool.c * app/tools/gimprotatetool.c * app/vectors/gimpvectors-export.c * app/vectors/gimpvectors-import.c * app/vectors/gimpvectors.c * app/widgets/gimpimagepropview.c * app/widgets/gimpnavigationview.c * app/widgets/gimpselectioneditor.c * app/widgets/gimpviewrendererdrawable.c * app/widgets/gimpviewrendererimage.c * app/xcf/xcf-load.c * app/xcf/xcf-save.c * tools/pdbgen/pdb/guides.pdb * tools/pdbgen/pdb/image.pdb: use accessors for many image properties. * app/pdb/guides_cmds.c * app/pdb/image_cmds.c: regenerated. svn path=/trunk/; revision=24432
2007-12-26 00:21:40 +08:00
width = gimp_image_get_width (image);
height = gimp_image_get_height (image);
}
break;
}
}
g_object_set (object,
width_property, width,
height_property, height,
NULL);
}
/**
* gimp_rectangle_tool_rectangle_is_new:
* @rect_tool:
*
* Returns: %TRUE if the user is creating a new rectangle from
* scratch, %FALSE if modifying n previously existing rectangle. This
* function is only meaningful in _motion and _button_release.
*/
gboolean
gimp_rectangle_tool_rectangle_is_new (GimpRectangleTool *rect_tool)
{
g_return_val_if_fail (GIMP_IS_RECTANGLE_TOOL (rect_tool), FALSE);
return GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool)->is_new;
}
/**
* gimp_rectangle_tool_point_in_rectangle:
* @rect_tool:
* @x: X-coord of point to test (in image coordinates)
* @y: Y-coord of point to test (in image coordinates)
*
* Returns: %TRUE if the passed point was within the rectangle
**/
gboolean
gimp_rectangle_tool_point_in_rectangle (GimpRectangleTool *rect_tool,
gdouble x,
gdouble y)
{
gboolean inside = FALSE;
g_return_val_if_fail (GIMP_IS_RECTANGLE_TOOL (rect_tool), FALSE);
if (GIMP_TOOL (rect_tool)->display)
{
gdouble pub_x1, pub_y1, pub_x2, pub_y2;
gimp_rectangle_tool_get_public_rect (rect_tool,
&pub_x1, &pub_y1, &pub_x2, &pub_y2);
inside = x >= pub_x1 && x <= pub_x2 &&
y >= pub_y1 && y <= pub_y2;
}
return inside;
}
/**
* gimp_rectangle_tool_frame_item:
* @rect_tool: a #GimpRectangleTool interface
* @item: a #GimpItem attached to the image on which a
* rectangle is being shown.
*
* Convenience function to set the corners of the rectangle to
* match the bounds of the specified item. The rectangle interface
* must be active (i.e., showing a rectangle), and the item must be
* attached to the image on which the rectangle is active.
**/
void
gimp_rectangle_tool_frame_item (GimpRectangleTool *rect_tool,
GimpItem *item)
{
GimpDisplay *display;
gint offset_x;
gint offset_y;
gint width;
gint height;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (rect_tool));
g_return_if_fail (GIMP_IS_ITEM (item));
g_return_if_fail (gimp_item_is_attached (item));
display = GIMP_TOOL (rect_tool)->display;
g_return_if_fail (GIMP_IS_DISPLAY (display));
g_return_if_fail (gimp_display_get_image (display) ==
gimp_item_get_image (item));
width = gimp_item_get_width (item);
height = gimp_item_get_height (item);
gimp_item_get_offset (item, &offset_x, &offset_y);
gimp_draw_tool_pause (GIMP_DRAW_TOOL (rect_tool));
gimp_rectangle_tool_set_function (rect_tool,
GIMP_RECTANGLE_TOOL_CREATING);
g_object_set (rect_tool,
"x1", offset_x,
"y1", offset_y,
"x2", offset_x + width,
"y2", offset_y + height,
NULL);
/* kludge to force handle sizes to update. This call may be
* harmful if this function is ever moved out of the text tool code.
*/
gimp_rectangle_tool_set_constraint (rect_tool,
GIMP_RECTANGLE_CONSTRAIN_NONE);
gimp_draw_tool_resume (GIMP_DRAW_TOOL (rect_tool));
}
void
gimp_rectangle_tool_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpRectangleTool *rect_tool = GIMP_RECTANGLE_TOOL (object);
GimpRectangleToolPrivate *private;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
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 (rect_tool, g_value_get_enum (value));
break;
case GIMP_RECTANGLE_TOOL_PROP_PRECISION:
private->precision = g_value_get_enum (value);
break;
case GIMP_RECTANGLE_TOOL_PROP_NARROW_MODE:
private->narrow_mode = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
gimp_rectangle_tool_update_int_rect (rect_tool);
}
void
gimp_rectangle_tool_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpRectangleTool *rect_tool = GIMP_RECTANGLE_TOOL (object);
GimpRectangleToolPrivate *private;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
switch (property_id)
{
case GIMP_RECTANGLE_TOOL_PROP_X1:
g_value_set_int (value, private->x1_int);
break;
case GIMP_RECTANGLE_TOOL_PROP_Y1:
g_value_set_int (value, private->y1_int);
break;
case GIMP_RECTANGLE_TOOL_PROP_X2:
g_value_set_int (value, private->x1_int + private->width_int);
break;
case GIMP_RECTANGLE_TOOL_PROP_Y2:
g_value_set_int (value, private->y1_int + private->height_int);
break;
case GIMP_RECTANGLE_TOOL_PROP_CONSTRAINT:
g_value_set_enum (value, gimp_rectangle_tool_get_constraint (rect_tool));
break;
case GIMP_RECTANGLE_TOOL_PROP_PRECISION:
g_value_set_enum (value, private->precision);
break;
case GIMP_RECTANGLE_TOOL_PROP_NARROW_MODE:
g_value_set_boolean (value, private->narrow_mode);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
void
gimp_rectangle_tool_constructor (GObject *object)
{
GimpRectangleTool *rect_tool = GIMP_RECTANGLE_TOOL (object);
GimpRectangleOptions *options;
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (object);
g_signal_connect_object (options, "notify",
G_CALLBACK (gimp_rectangle_tool_options_notify),
rect_tool, 0);
}
void
gimp_rectangle_tool_control (GimpTool *tool,
GimpToolAction action,
GimpDisplay *display)
{
GimpRectangleTool *rect_tool = GIMP_RECTANGLE_TOOL (tool);
GIMP_LOG (RECTANGLE_TOOL, "action = %s",
gimp_enum_get_value_name (GIMP_TYPE_TOOL_ACTION, action));
switch (action)
{
case GIMP_TOOL_ACTION_PAUSE:
break;
case GIMP_TOOL_ACTION_RESUME:
/* When highlightning is on, the shell gets paused/unpaused which means we
* will get here, but we only want to recalculate handle sizes when the
* zoom has changed.
*/
if (gimp_rectangle_tool_scale_has_changed (rect_tool))
gimp_rectangle_tool_update_handle_sizes (rect_tool);
break;
case GIMP_TOOL_ACTION_HALT:
gimp_rectangle_tool_halt (rect_tool);
break;
default:
break;
}
}
void
gimp_rectangle_tool_button_press (GimpTool *tool,
const GimpCoords *coords,
guint32 time,
GdkModifierType state,
GimpDisplay *display)
{
GimpRectangleTool *rect_tool;
GimpDrawTool *draw_tool;
GimpRectangleToolPrivate *private;
gdouble snapped_x, snapped_y;
gint snap_x, snap_y;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (tool));
rect_tool = GIMP_RECTANGLE_TOOL (tool);
draw_tool = GIMP_DRAW_TOOL (tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
gimp_draw_tool_pause (draw_tool);
GIMP_LOG (RECTANGLE_TOOL, "coords->x = %f, coords->y = %f",
coords->x, coords->y);
if (display != tool->display)
{
if (gimp_draw_tool_is_active (draw_tool))
{
GimpDisplayShell *shell = gimp_display_get_shell (draw_tool->display);
gimp_display_shell_set_highlight (shell, NULL);
gimp_draw_tool_stop (draw_tool);
}
gimp_rectangle_tool_set_function (rect_tool,
GIMP_RECTANGLE_TOOL_CREATING);
private->x1 = private->x2 = coords->x;
private->y1 = private->y2 = coords->y;
gimp_rectangle_tool_start (rect_tool, 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;
gimp_rectangle_tool_setup_snap_offsets (rect_tool,
coords);
gimp_tool_control_get_snap_offsets (tool->control,
&snap_x, &snap_y, NULL, NULL);
snapped_x = coords->x + snap_x;
snapped_y = coords->y + snap_y;
private->lastx = snapped_x;
private->lasty = snapped_y;
if (private->function == GIMP_RECTANGLE_TOOL_CREATING)
{
/* Remember that this rectangle was created from scratch. */
private->is_new = TRUE;
private->x1 = private->x2 = snapped_x;
private->y1 = private->y2 = snapped_y;
gimp_rectangle_tool_update_handle_sizes (rect_tool);
/* Created rectangles should not be started in narrow-mode */
private->narrow_mode = FALSE;
/* If the rectangle is being modified we want the center on
* fixed_center to be at the center of the currently existing
* rectangle, otherwise we want the point where the user clicked
* to be the center on fixed_center.
*/
private->center_x_on_fixed_center = snapped_x;
private->center_y_on_fixed_center = snapped_y;
/* When the user toggles modifier keys, we want to keep track of
* what coordinates the "other side" should have. If we are
* creating a rectangle, use the current mouse coordinates as
* the coordinate of the "other side", otherwise use the
* immidiate "other side" for that.
*/
private->other_side_x = snapped_x;
private->other_side_y = snapped_y;
}
else
{
/* This rectangle was not created from scratch. */
private->is_new = FALSE;
private->center_x_on_fixed_center = (private->x1 + private->x2) / 2;
private->center_y_on_fixed_center = (private->y1 + private->y2) / 2;
gimp_rectangle_tool_get_other_side_coord (rect_tool,
&private->other_side_x,
&private->other_side_y);
}
gimp_rectangle_tool_update_int_rect (rect_tool);
/* Is the rectangle being rubber-banded? */
private->rect_adjusting = gimp_rectangle_tool_rect_adjusting_func (rect_tool);
gimp_rectangle_tool_update_highlight (rect_tool);
gimp_draw_tool_resume (draw_tool);
}
void
gimp_rectangle_tool_button_release (GimpTool *tool,
const GimpCoords *coords,
guint32 time,
GdkModifierType state,
GimpButtonReleaseType release_type,
GimpDisplay *display)
{
GimpRectangleTool *rect_tool;
GimpRectangleToolPrivate *private;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (tool));
rect_tool = GIMP_RECTANGLE_TOOL (tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
GIMP_LOG (RECTANGLE_TOOL, "coords->x = %f, coords->y = %f",
coords->x, coords->y);
if (private->function == GIMP_RECTANGLE_TOOL_EXECUTING)
gimp_tool_pop_status (tool, display);
switch (release_type)
{
case GIMP_BUTTON_RELEASE_NORMAL:
gimp_rectangle_tool_rectangle_change_complete (rect_tool);
break;
case GIMP_BUTTON_RELEASE_CANCEL:
private->x1 = private->saved_x1;
private->y1 = private->saved_y1;
private->x2 = private->saved_x2;
private->y2 = private->saved_y2;
gimp_rectangle_tool_update_int_rect (rect_tool);
/* If the first created rectangle was canceled, halt the tool */
if (gimp_rectangle_tool_rectangle_is_new (rect_tool))
{
gimp_rectangle_tool_halt (rect_tool);
}
break;
case GIMP_BUTTON_RELEASE_CLICK:
/* When a dead area is clicked, don't execute. */
if (private->function == GIMP_RECTANGLE_TOOL_DEAD)
break;
if (gimp_rectangle_tool_execute (rect_tool))
gimp_rectangle_tool_halt (rect_tool);
break;
case GIMP_BUTTON_RELEASE_NO_MOTION:
break;
}
/* We must update this. */
private->center_x_on_fixed_center = (private->x1 + private->x2) / 2;
private->center_y_on_fixed_center = (private->y1 + private->y2) / 2;
gimp_tool_control_set_snap_offsets (tool->control, 0, 0, 0, 0);
/* On button release, we are not rubber-banding the rectangle any longer. */
private->rect_adjusting = FALSE;
gimp_rectangle_tool_update_highlight (rect_tool);
gimp_rectangle_tool_update_handle_sizes (rect_tool);
gimp_rectangle_tool_update_options (rect_tool, display);
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
}
void
gimp_rectangle_tool_motion (GimpTool *tool,
const GimpCoords *coords,
guint32 time,
GdkModifierType state,
GimpDisplay *display)
{
GimpRectangleTool *rect_tool;
GimpRectangleToolPrivate *private;
GimpRectangleOptions *options;
gdouble snapped_x;
gdouble snapped_y;
gint snap_x, snap_y;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (tool));
rect_tool = GIMP_RECTANGLE_TOOL (tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (tool);
/* Motion events should be ignored when we're just waiting for the
* button release event to execute or if the user has grabbed a dead
* area of the rectangle.
*/
if (private->function == GIMP_RECTANGLE_TOOL_EXECUTING ||
private->function == GIMP_RECTANGLE_TOOL_DEAD)
return;
GIMP_LOG (RECTANGLE_TOOL, "coords->x = %f, coords->y = %f",
coords->x, coords->y);
/* Handle snapping. */
gimp_tool_control_get_snap_offsets (tool->control,
&snap_x, &snap_y, NULL, NULL);
snapped_x = coords->x + snap_x;
snapped_y = coords->y + snap_y;
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
/* This is the core rectangle shape updating function: */
gimp_rectangle_tool_update_with_coord (rect_tool,
snapped_x,
snapped_y);
/* Update the highlight, but only if it is not being adjusted. If it
* is not being adjusted, the highlight is not shown anyway.
*/
if (gimp_rectangle_tool_rect_adjusting_func (rect_tool))
gimp_rectangle_tool_update_highlight (rect_tool);
if (private->function != GIMP_RECTANGLE_TOOL_MOVING &&
private->function != GIMP_RECTANGLE_TOOL_EXECUTING)
{
gdouble pub_x1, pub_y1, pub_x2, pub_y2;
gint w, h;
gimp_tool_pop_status (tool, display);
gimp_rectangle_tool_get_public_rect (rect_tool,
&pub_x1, &pub_y1, &pub_x2, &pub_y2);
w = pub_x2 - pub_x1;
h = pub_y2 - pub_y1;
if (w > 0.0 && h > 0.0)
{
gchar *aspect_text;
aspect_text = g_strdup_printf (" (%.2f:1)", w / (gdouble) h);
gimp_tool_push_status_coords (tool, display,
gimp_tool_control_get_precision (tool->control),
_("Rectangle: "),
w, " × ", h, aspect_text);
g_free (aspect_text);
}
}
if (private->function == GIMP_RECTANGLE_TOOL_CREATING)
{
GimpRectangleFunction function = GIMP_RECTANGLE_TOOL_CREATING;
gdouble dx = snapped_x - private->lastx;
gdouble dy = snapped_y - private->lasty;
/* When the user starts to move the cursor, set the current
* function to one of the corner-grabbed functions, depending on
* in what direction the user starts dragging the rectangle.
*/
if (dx < 0)
{
function = (dy < 0 ?
GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT :
GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT);
}
else if (dx > 0)
{
function = (dy < 0 ?
GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT :
GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT);
}
else if (dy < 0)
{
function = (dx < 0 ?
GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT :
GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT);
}
else if (dy > 0)
{
function = (dx < 0 ?
GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT :
GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT);
}
gimp_rectangle_tool_set_function (rect_tool, function);
if (gimp_rectangle_options_fixed_rule_active (options,
GIMP_RECTANGLE_TOOL_FIXED_SIZE))
{
/* For fixed size, set the function to moving immediately since the
* rectangle can not be resized anyway.
*/
/* We fake a coord update to get the right size. */
gimp_rectangle_tool_update_with_coord (rect_tool,
snapped_x,
snapped_y);
gimp_tool_control_set_snap_offsets (tool->control,
-(private->x2 - private->x1) / 2,
-(private->y2 - private->y1) / 2,
private->x2 - private->x1,
private->y2 - private->y1);
gimp_rectangle_tool_set_function (rect_tool,
GIMP_RECTANGLE_TOOL_MOVING);
}
}
gimp_rectangle_tool_update_options (rect_tool, display);
private->lastx = snapped_x;
private->lasty = snapped_y;
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)
{
GimpDrawTool *draw_tool;
GimpRectangleTool *rect_tool;
GimpRectangleOptions *options;
GimpRectangleOptionsPrivate *options_private;
GimpRectangleToolPrivate *private;
gboolean button1_down;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (tool));
draw_tool = GIMP_DRAW_TOOL (tool);
rect_tool = GIMP_RECTANGLE_TOOL (tool);
private = gimp_rectangle_tool_get_private (rect_tool);
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (tool);
options_private = GIMP_RECTANGLE_OPTIONS_GET_PRIVATE (options);
button1_down = state & GDK_BUTTON1_MASK;
gimp_draw_tool_pause (draw_tool);
if (key == GDK_SHIFT_MASK)
{
/* Here we want to handle manualy when to update the rectangle, so we
* don't want gimp_rectangle_tool_options_notify to do anything.
*/
g_signal_handlers_block_by_func (options,
gimp_rectangle_tool_options_notify,
rect_tool);
g_object_set (options,
2007-08-05 04:20:44 +08:00
"fixed-rule-active", ! options_private->fixed_rule_active,
NULL);
g_signal_handlers_unblock_by_func (options,
gimp_rectangle_tool_options_notify,
rect_tool);
/* Only change the shape if the mouse is still down (i.e. the user is
* still editing the rectangle.
*/
if (button1_down)
{
2007-08-05 04:20:44 +08:00
if (!options_private->fixed_rule_active)
{
/* Reset anchor point */
gimp_rectangle_tool_set_other_side_coord (rect_tool,
2007-08-05 04:20:44 +08:00
private->other_side_x,
private->other_side_y);
}
gimp_rectangle_tool_update_with_coord (rect_tool,
private->lastx,
private->lasty);
gimp_rectangle_tool_update_highlight (rect_tool);
}
}
if (key == gimp_get_toggle_behavior_mask ())
{
g_object_set (options,
"fixed-center", ! options_private->fixed_center,
NULL);
if (options_private->fixed_center)
{
gimp_rectangle_tool_update_with_coord (rect_tool,
private->lastx,
private->lasty);
gimp_rectangle_tool_update_highlight (rect_tool);
/* Only emit the rectangle-changed signal if the button is
* not down. If it is down, the signal will and shall be
* emited on _button_release instead.
*/
if (! button1_down)
{
gimp_rectangle_tool_rectangle_change_complete (rect_tool);
}
}
else if (button1_down)
{
/* If we are leaving fixed_center mode we want to set the
* "other side" where it should be. Don't do anything if we
* came here by a mouse-click though, since then the user
* has confirmed the shape and we don't want to modify it
* afterwards.
*/
gimp_rectangle_tool_set_other_side_coord (rect_tool,
private->other_side_x,
private->other_side_y);
gimp_rectangle_tool_update_highlight (rect_tool);
}
}
gimp_draw_tool_resume (draw_tool);
2007-08-05 04:20:44 +08:00
gimp_rectangle_tool_update_options (rect_tool, tool->display);
}
static void
swap_doubles (gdouble *i,
gdouble *j)
{
gdouble 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 *rect_tool)
{
GimpRectangleToolPrivate *private;
GimpRectangleFunction function;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
function = private->function;
if (private->x2 < private->x1)
{
swap_doubles (&private->x1, &private->x2);
switch (function)
{
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
function = GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
function = GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
function = GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
function = GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LEFT:
function = GIMP_RECTANGLE_TOOL_RESIZING_RIGHT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_RIGHT:
function = GIMP_RECTANGLE_TOOL_RESIZING_LEFT;
break;
/* avoid annoying warnings about unhandled enums */
default:
break;
}
}
if (private->y2 < private->y1)
{
swap_doubles (&private->y1, &private->y2);
switch (function)
{
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
function = GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
function = GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
function = GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
function = GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_TOP:
function = GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM:
function = GIMP_RECTANGLE_TOOL_RESIZING_TOP;
break;
default:
break;
}
}
gimp_rectangle_tool_set_function (rect_tool, function);
}
gboolean
gimp_rectangle_tool_key_press (GimpTool *tool,
GdkEventKey *kevent,
GimpDisplay *display)
{
GimpRectangleTool *rect_tool;
GimpRectangleToolPrivate *private;
gint dx = 0;
gint dy = 0;
gdouble new_x = 0;
gdouble new_y = 0;
g_return_val_if_fail (GIMP_IS_RECTANGLE_TOOL (tool), FALSE);
if (display != tool->display)
return FALSE;
rect_tool = GIMP_RECTANGLE_TOOL (tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
switch (kevent->keyval)
{
case GDK_KEY_Up:
dy = -1;
break;
case GDK_KEY_Left:
dx = -1;
break;
case GDK_KEY_Right:
dx = 1;
break;
case GDK_KEY_Down:
dy = 1;
break;
case GDK_KEY_Return:
case GDK_KEY_KP_Enter:
case GDK_KEY_ISO_Enter:
if (gimp_rectangle_tool_execute (rect_tool))
gimp_rectangle_tool_halt (rect_tool);
return TRUE;
case GDK_KEY_Escape:
gimp_rectangle_tool_cancel (rect_tool);
gimp_rectangle_tool_halt (rect_tool);
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;
}
gimp_tool_control_set_snap_offsets (GIMP_TOOL (rect_tool)->control,
0, 0, 0, 0);
/* Resize the rectangle if the mouse is over a handle, otherwise move it */
switch (private->function)
{
case GIMP_RECTANGLE_TOOL_MOVING:
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
new_x = private->x1 + dx;
new_y = private->y1 + dy;
private->lastx = new_x;
private->lasty = new_y;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
new_x = private->x2 + dx;
new_y = private->y1 + dy;
private->lastx = new_x;
private->lasty = new_y;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
new_x = private->x1 + dx;
new_y = private->y2 + dy;
private->lastx = new_x;
private->lasty = new_y;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
new_x = private->x2 + dx;
new_y = private->y2 + dy;
private->lastx = new_x;
private->lasty = new_y;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LEFT:
new_x = private->x1 + dx;
private->lastx = new_x;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_RIGHT:
new_x = private->x2 + dx;
private->lastx = new_x;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_TOP:
new_y = private->y1 + dy;
private->lasty = new_y;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM:
new_y = private->y2 + dy;
private->lasty = new_y;
break;
default:
return TRUE;
}
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
gimp_rectangle_tool_update_with_coord (rect_tool,
new_x,
new_y);
private->center_x_on_fixed_center = (private->x1 + private->x2) / 2;
private->center_y_on_fixed_center = (private->y1 + private->y2) / 2;
gimp_rectangle_tool_update_highlight (rect_tool);
gimp_rectangle_tool_update_handle_sizes (rect_tool);
gimp_rectangle_tool_update_options (rect_tool, tool->display);
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
gimp_rectangle_tool_rectangle_change_complete (rect_tool);
/* 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,
const GimpCoords *coords,
GdkModifierType state,
gboolean proximity,
GimpDisplay *display)
{
GimpRectangleToolPrivate *private;
GimpRectangleTool *rect_tool;
gint function;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (tool));
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
rect_tool = GIMP_RECTANGLE_TOOL (tool);
if (tool->display != display)
return;
if (private->suppress_updates)
{
private->suppress_updates--;
return;
}
if (! proximity)
{
function = GIMP_RECTANGLE_TOOL_DEAD;
}
else if (gimp_rectangle_tool_coord_outside (rect_tool, coords))
{
/* The cursor is outside of the rectangle, clicking should
* create a new rectangle.
*/
function = GIMP_RECTANGLE_TOOL_CREATING;
}
else if (gimp_rectangle_tool_coord_on_handle (rect_tool,
coords,
GIMP_HANDLE_ANCHOR_NORTH_WEST))
{
function = GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT;
}
else if (gimp_rectangle_tool_coord_on_handle (rect_tool,
coords,
GIMP_HANDLE_ANCHOR_SOUTH_EAST))
{
function = GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT;
}
else if (gimp_rectangle_tool_coord_on_handle (rect_tool,
coords,
GIMP_HANDLE_ANCHOR_NORTH_EAST))
{
function = GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT;
}
else if (gimp_rectangle_tool_coord_on_handle (rect_tool,
coords,
GIMP_HANDLE_ANCHOR_SOUTH_WEST))
{
function = GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT;
}
else if (gimp_rectangle_tool_coord_on_handle (rect_tool,
coords,
GIMP_HANDLE_ANCHOR_WEST))
{
function = GIMP_RECTANGLE_TOOL_RESIZING_LEFT;
}
else if (gimp_rectangle_tool_coord_on_handle (rect_tool,
coords,
GIMP_HANDLE_ANCHOR_EAST))
{
function = GIMP_RECTANGLE_TOOL_RESIZING_RIGHT;
}
else if (gimp_rectangle_tool_coord_on_handle (rect_tool,
coords,
GIMP_HANDLE_ANCHOR_NORTH))
{
function = GIMP_RECTANGLE_TOOL_RESIZING_TOP;
}
else if (gimp_rectangle_tool_coord_on_handle (rect_tool,
coords,
GIMP_HANDLE_ANCHOR_SOUTH))
{
function = GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM;
}
else if (gimp_rectangle_tool_coord_on_handle (rect_tool,
coords,
GIMP_HANDLE_ANCHOR_CENTER))
{
function = GIMP_RECTANGLE_TOOL_MOVING;
}
else
{
function = GIMP_RECTANGLE_TOOL_DEAD;
}
gimp_rectangle_tool_set_function (GIMP_RECTANGLE_TOOL (tool), function);
}
void
gimp_rectangle_tool_cursor_update (GimpTool *tool,
const GimpCoords *coords,
GdkModifierType state,
GimpDisplay *display)
{
GimpRectangleToolPrivate *private;
GimpCursorType cursor = GIMP_CURSOR_CROSSHAIR_SMALL;
GimpCursorModifier modifier = GIMP_CURSOR_MODIFIER_NONE;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (tool));
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
if (tool->display == display)
{
switch (private->function)
{
case GIMP_RECTANGLE_TOOL_CREATING:
cursors/Makefile.am cursors/cursor-corner-bottom-left.png 2006-06-02 Michael Natterer <mitch@gimp.org> * cursors/Makefile.am * cursors/cursor-corner-bottom-left.png * cursors/cursor-corner-bottom-right.png * cursors/cursor-corner-top-left.png * cursors/cursor-corner-top-right.png * cursors/cursor-side-bottom.png * cursors/cursor-side-left.png * cursors/cursor-side-right.png * cursors/cursor-side-top.png * cursors/xbm/cursor-corner-bottom-left-mask.xbm * cursors/xbm/cursor-corner-bottom-left.xbm * cursors/xbm/cursor-corner-bottom-right-mask.xbm * cursors/xbm/cursor-corner-bottom-right.xbm * cursors/xbm/cursor-corner-top-left-mask.xbm * cursors/xbm/cursor-corner-top-left.xbm * cursors/xbm/cursor-corner-top-right-mask.xbm * cursors/xbm/cursor-corner-top-right.xbm * cursors/xbm/cursor-side-bottom-mask.xbm * cursors/xbm/cursor-side-bottom.xbm * cursors/xbm/cursor-side-left-mask.xbm * cursors/xbm/cursor-side-left.xbm * cursors/xbm/cursor-side-right-mask.xbm * cursors/xbm/cursor-side-right.xbm * cursors/xbm/cursor-side-top-mask.xbm * cursors/xbm/cursor-side-top.xbm: new cursors for edge and corner resizing. They perfectly align with the small crosshair and can be used together with tool cursors and cursor modifiers. * cursors/gimp-tool-cursors.xcf: add them here too. * app/widgets/widgets-enums.h: add them to the GimpCursorType enum. * app/widgets/gimpcursor.c: add them here too. * app/display/gimpdisplayshell-cursor.c: treat them like the small crosshair (don't replace them by the small crosshair but use them as-is). Also allow the bad modifier with the large crosshair. * app/tools/gimprectangletool.c (gimp_rectangle_tool_cursor_update): use the new cursors. Don't call gimp_tool_set_cursor() here. (gimp_rectangle_tool_response): reset "function" to RECT_CREATING when resetting the tool. * app/tools/gimpselectiontool.[ch] (struct GimpSelectionTool): added boolean member "allow_move" which defalts to TRUE. (gimp_selection_tool_oper_update): don't move masks, floating selections or anything when "allow_move" is FALSE. Changed behavior of click inside a selection to simply create a new selection, need to press alt+shift now to drag-float the selection. Please test this, it's apretty fundamental change! (gimp_selection_tool_cursor_update): use the tool's configured cursor instead of always GIMP_CURSOR_MOUSE, so this function can be called after gimp_rectangle_tool_cursor_update() to add the plus, minus etc. modifiers. * app/tools/gimpnewrectselecttool.c: implement GimpTool::cursor_update() and call gimp_rectangle_tool_cursor_update() from there. Chain up to get the plus, minus etc. modifiers added. Re-enble selection moving: (gimp_new_rect_select_tool_oper_update): set GimpSelectionTool's "allow_move" to FALSE unless the rectangle tool is in an idle state. (gimp_new_rect_select_tool_button_press): allow a selection moving to be started if the rectangle tool is idle. Fall back to starting a rect select if gimp_selection_tool_start_edit() returned FALSE.
2006-06-02 23:23:47 +08:00
cursor = GIMP_CURSOR_CROSSHAIR_SMALL;
break;
case GIMP_RECTANGLE_TOOL_MOVING:
cursor = GIMP_CURSOR_MOVE;
modifier = GIMP_CURSOR_MODIFIER_MOVE;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
cursors/Makefile.am cursors/cursor-corner-bottom-left.png 2006-06-02 Michael Natterer <mitch@gimp.org> * cursors/Makefile.am * cursors/cursor-corner-bottom-left.png * cursors/cursor-corner-bottom-right.png * cursors/cursor-corner-top-left.png * cursors/cursor-corner-top-right.png * cursors/cursor-side-bottom.png * cursors/cursor-side-left.png * cursors/cursor-side-right.png * cursors/cursor-side-top.png * cursors/xbm/cursor-corner-bottom-left-mask.xbm * cursors/xbm/cursor-corner-bottom-left.xbm * cursors/xbm/cursor-corner-bottom-right-mask.xbm * cursors/xbm/cursor-corner-bottom-right.xbm * cursors/xbm/cursor-corner-top-left-mask.xbm * cursors/xbm/cursor-corner-top-left.xbm * cursors/xbm/cursor-corner-top-right-mask.xbm * cursors/xbm/cursor-corner-top-right.xbm * cursors/xbm/cursor-side-bottom-mask.xbm * cursors/xbm/cursor-side-bottom.xbm * cursors/xbm/cursor-side-left-mask.xbm * cursors/xbm/cursor-side-left.xbm * cursors/xbm/cursor-side-right-mask.xbm * cursors/xbm/cursor-side-right.xbm * cursors/xbm/cursor-side-top-mask.xbm * cursors/xbm/cursor-side-top.xbm: new cursors for edge and corner resizing. They perfectly align with the small crosshair and can be used together with tool cursors and cursor modifiers. * cursors/gimp-tool-cursors.xcf: add them here too. * app/widgets/widgets-enums.h: add them to the GimpCursorType enum. * app/widgets/gimpcursor.c: add them here too. * app/display/gimpdisplayshell-cursor.c: treat them like the small crosshair (don't replace them by the small crosshair but use them as-is). Also allow the bad modifier with the large crosshair. * app/tools/gimprectangletool.c (gimp_rectangle_tool_cursor_update): use the new cursors. Don't call gimp_tool_set_cursor() here. (gimp_rectangle_tool_response): reset "function" to RECT_CREATING when resetting the tool. * app/tools/gimpselectiontool.[ch] (struct GimpSelectionTool): added boolean member "allow_move" which defalts to TRUE. (gimp_selection_tool_oper_update): don't move masks, floating selections or anything when "allow_move" is FALSE. Changed behavior of click inside a selection to simply create a new selection, need to press alt+shift now to drag-float the selection. Please test this, it's apretty fundamental change! (gimp_selection_tool_cursor_update): use the tool's configured cursor instead of always GIMP_CURSOR_MOUSE, so this function can be called after gimp_rectangle_tool_cursor_update() to add the plus, minus etc. modifiers. * app/tools/gimpnewrectselecttool.c: implement GimpTool::cursor_update() and call gimp_rectangle_tool_cursor_update() from there. Chain up to get the plus, minus etc. modifiers added. Re-enble selection moving: (gimp_new_rect_select_tool_oper_update): set GimpSelectionTool's "allow_move" to FALSE unless the rectangle tool is in an idle state. (gimp_new_rect_select_tool_button_press): allow a selection moving to be started if the rectangle tool is idle. Fall back to starting a rect select if gimp_selection_tool_start_edit() returned FALSE.
2006-06-02 23:23:47 +08:00
cursor = GIMP_CURSOR_CORNER_TOP_LEFT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
cursors/Makefile.am cursors/cursor-corner-bottom-left.png 2006-06-02 Michael Natterer <mitch@gimp.org> * cursors/Makefile.am * cursors/cursor-corner-bottom-left.png * cursors/cursor-corner-bottom-right.png * cursors/cursor-corner-top-left.png * cursors/cursor-corner-top-right.png * cursors/cursor-side-bottom.png * cursors/cursor-side-left.png * cursors/cursor-side-right.png * cursors/cursor-side-top.png * cursors/xbm/cursor-corner-bottom-left-mask.xbm * cursors/xbm/cursor-corner-bottom-left.xbm * cursors/xbm/cursor-corner-bottom-right-mask.xbm * cursors/xbm/cursor-corner-bottom-right.xbm * cursors/xbm/cursor-corner-top-left-mask.xbm * cursors/xbm/cursor-corner-top-left.xbm * cursors/xbm/cursor-corner-top-right-mask.xbm * cursors/xbm/cursor-corner-top-right.xbm * cursors/xbm/cursor-side-bottom-mask.xbm * cursors/xbm/cursor-side-bottom.xbm * cursors/xbm/cursor-side-left-mask.xbm * cursors/xbm/cursor-side-left.xbm * cursors/xbm/cursor-side-right-mask.xbm * cursors/xbm/cursor-side-right.xbm * cursors/xbm/cursor-side-top-mask.xbm * cursors/xbm/cursor-side-top.xbm: new cursors for edge and corner resizing. They perfectly align with the small crosshair and can be used together with tool cursors and cursor modifiers. * cursors/gimp-tool-cursors.xcf: add them here too. * app/widgets/widgets-enums.h: add them to the GimpCursorType enum. * app/widgets/gimpcursor.c: add them here too. * app/display/gimpdisplayshell-cursor.c: treat them like the small crosshair (don't replace them by the small crosshair but use them as-is). Also allow the bad modifier with the large crosshair. * app/tools/gimprectangletool.c (gimp_rectangle_tool_cursor_update): use the new cursors. Don't call gimp_tool_set_cursor() here. (gimp_rectangle_tool_response): reset "function" to RECT_CREATING when resetting the tool. * app/tools/gimpselectiontool.[ch] (struct GimpSelectionTool): added boolean member "allow_move" which defalts to TRUE. (gimp_selection_tool_oper_update): don't move masks, floating selections or anything when "allow_move" is FALSE. Changed behavior of click inside a selection to simply create a new selection, need to press alt+shift now to drag-float the selection. Please test this, it's apretty fundamental change! (gimp_selection_tool_cursor_update): use the tool's configured cursor instead of always GIMP_CURSOR_MOUSE, so this function can be called after gimp_rectangle_tool_cursor_update() to add the plus, minus etc. modifiers. * app/tools/gimpnewrectselecttool.c: implement GimpTool::cursor_update() and call gimp_rectangle_tool_cursor_update() from there. Chain up to get the plus, minus etc. modifiers added. Re-enble selection moving: (gimp_new_rect_select_tool_oper_update): set GimpSelectionTool's "allow_move" to FALSE unless the rectangle tool is in an idle state. (gimp_new_rect_select_tool_button_press): allow a selection moving to be started if the rectangle tool is idle. Fall back to starting a rect select if gimp_selection_tool_start_edit() returned FALSE.
2006-06-02 23:23:47 +08:00
cursor = GIMP_CURSOR_CORNER_TOP_RIGHT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
cursors/Makefile.am cursors/cursor-corner-bottom-left.png 2006-06-02 Michael Natterer <mitch@gimp.org> * cursors/Makefile.am * cursors/cursor-corner-bottom-left.png * cursors/cursor-corner-bottom-right.png * cursors/cursor-corner-top-left.png * cursors/cursor-corner-top-right.png * cursors/cursor-side-bottom.png * cursors/cursor-side-left.png * cursors/cursor-side-right.png * cursors/cursor-side-top.png * cursors/xbm/cursor-corner-bottom-left-mask.xbm * cursors/xbm/cursor-corner-bottom-left.xbm * cursors/xbm/cursor-corner-bottom-right-mask.xbm * cursors/xbm/cursor-corner-bottom-right.xbm * cursors/xbm/cursor-corner-top-left-mask.xbm * cursors/xbm/cursor-corner-top-left.xbm * cursors/xbm/cursor-corner-top-right-mask.xbm * cursors/xbm/cursor-corner-top-right.xbm * cursors/xbm/cursor-side-bottom-mask.xbm * cursors/xbm/cursor-side-bottom.xbm * cursors/xbm/cursor-side-left-mask.xbm * cursors/xbm/cursor-side-left.xbm * cursors/xbm/cursor-side-right-mask.xbm * cursors/xbm/cursor-side-right.xbm * cursors/xbm/cursor-side-top-mask.xbm * cursors/xbm/cursor-side-top.xbm: new cursors for edge and corner resizing. They perfectly align with the small crosshair and can be used together with tool cursors and cursor modifiers. * cursors/gimp-tool-cursors.xcf: add them here too. * app/widgets/widgets-enums.h: add them to the GimpCursorType enum. * app/widgets/gimpcursor.c: add them here too. * app/display/gimpdisplayshell-cursor.c: treat them like the small crosshair (don't replace them by the small crosshair but use them as-is). Also allow the bad modifier with the large crosshair. * app/tools/gimprectangletool.c (gimp_rectangle_tool_cursor_update): use the new cursors. Don't call gimp_tool_set_cursor() here. (gimp_rectangle_tool_response): reset "function" to RECT_CREATING when resetting the tool. * app/tools/gimpselectiontool.[ch] (struct GimpSelectionTool): added boolean member "allow_move" which defalts to TRUE. (gimp_selection_tool_oper_update): don't move masks, floating selections or anything when "allow_move" is FALSE. Changed behavior of click inside a selection to simply create a new selection, need to press alt+shift now to drag-float the selection. Please test this, it's apretty fundamental change! (gimp_selection_tool_cursor_update): use the tool's configured cursor instead of always GIMP_CURSOR_MOUSE, so this function can be called after gimp_rectangle_tool_cursor_update() to add the plus, minus etc. modifiers. * app/tools/gimpnewrectselecttool.c: implement GimpTool::cursor_update() and call gimp_rectangle_tool_cursor_update() from there. Chain up to get the plus, minus etc. modifiers added. Re-enble selection moving: (gimp_new_rect_select_tool_oper_update): set GimpSelectionTool's "allow_move" to FALSE unless the rectangle tool is in an idle state. (gimp_new_rect_select_tool_button_press): allow a selection moving to be started if the rectangle tool is idle. Fall back to starting a rect select if gimp_selection_tool_start_edit() returned FALSE.
2006-06-02 23:23:47 +08:00
cursor = GIMP_CURSOR_CORNER_BOTTOM_LEFT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
cursors/Makefile.am cursors/cursor-corner-bottom-left.png 2006-06-02 Michael Natterer <mitch@gimp.org> * cursors/Makefile.am * cursors/cursor-corner-bottom-left.png * cursors/cursor-corner-bottom-right.png * cursors/cursor-corner-top-left.png * cursors/cursor-corner-top-right.png * cursors/cursor-side-bottom.png * cursors/cursor-side-left.png * cursors/cursor-side-right.png * cursors/cursor-side-top.png * cursors/xbm/cursor-corner-bottom-left-mask.xbm * cursors/xbm/cursor-corner-bottom-left.xbm * cursors/xbm/cursor-corner-bottom-right-mask.xbm * cursors/xbm/cursor-corner-bottom-right.xbm * cursors/xbm/cursor-corner-top-left-mask.xbm * cursors/xbm/cursor-corner-top-left.xbm * cursors/xbm/cursor-corner-top-right-mask.xbm * cursors/xbm/cursor-corner-top-right.xbm * cursors/xbm/cursor-side-bottom-mask.xbm * cursors/xbm/cursor-side-bottom.xbm * cursors/xbm/cursor-side-left-mask.xbm * cursors/xbm/cursor-side-left.xbm * cursors/xbm/cursor-side-right-mask.xbm * cursors/xbm/cursor-side-right.xbm * cursors/xbm/cursor-side-top-mask.xbm * cursors/xbm/cursor-side-top.xbm: new cursors for edge and corner resizing. They perfectly align with the small crosshair and can be used together with tool cursors and cursor modifiers. * cursors/gimp-tool-cursors.xcf: add them here too. * app/widgets/widgets-enums.h: add them to the GimpCursorType enum. * app/widgets/gimpcursor.c: add them here too. * app/display/gimpdisplayshell-cursor.c: treat them like the small crosshair (don't replace them by the small crosshair but use them as-is). Also allow the bad modifier with the large crosshair. * app/tools/gimprectangletool.c (gimp_rectangle_tool_cursor_update): use the new cursors. Don't call gimp_tool_set_cursor() here. (gimp_rectangle_tool_response): reset "function" to RECT_CREATING when resetting the tool. * app/tools/gimpselectiontool.[ch] (struct GimpSelectionTool): added boolean member "allow_move" which defalts to TRUE. (gimp_selection_tool_oper_update): don't move masks, floating selections or anything when "allow_move" is FALSE. Changed behavior of click inside a selection to simply create a new selection, need to press alt+shift now to drag-float the selection. Please test this, it's apretty fundamental change! (gimp_selection_tool_cursor_update): use the tool's configured cursor instead of always GIMP_CURSOR_MOUSE, so this function can be called after gimp_rectangle_tool_cursor_update() to add the plus, minus etc. modifiers. * app/tools/gimpnewrectselecttool.c: implement GimpTool::cursor_update() and call gimp_rectangle_tool_cursor_update() from there. Chain up to get the plus, minus etc. modifiers added. Re-enble selection moving: (gimp_new_rect_select_tool_oper_update): set GimpSelectionTool's "allow_move" to FALSE unless the rectangle tool is in an idle state. (gimp_new_rect_select_tool_button_press): allow a selection moving to be started if the rectangle tool is idle. Fall back to starting a rect select if gimp_selection_tool_start_edit() returned FALSE.
2006-06-02 23:23:47 +08:00
cursor = GIMP_CURSOR_CORNER_BOTTOM_RIGHT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LEFT:
cursors/Makefile.am cursors/cursor-corner-bottom-left.png 2006-06-02 Michael Natterer <mitch@gimp.org> * cursors/Makefile.am * cursors/cursor-corner-bottom-left.png * cursors/cursor-corner-bottom-right.png * cursors/cursor-corner-top-left.png * cursors/cursor-corner-top-right.png * cursors/cursor-side-bottom.png * cursors/cursor-side-left.png * cursors/cursor-side-right.png * cursors/cursor-side-top.png * cursors/xbm/cursor-corner-bottom-left-mask.xbm * cursors/xbm/cursor-corner-bottom-left.xbm * cursors/xbm/cursor-corner-bottom-right-mask.xbm * cursors/xbm/cursor-corner-bottom-right.xbm * cursors/xbm/cursor-corner-top-left-mask.xbm * cursors/xbm/cursor-corner-top-left.xbm * cursors/xbm/cursor-corner-top-right-mask.xbm * cursors/xbm/cursor-corner-top-right.xbm * cursors/xbm/cursor-side-bottom-mask.xbm * cursors/xbm/cursor-side-bottom.xbm * cursors/xbm/cursor-side-left-mask.xbm * cursors/xbm/cursor-side-left.xbm * cursors/xbm/cursor-side-right-mask.xbm * cursors/xbm/cursor-side-right.xbm * cursors/xbm/cursor-side-top-mask.xbm * cursors/xbm/cursor-side-top.xbm: new cursors for edge and corner resizing. They perfectly align with the small crosshair and can be used together with tool cursors and cursor modifiers. * cursors/gimp-tool-cursors.xcf: add them here too. * app/widgets/widgets-enums.h: add them to the GimpCursorType enum. * app/widgets/gimpcursor.c: add them here too. * app/display/gimpdisplayshell-cursor.c: treat them like the small crosshair (don't replace them by the small crosshair but use them as-is). Also allow the bad modifier with the large crosshair. * app/tools/gimprectangletool.c (gimp_rectangle_tool_cursor_update): use the new cursors. Don't call gimp_tool_set_cursor() here. (gimp_rectangle_tool_response): reset "function" to RECT_CREATING when resetting the tool. * app/tools/gimpselectiontool.[ch] (struct GimpSelectionTool): added boolean member "allow_move" which defalts to TRUE. (gimp_selection_tool_oper_update): don't move masks, floating selections or anything when "allow_move" is FALSE. Changed behavior of click inside a selection to simply create a new selection, need to press alt+shift now to drag-float the selection. Please test this, it's apretty fundamental change! (gimp_selection_tool_cursor_update): use the tool's configured cursor instead of always GIMP_CURSOR_MOUSE, so this function can be called after gimp_rectangle_tool_cursor_update() to add the plus, minus etc. modifiers. * app/tools/gimpnewrectselecttool.c: implement GimpTool::cursor_update() and call gimp_rectangle_tool_cursor_update() from there. Chain up to get the plus, minus etc. modifiers added. Re-enble selection moving: (gimp_new_rect_select_tool_oper_update): set GimpSelectionTool's "allow_move" to FALSE unless the rectangle tool is in an idle state. (gimp_new_rect_select_tool_button_press): allow a selection moving to be started if the rectangle tool is idle. Fall back to starting a rect select if gimp_selection_tool_start_edit() returned FALSE.
2006-06-02 23:23:47 +08:00
cursor = GIMP_CURSOR_SIDE_LEFT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_RIGHT:
cursors/Makefile.am cursors/cursor-corner-bottom-left.png 2006-06-02 Michael Natterer <mitch@gimp.org> * cursors/Makefile.am * cursors/cursor-corner-bottom-left.png * cursors/cursor-corner-bottom-right.png * cursors/cursor-corner-top-left.png * cursors/cursor-corner-top-right.png * cursors/cursor-side-bottom.png * cursors/cursor-side-left.png * cursors/cursor-side-right.png * cursors/cursor-side-top.png * cursors/xbm/cursor-corner-bottom-left-mask.xbm * cursors/xbm/cursor-corner-bottom-left.xbm * cursors/xbm/cursor-corner-bottom-right-mask.xbm * cursors/xbm/cursor-corner-bottom-right.xbm * cursors/xbm/cursor-corner-top-left-mask.xbm * cursors/xbm/cursor-corner-top-left.xbm * cursors/xbm/cursor-corner-top-right-mask.xbm * cursors/xbm/cursor-corner-top-right.xbm * cursors/xbm/cursor-side-bottom-mask.xbm * cursors/xbm/cursor-side-bottom.xbm * cursors/xbm/cursor-side-left-mask.xbm * cursors/xbm/cursor-side-left.xbm * cursors/xbm/cursor-side-right-mask.xbm * cursors/xbm/cursor-side-right.xbm * cursors/xbm/cursor-side-top-mask.xbm * cursors/xbm/cursor-side-top.xbm: new cursors for edge and corner resizing. They perfectly align with the small crosshair and can be used together with tool cursors and cursor modifiers. * cursors/gimp-tool-cursors.xcf: add them here too. * app/widgets/widgets-enums.h: add them to the GimpCursorType enum. * app/widgets/gimpcursor.c: add them here too. * app/display/gimpdisplayshell-cursor.c: treat them like the small crosshair (don't replace them by the small crosshair but use them as-is). Also allow the bad modifier with the large crosshair. * app/tools/gimprectangletool.c (gimp_rectangle_tool_cursor_update): use the new cursors. Don't call gimp_tool_set_cursor() here. (gimp_rectangle_tool_response): reset "function" to RECT_CREATING when resetting the tool. * app/tools/gimpselectiontool.[ch] (struct GimpSelectionTool): added boolean member "allow_move" which defalts to TRUE. (gimp_selection_tool_oper_update): don't move masks, floating selections or anything when "allow_move" is FALSE. Changed behavior of click inside a selection to simply create a new selection, need to press alt+shift now to drag-float the selection. Please test this, it's apretty fundamental change! (gimp_selection_tool_cursor_update): use the tool's configured cursor instead of always GIMP_CURSOR_MOUSE, so this function can be called after gimp_rectangle_tool_cursor_update() to add the plus, minus etc. modifiers. * app/tools/gimpnewrectselecttool.c: implement GimpTool::cursor_update() and call gimp_rectangle_tool_cursor_update() from there. Chain up to get the plus, minus etc. modifiers added. Re-enble selection moving: (gimp_new_rect_select_tool_oper_update): set GimpSelectionTool's "allow_move" to FALSE unless the rectangle tool is in an idle state. (gimp_new_rect_select_tool_button_press): allow a selection moving to be started if the rectangle tool is idle. Fall back to starting a rect select if gimp_selection_tool_start_edit() returned FALSE.
2006-06-02 23:23:47 +08:00
cursor = GIMP_CURSOR_SIDE_RIGHT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_TOP:
cursors/Makefile.am cursors/cursor-corner-bottom-left.png 2006-06-02 Michael Natterer <mitch@gimp.org> * cursors/Makefile.am * cursors/cursor-corner-bottom-left.png * cursors/cursor-corner-bottom-right.png * cursors/cursor-corner-top-left.png * cursors/cursor-corner-top-right.png * cursors/cursor-side-bottom.png * cursors/cursor-side-left.png * cursors/cursor-side-right.png * cursors/cursor-side-top.png * cursors/xbm/cursor-corner-bottom-left-mask.xbm * cursors/xbm/cursor-corner-bottom-left.xbm * cursors/xbm/cursor-corner-bottom-right-mask.xbm * cursors/xbm/cursor-corner-bottom-right.xbm * cursors/xbm/cursor-corner-top-left-mask.xbm * cursors/xbm/cursor-corner-top-left.xbm * cursors/xbm/cursor-corner-top-right-mask.xbm * cursors/xbm/cursor-corner-top-right.xbm * cursors/xbm/cursor-side-bottom-mask.xbm * cursors/xbm/cursor-side-bottom.xbm * cursors/xbm/cursor-side-left-mask.xbm * cursors/xbm/cursor-side-left.xbm * cursors/xbm/cursor-side-right-mask.xbm * cursors/xbm/cursor-side-right.xbm * cursors/xbm/cursor-side-top-mask.xbm * cursors/xbm/cursor-side-top.xbm: new cursors for edge and corner resizing. They perfectly align with the small crosshair and can be used together with tool cursors and cursor modifiers. * cursors/gimp-tool-cursors.xcf: add them here too. * app/widgets/widgets-enums.h: add them to the GimpCursorType enum. * app/widgets/gimpcursor.c: add them here too. * app/display/gimpdisplayshell-cursor.c: treat them like the small crosshair (don't replace them by the small crosshair but use them as-is). Also allow the bad modifier with the large crosshair. * app/tools/gimprectangletool.c (gimp_rectangle_tool_cursor_update): use the new cursors. Don't call gimp_tool_set_cursor() here. (gimp_rectangle_tool_response): reset "function" to RECT_CREATING when resetting the tool. * app/tools/gimpselectiontool.[ch] (struct GimpSelectionTool): added boolean member "allow_move" which defalts to TRUE. (gimp_selection_tool_oper_update): don't move masks, floating selections or anything when "allow_move" is FALSE. Changed behavior of click inside a selection to simply create a new selection, need to press alt+shift now to drag-float the selection. Please test this, it's apretty fundamental change! (gimp_selection_tool_cursor_update): use the tool's configured cursor instead of always GIMP_CURSOR_MOUSE, so this function can be called after gimp_rectangle_tool_cursor_update() to add the plus, minus etc. modifiers. * app/tools/gimpnewrectselecttool.c: implement GimpTool::cursor_update() and call gimp_rectangle_tool_cursor_update() from there. Chain up to get the plus, minus etc. modifiers added. Re-enble selection moving: (gimp_new_rect_select_tool_oper_update): set GimpSelectionTool's "allow_move" to FALSE unless the rectangle tool is in an idle state. (gimp_new_rect_select_tool_button_press): allow a selection moving to be started if the rectangle tool is idle. Fall back to starting a rect select if gimp_selection_tool_start_edit() returned FALSE.
2006-06-02 23:23:47 +08:00
cursor = GIMP_CURSOR_SIDE_TOP;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM:
cursors/Makefile.am cursors/cursor-corner-bottom-left.png 2006-06-02 Michael Natterer <mitch@gimp.org> * cursors/Makefile.am * cursors/cursor-corner-bottom-left.png * cursors/cursor-corner-bottom-right.png * cursors/cursor-corner-top-left.png * cursors/cursor-corner-top-right.png * cursors/cursor-side-bottom.png * cursors/cursor-side-left.png * cursors/cursor-side-right.png * cursors/cursor-side-top.png * cursors/xbm/cursor-corner-bottom-left-mask.xbm * cursors/xbm/cursor-corner-bottom-left.xbm * cursors/xbm/cursor-corner-bottom-right-mask.xbm * cursors/xbm/cursor-corner-bottom-right.xbm * cursors/xbm/cursor-corner-top-left-mask.xbm * cursors/xbm/cursor-corner-top-left.xbm * cursors/xbm/cursor-corner-top-right-mask.xbm * cursors/xbm/cursor-corner-top-right.xbm * cursors/xbm/cursor-side-bottom-mask.xbm * cursors/xbm/cursor-side-bottom.xbm * cursors/xbm/cursor-side-left-mask.xbm * cursors/xbm/cursor-side-left.xbm * cursors/xbm/cursor-side-right-mask.xbm * cursors/xbm/cursor-side-right.xbm * cursors/xbm/cursor-side-top-mask.xbm * cursors/xbm/cursor-side-top.xbm: new cursors for edge and corner resizing. They perfectly align with the small crosshair and can be used together with tool cursors and cursor modifiers. * cursors/gimp-tool-cursors.xcf: add them here too. * app/widgets/widgets-enums.h: add them to the GimpCursorType enum. * app/widgets/gimpcursor.c: add them here too. * app/display/gimpdisplayshell-cursor.c: treat them like the small crosshair (don't replace them by the small crosshair but use them as-is). Also allow the bad modifier with the large crosshair. * app/tools/gimprectangletool.c (gimp_rectangle_tool_cursor_update): use the new cursors. Don't call gimp_tool_set_cursor() here. (gimp_rectangle_tool_response): reset "function" to RECT_CREATING when resetting the tool. * app/tools/gimpselectiontool.[ch] (struct GimpSelectionTool): added boolean member "allow_move" which defalts to TRUE. (gimp_selection_tool_oper_update): don't move masks, floating selections or anything when "allow_move" is FALSE. Changed behavior of click inside a selection to simply create a new selection, need to press alt+shift now to drag-float the selection. Please test this, it's apretty fundamental change! (gimp_selection_tool_cursor_update): use the tool's configured cursor instead of always GIMP_CURSOR_MOUSE, so this function can be called after gimp_rectangle_tool_cursor_update() to add the plus, minus etc. modifiers. * app/tools/gimpnewrectselecttool.c: implement GimpTool::cursor_update() and call gimp_rectangle_tool_cursor_update() from there. Chain up to get the plus, minus etc. modifiers added. Re-enble selection moving: (gimp_new_rect_select_tool_oper_update): set GimpSelectionTool's "allow_move" to FALSE unless the rectangle tool is in an idle state. (gimp_new_rect_select_tool_button_press): allow a selection moving to be started if the rectangle tool is idle. Fall back to starting a rect select if gimp_selection_tool_start_edit() returned FALSE.
2006-06-02 23:23:47 +08:00
cursor = GIMP_CURSOR_SIDE_BOTTOM;
break;
default:
break;
}
}
gimp_tool_control_set_cursor (tool->control, cursor);
gimp_tool_control_set_cursor_modifier (tool->control, modifier);
}
void
gimp_rectangle_tool_draw (GimpDrawTool *draw_tool,
GimpCanvasGroup *stroke_group)
{
GimpTool *tool;
GimpRectangleToolPrivate *private;
GimpRectangleOptions *options;
GimpRectangleOptionsPrivate *options_private;
gdouble x1, y1, x2, y2;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (draw_tool));
g_return_if_fail (stroke_group == NULL || GIMP_IS_CANVAS_GROUP (stroke_group));
tool = GIMP_TOOL (draw_tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (tool);
options_private = GIMP_RECTANGLE_OPTIONS_GET_PRIVATE (options);
gimp_rectangle_tool_get_public_rect (GIMP_RECTANGLE_TOOL (draw_tool),
&x1, &y1, &x2, &y2);
if (private->function == GIMP_RECTANGLE_TOOL_INACTIVE)
return;
if (! stroke_group)
stroke_group = GIMP_CANVAS_GROUP (gimp_draw_tool_add_stroke_group (draw_tool));
gimp_draw_tool_push_group (draw_tool, stroke_group);
gimp_draw_tool_add_rectangle_guides (draw_tool,
options_private->guide,
x1, y1,
x2 - x1,
y2 - y1);
gimp_draw_tool_add_rectangle (draw_tool, FALSE,
x1, y1,
x2 - x1,
y2 - y1);
gimp_draw_tool_pop_group (draw_tool);
switch (private->function)
{
case GIMP_RECTANGLE_TOOL_MOVING:
if (gimp_tool_control_is_active (tool->control))
{
/* Mark the center because we snap to it */
gimp_draw_tool_add_handle (draw_tool,
GIMP_HANDLE_CROSS,
(x1 + x2) / 2.0,
(y1 + y2) / 2.0,
GIMP_TOOL_HANDLE_SIZE_SMALL,
GIMP_TOOL_HANDLE_SIZE_SMALL,
GIMP_HANDLE_ANCHOR_CENTER);
break;
}
else
{
/* Fallthrough */
}
case GIMP_RECTANGLE_TOOL_DEAD:
case GIMP_RECTANGLE_TOOL_CREATING:
gimp_draw_tool_push_group (draw_tool, stroke_group);
gimp_draw_tool_add_corner (draw_tool, FALSE, private->narrow_mode,
x1, y1,
x2, y2,
private->corner_handle_w,
private->corner_handle_h,
GIMP_HANDLE_ANCHOR_NORTH_WEST);
gimp_draw_tool_add_corner (draw_tool, FALSE, private->narrow_mode,
x1, y1,
x2, y2,
private->corner_handle_w,
private->corner_handle_h,
GIMP_HANDLE_ANCHOR_NORTH_EAST);
gimp_draw_tool_add_corner (draw_tool, FALSE, private->narrow_mode,
x1, y1,
x2, y2,
private->corner_handle_w,
private->corner_handle_h,
GIMP_HANDLE_ANCHOR_SOUTH_WEST);
gimp_draw_tool_add_corner (draw_tool, FALSE, private->narrow_mode,
x1, y1,
x2, y2,
private->corner_handle_w,
private->corner_handle_h,
GIMP_HANDLE_ANCHOR_SOUTH_EAST);
gimp_draw_tool_pop_group (draw_tool);
break;
case GIMP_RECTANGLE_TOOL_RESIZING_TOP:
case GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM:
if (gimp_tool_control_is_active (tool->control))
gimp_draw_tool_push_group (draw_tool, stroke_group);
gimp_draw_tool_add_corner (draw_tool,
! gimp_tool_control_is_active (tool->control),
private->narrow_mode,
x1, y1,
x2, y2,
private->top_and_bottom_handle_w,
private->corner_handle_h,
gimp_rectangle_tool_get_anchor (private));
if (gimp_tool_control_is_active (tool->control))
gimp_draw_tool_pop_group (draw_tool);
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_RIGHT:
if (gimp_tool_control_is_active (tool->control))
gimp_draw_tool_push_group (draw_tool, stroke_group);
gimp_draw_tool_add_corner (draw_tool,
! gimp_tool_control_is_active (tool->control),
private->narrow_mode,
x1, y1,
x2, y2,
private->corner_handle_w,
private->left_and_right_handle_h,
gimp_rectangle_tool_get_anchor (private));
if (gimp_tool_control_is_active (tool->control))
gimp_draw_tool_pop_group (draw_tool);
break;
default:
if (gimp_tool_control_is_active (tool->control))
gimp_draw_tool_push_group (draw_tool, stroke_group);
gimp_draw_tool_add_corner (draw_tool,
! gimp_tool_control_is_active (tool->control),
private->narrow_mode,
x1, y1,
x2, y2,
private->corner_handle_w,
private->corner_handle_h,
gimp_rectangle_tool_get_anchor (private));
if (gimp_tool_control_is_active (tool->control))
gimp_draw_tool_pop_group (draw_tool);
break;
}
}
static void
gimp_rectangle_tool_update_handle_sizes (GimpRectangleTool *rect_tool)
{
GimpTool *tool;
GimpRectangleToolPrivate *private;
GimpDisplayShell *shell;
gint visible_rectangle_width;
gint visible_rectangle_height;
gint rectangle_width;
gint rectangle_height;
gdouble pub_x1, pub_y1;
gdouble pub_x2, pub_y2;
tool = GIMP_TOOL (rect_tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
if (! (tool && tool->display))
return;
shell = gimp_display_get_shell (tool->display);
gimp_rectangle_tool_get_public_rect (rect_tool,
&pub_x1, &pub_y1, &pub_x2, &pub_y2);
{
/* Calculate rectangles of the selection rectangle and the display shell,
* with origin at (0, 0) of image, and in screen coordinate scale.
*/
gint x1 = pub_x1 * shell->scale_x;
gint y1 = pub_y1 * shell->scale_y;
gint w1 = (pub_x2 - pub_x1) * shell->scale_x;
gint h1 = (pub_y2 - pub_y1) * shell->scale_y;
gint x2, y2, w2, h2;
gimp_display_shell_scroll_get_scaled_viewport (shell, &x2, &y2, &w2, &h2);
rectangle_width = w1;
rectangle_height = h1;
/* Handle size calculations shall be based on the visible part of the
* rectangle, so calculate the size for the visible rectangle by
* intersecting with the viewport rectangle.
*/
gimp_rectangle_intersect (x1, y1,
w1, h1,
x2, y2,
w2, h2,
NULL, NULL,
&visible_rectangle_width,
&visible_rectangle_height);
/* Determine if we are in narrow-mode or not. */
private->narrow_mode = (visible_rectangle_width < NARROW_MODE_THRESHOLD ||
visible_rectangle_height < NARROW_MODE_THRESHOLD);
}
if (private->narrow_mode)
{
/* Corner handles always have the same (on-screen) size in
* narrow-mode.
*/
private->corner_handle_w = NARROW_MODE_HANDLE_SIZE;
private->corner_handle_h = NARROW_MODE_HANDLE_SIZE;
private->top_and_bottom_handle_w = CLAMP (rectangle_width,
MIN (rectangle_width - 2,
NARROW_MODE_HANDLE_SIZE),
G_MAXINT);
private->left_and_right_handle_h = CLAMP (rectangle_height,
MIN (rectangle_height - 2,
NARROW_MODE_HANDLE_SIZE),
G_MAXINT);
}
else
{
/* Calculate and clamp corner handle size. */
private->corner_handle_w = visible_rectangle_width / 4;
private->corner_handle_h = visible_rectangle_height / 4;
private->corner_handle_w = CLAMP (private->corner_handle_w,
MIN_HANDLE_SIZE,
MAX_HANDLE_SIZE);
private->corner_handle_h = CLAMP (private->corner_handle_h,
MIN_HANDLE_SIZE,
MAX_HANDLE_SIZE);
/* Calculate and clamp side handle size. */
private->top_and_bottom_handle_w = rectangle_width - 3 * private->corner_handle_w;
private->left_and_right_handle_h = rectangle_height - 3 * private->corner_handle_h;
private->top_and_bottom_handle_w = CLAMP (private->top_and_bottom_handle_w,
MIN_HANDLE_SIZE,
G_MAXINT);
private->left_and_right_handle_h = CLAMP (private->left_and_right_handle_h,
MIN_HANDLE_SIZE,
G_MAXINT);
}
/* Keep track of when we need to calculate handle sizes because of a display
* shell change.
*/
private->scale_x_used_for_handle_size_calculations = shell->scale_x;
private->scale_y_used_for_handle_size_calculations = shell->scale_y;
}
/**
* gimp_rectangle_tool_scale_has_changed:
* @rect_tool: A #GimpRectangleTool.
*
* Returns: %TRUE if the scale that was used to calculate handle sizes
* is not the same as the current shell scale.
*/
static gboolean
gimp_rectangle_tool_scale_has_changed (GimpRectangleTool *rect_tool)
{
GimpTool *tool = GIMP_TOOL (rect_tool);
GimpRectangleToolPrivate *private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
GimpDisplayShell *shell;
if (! tool->display)
return TRUE;
shell = gimp_display_get_shell (tool->display);
return (shell->scale_x != private->scale_x_used_for_handle_size_calculations
||
shell->scale_y != private->scale_y_used_for_handle_size_calculations);
}
static void
gimp_rectangle_tool_start (GimpRectangleTool *rect_tool,
GimpDisplay *display)
{
GimpTool *tool = GIMP_TOOL (rect_tool);
GimpRectangleOptionsPrivate *options_private;
GimpImage *image;
gdouble xres;
gdouble yres;
options_private =
GIMP_RECTANGLE_OPTIONS_GET_PRIVATE (gimp_tool_get_options (tool));
image = gimp_display_get_image (display);
tool->display = display;
g_signal_connect_object (gimp_display_get_shell (tool->display), "scrolled",
G_CALLBACK (gimp_rectangle_tool_shell_scrolled),
rect_tool, 0);
gimp_rectangle_tool_update_highlight (rect_tool);
gimp_rectangle_tool_update_handle_sizes (rect_tool);
/* initialize the statusbar display */
gimp_tool_push_status_coords (tool, tool->display,
gimp_tool_control_get_precision (tool->control),
_("Rectangle: "), 0, " × ", 0, NULL);
gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), tool->display);
gimp_image_get_resolution (image, &xres, &yres);
if (options_private->fixed_width_entry)
{
GtkWidget *entry = options_private->fixed_width_entry;
gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 0, xres, FALSE);
gimp_size_entry_set_size (GIMP_SIZE_ENTRY (entry), 0,
0, gimp_image_get_width (image));
}
if (options_private->fixed_height_entry)
{
GtkWidget *entry = options_private->fixed_height_entry;
gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 0, yres, FALSE);
gimp_size_entry_set_size (GIMP_SIZE_ENTRY (entry), 0,
0, gimp_image_get_height (image));
}
if (options_private->x_entry)
{
GtkWidget *entry = options_private->x_entry;
gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 0, xres, FALSE);
gimp_size_entry_set_size (GIMP_SIZE_ENTRY (entry), 0,
0, gimp_image_get_width (image));
}
if (options_private->y_entry)
{
GtkWidget *entry = options_private->y_entry;
gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 0, yres, FALSE);
gimp_size_entry_set_size (GIMP_SIZE_ENTRY (entry), 0,
0, gimp_image_get_height (image));
}
if (options_private->width_entry)
{
GtkWidget *entry = options_private->width_entry;
gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 0, xres, FALSE);
gimp_size_entry_set_size (GIMP_SIZE_ENTRY (entry), 0,
0, gimp_image_get_width (image));
}
if (options_private->height_entry)
{
GtkWidget *entry = options_private->height_entry;
gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 0, yres, FALSE);
gimp_size_entry_set_size (GIMP_SIZE_ENTRY (entry), 0,
0, gimp_image_get_height (image));
}
if (options_private->auto_shrink_button)
{
g_signal_connect_swapped (options_private->auto_shrink_button, "clicked",
G_CALLBACK (gimp_rectangle_tool_auto_shrink),
rect_tool);
gtk_widget_set_sensitive (options_private->auto_shrink_button, TRUE);
}
}
static void
gimp_rectangle_tool_halt (GimpRectangleTool *rect_tool)
{
GimpTool *tool = GIMP_TOOL (rect_tool);
GimpRectangleOptionsPrivate *options_private;
options_private =
GIMP_RECTANGLE_OPTIONS_GET_PRIVATE (gimp_tool_get_options (tool));
if (tool->display)
{
GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
gimp_display_shell_set_highlight (shell, NULL);
g_signal_handlers_disconnect_by_func (shell,
gimp_rectangle_tool_shell_scrolled,
rect_tool);
}
if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (rect_tool)))
gimp_draw_tool_stop (GIMP_DRAW_TOOL (rect_tool));
tool->display = NULL;
tool->drawable = NULL;
gimp_rectangle_tool_set_function (rect_tool, GIMP_RECTANGLE_TOOL_INACTIVE);
if (options_private->auto_shrink_button)
{
gtk_widget_set_sensitive (options_private->auto_shrink_button, FALSE);
g_signal_handlers_disconnect_by_func (options_private->auto_shrink_button,
gimp_rectangle_tool_auto_shrink,
rect_tool);
}
}
gboolean
gimp_rectangle_tool_execute (GimpRectangleTool *rect_tool)
{
GimpRectangleToolInterface *iface;
gboolean retval = FALSE;
iface = GIMP_RECTANGLE_TOOL_GET_INTERFACE (rect_tool);
if (iface->execute)
{
gdouble pub_x1, pub_y1;
gdouble pub_x2, pub_y2;
gimp_rectangle_tool_get_public_rect (rect_tool,
&pub_x1, &pub_y1, &pub_x2, &pub_y2);
gimp_draw_tool_pause (GIMP_DRAW_TOOL (rect_tool));
retval = iface->execute (rect_tool,
pub_x1,
pub_y1,
pub_x2 - pub_x1,
pub_y2 - pub_y1);
gimp_rectangle_tool_update_highlight (rect_tool);
gimp_draw_tool_resume (GIMP_DRAW_TOOL (rect_tool));
}
return retval;
}
void
gimp_rectangle_tool_cancel (GimpRectangleTool *rect_tool)
{
GimpRectangleToolInterface *iface;
iface = GIMP_RECTANGLE_TOOL_GET_INTERFACE (rect_tool);
if (iface->cancel)
iface->cancel (rect_tool);
}
static void
gimp_rectangle_tool_update_options (GimpRectangleTool *rect_tool,
GimpDisplay *display)
{
GimpRectangleOptions *options;
gdouble x1, y1;
gdouble x2, y2;
gdouble old_x;
gdouble old_y;
gdouble old_width;
gdouble old_height;
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (rect_tool);
gimp_rectangle_tool_get_public_rect (rect_tool, &x1, &y1, &x2, &y2);
g_signal_handlers_block_by_func (options,
gimp_rectangle_tool_options_notify,
rect_tool);
g_object_get (options,
"x", &old_x,
"y", &old_y,
"width", &old_width,
"height", &old_height,
NULL);
g_object_freeze_notify (G_OBJECT (options));
if (! FEQUAL (old_x, x1))
g_object_set (options, "x", x1, NULL);
if (! FEQUAL (old_y, y1))
g_object_set (options, "y", y1, NULL);
if (! FEQUAL (old_width, x2 - x1))
g_object_set (options, "width", x2 - x1, NULL);
if (! FEQUAL (old_height, y2 - y1))
g_object_set (options, "height", y2 - y1, NULL);
g_object_thaw_notify (G_OBJECT (options));
g_signal_handlers_unblock_by_func (options,
gimp_rectangle_tool_options_notify,
rect_tool);
}
static void
gimp_rectangle_tool_synthesize_motion (GimpRectangleTool *rect_tool,
gint function,
gdouble new_x,
gdouble new_y)
{
GimpTool *tool;
GimpDrawTool *draw_tool;
GimpRectangleToolPrivate *private;
GimpRectangleFunction old_function;
tool = GIMP_TOOL (rect_tool);
draw_tool = GIMP_DRAW_TOOL (rect_tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
/* We don't want to synthesize motions if the tool control is active
* since that means the mouse button is down and the rectangle will
* get updated in _motion anyway. The reason we want to prevent this
* function from executing is that is emits the
* rectangle-changed-complete signal which we don't want in the
* middle of a rectangle change.
*
* In addition to that, we don't want to synthesize a motion if
* there is no pending rectangle because that doesn't make any
* sense.
*/
if (gimp_tool_control_is_active (tool->control) ||
! tool->display)
return;
old_function = private->function;
gimp_draw_tool_pause (draw_tool);
gimp_rectangle_tool_set_function (rect_tool, function);
gimp_rectangle_tool_update_with_coord (rect_tool,
new_x,
new_y);
/* We must update this. */
private->center_x_on_fixed_center = (private->x1 + private->x2) / 2;
private->center_y_on_fixed_center = (private->y1 + private->y2) / 2;
gimp_rectangle_tool_update_options (rect_tool,
tool->display);
gimp_rectangle_tool_set_function (rect_tool, old_function);
gimp_rectangle_tool_update_highlight (rect_tool);
gimp_rectangle_tool_update_handle_sizes (rect_tool);
gimp_draw_tool_resume (draw_tool);
gimp_rectangle_tool_rectangle_change_complete (rect_tool);
}
static void
gimp_rectangle_tool_options_notify (GimpRectangleOptions *options,
GParamSpec *pspec,
GimpRectangleTool *rect_tool)
{
GimpTool *tool;
GimpRectangleToolPrivate *private;
GimpRectangleOptionsPrivate *options_private;
tool = GIMP_TOOL (rect_tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
options_private = GIMP_RECTANGLE_OPTIONS_GET_PRIVATE (options);
if (strcmp (pspec->name, "guide") == 0)
{
gimp_draw_tool_pause (GIMP_DRAW_TOOL (rect_tool));
gimp_draw_tool_resume (GIMP_DRAW_TOOL (rect_tool));
}
else if (strcmp (pspec->name, "x") == 0 &&
!PIXEL_FEQUAL (private->x1, options_private->x))
{
gimp_rectangle_tool_synthesize_motion (rect_tool,
GIMP_RECTANGLE_TOOL_MOVING,
options_private->x,
private->y1);
}
else if (strcmp (pspec->name, "y") == 0 &&
!PIXEL_FEQUAL (private->y1, options_private->y))
{
gimp_rectangle_tool_synthesize_motion (rect_tool,
GIMP_RECTANGLE_TOOL_MOVING,
private->x1,
options_private->y);
}
else if (strcmp (pspec->name, "width") == 0 &&
!PIXEL_FEQUAL (private->x2 - private->x1, options_private->width))
{
/* Calculate x2, y2 that will create a rectangle of given width, for the
* current options.
*/
gdouble x2;
if (options_private->fixed_center)
{
x2 = private->center_x_on_fixed_center +
options_private->width / 2;
}
else
{
x2 = private->x1 + options_private->width;
}
gimp_rectangle_tool_synthesize_motion (rect_tool,
GIMP_RECTANGLE_TOOL_RESIZING_RIGHT,
x2,
private->y2);
}
else if (strcmp (pspec->name, "height") == 0 &&
!PIXEL_FEQUAL (private->y2 - private->y1, options_private->height))
{
/* Calculate x2, y2 that will create a rectangle of given height, for the
* current options.
*/
gdouble y2;
if (options_private->fixed_center)
{
y2 = private->center_y_on_fixed_center +
options_private->height / 2;
}
else
{
y2 = private->y1 + options_private->height;
}
gimp_rectangle_tool_synthesize_motion (rect_tool,
GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM,
private->x2,
y2);
}
else if (strcmp (pspec->name, "desired-fixed-size-width") == 0)
{
/* We are only interested in when width and height swaps, so
* it's enough to only check e.g. for width.
*/
gdouble width = private->x2 - private->x1;
gdouble height = private->y2 - private->y1;
/* Depending on a bunch of conditions, we might want to
* immedieately switch width and height of the pending
* rectangle.
*/
if (options_private->fixed_rule_active &&
tool->display != NULL &&
tool->button_press_state == 0 &&
tool->active_modifier_state == 0 &&
FEQUAL (options_private->desired_fixed_size_width, height) &&
FEQUAL (options_private->desired_fixed_size_height, width))
{
gdouble x = private->x1;
gdouble y = private->y1;
gimp_rectangle_tool_synthesize_motion (rect_tool,
GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT,
private->x2,
private->y2);
/* For some reason these needs to be set separately... */
g_object_set (options,
"x", x,
NULL);
g_object_set (options,
"y", y,
NULL);
}
}
else if (strcmp (pspec->name, "aspect-numerator") == 0)
{
/* We are only interested in when numerator and denominator
* swaps, so it's enough to only check e.g. for numerator.
*/
double width = private->x2 - private->x1;
double height = private->y2 - private->y1;
gdouble new_inverse_ratio = options_private->aspect_denominator /
options_private->aspect_numerator;
gdouble lower_ratio;
gdouble higher_ratio;
/* The ratio of the Fixed: Aspect ratio rule and the pending
* rectangle is very rarely exactly the same so use an
* interval. For small rectangles the below code will
* automatically yield a more generous accepted ratio interval
* which is exactly what we want.
*/
if (width > height && height > 1.0)
{
lower_ratio = width / (height + 1.0);
higher_ratio = width / (height - 1.0);
}
else
{
lower_ratio = (width - 1.0) / height;
higher_ratio = (width + 1.0) / height;
}
/* Depending on a bunch of conditions, we might want to
* immedieately switch width and height of the pending
* rectangle.
*/
if (options_private->fixed_rule_active &&
tool->display != NULL &&
tool->button_press_state == 0 &&
tool->active_modifier_state == 0 &&
lower_ratio < new_inverse_ratio &&
higher_ratio > new_inverse_ratio)
{
gdouble new_x2 = private->x1 + private->y2 - private->y1;
gdouble new_y2 = private->y1 + private->x2 - private->x1;
gimp_rectangle_tool_synthesize_motion (rect_tool,
GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT,
new_x2,
new_y2);
}
}
else if (strcmp (pspec->name, "highlight") == 0)
{
gimp_rectangle_tool_update_highlight (rect_tool);
}
}
static void
gimp_rectangle_tool_shell_scrolled (GimpDisplayShell *shell,
GimpRectangleTool *rect_tool)
{
GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (rect_tool);
gimp_draw_tool_pause (draw_tool);
gimp_rectangle_tool_update_handle_sizes (rect_tool);
gimp_draw_tool_resume (draw_tool);
}
GimpRectangleFunction
gimp_rectangle_tool_get_function (GimpRectangleTool *rect_tool)
{
g_return_val_if_fail (GIMP_IS_RECTANGLE_TOOL (rect_tool),
GIMP_RECTANGLE_TOOL_INACTIVE);
return GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool)->function;
}
void
gimp_rectangle_tool_set_function (GimpRectangleTool *rect_tool,
GimpRectangleFunction function)
{
GimpRectangleToolPrivate *private;
g_return_if_fail (GIMP_IS_RECTANGLE_TOOL (rect_tool));
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
/* redraw the tool when the function changes */
/* FIXME: should also update the cursor */
if (private->function != function)
{
GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (rect_tool);
gimp_draw_tool_pause (draw_tool);
private->function = function;
gimp_draw_tool_resume (draw_tool);
}
}
static void
gimp_rectangle_tool_rectangle_change_complete (GimpRectangleTool *rect_tool)
{
g_signal_emit (rect_tool,
gimp_rectangle_tool_signals[RECTANGLE_CHANGE_COMPLETE], 0);
}
static void
gimp_rectangle_tool_auto_shrink (GimpRectangleTool *rect_tool)
{
GimpTool *tool = GIMP_TOOL (rect_tool);
GimpRectangleToolPrivate *private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
GimpDisplay *display = tool->display;
GimpImage *image;
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;
if (! display)
return;
image = gimp_display_get_image (display);
width = gimp_image_get_width (image);
height = gimp_image_get_height (image);
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 (image,
x1, y1, x2, y2,
! shrink_merged,
&shrunk_x1,
&shrunk_y1,
&shrunk_x2,
&shrunk_y2))
{
gimp_draw_tool_pause (GIMP_DRAW_TOOL (rect_tool));
private->x1 = offset_x + shrunk_x1;
private->y1 = offset_x + shrunk_y1;
private->x2 = offset_x + shrunk_x2;
private->y2 = offset_x + shrunk_y2;
gimp_rectangle_tool_update_int_rect (rect_tool);
gimp_rectangle_tool_rectangle_change_complete (rect_tool);
gimp_rectangle_tool_update_handle_sizes (rect_tool);
gimp_rectangle_tool_update_highlight (rect_tool);
gimp_draw_tool_resume (GIMP_DRAW_TOOL (rect_tool));
}
gimp_rectangle_tool_update_options (rect_tool, tool->display);
}
/**
* gimp_rectangle_tool_coord_outside:
*
* Returns: %TRUE if the coord is outside the rectange bounds
* including any outside handles.
*/
static gboolean
gimp_rectangle_tool_coord_outside (GimpRectangleTool *rect_tool,
const GimpCoords *coord)
{
GimpRectangleToolPrivate *private;
GimpDisplayShell *shell;
gboolean narrow_mode;
gdouble pub_x1, pub_y1, pub_x2, pub_y2;
gdouble x1_b, y1_b, x2_b, y2_b;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
narrow_mode = private->narrow_mode;
shell = gimp_display_get_shell (GIMP_TOOL (rect_tool)->display);
gimp_rectangle_tool_get_public_rect (rect_tool,
&pub_x1, &pub_y1, &pub_x2, &pub_y2);
x1_b = pub_x1 - (narrow_mode ? private->corner_handle_w / shell->scale_x : 0);
x2_b = pub_x2 + (narrow_mode ? private->corner_handle_w / shell->scale_x : 0);
y1_b = pub_y1 - (narrow_mode ? private->corner_handle_h / shell->scale_y : 0);
y2_b = pub_y2 + (narrow_mode ? private->corner_handle_h / shell->scale_y : 0);
return (coord->x < x1_b ||
coord->x > x2_b ||
coord->y < y1_b ||
coord->y > y2_b);
}
/**
* gimp_rectangle_tool_coord_on_handle:
*
* Returns: %TRUE if the coord is on the handle that corresponds to
* @anchor.
*/
static gboolean
gimp_rectangle_tool_coord_on_handle (GimpRectangleTool *rect_tool,
const GimpCoords *coords,
GimpHandleAnchor anchor)
{
GimpRectangleToolPrivate *private;
GimpDisplayShell *shell;
GimpDrawTool *draw_tool;
GimpTool *tool;
gdouble pub_x1, pub_y1, pub_x2, pub_y2;
gdouble rect_w, rect_h;
gdouble handle_x = 0;
gdouble handle_y = 0;
gdouble handle_width = 0;
gdouble handle_height = 0;
gint narrow_mode_x_dir = 0;
gint narrow_mode_y_dir = 0;
tool = GIMP_TOOL (rect_tool);
draw_tool = GIMP_DRAW_TOOL (tool);
shell = gimp_display_get_shell (tool->display);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
gimp_rectangle_tool_get_public_rect (rect_tool,
&pub_x1, &pub_y1, &pub_x2, &pub_y2);
rect_w = pub_x2 - pub_x1;
rect_h = pub_y2 - pub_y1;
switch (anchor)
{
case GIMP_HANDLE_ANCHOR_NORTH_WEST:
handle_x = pub_x1;
handle_y = pub_y1;
handle_width = private->corner_handle_w;
handle_height = private->corner_handle_h;
narrow_mode_x_dir = -1;
narrow_mode_y_dir = -1;
break;
case GIMP_HANDLE_ANCHOR_SOUTH_EAST:
handle_x = pub_x2;
handle_y = pub_y2;
handle_width = private->corner_handle_w;
handle_height = private->corner_handle_h;
narrow_mode_x_dir = 1;
narrow_mode_y_dir = 1;
break;
case GIMP_HANDLE_ANCHOR_NORTH_EAST:
handle_x = pub_x2;
handle_y = pub_y1;
handle_width = private->corner_handle_w;
handle_height = private->corner_handle_h;
narrow_mode_x_dir = 1;
narrow_mode_y_dir = -1;
break;
case GIMP_HANDLE_ANCHOR_SOUTH_WEST:
handle_x = pub_x1;
handle_y = pub_y2;
handle_width = private->corner_handle_w;
handle_height = private->corner_handle_h;
narrow_mode_x_dir = -1;
narrow_mode_y_dir = 1;
break;
case GIMP_HANDLE_ANCHOR_WEST:
handle_x = pub_x1;
handle_y = pub_y1 + rect_h / 2;
handle_width = private->corner_handle_w;
handle_height = private->left_and_right_handle_h;
narrow_mode_x_dir = -1;
narrow_mode_y_dir = 0;
break;
case GIMP_HANDLE_ANCHOR_EAST:
handle_x = pub_x2;
handle_y = pub_y1 + rect_h / 2;
handle_width = private->corner_handle_w;
handle_height = private->left_and_right_handle_h;
narrow_mode_x_dir = 1;
narrow_mode_y_dir = 0;
break;
case GIMP_HANDLE_ANCHOR_NORTH:
handle_x = pub_x1 + rect_w / 2;
handle_y = pub_y1;
handle_width = private->top_and_bottom_handle_w;
handle_height = private->corner_handle_h;
narrow_mode_x_dir = 0;
narrow_mode_y_dir = -1;
break;
case GIMP_HANDLE_ANCHOR_SOUTH:
handle_x = pub_x1 + rect_w / 2;
handle_y = pub_y2;
handle_width = private->top_and_bottom_handle_w;
handle_height = private->corner_handle_h;
narrow_mode_x_dir = 0;
narrow_mode_y_dir = 1;
break;
case GIMP_HANDLE_ANCHOR_CENTER:
handle_x = pub_x1 + rect_w / 2;
handle_y = pub_y1 + rect_h / 2;
if (private->narrow_mode)
{
handle_width = rect_w * shell->scale_x;
handle_height = rect_h * shell->scale_y;
}
else
{
handle_width = rect_w * shell->scale_x - private->corner_handle_w * 2;
handle_height = rect_h * shell->scale_y - private->corner_handle_h * 2;
}
narrow_mode_x_dir = 0;
narrow_mode_y_dir = 0;
break;
}
if (private->narrow_mode)
{
handle_x += narrow_mode_x_dir * handle_width / shell->scale_x;
handle_y += narrow_mode_y_dir * handle_height / shell->scale_y;
}
return gimp_draw_tool_on_handle (draw_tool, shell->display,
coords->x, coords->y,
GIMP_HANDLE_SQUARE,
handle_x, handle_y,
handle_width, handle_height,
anchor);
}
static GimpHandleAnchor
gimp_rectangle_tool_get_anchor (GimpRectangleToolPrivate *private)
{
switch (private->function)
{
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
return GIMP_HANDLE_ANCHOR_NORTH_WEST;
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
return GIMP_HANDLE_ANCHOR_NORTH_EAST;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
return GIMP_HANDLE_ANCHOR_SOUTH_WEST;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
return GIMP_HANDLE_ANCHOR_SOUTH_EAST;
case GIMP_RECTANGLE_TOOL_RESIZING_LEFT:
return GIMP_HANDLE_ANCHOR_WEST;
case GIMP_RECTANGLE_TOOL_RESIZING_RIGHT:
return GIMP_HANDLE_ANCHOR_EAST;
case GIMP_RECTANGLE_TOOL_RESIZING_TOP:
return GIMP_HANDLE_ANCHOR_NORTH;
case GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM:
return GIMP_HANDLE_ANCHOR_SOUTH;
default:
return GIMP_HANDLE_ANCHOR_CENTER;
}
}
static void
gimp_rectangle_tool_update_highlight (GimpRectangleTool *rect_tool)
{
GimpRectangleToolPrivate *private;
GimpTool *tool;
GimpRectangleOptions *options;
GimpDisplayShell *shell;
gboolean highlight;
tool = GIMP_TOOL (rect_tool);
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
highlight = FALSE;
if (! tool->display)
return;
shell = gimp_display_get_shell (tool->display);
g_object_get (options, "highlight", &highlight, NULL);
/* Don't show the highlight when the mouse is down. */
if (! highlight || private->rect_adjusting)
{
gimp_display_shell_set_highlight (shell, NULL);
}
else
{
GdkRectangle rect;
gdouble pub_x1, pub_y1;
gdouble pub_x2, pub_y2;
gimp_rectangle_tool_get_public_rect (rect_tool,
&pub_x1, &pub_y1, &pub_x2, &pub_y2);
rect.x = pub_x1;
rect.y = pub_y1;
rect.width = pub_x2 - pub_x1;
rect.height = pub_y2 - pub_y1;
gimp_display_shell_set_highlight (shell, &rect);
}
}
static gboolean
gimp_rectangle_tool_rect_rubber_banding_func (GimpRectangleTool *rect_tool)
{
GimpRectangleToolPrivate *private;
gboolean rect_rubber_banding_func;
2011-10-06 11:07:41 +08:00
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
switch (private->function)
{
case GIMP_RECTANGLE_TOOL_CREATING:
case GIMP_RECTANGLE_TOOL_RESIZING_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_TOP:
case GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM:
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
rect_rubber_banding_func = TRUE;
break;
case GIMP_RECTANGLE_TOOL_MOVING:
case GIMP_RECTANGLE_TOOL_INACTIVE:
case GIMP_RECTANGLE_TOOL_DEAD:
default:
rect_rubber_banding_func = FALSE;
break;
}
return rect_rubber_banding_func;
}
/**
* gimp_rectangle_tool_rect_adjusting_func:
* @rect_tool:
*
* Returns: %TRUE if the current function is a rectangle adjusting
* function.
*/
static gboolean
gimp_rectangle_tool_rect_adjusting_func (GimpRectangleTool *rect_tool)
{
GimpRectangleToolPrivate *private;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
return (gimp_rectangle_tool_rect_rubber_banding_func (rect_tool) ||
private->function == GIMP_RECTANGLE_TOOL_MOVING);
}
/**
* gimp_rectangle_tool_get_other_side:
* @rect_tool: A #GimpRectangleTool.
* @other_x: Pointer to double of the other-x double.
* @other_y: Pointer to double of the other-y double.
*
* Calculates pointers to member variables that hold the coordinates
* of the opposite side (either the opposite corner or literally the
* opposite side), based on the current function. The opposite of a
* corner needs two coordinates, the opposite of a side only needs
* one.
*/
static void
gimp_rectangle_tool_get_other_side (GimpRectangleTool *rect_tool,
gdouble **other_x,
gdouble **other_y)
{
GimpRectangleToolPrivate *private;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
switch (private->function)
{
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_RIGHT:
*other_x = &private->x1;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_LEFT:
*other_x = &private->x2;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_TOP:
case GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM:
default:
*other_x = NULL;
break;
}
switch (private->function)
{
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM:
*other_y = &private->y1;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_TOP:
*other_y = &private->y2;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_RIGHT:
default:
*other_y = NULL;
break;
}
}
static void
gimp_rectangle_tool_get_other_side_coord (GimpRectangleTool *rect_tool,
gdouble *other_side_x,
gdouble *other_side_y)
{
gdouble *other_x = NULL;
gdouble *other_y = NULL;
gimp_rectangle_tool_get_other_side (rect_tool,
&other_x,
&other_y);
if (other_x)
*other_side_x = *other_x;
if (other_y)
*other_side_y = *other_y;
}
static void
gimp_rectangle_tool_set_other_side_coord (GimpRectangleTool *rect_tool,
gdouble other_side_x,
gdouble other_side_y)
{
gdouble *other_x = NULL;
gdouble *other_y = NULL;
gimp_rectangle_tool_get_other_side (rect_tool,
&other_x,
&other_y);
if (other_x)
*other_x = other_side_x;
if (other_y)
*other_y = other_side_y;
gimp_rectangle_tool_check_function (rect_tool);
gimp_rectangle_tool_update_int_rect (rect_tool);
}
/**
* gimp_rectangle_tool_apply_coord:
* @param: A #GimpRectangleTool.
* @coord_x: X of coord.
* @coord_y: Y of coord.
*
* Adjust the rectangle to the new position specified by passed
* coordinate, taking fixed_center into account, which means it
* expands the rectagle around the center point.
*/
static void
gimp_rectangle_tool_apply_coord (GimpRectangleTool *rect_tool,
gdouble coord_x,
gdouble coord_y)
{
GimpRectangleToolPrivate *private;
GimpRectangleOptions *options;
GimpRectangleOptionsPrivate *options_private;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (rect_tool);
options_private = GIMP_RECTANGLE_OPTIONS_GET_PRIVATE (options);
if (private->function == GIMP_RECTANGLE_TOOL_INACTIVE)
g_warning ("function is GIMP_RECTANGLE_TOOL_INACTIVE while mouse is moving");
if (private->function == GIMP_RECTANGLE_TOOL_MOVING)
{
/* Preserve width and height while moving the grab-point to where the
* cursor is.
*/
gdouble w = private->x2 - private->x1;
gdouble h = private->y2 - private->y1;
private->x1 = coord_x;
private->y1 = coord_y;
private->x2 = private->x1 + w;
private->y2 = private->y1 + h;
/* We are done already. */
return;
}
switch (private->function)
{
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_LEFT:
private->x1 = coord_x;
if (options_private->fixed_center)
private->x2 = 2 * private->center_x_on_fixed_center - private->x1;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_RIGHT:
private->x2 = coord_x;
if (options_private->fixed_center)
private->x1 = 2 * private->center_x_on_fixed_center - private->x2;
break;
}
switch (private->function)
{
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_TOP:
private->y1 = coord_y;
if (options_private->fixed_center)
private->y2 = 2 * private->center_y_on_fixed_center - private->y1;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM:
private->y2 = coord_y;
if (options_private->fixed_center)
private->y1 = 2 * private->center_y_on_fixed_center - private->y2;
break;
}
}
static void
gimp_rectangle_tool_setup_snap_offsets (GimpRectangleTool *rect_tool,
const GimpCoords *coords)
{
GimpTool *tool;
GimpRectangleToolPrivate *private;
gdouble pub_x1, pub_y1, pub_x2, pub_y2;
gdouble pub_coord_x, pub_coord_y;
tool = GIMP_TOOL (rect_tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
gimp_rectangle_tool_get_public_rect (rect_tool,
&pub_x1, &pub_y1, &pub_x2, &pub_y2);
gimp_rectangle_tool_adjust_coord (rect_tool,
coords->x, coords->y,
&pub_coord_x, &pub_coord_y);
switch (private->function)
{
gimp_tool_control_set_snap_offsets (tool->control, 0, 0, 0, 0);
break;
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
gimp_tool_control_set_snap_offsets (tool->control,
pub_x1 - pub_coord_x,
pub_y1 - pub_coord_y,
0, 0);
break;
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
gimp_tool_control_set_snap_offsets (tool->control,
pub_x2 - pub_coord_x,
pub_y1 - pub_coord_y,
0, 0);
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
gimp_tool_control_set_snap_offsets (tool->control,
pub_x1 - pub_coord_x,
pub_y2 - pub_coord_y,
0, 0);
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
gimp_tool_control_set_snap_offsets (tool->control,
pub_x2 - pub_coord_x,
pub_y2 - pub_coord_y,
0, 0);
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LEFT:
gimp_tool_control_set_snap_offsets (tool->control,
pub_x1 - pub_coord_x, 0,
0, 0);
break;
case GIMP_RECTANGLE_TOOL_RESIZING_RIGHT:
gimp_tool_control_set_snap_offsets (tool->control,
pub_x2 - pub_coord_x, 0,
0, 0);
break;
case GIMP_RECTANGLE_TOOL_RESIZING_TOP:
gimp_tool_control_set_snap_offsets (tool->control,
0, pub_y1 - pub_coord_y,
0, 0);
break;
case GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM:
gimp_tool_control_set_snap_offsets (tool->control,
0, pub_y2 - pub_coord_y,
0, 0);
break;
case GIMP_RECTANGLE_TOOL_MOVING:
gimp_tool_control_set_snap_offsets (tool->control,
pub_x1 - pub_coord_x,
pub_y1 - pub_coord_y,
pub_x2 - pub_x1,
pub_y2 - pub_y1);
break;
default:
break;
}
}
/**
* gimp_rectangle_tool_clamp_width:
* @rect_tool: A #GimpRectangleTool.
* @clamped_sides: Where to put contrainment information.
* @constraint: Constraint to use.
* @symmetrically: Whether or not to clamp symmetrically.
*
* Clamps rectangle inside specified bounds, providing information of
* where clamping was done. Can also clamp symmetrically.
*/
static void
gimp_rectangle_tool_clamp (GimpRectangleTool *rect_tool,
ClampedSide *clamped_sides,
GimpRectangleConstraint constraint,
gboolean symmetrically)
{
gimp_rectangle_tool_clamp_width (rect_tool,
clamped_sides,
constraint,
symmetrically);
gimp_rectangle_tool_clamp_height (rect_tool,
clamped_sides,
constraint,
symmetrically);
}
/**
* gimp_rectangle_tool_clamp_width:
* @rect_tool: A #GimpRectangleTool.
* @clamped_sides: Where to put contrainment information.
* @constraint: Constraint to use.
* @symmetrically: Whether or not to clamp symmetrically.
*
* Clamps height of rectangle. Set symmetrically to true when using
* for fixed_center:ed rectangles, since that will clamp symmetrically
* which is just what is needed.
*
* When this function constrains, it puts what it constrains in
* @constraint. This information is essential when an aspect ratio is
* to be applied.
*/
static void
gimp_rectangle_tool_clamp_width (GimpRectangleTool *rect_tool,
ClampedSide *clamped_sides,
GimpRectangleConstraint constraint,
gboolean symmetrically)
{
GimpRectangleToolPrivate *private;
gint min_x;
gint max_x;
if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE)
return;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
gimp_rectangle_tool_get_constraints (rect_tool,
&min_x,
NULL,
&max_x,
NULL,
constraint);
if (private->x1 < min_x)
{
gdouble dx = min_x - private->x1;
private->x1 += dx;
if (symmetrically)
private->x2 -= dx;
if (private->x2 < min_x)
private->x2 = min_x;
if (clamped_sides)
*clamped_sides |= CLAMPED_LEFT;
}
if (private->x2 > max_x)
{
gdouble dx = max_x - private->x2;
private->x2 += dx;
if (symmetrically)
private->x1 -= dx;
if (private->x1 > max_x)
private->x1 = max_x;
if (clamped_sides)
*clamped_sides |= CLAMPED_RIGHT;
}
}
/**
* gimp_rectangle_tool_clamp_height:
* @rect_tool: A #GimpRectangleTool.
* @clamped_sides: Where to put contrainment information.
* @constraint: Constraint to use.
* @symmetrically: Whether or not to clamp symmetrically.
*
* Clamps height of rectangle. Set symmetrically to true when using for
* fixed_center:ed rectangles, since that will clamp symmetrically which is just
* what is needed.
*
* When this function constrains, it puts what it constrains in
* @constraint. This information is essential when an aspect ratio is to be
* applied.
*/
static void
gimp_rectangle_tool_clamp_height (GimpRectangleTool *rect_tool,
ClampedSide *clamped_sides,
GimpRectangleConstraint constraint,
gboolean symmetrically)
{
GimpRectangleToolPrivate *private;
gint min_y;
gint max_y;
if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE)
return;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
gimp_rectangle_tool_get_constraints (rect_tool,
NULL,
&min_y,
NULL,
&max_y,
constraint);
if (private->y1 < min_y)
{
gdouble dy = min_y - private->y1;
private->y1 += dy;
if (symmetrically)
private->y2 -= dy;
if (private->y2 < min_y)
private->y2 = min_y;
if (clamped_sides)
*clamped_sides |= CLAMPED_TOP;
}
if (private->y2 > max_y)
{
gdouble dy = max_y - private->y2;
private->y2 += dy;
if (symmetrically)
private->y1 -= dy;
if (private->y1 > max_y)
private->y1 = max_y;
if (clamped_sides)
*clamped_sides |= CLAMPED_BOTTOM;
}
}
/**
* gimp_rectangle_tool_keep_inside:
* @rect_tool: A #GimpRectangleTool.
*
* If the rectangle is outside of the canvas, move it into it. If the rectangle is
* larger than the canvas in any direction, make it fill the canvas in that direction.
*/
static void
gimp_rectangle_tool_keep_inside (GimpRectangleTool *rect_tool,
GimpRectangleConstraint constraint)
{
gimp_rectangle_tool_keep_inside_horizontally (rect_tool,
constraint);
gimp_rectangle_tool_keep_inside_vertically (rect_tool,
constraint);
}
/**
* gimp_rectangle_tool_keep_inside_horizontally:
* @rect_tool: A #GimpRectangleTool.
* @constraint: Constraint to use.
*
* If the rectangle is outside of the given constraint horizontally, move it
* inside. If it is too big to fit inside, make it just as big as the width
* limit.
*/
static void
gimp_rectangle_tool_keep_inside_horizontally (GimpRectangleTool *rect_tool,
GimpRectangleConstraint constraint)
{
GimpRectangleToolPrivate *private;
gint min_x;
gint max_x;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE)
return;
gimp_rectangle_tool_get_constraints (rect_tool,
&min_x,
NULL,
&max_x,
NULL,
constraint);
if (max_x - min_x < private->x2 - private->x1)
{
private->x1 = min_x;
private->x2 = max_x;
}
else
{
if (private->x1 < min_x)
{
gdouble dx = min_x - private->x1;
private->x1 += dx;
private->x2 += dx;
}
if (private->x2 > max_x)
{
gdouble dx = max_x - private->x2;
private->x1 += dx;
private->x2 += dx;
}
}
}
/**
* gimp_rectangle_tool_keep_inside_vertically:
* @rect_tool: A #GimpRectangleTool.
* @constraint: Constraint to use.
*
* If the rectangle is outside of the given constraint vertically,
* move it inside. If it is too big to fit inside, make it just as big
* as the width limit.
*/
static void
gimp_rectangle_tool_keep_inside_vertically (GimpRectangleTool *rect_tool,
GimpRectangleConstraint constraint)
{
GimpRectangleToolPrivate *private;
gint min_y;
gint max_y;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE)
return;
gimp_rectangle_tool_get_constraints (rect_tool,
NULL,
&min_y,
NULL,
&max_y,
constraint);
if (max_y - min_y < private->y2 - private->y1)
{
private->y1 = min_y;
private->y2 = max_y;
}
else
{
if (private->y1 < min_y)
{
gdouble dy = min_y - private->y1;
private->y1 += dy;
private->y2 += dy;
}
if (private->y2 > max_y)
{
gdouble dy = max_y - private->y2;
private->y1 += dy;
private->y2 += dy;
}
}
}
/**
* gimp_rectangle_tool_apply_fixed_width:
* @rect_tool: A #GimpRectangleTool.
* @constraint: Constraint to use.
* @width:
*
* Makes the rectangle have a fixed_width, following the constrainment
* rules of fixed widths as well. Please refer to the rectangle tools
* spec.
*/
static void
gimp_rectangle_tool_apply_fixed_width (GimpRectangleTool *rect_tool,
GimpRectangleConstraint constraint,
gdouble width)
{
GimpRectangleToolPrivate *private;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
switch (private->function)
{
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_LEFT:
/* We always want to center around fixed_center here, since we want the
* anchor point to be directly on the opposite side.
*/
private->x1 = private->center_x_on_fixed_center -
width / 2;
private->x2 = private->x1 + width;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_RIGHT:
/* We always want to center around fixed_center here, since we want the
* anchor point to be directly on the opposite side.
*/
private->x1 = private->center_x_on_fixed_center -
width / 2;
private->x2 = private->x1 + width;
break;
}
/* Width shall be kept even after constraints, so we move the
* rectangle sideways rather than adjusting a side.
*/
gimp_rectangle_tool_keep_inside_horizontally (rect_tool,
constraint);
}
/**
* gimp_rectangle_tool_apply_fixed_height:
* @rect_tool: A #GimpRectangleTool.
* @constraint: Constraint to use.
* @height:
*
* Makes the rectangle have a fixed_height, following the
* constrainment rules of fixed heights as well. Please refer to the
* rectangle tools spec.
*/
static void
gimp_rectangle_tool_apply_fixed_height (GimpRectangleTool *rect_tool,
GimpRectangleConstraint constraint,
gdouble height)
{
GimpRectangleToolPrivate *private;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
switch (private->function)
{
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_TOP:
/* We always want to center around fixed_center here, since we
* want the anchor point to be directly on the opposite side.
*/
private->y1 = private->center_y_on_fixed_center -
height / 2;
private->y2 = private->y1 + height;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM:
/* We always want to center around fixed_center here, since we
* want the anchor point to be directly on the opposite side.
*/
private->y1 = private->center_y_on_fixed_center -
height / 2;
private->y2 = private->y1 + height;
break;
}
/* Width shall be kept even after constraints, so we move the
* rectangle sideways rather than adjusting a side.
*/
gimp_rectangle_tool_keep_inside_vertically (rect_tool,
constraint);
}
/**
* gimp_rectangle_tool_apply_aspect:
* @rect_tool: A #GimpRectangleTool.
* @aspect: The desired aspect.
* @clamped_sides: Bitfield of sides that have been clamped.
*
* Adjust the rectangle to the desired aspect.
*
* Sometimes, a side must not be moved outwards, for example if a the
* RIGHT side has been clamped previously, we must not move the RIGHT
* side to the right, since that would violate the constraint
* again. The clamped_sides bitfield keeps track of sides that have
* previously been clamped.
*
* If fixed_center is used, the function adjusts the aspect by
* symmetrically adjusting the left and right, or top and bottom side.
*/
static void
gimp_rectangle_tool_apply_aspect (GimpRectangleTool *rect_tool,
gdouble aspect,
gint clamped_sides)
{
GimpRectangleToolPrivate *private;
GimpRectangleOptions *options;
GimpRectangleOptionsPrivate *options_private;
gdouble current_w;
gdouble current_h;
gdouble current_aspect;
SideToResize side_to_resize = SIDE_TO_RESIZE_NONE;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (rect_tool);
options_private = GIMP_RECTANGLE_OPTIONS_GET_PRIVATE (options);
current_w = private->x2 - private->x1;
current_h = private->y2 - private->y1;
current_aspect = current_w / (gdouble) current_h;
/* Do we have to do anything? */
if (current_aspect == aspect)
return;
if (options_private->fixed_center)
{
/* We may only adjust the sides symmetrically to get desired aspect. */
if (current_aspect > aspect)
{
/* We prefer to use top and bottom (since that will make the
* cursor remain on the rectangle edge), unless that is what
* the user has grabbed.
*/
switch (private->function)
{
case GIMP_RECTANGLE_TOOL_RESIZING_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
if (!(clamped_sides & CLAMPED_TOP) &&
!(clamped_sides & CLAMPED_BOTTOM))
{
side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY;
}
else
{
side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY;
}
break;
case GIMP_RECTANGLE_TOOL_RESIZING_TOP:
case GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM:
default:
side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY;
break;
}
}
else /* (current_aspect < aspect) */
{
/* We prefer to use left and right (since that will make the
* cursor remain on the rectangle edge), unless that is what
* the user has grabbed.
*/
switch (private->function)
{
case GIMP_RECTANGLE_TOOL_RESIZING_TOP:
case GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM:
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
if (!(clamped_sides & CLAMPED_LEFT) &&
!(clamped_sides & CLAMPED_RIGHT))
{
side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY;
}
else
{
side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY;
}
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_RIGHT:
default:
side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY;
break;
}
}
}
else if (current_aspect > aspect)
{
/* We can safely pick LEFT or RIGHT, since using those sides
* will make the rectangle smaller, so we don't need to check
* for clamped_sides. We may only use TOP and BOTTOM if not
* those sides have been clamped, since using them will make the
* rectangle bigger.
*/
switch (private->function)
{
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
if (!(clamped_sides & CLAMPED_TOP))
side_to_resize = SIDE_TO_RESIZE_TOP;
else
side_to_resize = SIDE_TO_RESIZE_LEFT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
if (!(clamped_sides & CLAMPED_TOP))
side_to_resize = SIDE_TO_RESIZE_TOP;
else
side_to_resize = SIDE_TO_RESIZE_RIGHT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
if (!(clamped_sides & CLAMPED_BOTTOM))
side_to_resize = SIDE_TO_RESIZE_BOTTOM;
else
side_to_resize = SIDE_TO_RESIZE_LEFT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
if (!(clamped_sides & CLAMPED_BOTTOM))
side_to_resize = SIDE_TO_RESIZE_BOTTOM;
else
side_to_resize = SIDE_TO_RESIZE_RIGHT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LEFT:
if (!(clamped_sides & CLAMPED_TOP) &&
!(clamped_sides & CLAMPED_BOTTOM))
side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY;
else
side_to_resize = SIDE_TO_RESIZE_LEFT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_RIGHT:
if (!(clamped_sides & CLAMPED_TOP) &&
!(clamped_sides & CLAMPED_BOTTOM))
side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY;
else
side_to_resize = SIDE_TO_RESIZE_RIGHT;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM:
case GIMP_RECTANGLE_TOOL_RESIZING_TOP:
side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY;
break;
case GIMP_RECTANGLE_TOOL_MOVING:
default:
if (!(clamped_sides & CLAMPED_BOTTOM))
side_to_resize = SIDE_TO_RESIZE_BOTTOM;
else if (!(clamped_sides & CLAMPED_RIGHT))
side_to_resize = SIDE_TO_RESIZE_RIGHT;
else if (!(clamped_sides & CLAMPED_TOP))
side_to_resize = SIDE_TO_RESIZE_TOP;
else if (!(clamped_sides & CLAMPED_LEFT))
side_to_resize = SIDE_TO_RESIZE_LEFT;
break;
}
}
else /* (current_aspect < aspect) */
{
/* We can safely pick TOP or BOTTOM, since using those sides
* will make the rectangle smaller, so we don't need to check
* for clamped_sides. We may only use LEFT and RIGHT if not
* those sides have been clamped, since using them will make the
* rectangle bigger.
*/
switch (private->function)
{
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_LEFT:
if (!(clamped_sides & CLAMPED_LEFT))
side_to_resize = SIDE_TO_RESIZE_LEFT;
else
side_to_resize = SIDE_TO_RESIZE_TOP;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_UPPER_RIGHT:
if (!(clamped_sides & CLAMPED_RIGHT))
side_to_resize = SIDE_TO_RESIZE_RIGHT;
else
side_to_resize = SIDE_TO_RESIZE_TOP;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_LEFT:
if (!(clamped_sides & CLAMPED_LEFT))
side_to_resize = SIDE_TO_RESIZE_LEFT;
else
side_to_resize = SIDE_TO_RESIZE_BOTTOM;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LOWER_RIGHT:
if (!(clamped_sides & CLAMPED_RIGHT))
side_to_resize = SIDE_TO_RESIZE_RIGHT;
else
side_to_resize = SIDE_TO_RESIZE_BOTTOM;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_TOP:
if (!(clamped_sides & CLAMPED_LEFT) &&
!(clamped_sides & CLAMPED_RIGHT))
side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY;
else
side_to_resize = SIDE_TO_RESIZE_TOP;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_BOTTOM:
if (!(clamped_sides & CLAMPED_LEFT) &&
!(clamped_sides & CLAMPED_RIGHT))
side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY;
else
side_to_resize = SIDE_TO_RESIZE_BOTTOM;
break;
case GIMP_RECTANGLE_TOOL_RESIZING_LEFT:
case GIMP_RECTANGLE_TOOL_RESIZING_RIGHT:
side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY;
break;
case GIMP_RECTANGLE_TOOL_MOVING:
default:
if (!(clamped_sides & CLAMPED_BOTTOM))
side_to_resize = SIDE_TO_RESIZE_BOTTOM;
else if (!(clamped_sides & CLAMPED_RIGHT))
side_to_resize = SIDE_TO_RESIZE_RIGHT;
else if (!(clamped_sides & CLAMPED_TOP))
side_to_resize = SIDE_TO_RESIZE_TOP;
else if (!(clamped_sides & CLAMPED_LEFT))
side_to_resize = SIDE_TO_RESIZE_LEFT;
break;
}
}
/* We now know what side(s) we should resize, so now we just solve
* the aspect equation for that side(s).
*/
switch (side_to_resize)
{
case SIDE_TO_RESIZE_NONE:
return;
case SIDE_TO_RESIZE_LEFT:
private->x1 = private->x2 - aspect * current_h;
break;
case SIDE_TO_RESIZE_RIGHT:
private->x2 = private->x1 + aspect * current_h;
break;
case SIDE_TO_RESIZE_TOP:
private->y1 = private->y2 - current_w / aspect;
break;
case SIDE_TO_RESIZE_BOTTOM:
private->y2 = private->y1 + current_w / aspect;
break;
case SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY:
{
gdouble correct_h = current_w / aspect;
private->y1 = private->center_y_on_fixed_center - correct_h / 2;
private->y2 = private->y1 + correct_h;
}
break;
case SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY:
{
gdouble correct_w = current_h * aspect;
private->x1 = private->center_x_on_fixed_center - correct_w / 2;
private->x2 = private->x1 + correct_w;
}
break;
}
}
/**
* gimp_rectangle_tool_update_with_coord:
* @rect_tool: A #GimpRectangleTool.
* @new_x: New X-coordinate in the context of the current function.
* @new_y: New Y-coordinate in the context of the current function.
*
* The core rectangle adjustment function. It updates the rectangle
* for the passed cursor coordinate, taking current function and tool
* options into account. It also updates the current
* private->function if necessary.
*/
static void
gimp_rectangle_tool_update_with_coord (GimpRectangleTool *rect_tool,
gdouble new_x,
gdouble new_y)
{
GimpRectangleToolPrivate *private;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
/* Move the corner or edge the user currently has grabbed. */
gimp_rectangle_tool_apply_coord (rect_tool,
new_x,
new_y);
/* Update private->function. The function changes if the user
* "flips" the rectangle.
*/
gimp_rectangle_tool_check_function (rect_tool);
/* Clamp the rectangle if necessary */
gimp_rectangle_tool_handle_general_clamping (rect_tool);
/* If the rectangle is being moved, do not run through any further
* rectangle adjusting functions since it's shape should not change
* then.
*/
if (private->function != GIMP_RECTANGLE_TOOL_MOVING)
{
gimp_rectangle_tool_apply_fixed_rule (rect_tool);
}
gimp_rectangle_tool_update_int_rect (rect_tool);
}
static void
gimp_rectangle_tool_apply_fixed_rule (GimpRectangleTool *rect_tool)
{
GimpTool *tool;
GimpRectangleToolPrivate *private;
GimpRectangleOptions *options;
GimpRectangleOptionsPrivate *options_private;
GimpRectangleConstraint constraint_to_use;
GimpImage *image;
tool = GIMP_TOOL (rect_tool);
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (tool);
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (tool);
options_private = GIMP_RECTANGLE_OPTIONS_GET_PRIVATE (options);
image = gimp_display_get_image (tool->display);
/* Calculate what constraint to use when needed. */
constraint_to_use = gimp_rectangle_tool_get_constraint (rect_tool);
2007-08-05 04:20:44 +08:00
if (gimp_rectangle_options_fixed_rule_active (options,
GIMP_RECTANGLE_TOOL_FIXED_ASPECT))
{
gdouble aspect;
aspect = CLAMP (options_private->aspect_numerator /
options_private->aspect_denominator,
1.0 / gimp_image_get_height (image),
gimp_image_get_width (image));
if (constraint_to_use == GIMP_RECTANGLE_CONSTRAIN_NONE)
{
gimp_rectangle_tool_apply_aspect (rect_tool,
aspect,
CLAMPED_NONE);
}
else
{
if (private->function != GIMP_RECTANGLE_TOOL_MOVING)
{
ClampedSide clamped_sides = CLAMPED_NONE;
gimp_rectangle_tool_apply_aspect (rect_tool,
aspect,
clamped_sides);
/* After we have applied aspect, we might have taken the
* rectangle outside of constraint, so clamp and apply
* aspect again. We will get the right result this time,
* since 'clamped_sides' will be setup correctly now.
*/
gimp_rectangle_tool_clamp (rect_tool,
&clamped_sides,
constraint_to_use,
options_private->fixed_center);
gimp_rectangle_tool_apply_aspect (rect_tool,
aspect,
clamped_sides);
}
else
{
gimp_rectangle_tool_apply_aspect (rect_tool,
aspect,
CLAMPED_NONE);
gimp_rectangle_tool_keep_inside (rect_tool,
constraint_to_use);
}
}
}
else if (gimp_rectangle_options_fixed_rule_active (options,
GIMP_RECTANGLE_TOOL_FIXED_SIZE))
{
gimp_rectangle_tool_apply_fixed_width (rect_tool,
constraint_to_use,
options_private->desired_fixed_size_width);
gimp_rectangle_tool_apply_fixed_height (rect_tool,
constraint_to_use,
options_private->desired_fixed_size_height);
}
else if (gimp_rectangle_options_fixed_rule_active (options,
GIMP_RECTANGLE_TOOL_FIXED_WIDTH))
{
gimp_rectangle_tool_apply_fixed_width (rect_tool,
constraint_to_use,
options_private->desired_fixed_width);
}
else if (gimp_rectangle_options_fixed_rule_active (options,
GIMP_RECTANGLE_TOOL_FIXED_HEIGHT))
{
gimp_rectangle_tool_apply_fixed_height (rect_tool,
constraint_to_use,
options_private->desired_fixed_height);
}
}
/**
* gimp_rectangle_tool_get_constraints:
* @rect_tool: A #GimpRectangleTool.
* @min_x:
* @min_y:
* @max_x:
* @max_y: Pointers of where to put constraints. NULL allowed.
* @constraint: Wether to return image or layer constraints.
*
* Calculates constraint coordinates for image or layer.
*/
static void
gimp_rectangle_tool_get_constraints (GimpRectangleTool *rect_tool,
gint *min_x,
gint *min_y,
gint *max_x,
gint *max_y,
GimpRectangleConstraint constraint)
{
GimpTool *tool = GIMP_TOOL (rect_tool);
GimpImage *image;
gint min_x_dummy;
gint min_y_dummy;
gint max_x_dummy;
gint max_y_dummy;
if (! min_x) min_x = &min_x_dummy;
if (! min_y) min_y = &min_y_dummy;
if (! max_x) max_x = &max_x_dummy;
if (! max_y) max_y = &max_y_dummy;
*min_x = 0;
*min_y = 0;
*max_x = 0;
*max_y = 0;
if (! tool->display)
return;
image = gimp_display_get_image (tool->display);
switch (constraint)
{
case GIMP_RECTANGLE_CONSTRAIN_IMAGE:
*min_x = 0;
*min_y = 0;
*max_x = gimp_image_get_width (image);
*max_y = gimp_image_get_height (image);
break;
case GIMP_RECTANGLE_CONSTRAIN_DRAWABLE:
{
GimpItem *item = GIMP_ITEM (tool->drawable);
gimp_item_get_offset (item, min_x, min_y);
*max_x = *min_x + gimp_item_get_width (item);
*max_y = *min_y + gimp_item_get_height (item);
}
break;
default:
g_warning ("Invalid rectangle constraint.\n");
return;
}
}
/**
* gimp_rectangle_tool_handle_general_clamping:
* @rect_tool: A #GimpRectangleTool.
*
* Make sure that contraints are applied to the rectangle, either by
* manually doing it, or by looking at the rectangle tool options and
* concluding it will be done later.
*/
static void
gimp_rectangle_tool_handle_general_clamping (GimpRectangleTool *rect_tool)
{
GimpRectangleToolPrivate *private;
GimpRectangleOptions *options;
GimpRectangleOptionsPrivate *options_private;
GimpRectangleConstraint constraint;
private = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
options = GIMP_RECTANGLE_TOOL_GET_OPTIONS (rect_tool);
options_private = GIMP_RECTANGLE_OPTIONS_GET_PRIVATE (options);
constraint = gimp_rectangle_tool_get_constraint (rect_tool);
/* fixed_aspect takes care of clamping by it self, so just return in
* case that is in use. Also return if no constraints should be
* enforced.
*/
if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE)
return;
if (private->function != GIMP_RECTANGLE_TOOL_MOVING)
{
gimp_rectangle_tool_clamp (rect_tool,
NULL,
constraint,
options_private->fixed_center);
}
else
{
gimp_rectangle_tool_keep_inside (rect_tool,
constraint);
}
}
/**
* gimp_rectangle_tool_update_int_rect:
* @rect_tool:
*
* Update integer representation of rectangle.
**/
static void
gimp_rectangle_tool_update_int_rect (GimpRectangleTool *rect_tool)
{
GimpRectangleToolPrivate *priv = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
priv->x1_int = ROUND (priv->x1);
priv->y1_int = ROUND (priv->y1);
if (gimp_rectangle_tool_rect_rubber_banding_func (rect_tool))
{
priv->width_int = (gint) ROUND (priv->x2) - priv->x1_int;
priv->height_int = (gint) ROUND (priv->y2) - priv->y1_int;
}
}
/**
* gimp_rectangle_tool_get_public_rect:
* @rect_tool:
* @pub_x1:
* @pub_y1:
* @pub_x2:
* @pub_y2:
*
* This function returns the rectangle as it appears to be publicly
* (based on integer or double precision-mode).
**/
static void
gimp_rectangle_tool_get_public_rect (GimpRectangleTool *rect_tool,
gdouble *pub_x1,
gdouble *pub_y1,
gdouble *pub_x2,
gdouble *pub_y2)
{
GimpRectangleToolPrivate *priv;
priv = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
switch (priv->precision)
{
case GIMP_RECTANGLE_PRECISION_INT:
*pub_x1 = priv->x1_int;
*pub_y1 = priv->y1_int;
*pub_x2 = priv->x1_int + priv->width_int;
*pub_y2 = priv->y1_int + priv->height_int;
break;
case GIMP_RECTANGLE_PRECISION_DOUBLE:
default:
*pub_x1 = priv->x1;
*pub_y1 = priv->y1;
*pub_x2 = priv->x2;
*pub_y2 = priv->y2;
break;
}
}
/**
* gimp_rectangle_tool_adjust_coord:
* @rect_tool:
* @ccoord_x_input:
* @ccoord_x_input:
* @ccoord_x_output:
* @ccoord_x_output:
*
* Transforms a coordinate to better fit the public behaviour of the
* rectangle.
*/
static void
gimp_rectangle_tool_adjust_coord (GimpRectangleTool *rect_tool,
gdouble coord_x_input,
gdouble coord_y_input,
gdouble *coord_x_output,
gdouble *coord_y_output)
{
GimpRectangleToolPrivate *priv;
priv = GIMP_RECTANGLE_TOOL_GET_PRIVATE (rect_tool);
switch (priv->precision)
{
case GIMP_RECTANGLE_PRECISION_INT:
*coord_x_output = RINT (coord_x_input);
*coord_y_output = RINT (coord_y_input);
break;
case GIMP_RECTANGLE_PRECISION_DOUBLE:
default:
*coord_x_output = coord_x_input;
*coord_y_output = coord_y_input;
break;
}
}