[gimp] app: add new GimpToolWidget subclass GimpToolRectangle



commit be63a4a8369958a539630a62181e335ab123ed87
Author: Michael Natterer <mitch gimp org>
Date:   Mon Jun 26 18:34:18 2017 +0200

    app: add new GimpToolWidget subclass GimpToolRectangle
    
    which is a replacement for GimpRectangleTool. It's a massive piece of
    code and I'm not sure everyting works as it should, but it seems to do
    crop stuff without any glitches.

 app/display/Makefile.am         |    2 +
 app/display/gimptoolrectangle.c | 3950 +++++++++++++++++++++++++++++++++++++++
 app/display/gimptoolrectangle.h |  122 ++
 po/POTFILES.in                  |    1 +
 4 files changed, 4075 insertions(+), 0 deletions(-)
---
diff --git a/app/display/Makefile.am b/app/display/Makefile.am
index e4f9891..410e8b8 100644
--- a/app/display/Makefile.am
+++ b/app/display/Makefile.am
@@ -174,6 +174,8 @@ libappdisplay_a_sources = \
        gimptoolpath.h                          \
        gimptoolpolygon.c                       \
        gimptoolpolygon.h                       \
+       gimptoolrectangle.c                     \
+       gimptoolrectangle.h                     \
        gimptoolrotategrid.c                    \
        gimptoolrotategrid.h                    \
        gimptoolsheargrid.c                     \
diff --git a/app/display/gimptoolrectangle.c b/app/display/gimptoolrectangle.c
new file mode 100644
index 0000000..cecf8b4
--- /dev/null
+++ b/app/display/gimptoolrectangle.c
@@ -0,0 +1,3950 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimptoolrectangle.c
+ * Copyright (C) 2017 Michael Natterer <mitch gimp org>
+ *
+ * Based on GimpRectangleTool
+ * 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 <gegl.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "display-types.h"
+
+#include "core/gimp.h"
+#include "core/gimpcontext.h"
+#include "core/gimpimage.h"
+#include "core/gimpitem.h"
+#include "core/gimpmarshal.h"
+#include "core/gimppickable.h"
+#include "core/gimppickable-auto-shrink.h"
+
+#include "widgets/gimpwidgets-utils.h"
+
+#include "gimpcanvascorner.h"
+#include "gimpcanvashandle.h"
+#include "gimpcanvasitem-utils.h"
+#include "gimpcanvasrectangle.h"
+#include "gimpcanvasrectangleguides.h"
+#include "gimpdisplay.h"
+#include "gimpdisplayshell.h"
+#include "gimpdisplayshell-scroll.h"
+#include "gimptoolrectangle.h"
+
+#include "gimp-intl.h"
+
+
+/*  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
+
+
+enum
+{
+  PROP_0,
+  PROP_X1,
+  PROP_Y1,
+  PROP_X2,
+  PROP_Y2,
+  PROP_CONSTRAINT,
+  PROP_PRECISION,
+  PROP_NARROW_MODE,
+
+  PROP_HIGHLIGHT,
+  PROP_GUIDE,
+  PROP_X,
+  PROP_Y,
+  PROP_WIDTH,
+  PROP_HEIGHT,
+  PROP_FIXED_RULE_ACTIVE,
+  PROP_FIXED_RULE,
+  PROP_DESIRED_FIXED_WIDTH,
+  PROP_DESIRED_FIXED_HEIGHT,
+  PROP_DESIRED_FIXED_SIZE_WIDTH,
+  PROP_DESIRED_FIXED_SIZE_HEIGHT,
+  PROP_ASPECT_NUMERATOR,
+  PROP_ASPECT_DENOMINATOR,
+  PROP_FIXED_CENTER
+};
+
+enum
+{
+  CHANGE_COMPLETE,
+  LAST_SIGNAL
+};
+
+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)
+
+
+struct _GimpToolRectanglePrivate
+{
+  /* The following members are "constants", that is, variables that are setup
+   * during gimp_tool_rectangle_button_press and then only read.
+   */
+
+  /* Whether 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 consistent
+   * results and not depend on position of the rectangle.
+   */
+  gint                    x1_int,    y1_int;
+  gint                    width_int, height_int;
+
+  /* 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;
+
+  /* Whether 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;
+
+  GimpRectangleFunction   function;
+
+  /* The following values are externally synced with GimpRectangleOptions */
+
+  gboolean                highlight;
+  GimpGuidesType          guide;
+
+  gdouble                 x;
+  gdouble                 y;
+  gdouble                 width;
+  gdouble                 height;
+
+  gboolean                fixed_rule_active;
+  GimpRectangleFixedRule  fixed_rule;
+  gdouble                 desired_fixed_width;
+  gdouble                 desired_fixed_height;
+  gdouble                 desired_fixed_size_width;
+  gdouble                 desired_fixed_size_height;
+  gdouble                 aspect_numerator;
+  gdouble                 aspect_denominator;
+  gboolean                fixed_center;
+
+  /* Canvas items for drawing the GUI */
+
+  GimpCanvasItem         *guides;
+  GimpCanvasItem         *rectangle;
+  GimpCanvasItem         *center;
+  GimpCanvasItem         *creating_corners[4];
+  GimpCanvasItem         *handles[GIMP_N_TOOL_RECTANGLE_FUNCTIONS];
+  GimpCanvasItem         *highlight_handles[GIMP_N_TOOL_RECTANGLE_FUNCTIONS];
+};
+
+
+/*  local function prototypes  */
+
+static void     gimp_tool_rectangle_constructed     (GObject               *object);
+static void     gimp_tool_rectangle_set_property    (GObject               *object,
+                                                     guint                  property_id,
+                                                     const GValue          *value,
+                                                     GParamSpec            *pspec);
+static void     gimp_tool_rectangle_get_property    (GObject               *object,
+                                                     guint                  property_id,
+                                                     GValue                *value,
+                                                     GParamSpec            *pspec);
+static void     gimp_tool_rectangle_notify          (GObject               *object,
+                                                     GParamSpec            *pspec);
+
+static void     gimp_tool_rectangle_changed         (GimpToolWidget        *widget);
+static gint     gimp_tool_rectangle_button_press    (GimpToolWidget        *widget,
+                                                     const GimpCoords      *coords,
+                                                     guint32                time,
+                                                     GdkModifierType        state,
+                                                     GimpButtonPressType    press_type);
+static void     gimp_tool_rectangle_button_release  (GimpToolWidget        *widget,
+                                                     const GimpCoords      *coords,
+                                                     guint32                time,
+                                                     GdkModifierType        state,
+                                                     GimpButtonReleaseType  release_type);
+static void     gimp_tool_rectangle_motion          (GimpToolWidget        *widget,
+                                                     const GimpCoords      *coords,
+                                                     guint32                time,
+                                                     GdkModifierType        state);
+static void     gimp_tool_rectangle_hover           (GimpToolWidget        *widget,
+                                                     const GimpCoords      *coords,
+                                                     GdkModifierType        state,
+                                                     gboolean               proximity);
+static gboolean gimp_tool_rectangle_key_press       (GimpToolWidget        *widget,
+                                                     GdkEventKey           *kevent);
+static void     gimp_tool_rectangle_motion_modifier (GimpToolWidget        *widget,
+                                                     GdkModifierType        key,
+                                                     gboolean               press,
+                                                     GdkModifierType        state);
+static gboolean gimp_tool_rectangle_get_cursor      (GimpToolWidget        *widget,
+                                                     const GimpCoords      *coords,
+                                                     GdkModifierType        state,
+                                                     GimpCursorType        *cursor,
+                                                     GimpToolCursorType    *tool_cursor,
+                                                     GimpCursorModifier    *cursor_modifier);
+
+static void     gimp_tool_rectangle_change_complete (GimpToolRectangle     *rectangle);
+
+static void     gimp_tool_rectangle_update_options  (GimpToolRectangle     *rectangle);
+static void     gimp_tool_rectangle_update_handle_sizes
+                                                    (GimpToolRectangle     *rectangle);
+
+static void     gimp_tool_rectangle_synthesize_motion
+                                                    (GimpToolRectangle     *rectangle,
+                                                     gint               function,
+                                                     gdouble            new_x,
+                                                     gdouble            new_y);
+
+static void     gimp_tool_rectangle_check_function  (GimpToolRectangle     *rectangle);
+
+static gboolean gimp_tool_rectangle_coord_outside   (GimpToolRectangle     *rectangle,
+                                                     const GimpCoords      *coords);
+
+static gboolean gimp_tool_rectangle_coord_on_handle (GimpToolRectangle     *rectangle,
+                                                     const GimpCoords      *coords,
+                                                     GimpHandleAnchor       anchor);
+
+static GimpHandleAnchor gimp_tool_rectangle_get_anchor
+                                                    (GimpRectangleFunction  function);
+static gboolean gimp_tool_rectangle_rect_rubber_banding_func
+                                                    (GimpToolRectangle     *rectangle);
+static gboolean gimp_tool_rectangle_rect_adjusting_func
+                                                    (GimpToolRectangle     *rectangle);
+
+static void     gimp_tool_rectangle_get_other_side  (GimpToolRectangle     *rectangle,
+                                                     gdouble              **other_x,
+                                                     gdouble              **other_y);
+static void     gimp_tool_rectangle_get_other_side_coord
+                                                    (GimpToolRectangle     *rectangle,
+                                                     gdouble               *other_side_x,
+                                                     gdouble               *other_side_y);
+static void     gimp_tool_rectangle_set_other_side_coord
+                                                    (GimpToolRectangle     *rectangle,
+                                                     gdouble                other_side_x,
+                                                     gdouble                other_side_y);
+
+static void     gimp_tool_rectangle_apply_coord     (GimpToolRectangle     *rectangle,
+                                                     gdouble                coord_x,
+                                                     gdouble                coord_y);
+static void     gimp_tool_rectangle_setup_snap_offsets
+                                                    (GimpToolRectangle     *rectangle,
+                                                     const GimpCoords      *coords);
+
+static void     gimp_tool_rectangle_clamp           (GimpToolRectangle     *rectangle,
+                                                     ClampedSide           *clamped_sides,
+                                                     GimpRectangleConstraint constraint,
+                                                     gboolean               symmetrically);
+static void     gimp_tool_rectangle_clamp_width     (GimpToolRectangle     *rectangle,
+                                                     ClampedSide           *clamped_sides,
+                                                     GimpRectangleConstraint constraint,
+                                                     gboolean               symmetrically);
+static void     gimp_tool_rectangle_clamp_height    (GimpToolRectangle     *rectangle,
+                                                     ClampedSide           *clamped_sides,
+                                                     GimpRectangleConstraint constraint,
+                                                     gboolean               symmetrically);
+
+static void     gimp_tool_rectangle_keep_inside     (GimpToolRectangle     *rectangle,
+                                                     GimpRectangleConstraint constraint);
+static void     gimp_tool_rectangle_keep_inside_horizontally
+                                                    (GimpToolRectangle     *rectangle,
+                                                     GimpRectangleConstraint constraint);
+static void     gimp_tool_rectangle_keep_inside_vertically
+                                                    (GimpToolRectangle     *rectangle,
+                                                     GimpRectangleConstraint constraint);
+
+static void     gimp_tool_rectangle_apply_fixed_width
+                                                    (GimpToolRectangle     *rectangle,
+                                                     GimpRectangleConstraint constraint,
+                                                     gdouble                width);
+static void     gimp_tool_rectangle_apply_fixed_height
+                                                    (GimpToolRectangle     *rectangle,
+                                                     GimpRectangleConstraint constraint,
+                                                     gdouble                height);
+
+static void     gimp_tool_rectangle_apply_aspect    (GimpToolRectangle     *rectangle,
+                                                     gdouble                aspect,
+                                                     gint                   clamped_sides);
+
+static void     gimp_tool_rectangle_update_with_coord
+                                                    (GimpToolRectangle     *rectangle,
+                                                     gdouble                new_x,
+                                                     gdouble                new_y);
+static void     gimp_tool_rectangle_apply_fixed_rule(GimpToolRectangle     *rectangle);
+
+static void     gimp_tool_rectangle_get_constraints (GimpToolRectangle     *rectangle,
+                                                     gint                  *min_x,
+                                                     gint                  *min_y,
+                                                     gint                  *max_x,
+                                                     gint                  *max_y,
+                                                     GimpRectangleConstraint constraint);
+
+static void     gimp_tool_rectangle_handle_general_clamping
+                                                    (GimpToolRectangle     *rectangle);
+static void     gimp_tool_rectangle_update_int_rect (GimpToolRectangle     *rectangle);
+static void     gimp_tool_rectangle_adjust_coord    (GimpToolRectangle     *rectangle,
+                                                     gdouble                coord_x_input,
+                                                     gdouble                coord_y_input,
+                                                     gdouble               *coord_x_output,
+                                                     gdouble               *coord_y_output);
+
+
+G_DEFINE_TYPE (GimpToolRectangle, gimp_tool_rectangle,
+               GIMP_TYPE_TOOL_WIDGET)
+
+#define parent_class gimp_tool_rectangle_parent_class
+
+static guint rectangle_signals[LAST_SIGNAL] = { 0, };
+
+
+static void
+gimp_tool_rectangle_class_init (GimpToolRectangleClass *klass)
+{
+  GObjectClass        *object_class = G_OBJECT_CLASS (klass);
+  GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass);
+
+  object_class->constructed     = gimp_tool_rectangle_constructed;
+  object_class->set_property    = gimp_tool_rectangle_set_property;
+  object_class->get_property    = gimp_tool_rectangle_get_property;
+  object_class->notify          = gimp_tool_rectangle_notify;
+
+  widget_class->changed         = gimp_tool_rectangle_changed;
+  widget_class->button_press    = gimp_tool_rectangle_button_press;
+  widget_class->button_release  = gimp_tool_rectangle_button_release;
+  widget_class->motion          = gimp_tool_rectangle_motion;
+  widget_class->hover           = gimp_tool_rectangle_hover;
+  widget_class->key_press       = gimp_tool_rectangle_key_press;
+  widget_class->motion_modifier = gimp_tool_rectangle_motion_modifier;
+  widget_class->get_cursor      = gimp_tool_rectangle_get_cursor;
+
+  rectangle_signals[CHANGE_COMPLETE] =
+    g_signal_new ("change-complete",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (GimpToolRectangleClass, change_complete),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+
+  g_object_class_install_property (object_class, PROP_X1,
+                                   g_param_spec_double ("x1",
+                                                        NULL, NULL,
+                                                        -GIMP_MAX_IMAGE_SIZE,
+                                                        GIMP_MAX_IMAGE_SIZE,
+                                                        0.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_Y1,
+                                   g_param_spec_double ("y1",
+                                                        NULL, NULL,
+                                                        -GIMP_MAX_IMAGE_SIZE,
+                                                        GIMP_MAX_IMAGE_SIZE,
+                                                        0.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_X2,
+                                   g_param_spec_double ("x2",
+                                                        NULL, NULL,
+                                                        -GIMP_MAX_IMAGE_SIZE,
+                                                        GIMP_MAX_IMAGE_SIZE,
+                                                        0.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_Y2,
+                                   g_param_spec_double ("y2",
+                                                        NULL, NULL,
+                                                        -GIMP_MAX_IMAGE_SIZE,
+                                                        GIMP_MAX_IMAGE_SIZE,
+                                                        0.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_CONSTRAINT,
+                                   g_param_spec_enum ("constraint",
+                                                      NULL, NULL,
+                                                      GIMP_TYPE_RECTANGLE_CONSTRAINT,
+                                                      GIMP_RECTANGLE_CONSTRAIN_NONE,
+                                                      GIMP_PARAM_READWRITE |
+                                                      G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_PRECISION,
+                                   g_param_spec_enum ("precision",
+                                                      NULL, NULL,
+                                                      GIMP_TYPE_RECTANGLE_PRECISION,
+                                                      GIMP_RECTANGLE_PRECISION_INT,
+                                                      GIMP_PARAM_READWRITE |
+                                                      G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_NARROW_MODE,
+                                   g_param_spec_boolean ("narrow-mode",
+                                                         NULL, NULL,
+                                                         FALSE,
+                                                         GIMP_PARAM_READWRITE |
+                                                         G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_HIGHLIGHT,
+                                   g_param_spec_boolean ("highlight",
+                                                         NULL, NULL,
+                                                         FALSE,
+                                                         GIMP_PARAM_READWRITE |
+                                                         G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_GUIDE,
+                                   g_param_spec_enum ("guide",
+                                                      NULL, NULL,
+                                                      GIMP_TYPE_GUIDES_TYPE,
+                                                      GIMP_GUIDES_NONE,
+                                                      GIMP_PARAM_READWRITE |
+                                                      G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_X,
+                                   g_param_spec_double ("x",
+                                                        NULL, NULL,
+                                                        -GIMP_MAX_IMAGE_SIZE,
+                                                        GIMP_MAX_IMAGE_SIZE,
+                                                        0.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_Y,
+                                   g_param_spec_double ("y",
+                                                        NULL, NULL,
+                                                        -GIMP_MAX_IMAGE_SIZE,
+                                                        GIMP_MAX_IMAGE_SIZE,
+                                                        0.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_WIDTH,
+                                   g_param_spec_double ("width",
+                                                        NULL, NULL,
+                                                        0.0,
+                                                        GIMP_MAX_IMAGE_SIZE,
+                                                        0.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_HEIGHT,
+                                   g_param_spec_double ("height",
+                                                        NULL, NULL,
+                                                        0.0,
+                                                        GIMP_MAX_IMAGE_SIZE,
+                                                        0.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_FIXED_RULE_ACTIVE,
+                                   g_param_spec_boolean ("fixed-rule-active",
+                                                         NULL, NULL,
+                                                         FALSE,
+                                                         GIMP_PARAM_READWRITE |
+                                                         G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_FIXED_RULE,
+                                   g_param_spec_enum ("fixed-rule",
+                                                      NULL, NULL,
+                                                      GIMP_TYPE_RECTANGLE_FIXED_RULE,
+                                                      GIMP_RECTANGLE_FIXED_ASPECT,
+                                                      GIMP_PARAM_READWRITE |
+                                                      G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_DESIRED_FIXED_WIDTH,
+                                   g_param_spec_double ("desired-fixed-width",
+                                                        NULL, NULL,
+                                                        0.0, GIMP_MAX_IMAGE_SIZE,
+                                                        100.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_DESIRED_FIXED_HEIGHT,
+                                   g_param_spec_double ("desired-fixed-height",
+                                                        NULL, NULL,
+                                                        0.0, GIMP_MAX_IMAGE_SIZE,
+                                                        100.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_DESIRED_FIXED_SIZE_WIDTH,
+                                   g_param_spec_double ("desired-fixed-size-width",
+                                                        NULL, NULL,
+                                                        0.0, GIMP_MAX_IMAGE_SIZE,
+                                                        100.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_DESIRED_FIXED_SIZE_HEIGHT,
+                                   g_param_spec_double ("desired-fixed-size-height",
+                                                        NULL, NULL,
+                                                        0.0, GIMP_MAX_IMAGE_SIZE,
+                                                        100.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_ASPECT_NUMERATOR,
+                                   g_param_spec_double ("aspect-numerator",
+                                                        NULL, NULL,
+                                                        0.0, GIMP_MAX_IMAGE_SIZE,
+                                                        1.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_ASPECT_DENOMINATOR,
+                                   g_param_spec_double ("aspect-denominator",
+                                                        NULL, NULL,
+                                                        0.0, GIMP_MAX_IMAGE_SIZE,
+                                                        1.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_FIXED_CENTER,
+                                   g_param_spec_boolean ("fixed-center",
+                                                         NULL, NULL,
+                                                         FALSE,
+                                                         GIMP_PARAM_READWRITE |
+                                                         G_PARAM_CONSTRUCT));
+
+  g_type_class_add_private (klass, sizeof (GimpToolRectanglePrivate));
+}
+
+static void
+gimp_tool_rectangle_init (GimpToolRectangle *rectangle)
+{
+  rectangle->private = G_TYPE_INSTANCE_GET_PRIVATE (rectangle,
+                                                    GIMP_TYPE_TOOL_RECTANGLE,
+                                                    GimpToolRectanglePrivate);
+
+  rectangle->private->function = GIMP_TOOL_RECTANGLE_CREATING;
+}
+
+static void
+gimp_tool_rectangle_constructed (GObject *object)
+{
+  GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (object);
+  GimpToolWidget           *widget    = GIMP_TOOL_WIDGET (object);
+  GimpToolRectanglePrivate *private   = rectangle->private;
+  GimpCanvasGroup          *stroke_group;
+  gint                      i;
+
+  G_OBJECT_CLASS (parent_class)->constructed (object);
+
+  stroke_group = gimp_tool_widget_add_stroke_group (widget);
+
+  gimp_tool_widget_push_group (widget, stroke_group);
+
+  private->guides = gimp_tool_widget_add_rectangle_guides (widget,
+                                                           0, 0, 10, 10,
+                                                           GIMP_GUIDES_NONE);
+
+  private->rectangle = gimp_tool_widget_add_rectangle (widget,
+                                                       0, 0, 10, 10,
+                                                       FALSE);
+
+  gimp_tool_widget_pop_group (widget);
+
+  private->center = gimp_tool_widget_add_handle (widget,
+                                                 GIMP_HANDLE_CROSS,
+                                                 0, 0,
+                                                 GIMP_CANVAS_HANDLE_SIZE_SMALL,
+                                                 GIMP_CANVAS_HANDLE_SIZE_SMALL,
+                                                 GIMP_HANDLE_ANCHOR_CENTER);
+
+  gimp_tool_widget_push_group (widget, stroke_group);
+
+  private->creating_corners[0] =
+    gimp_tool_widget_add_corner (widget,
+                                 0, 0, 10, 10,
+                                 GIMP_HANDLE_ANCHOR_NORTH_WEST,
+                                 10, 10,
+                                 FALSE);
+
+  private->creating_corners[1] =
+    gimp_tool_widget_add_corner (widget,
+                                 0, 0, 10, 10,
+                                 GIMP_HANDLE_ANCHOR_NORTH_EAST,
+                                 10, 10,
+                                 FALSE);
+
+  private->creating_corners[2] =
+    gimp_tool_widget_add_corner (widget,
+                                 0, 0, 10, 10,
+                                 GIMP_HANDLE_ANCHOR_SOUTH_WEST,
+                                 10, 10,
+                                 FALSE);
+
+  private->creating_corners[3] =
+    gimp_tool_widget_add_corner (widget,
+                                 0, 0, 10, 10,
+                                 GIMP_HANDLE_ANCHOR_SOUTH_EAST,
+                                 10, 10,
+                                 FALSE);
+
+  gimp_tool_widget_pop_group (widget);
+
+  for (i = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT;
+       i <= GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM;
+       i++)
+    {
+      GimpHandleAnchor anchor;
+
+      anchor = gimp_tool_rectangle_get_anchor (i);
+
+      gimp_tool_widget_push_group (widget, stroke_group);
+
+      private->handles[i] = gimp_tool_widget_add_corner (widget,
+                                                         0, 0, 10, 10,
+                                                         anchor,
+                                                         10, 10,
+                                                         FALSE);
+
+      gimp_tool_widget_pop_group (widget);
+
+      private->highlight_handles[i] = gimp_tool_widget_add_corner (widget,
+                                                                   0, 0, 10, 10,
+                                                                   anchor,
+                                                                   10, 10,
+                                                                   FALSE);
+      gimp_canvas_item_set_highlight (private->highlight_handles[i], TRUE);
+    }
+
+  g_signal_connect_object (gimp_tool_widget_get_shell (widget), "scaled",
+                           G_CALLBACK (gimp_tool_rectangle_changed),
+                           widget, G_CONNECT_SWAPPED);
+
+  gimp_tool_rectangle_changed (widget);
+}
+
+static void
+gimp_tool_rectangle_set_property (GObject      *object,
+                                  guint         property_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (object);
+  GimpToolRectanglePrivate *private   = rectangle->private;
+
+  switch (property_id)
+    {
+    case PROP_X1:
+      private->x1 = g_value_get_double (value);
+      break;
+    case PROP_Y1:
+      private->y1 = g_value_get_double (value);
+      break;
+    case PROP_X2:
+      private->x2 = g_value_get_double (value);
+      break;
+    case PROP_Y2:
+      private->y2 = g_value_get_double (value);
+      break;
+
+    case PROP_CONSTRAINT:
+      private->constraint = g_value_get_enum (value);
+      break;
+    case PROP_PRECISION:
+      private->precision = g_value_get_enum (value);
+      break;
+
+    case PROP_NARROW_MODE:
+      private->narrow_mode = g_value_get_boolean (value);
+      break;
+
+    case PROP_HIGHLIGHT:
+      private->highlight = g_value_get_boolean (value);
+      break;
+    case PROP_GUIDE:
+      private->guide = g_value_get_enum (value);
+      break;
+
+    case PROP_X:
+      private->x = g_value_get_double (value);
+      break;
+    case PROP_Y:
+      private->y = g_value_get_double (value);
+      break;
+    case PROP_WIDTH:
+      private->width = g_value_get_double (value);
+      break;
+    case PROP_HEIGHT:
+      private->height = g_value_get_double (value);
+      break;
+
+    case PROP_FIXED_RULE_ACTIVE:
+      private->fixed_rule_active = g_value_get_boolean (value);
+      break;
+    case PROP_FIXED_RULE:
+      private->fixed_rule = g_value_get_enum (value);
+      break;
+    case PROP_DESIRED_FIXED_WIDTH:
+      private->desired_fixed_width = g_value_get_double (value);
+      break;
+    case PROP_DESIRED_FIXED_HEIGHT:
+      private->desired_fixed_height = g_value_get_double (value);
+      break;
+    case PROP_DESIRED_FIXED_SIZE_WIDTH:
+      private->desired_fixed_size_width = g_value_get_double (value);
+      break;
+    case PROP_DESIRED_FIXED_SIZE_HEIGHT:
+      private->desired_fixed_size_height = g_value_get_double (value);
+      break;
+    case PROP_ASPECT_NUMERATOR:
+      private->aspect_numerator = g_value_get_double (value);
+      break;
+    case PROP_ASPECT_DENOMINATOR:
+      private->aspect_denominator = g_value_get_double (value);
+      break;
+
+    case PROP_FIXED_CENTER:
+      private->fixed_center = g_value_get_boolean (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_tool_rectangle_get_property (GObject    *object,
+                                  guint       property_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (object);
+  GimpToolRectanglePrivate *private   = rectangle->private;
+
+  switch (property_id)
+    {
+    case PROP_X1:
+      g_value_set_double (value, private->x1);
+      break;
+    case PROP_Y1:
+      g_value_set_double (value, private->y1);
+      break;
+    case PROP_X2:
+      g_value_set_double (value, private->x2);
+      break;
+    case PROP_Y2:
+      g_value_set_double (value, private->y2);
+      break;
+
+    case PROP_CONSTRAINT:
+      g_value_set_enum (value, private->constraint);
+      break;
+    case PROP_PRECISION:
+      g_value_set_enum (value, private->precision);
+      break;
+
+    case PROP_NARROW_MODE:
+      g_value_set_boolean (value, private->narrow_mode);
+      break;
+
+    case PROP_HIGHLIGHT:
+      g_value_set_boolean (value, private->highlight);
+      break;
+    case PROP_GUIDE:
+      g_value_set_enum (value, private->guide);
+      break;
+
+    case PROP_X:
+      g_value_set_double (value, private->x);
+      break;
+    case PROP_Y:
+      g_value_set_double (value, private->y);
+      break;
+    case PROP_WIDTH:
+      g_value_set_double (value, private->width);
+      break;
+    case PROP_HEIGHT:
+      g_value_set_double (value, private->height);
+      break;
+
+    case PROP_FIXED_RULE_ACTIVE:
+      g_value_set_boolean (value, private->fixed_rule_active);
+      break;
+    case PROP_FIXED_RULE:
+      g_value_set_enum (value, private->fixed_rule);
+      break;
+    case PROP_DESIRED_FIXED_WIDTH:
+      g_value_set_double (value, private->desired_fixed_width);
+      break;
+    case PROP_DESIRED_FIXED_HEIGHT:
+      g_value_set_double (value, private->desired_fixed_height);
+      break;
+    case PROP_DESIRED_FIXED_SIZE_WIDTH:
+      g_value_set_double (value, private->desired_fixed_size_width);
+      break;
+    case PROP_DESIRED_FIXED_SIZE_HEIGHT:
+      g_value_set_double (value, private->desired_fixed_size_height);
+      break;
+    case PROP_ASPECT_NUMERATOR:
+      g_value_set_double (value, private->aspect_numerator);
+      break;
+    case PROP_ASPECT_DENOMINATOR:
+      g_value_set_double (value, private->aspect_denominator);
+      break;
+
+    case PROP_FIXED_CENTER:
+      g_value_set_boolean (value, private->fixed_center);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_tool_rectangle_notify (GObject    *object,
+                            GParamSpec *pspec)
+{
+  GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (object);
+  GimpToolRectanglePrivate *private   = rectangle->private;
+
+  if (G_OBJECT_CLASS (parent_class)->notify)
+    G_OBJECT_CLASS (parent_class)->notify (object, pspec);
+
+  if (! strcmp  (pspec->name, "x") &&
+      ! PIXEL_FEQUAL (private->x1, private->x))
+    {
+      gimp_tool_rectangle_synthesize_motion (rectangle,
+                                             GIMP_TOOL_RECTANGLE_MOVING,
+                                             private->x,
+                                             private->y1);
+    }
+  else if (! strcmp  (pspec->name, "y") &&
+           ! PIXEL_FEQUAL (private->y1, private->y))
+    {
+      gimp_tool_rectangle_synthesize_motion (rectangle,
+                                             GIMP_TOOL_RECTANGLE_MOVING,
+                                             private->x1,
+                                             private->y);
+    }
+  else if (! strcmp  (pspec->name, "width") &&
+           ! PIXEL_FEQUAL (private->x2 - private->x1, private->width))
+    {
+      /* Calculate x2, y2 that will create a rectangle of given width,
+       * for the current options.
+       */
+      gdouble x2;
+
+      if (private->fixed_center)
+        {
+          x2 = private->center_x_on_fixed_center +
+               private->width / 2;
+        }
+      else
+        {
+          x2 = private->x1 + private->width;
+        }
+
+      gimp_tool_rectangle_synthesize_motion (rectangle,
+                                             GIMP_TOOL_RECTANGLE_RESIZING_RIGHT,
+                                             x2,
+                                             private->y2);
+    }
+  else if (! strcmp  (pspec->name, "height") &&
+           ! PIXEL_FEQUAL (private->y2 - private->y1, private->height))
+    {
+      /* Calculate x2, y2 that will create a rectangle of given
+       * height, for the current options.
+       */
+      gdouble y2;
+
+      if (private->fixed_center)
+        {
+          y2 = private->center_y_on_fixed_center +
+               private->height / 2;
+        }
+      else
+        {
+          y2 = private->y1 + private->height;
+        }
+
+      gimp_tool_rectangle_synthesize_motion (rectangle,
+                                             GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM,
+                                             private->x2,
+                                             y2);
+    }
+  else if (! strcmp (pspec->name, "desired-fixed-size-width"))
+    {
+      /* 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 (private->fixed_rule_active                          &&
+#if 0
+          tool->button_press_state                            == 0    &&
+          tool->active_modifier_state                         == 0    &&
+#endif
+          FEQUAL (private->desired_fixed_size_width,  height) &&
+          FEQUAL (private->desired_fixed_size_height, width))
+        {
+          gdouble x = private->x1;
+          gdouble y = private->y1;
+
+          gimp_tool_rectangle_synthesize_motion (rectangle,
+                                                 GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT,
+                                                 private->x2,
+                                                 private->y2);
+
+          /* For some reason these needs to be set separately... */
+          g_object_set (rectangle,
+                        "x", x,
+                        NULL);
+          g_object_set (rectangle,
+                        "y", y,
+                        NULL);
+        }
+    }
+  else if (! strcmp (pspec->name, "aspect-numerator"))
+    {
+      /* 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 = private->aspect_denominator /
+                                    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 (private->fixed_rule_active               &&
+#if 0
+          tool->button_press_state    == 0                 &&
+          tool->active_modifier_state == 0                 &&
+#endif
+          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_tool_rectangle_synthesize_motion (rectangle,
+                                                 GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT,
+                                                 new_x2,
+                                                 new_y2);
+        }
+    }
+}
+
+static void
+gimp_tool_rectangle_changed (GimpToolWidget *widget)
+{
+  GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (widget);
+  GimpToolRectanglePrivate *private   = rectangle->private;
+  GimpDisplayShell         *shell     = gimp_tool_widget_get_shell (widget);
+  gdouble                   x1, y1, x2, y2;
+  gint                      handle_width;
+  gint                      handle_height;
+  gint                      i;
+
+  gimp_tool_rectangle_update_handle_sizes (rectangle);
+
+  gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2);
+
+  gimp_canvas_rectangle_guides_set (private->guides,
+                                    x1, y1,
+                                    x2 - x1,
+                                    y2 - y1,
+                                    private->guide, 4);
+
+  gimp_canvas_rectangle_set (private->rectangle,
+                             x1, y1,
+                             x2 - x1,
+                             y2 - y1);
+
+  gimp_canvas_item_set_visible (private->center, FALSE);
+
+  for (i = 0; i < 4; i++)
+    {
+      gimp_canvas_item_set_visible (private->creating_corners[i], FALSE);
+      gimp_canvas_item_set_highlight (private->creating_corners[i], FALSE);
+    }
+
+  for (i = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT;
+       i <= GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM;
+       i++)
+    {
+      gimp_canvas_item_set_visible (private->handles[i], FALSE);
+      gimp_canvas_item_set_visible (private->highlight_handles[i], FALSE);
+    }
+
+  handle_width  = private->corner_handle_w;
+  handle_height = private->corner_handle_h;
+
+  switch (private->function)
+    {
+    case GIMP_TOOL_RECTANGLE_MOVING:
+      if (private->rect_adjusting)
+        {
+          /* Mark the center because we snap to it */
+          gimp_canvas_handle_set_position (private->center,
+                                           (x1 + x2) / 2.0,
+                                           (y1 + y2) / 2.0);
+          gimp_canvas_item_set_visible (private->center, TRUE);
+          break;
+        }
+
+      /* else fallthrough */
+
+    case GIMP_TOOL_RECTANGLE_DEAD:
+    case GIMP_TOOL_RECTANGLE_CREATING:
+    case GIMP_TOOL_RECTANGLE_AUTO_SHRINK:
+      for (i = 0; i < 4; i++)
+        {
+          gimp_canvas_corner_set (private->creating_corners[i],
+                                  x1, y1, x2 - x1, y2 - y1,
+                                  private->corner_handle_w,
+                                  private->corner_handle_h,
+                                  private->narrow_mode);
+          gimp_canvas_item_set_visible (private->creating_corners[i], TRUE);
+        }
+      break;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
+    case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
+      handle_width  = private->top_and_bottom_handle_w;
+      handle_height = private->corner_handle_h;
+      break;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
+      handle_width  = private->corner_handle_w;
+      handle_height = private->left_and_right_handle_h;
+      break;
+
+    default:
+      break;
+    }
+
+  if (handle_width  > 0                                            &&
+      handle_height > 0                                            &&
+      private->function >= GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT &&
+      private->function <= GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM)
+    {
+      GimpCanvasItem *corner;
+
+      if (private->rect_adjusting)
+        corner = private->handles[private->function];
+      else
+        corner = private->highlight_handles[private->function];
+
+      gimp_canvas_corner_set (corner,
+                              x1, y1, x2 - x1, y2 - y1,
+                              handle_width, handle_height,
+                              private->narrow_mode);
+      gimp_canvas_item_set_visible (corner, TRUE);
+    }
+
+  if (private->highlight && ! private->rect_adjusting)
+    {
+      GdkRectangle rect;
+
+      rect.x      = x1;
+      rect.y      = y1;
+      rect.width  = x2 - x1;
+      rect.height = y2 - y1;
+
+      gimp_display_shell_set_highlight (shell, &rect);
+    }
+  else
+    {
+      gimp_display_shell_set_highlight (shell, NULL);
+    }
+}
+
+gint
+gimp_tool_rectangle_button_press (GimpToolWidget      *widget,
+                                  const GimpCoords    *coords,
+                                  guint32              time,
+                                  GdkModifierType      state,
+                                  GimpButtonPressType  press_type)
+{
+  GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (widget);
+  GimpToolRectanglePrivate *private   = rectangle->private;
+  gdouble                   snapped_x, snapped_y;
+  gint                      snap_x, snap_y;
+
+  /* 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_tool_rectangle_setup_snap_offsets (rectangle, coords);
+  gimp_tool_widget_get_snap_offsets (widget, &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_TOOL_RECTANGLE_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_tool_rectangle_update_handle_sizes (rectangle);
+
+      /* 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_tool_rectangle_get_other_side_coord (rectangle,
+                                                &private->other_side_x,
+                                                &private->other_side_y);
+    }
+
+  gimp_tool_rectangle_update_int_rect (rectangle);
+
+  /* Is the rectangle being rubber-banded? */
+  private->rect_adjusting = gimp_tool_rectangle_rect_adjusting_func (rectangle);
+
+  gimp_tool_rectangle_changed (widget);
+
+  return 1;
+}
+
+void
+gimp_tool_rectangle_button_release (GimpToolWidget        *widget,
+                                    const GimpCoords      *coords,
+                                    guint32                time,
+                                    GdkModifierType        state,
+                                    GimpButtonReleaseType  release_type)
+{
+  GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (widget);
+  GimpToolRectanglePrivate *private   = rectangle->private;
+
+  if (private->function == GIMP_TOOL_RECTANGLE_EXECUTING)
+    gimp_tool_widget_set_status (widget, NULL);
+
+  g_object_ref (rectangle);
+
+  gimp_tool_widget_set_snap_offsets (widget, 0, 0, 0, 0);
+
+  switch (release_type)
+    {
+    case GIMP_BUTTON_RELEASE_NORMAL:
+      gimp_tool_rectangle_change_complete (rectangle);
+      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_tool_rectangle_update_int_rect (rectangle);
+
+      /* If the first created rectangle was canceled, halt the tool */
+      if (gimp_tool_rectangle_rectangle_is_new (rectangle))
+        {
+          gimp_tool_widget_response (widget, GIMP_TOOL_WIDGET_RESPONSE_CANCEL);
+          return;
+        }
+      break;
+
+    case GIMP_BUTTON_RELEASE_CLICK:
+
+      /* When a dead area is clicked, don't execute. */
+      if (private->function != GIMP_TOOL_RECTANGLE_DEAD)
+        {
+          gimp_tool_widget_response (widget, GIMP_TOOL_WIDGET_RESPONSE_CONFIRM);
+          return;
+        }
+      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;
+
+  /* On button release, we are not rubber-banding the rectangle any longer. */
+  private->rect_adjusting = FALSE;
+
+  gimp_tool_rectangle_update_options (rectangle);
+
+  gimp_tool_rectangle_changed (widget);
+
+  g_object_unref (rectangle);
+}
+
+void
+gimp_tool_rectangle_motion (GimpToolWidget   *widget,
+                            const GimpCoords *coords,
+                            guint32           time,
+                            GdkModifierType   state)
+{
+  GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (widget);
+  GimpToolRectanglePrivate *private   = rectangle->private;
+  gdouble                   snapped_x;
+  gdouble                   snapped_y;
+  gint                      snap_x, snap_y;
+
+  /* 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_TOOL_RECTANGLE_EXECUTING ||
+      private->function == GIMP_TOOL_RECTANGLE_DEAD)
+    return;
+
+  /* Handle snapping. */
+  gimp_tool_widget_get_snap_offsets (widget, &snap_x, &snap_y, NULL, NULL);
+
+  snapped_x = coords->x + snap_x;
+  snapped_y = coords->y + snap_y;
+
+  /* This is the core rectangle shape updating function: */
+  gimp_tool_rectangle_update_with_coord (rectangle, snapped_x, snapped_y);
+
+  if (private->function != GIMP_TOOL_RECTANGLE_MOVING &&
+      private->function != GIMP_TOOL_RECTANGLE_EXECUTING)
+    {
+      gdouble x1, y1, x2, y2;
+      gint    width, height;
+
+      gimp_tool_widget_set_status (widget, NULL);
+
+      gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2);
+      width  = x2 - x1;
+      height = y2 - y1;
+
+      if (width > 0.0 && height > 0.0)
+        {
+          gchar *aspect_text;
+
+          aspect_text = g_strdup_printf ("  (%.2f:1)",
+                                         (gdouble) width / (gdouble) height);
+
+          gimp_tool_widget_set_status_coords (widget,
+                                              _("Rectangle: "),
+                                              width, " × ", height,
+                                              aspect_text);
+          g_free (aspect_text);
+        }
+    }
+
+  if (private->function == GIMP_TOOL_RECTANGLE_CREATING)
+    {
+      GimpRectangleFunction function = GIMP_TOOL_RECTANGLE_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_TOOL_RECTANGLE_RESIZING_UPPER_LEFT :
+                      GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT);
+        }
+      else if (dx > 0)
+        {
+          function = (dy < 0 ?
+                      GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT :
+                      GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT);
+        }
+      else if (dy < 0)
+        {
+          function = (dx < 0 ?
+                      GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT :
+                      GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT);
+        }
+      else if (dy > 0)
+        {
+          function = (dx < 0 ?
+                      GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT :
+                      GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT);
+        }
+
+      gimp_tool_rectangle_set_function (rectangle, function);
+
+      if (private->fixed_rule_active &&
+          private->fixed_rule == GIMP_RECTANGLE_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_tool_rectangle_update_with_coord (rectangle,
+                                                 snapped_x,
+                                                 snapped_y);
+
+          gimp_tool_widget_set_snap_offsets (widget,
+                                             -(private->x2 - private->x1) / 2,
+                                             -(private->y2 - private->y1) / 2,
+                                             private->x2 - private->x1,
+                                             private->y2 - private->y1);
+
+          gimp_tool_rectangle_set_function (rectangle,
+                                            GIMP_TOOL_RECTANGLE_MOVING);
+        }
+    }
+
+  gimp_tool_rectangle_update_options (rectangle);
+
+  private->lastx = snapped_x;
+  private->lasty = snapped_y;
+}
+
+void
+gimp_tool_rectangle_hover (GimpToolWidget   *widget,
+                           const GimpCoords *coords,
+                           GdkModifierType   state,
+                           gboolean          proximity)
+{
+  GimpToolRectangle     *rectangle = GIMP_TOOL_RECTANGLE (widget);
+  GimpRectangleFunction  function  = GIMP_TOOL_RECTANGLE_DEAD;
+
+#if 0
+  if (private->suppress_updates)
+    {
+      private->suppress_updates--;
+      return;
+    }
+#endif
+
+  if (! proximity)
+    {
+      function = GIMP_TOOL_RECTANGLE_DEAD;
+    }
+  else if (gimp_tool_rectangle_coord_outside (rectangle, coords))
+    {
+      /* The cursor is outside of the rectangle, clicking should
+       * create a new rectangle.
+       */
+      function = GIMP_TOOL_RECTANGLE_CREATING;
+    }
+  else if (gimp_tool_rectangle_coord_on_handle (rectangle,
+                                                coords,
+                                                GIMP_HANDLE_ANCHOR_NORTH_WEST))
+    {
+      function = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT;
+    }
+  else if (gimp_tool_rectangle_coord_on_handle (rectangle,
+                                                coords,
+                                                GIMP_HANDLE_ANCHOR_SOUTH_EAST))
+    {
+      function = GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT;
+    }
+  else if  (gimp_tool_rectangle_coord_on_handle (rectangle,
+                                                 coords,
+                                                 GIMP_HANDLE_ANCHOR_NORTH_EAST))
+    {
+      function = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT;
+    }
+  else if (gimp_tool_rectangle_coord_on_handle (rectangle,
+                                                coords,
+                                                GIMP_HANDLE_ANCHOR_SOUTH_WEST))
+    {
+      function = GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT;
+    }
+  else if (gimp_tool_rectangle_coord_on_handle (rectangle,
+                                                coords,
+                                                GIMP_HANDLE_ANCHOR_WEST))
+    {
+      function = GIMP_TOOL_RECTANGLE_RESIZING_LEFT;
+    }
+  else if (gimp_tool_rectangle_coord_on_handle (rectangle,
+                                                coords,
+                                                GIMP_HANDLE_ANCHOR_EAST))
+    {
+      function = GIMP_TOOL_RECTANGLE_RESIZING_RIGHT;
+    }
+  else if (gimp_tool_rectangle_coord_on_handle (rectangle,
+                                                coords,
+                                                GIMP_HANDLE_ANCHOR_NORTH))
+    {
+      function = GIMP_TOOL_RECTANGLE_RESIZING_TOP;
+    }
+  else if (gimp_tool_rectangle_coord_on_handle (rectangle,
+                                                coords,
+                                                GIMP_HANDLE_ANCHOR_SOUTH))
+    {
+      function = GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM;
+    }
+  else if (gimp_tool_rectangle_coord_on_handle (rectangle,
+                                                coords,
+                                                GIMP_HANDLE_ANCHOR_CENTER))
+    {
+      function = GIMP_TOOL_RECTANGLE_MOVING;
+    }
+  else
+    {
+      function = GIMP_TOOL_RECTANGLE_DEAD;
+    }
+
+  gimp_tool_rectangle_set_function (rectangle, function);
+}
+
+static gboolean
+gimp_tool_rectangle_key_press (GimpToolWidget *widget,
+                               GdkEventKey    *kevent)
+{
+  GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (widget);
+  GimpToolRectanglePrivate *private   = rectangle->private;
+  gint                      dx        = 0;
+  gint                      dy        = 0;
+  gdouble                   new_x     = 0;
+  gdouble                   new_y     = 0;
+
+  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;
+
+    default:
+      return GIMP_TOOL_WIDGET_CLASS (parent_class)->key_press (widget, kevent);
+    }
+
+  /*  If the shift key is down, move by an accelerated increment  */
+  if (kevent->state & gimp_get_extend_selection_mask ())
+    {
+      dx *= ARROW_VELOCITY;
+      dy *= ARROW_VELOCITY;
+    }
+
+  gimp_tool_widget_set_snap_offsets (widget, 0, 0, 0, 0);
+
+  /*  Resize the rectangle if the mouse is over a handle, otherwise move it  */
+  switch (private->function)
+    {
+    case GIMP_TOOL_RECTANGLE_MOVING:
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
+      new_x = private->x1 + dx;
+      new_y = private->y1 + dy;
+      private->lastx = new_x;
+      private->lasty = new_y;
+      break;
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
+      new_x = private->x2 + dx;
+      new_y = private->y1 + dy;
+      private->lastx = new_x;
+      private->lasty = new_y;
+      break;
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
+      new_x = private->x1 + dx;
+      new_y = private->y2 + dy;
+      private->lastx = new_x;
+      private->lasty = new_y;
+      break;
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
+      new_x = private->x2 + dx;
+      new_y = private->y2 + dy;
+      private->lastx = new_x;
+      private->lasty = new_y;
+      break;
+    case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
+      new_x = private->x1 + dx;
+      private->lastx = new_x;
+      break;
+    case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
+      new_x = private->x2 + dx;
+      private->lastx = new_x;
+      break;
+    case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
+      new_y = private->y1 + dy;
+      private->lasty = new_y;
+      break;
+    case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
+      new_y = private->y2 + dy;
+      private->lasty = new_y;
+      break;
+
+    default:
+      return TRUE;
+    }
+
+  gimp_tool_rectangle_update_with_coord (rectangle, 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_tool_rectangle_update_options (rectangle);
+
+  gimp_tool_rectangle_change_complete (rectangle);
+
+#if 0
+  /*  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;
+#endif
+
+  return TRUE;
+}
+
+static void
+gimp_tool_rectangle_motion_modifier (GimpToolWidget  *widget,
+                                     GdkModifierType  key,
+                                     gboolean         press,
+                                     GdkModifierType  state)
+{
+  GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (widget);
+  GimpToolRectanglePrivate *private   = rectangle->private;
+  gboolean                  button1_down;
+
+  button1_down = (state & GDK_BUTTON1_MASK);
+
+  if (key == gimp_get_extend_selection_mask ())
+    {
+#if 0
+      /* Here we want to handle manualy when to update the rectangle, so we
+       * don't want gimp_tool_rectangle_options_notify to do anything.
+       */
+      g_signal_handlers_block_by_func (options,
+                                       gimp_tool_rectangle_options_notify,
+                                       rectangle);
+#endif
+
+      g_object_set (rectangle,
+                    "fixed-rule-active", ! private->fixed_rule_active,
+                    NULL);
+
+#if 0
+      g_signal_handlers_unblock_by_func (options,
+                                         gimp_tool_rectangle_options_notify,
+                                         rectangle);
+#endif
+
+      /* Only change the shape if the mouse is still down (i.e. the user is
+       * still editing the rectangle.
+       */
+      if (button1_down)
+        {
+          if (! private->fixed_rule_active)
+            {
+              /* Reset anchor point */
+              gimp_tool_rectangle_set_other_side_coord (rectangle,
+                                                        private->other_side_x,
+                                                        private->other_side_y);
+            }
+
+          gimp_tool_rectangle_update_with_coord (rectangle,
+                                                 private->lastx,
+                                                 private->lasty);
+        }
+    }
+
+  if (key == gimp_get_toggle_behavior_mask ())
+    {
+      g_object_set (rectangle,
+                    "fixed-center", ! private->fixed_center,
+                    NULL);
+
+      if (private->fixed_center)
+        {
+          gimp_tool_rectangle_update_with_coord (rectangle,
+                                                 private->lastx,
+                                                 private->lasty);
+
+          /* Only emit the rectangle-changed signal if the button is
+           * not down. If it is down, the signal will and shall be
+           * emitted on _button_release instead.
+           */
+          if (! button1_down)
+            {
+              gimp_tool_rectangle_change_complete (rectangle);
+            }
+        }
+      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_tool_rectangle_set_other_side_coord (rectangle,
+                                                    private->other_side_x,
+                                                    private->other_side_y);
+        }
+    }
+
+  gimp_tool_rectangle_update_options (rectangle);
+}
+
+static gboolean
+gimp_tool_rectangle_get_cursor (GimpToolWidget     *widget,
+                                const GimpCoords   *coords,
+                                GdkModifierType     state,
+                                GimpCursorType     *cursor,
+                                GimpToolCursorType *tool_cursor,
+                                GimpCursorModifier *modifier)
+{
+  GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (widget);
+  GimpToolRectanglePrivate *private   = rectangle->private;
+
+  switch (private->function)
+    {
+    case GIMP_TOOL_RECTANGLE_CREATING:
+      *cursor = GIMP_CURSOR_CROSSHAIR_SMALL;
+      break;
+    case GIMP_TOOL_RECTANGLE_MOVING:
+      *cursor   = GIMP_CURSOR_MOVE;
+      *modifier = GIMP_CURSOR_MODIFIER_MOVE;
+      break;
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
+      *cursor = GIMP_CURSOR_CORNER_TOP_LEFT;
+      break;
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
+      *cursor = GIMP_CURSOR_CORNER_TOP_RIGHT;
+      break;
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
+      *cursor = GIMP_CURSOR_CORNER_BOTTOM_LEFT;
+      break;
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
+      *cursor = GIMP_CURSOR_CORNER_BOTTOM_RIGHT;
+      break;
+    case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
+      *cursor = GIMP_CURSOR_SIDE_LEFT;
+      break;
+    case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
+      *cursor = GIMP_CURSOR_SIDE_RIGHT;
+      break;
+    case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
+      *cursor = GIMP_CURSOR_SIDE_TOP;
+      break;
+    case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
+      *cursor = GIMP_CURSOR_SIDE_BOTTOM;
+      break;
+
+    default:
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+gimp_tool_rectangle_change_complete (GimpToolRectangle *rectangle)
+{
+  g_signal_emit (rectangle, rectangle_signals[CHANGE_COMPLETE], 0);
+}
+
+static void
+gimp_tool_rectangle_update_options (GimpToolRectangle *rectangle)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+  gdouble                   x1, y1;
+  gdouble                   x2, y2;
+
+  gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2);
+
+#if 0
+  g_signal_handlers_block_by_func (options,
+                                   gimp_tool_rectangle_options_notify,
+                                   rect_tool);
+#endif
+
+  g_object_freeze_notify (G_OBJECT (rectangle));
+
+  if (! FEQUAL (private->x, x1))
+    g_object_set (rectangle, "x", x1, NULL);
+
+  if (! FEQUAL (private->y, y1))
+    g_object_set (rectangle, "y", y1, NULL);
+
+  if (! FEQUAL (private->width, x2 - x1))
+    g_object_set (rectangle, "width", x2 - x1, NULL);
+
+  if (! FEQUAL (private->height, y2 - y1))
+    g_object_set (rectangle, "height", y2 - y1, NULL);
+
+  g_object_thaw_notify (G_OBJECT (rectangle));
+
+#if 0
+  g_signal_handlers_unblock_by_func (options,
+                                     gimp_tool_rectangle_options_notify,
+                                     rect_tool);
+#endif
+}
+
+static void
+gimp_tool_rectangle_update_handle_sizes (GimpToolRectangle *rectangle)
+{
+  GimpToolRectanglePrivate *private = rectangle->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;
+
+  shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle));
+
+  gimp_tool_rectangle_get_public_rect (rectangle,
+                                       &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;
+}
+
+static void
+gimp_tool_rectangle_synthesize_motion (GimpToolRectangle *rectangle,
+                                       gint               function,
+                                       gdouble            new_x,
+                                       gdouble            new_y)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+  GimpRectangleFunction     old_function;
+
+  /* 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 (private->rect_adjusting)
+    return;
+
+  old_function = private->function;
+
+  gimp_tool_rectangle_set_function (rectangle, function);
+
+  gimp_tool_rectangle_update_with_coord (rectangle, 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_tool_rectangle_update_options (rectangle);
+
+  gimp_tool_rectangle_set_function (rectangle, old_function);
+
+  gimp_tool_rectangle_change_complete (rectangle);
+}
+
+static void
+swap_doubles (gdouble *i,
+              gdouble *j)
+{
+  gdouble tmp;
+
+  tmp = *i;
+  *i = *j;
+  *j = tmp;
+}
+
+/* gimp_tool_rectangle_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_tool_rectangle_check_function (GimpToolRectangle *rectangle)
+
+{
+  GimpToolRectanglePrivate *private  = rectangle->private;
+  GimpRectangleFunction     function = private->function;
+
+  if (private->x2 < private->x1)
+    {
+      swap_doubles (&private->x1, &private->x2);
+
+      switch (function)
+        {
+        case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
+          function = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT;
+          break;
+        case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
+          function = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT;
+          break;
+        case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
+          function = GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT;
+          break;
+        case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
+          function = GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT;
+          break;
+        case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
+          function = GIMP_TOOL_RECTANGLE_RESIZING_RIGHT;
+          break;
+        case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
+          function = GIMP_TOOL_RECTANGLE_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_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
+          function = GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT;
+          break;
+        case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
+          function = GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT;
+          break;
+        case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
+          function = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT;
+          break;
+        case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
+          function = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT;
+          break;
+        case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
+          function = GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM;
+          break;
+        case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
+          function = GIMP_TOOL_RECTANGLE_RESIZING_TOP;
+          break;
+        default:
+          break;
+        }
+    }
+
+  gimp_tool_rectangle_set_function (rectangle, function);
+}
+
+/**
+ * gimp_tool_rectangle_coord_outside:
+ *
+ * Returns: %TRUE if the coord is outside the rectange bounds
+ *          including any outside handles.
+ */
+static gboolean
+gimp_tool_rectangle_coord_outside (GimpToolRectangle *rectangle,
+                                   const GimpCoords  *coord)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+  GimpDisplayShell         *shell;
+  gboolean                  narrow_mode = private->narrow_mode;
+  gdouble                   x1, y1, x2, y2;
+  gdouble                   x1_b, y1_b, x2_b, y2_b;
+
+  shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle));
+
+  gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2);
+
+  x1_b = x1 - (narrow_mode ? private->corner_handle_w / shell->scale_x : 0);
+  x2_b = x2 + (narrow_mode ? private->corner_handle_w / shell->scale_x : 0);
+  y1_b = y1 - (narrow_mode ? private->corner_handle_h / shell->scale_y : 0);
+  y2_b = 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_tool_rectangle_coord_on_handle:
+ *
+ * Returns: %TRUE if the coord is on the handle that corresponds to
+ *          @anchor.
+ */
+static gboolean
+gimp_tool_rectangle_coord_on_handle (GimpToolRectangle *rectangle,
+                                     const GimpCoords  *coords,
+                                     GimpHandleAnchor   anchor)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+  GimpDisplayShell         *shell;
+  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;
+
+  shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle));
+
+  gimp_tool_rectangle_get_public_rect (rectangle,
+                                       &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_canvas_item_on_handle (private->rectangle,
+                                     coords->x, coords->y,
+                                     GIMP_HANDLE_SQUARE,
+                                     handle_x,     handle_y,
+                                     handle_width, handle_height,
+                                     anchor);
+}
+
+static GimpHandleAnchor
+gimp_tool_rectangle_get_anchor (GimpRectangleFunction function)
+{
+  switch (function)
+    {
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
+      return GIMP_HANDLE_ANCHOR_NORTH_WEST;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
+      return GIMP_HANDLE_ANCHOR_NORTH_EAST;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
+      return GIMP_HANDLE_ANCHOR_SOUTH_WEST;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
+      return GIMP_HANDLE_ANCHOR_SOUTH_EAST;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
+      return GIMP_HANDLE_ANCHOR_WEST;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
+      return GIMP_HANDLE_ANCHOR_EAST;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
+      return GIMP_HANDLE_ANCHOR_NORTH;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
+      return GIMP_HANDLE_ANCHOR_SOUTH;
+
+    default:
+      return GIMP_HANDLE_ANCHOR_CENTER;
+    }
+}
+
+static gboolean
+gimp_tool_rectangle_rect_rubber_banding_func (GimpToolRectangle *rectangle)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+
+  switch (private->function)
+    {
+      case GIMP_TOOL_RECTANGLE_CREATING:
+      case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
+      case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
+      case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
+      case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
+      case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
+      case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
+      case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
+      case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
+      case GIMP_TOOL_RECTANGLE_AUTO_SHRINK:
+        return TRUE;
+
+    case GIMP_TOOL_RECTANGLE_MOVING:
+    case GIMP_TOOL_RECTANGLE_DEAD:
+    default:
+      break;
+    }
+
+  return FALSE;
+}
+
+/**
+ * gimp_tool_rectangle_rect_adjusting_func:
+ * @rectangle:
+ *
+ * Returns: %TRUE if the current function is a rectangle adjusting
+ *          function.
+ */
+static gboolean
+gimp_tool_rectangle_rect_adjusting_func (GimpToolRectangle *rectangle)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+
+  return (gimp_tool_rectangle_rect_rubber_banding_func (rectangle) ||
+          private->function == GIMP_TOOL_RECTANGLE_MOVING);
+}
+
+/**
+ * gimp_tool_rectangle_get_other_side:
+ * @rectangle: A #GimpToolRectangle.
+ * @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_tool_rectangle_get_other_side (GimpToolRectangle  *rectangle,
+                                    gdouble           **other_x,
+                                    gdouble           **other_y)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+
+  switch (private->function)
+    {
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
+      *other_x = &private->x1;
+      break;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
+      *other_x = &private->x2;
+      break;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
+    case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
+    default:
+      *other_x = NULL;
+      break;
+    }
+
+  switch (private->function)
+    {
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
+      *other_y = &private->y1;
+      break;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
+      *other_y = &private->y2;
+      break;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
+    default:
+      *other_y = NULL;
+      break;
+    }
+}
+
+static void
+gimp_tool_rectangle_get_other_side_coord (GimpToolRectangle *rectangle,
+                                          gdouble           *other_side_x,
+                                          gdouble           *other_side_y)
+{
+  gdouble *other_x = NULL;
+  gdouble *other_y = NULL;
+
+  gimp_tool_rectangle_get_other_side (rectangle, &other_x, &other_y);
+
+  if (other_x)
+    *other_side_x = *other_x;
+  if (other_y)
+    *other_side_y = *other_y;
+}
+
+static void
+gimp_tool_rectangle_set_other_side_coord (GimpToolRectangle *rectangle,
+                                          gdouble            other_side_x,
+                                          gdouble            other_side_y)
+{
+  gdouble *other_x = NULL;
+  gdouble *other_y = NULL;
+
+  gimp_tool_rectangle_get_other_side (rectangle, &other_x, &other_y);
+
+  if (other_x)
+    *other_x = other_side_x;
+  if (other_y)
+    *other_y = other_side_y;
+
+  gimp_tool_rectangle_check_function (rectangle);
+
+  gimp_tool_rectangle_update_int_rect (rectangle);
+}
+
+/**
+ * gimp_tool_rectangle_apply_coord:
+ * @param:     A #GimpToolRectangle.
+ * @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_tool_rectangle_apply_coord (GimpToolRectangle *rectangle,
+                                 gdouble            coord_x,
+                                 gdouble            coord_y)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+
+  if (private->function == GIMP_TOOL_RECTANGLE_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_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
+      private->x1 = coord_x;
+
+      if (private->fixed_center)
+        private->x2 = 2 * private->center_x_on_fixed_center - private->x1;
+
+      break;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
+      private->x2 = coord_x;
+
+      if (private->fixed_center)
+        private->x1 = 2 * private->center_x_on_fixed_center - private->x2;
+
+      break;
+
+    default:
+      break;
+    }
+
+  switch (private->function)
+    {
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
+      private->y1 = coord_y;
+
+      if (private->fixed_center)
+        private->y2 = 2 * private->center_y_on_fixed_center - private->y1;
+
+      break;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
+      private->y2 = coord_y;
+
+      if (private->fixed_center)
+        private->y1 = 2 * private->center_y_on_fixed_center - private->y2;
+
+      break;
+
+    default:
+      break;
+    }
+}
+
+static void
+gimp_tool_rectangle_setup_snap_offsets (GimpToolRectangle *rectangle,
+                                        const GimpCoords  *coords)
+{
+  GimpToolWidget           *widget  = GIMP_TOOL_WIDGET (rectangle);
+  GimpToolRectanglePrivate *private = rectangle->private;
+  gdouble                   pub_x1, pub_y1;
+  gdouble                   pub_x2, pub_y2;
+  gdouble                   pub_coord_x, pub_coord_y;
+
+  gimp_tool_rectangle_get_public_rect (rectangle,
+                                       &pub_x1, &pub_y1, &pub_x2, &pub_y2);
+  gimp_tool_rectangle_adjust_coord (rectangle,
+                                    coords->x, coords->y,
+                                    &pub_coord_x, &pub_coord_y);
+
+  switch (private->function)
+    {
+      gimp_tool_widget_set_snap_offsets (widget, 0, 0, 0, 0);
+      break;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
+      gimp_tool_widget_set_snap_offsets (widget,
+                                         pub_x1 - pub_coord_x,
+                                         pub_y1 - pub_coord_y,
+                                         0, 0);
+      break;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
+      gimp_tool_widget_set_snap_offsets (widget,
+                                         pub_x2 - pub_coord_x,
+                                         pub_y1 - pub_coord_y,
+                                         0, 0);
+      break;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
+      gimp_tool_widget_set_snap_offsets (widget,
+                                         pub_x1 - pub_coord_x,
+                                         pub_y2 - pub_coord_y,
+                                         0, 0);
+      break;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
+      gimp_tool_widget_set_snap_offsets (widget,
+                                         pub_x2 - pub_coord_x,
+                                         pub_y2 - pub_coord_y,
+                                         0, 0);
+      break;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
+      gimp_tool_widget_set_snap_offsets (widget,
+                                         pub_x1 - pub_coord_x, 0,
+                                         0, 0);
+      break;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
+      gimp_tool_widget_set_snap_offsets (widget,
+                                         pub_x2 - pub_coord_x, 0,
+                                         0, 0);
+      break;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
+      gimp_tool_widget_set_snap_offsets (widget,
+                                         0, pub_y1 - pub_coord_y,
+                                         0, 0);
+      break;
+
+    case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
+      gimp_tool_widget_set_snap_offsets (widget,
+                                         0, pub_y2 - pub_coord_y,
+                                         0, 0);
+      break;
+
+    case GIMP_TOOL_RECTANGLE_MOVING:
+      gimp_tool_widget_set_snap_offsets (widget,
+                                         pub_x1 - pub_coord_x,
+                                         pub_y1 - pub_coord_y,
+                                         pub_x2 - pub_x1,
+                                         pub_y2 - pub_y1);
+      break;
+
+    default:
+      break;
+    }
+}
+
+/**
+ * gimp_tool_rectangle_clamp:
+ * @rectangle:      A #GimpToolRectangle.
+ * @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_tool_rectangle_clamp (GimpToolRectangle       *rectangle,
+                           ClampedSide             *clamped_sides,
+                           GimpRectangleConstraint  constraint,
+                           gboolean                 symmetrically)
+{
+  gimp_tool_rectangle_clamp_width (rectangle,
+                                   clamped_sides,
+                                   constraint,
+                                   symmetrically);
+
+  gimp_tool_rectangle_clamp_height (rectangle,
+                                    clamped_sides,
+                                    constraint,
+                                    symmetrically);
+}
+
+/**
+ * gimp_tool_rectangle_clamp_width:
+ * @rectangle:      A #GimpToolRectangle.
+ * @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_tool_rectangle_clamp_width (GimpToolRectangle       *rectangle,
+                                 ClampedSide             *clamped_sides,
+                                 GimpRectangleConstraint  constraint,
+                                 gboolean                 symmetrically)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+  gint                      min_x;
+  gint                      max_x;
+
+  if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE)
+    return;
+
+  gimp_tool_rectangle_get_constraints (rectangle,
+                                       &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_tool_rectangle_clamp_height:
+ * @rectangle:      A #GimpToolRectangle.
+ * @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_tool_rectangle_clamp_height (GimpToolRectangle       *rectangle,
+                                  ClampedSide             *clamped_sides,
+                                  GimpRectangleConstraint  constraint,
+                                  gboolean                 symmetrically)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+  gint                      min_y;
+  gint                      max_y;
+
+  if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE)
+    return;
+
+  gimp_tool_rectangle_get_constraints (rectangle,
+                                       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_tool_rectangle_keep_inside:
+ * @rectangle: A #GimpToolRectangle.
+ *
+ * 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_tool_rectangle_keep_inside (GimpToolRectangle      *rectangle,
+                                 GimpRectangleConstraint constraint)
+{
+  gimp_tool_rectangle_keep_inside_horizontally (rectangle, constraint);
+  gimp_tool_rectangle_keep_inside_vertically   (rectangle, constraint);
+}
+
+/**
+ * gimp_tool_rectangle_keep_inside_horizontally:
+ * @rectangle:      A #GimpToolRectangle.
+ * @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_tool_rectangle_keep_inside_horizontally (GimpToolRectangle       *rectangle,
+                                              GimpRectangleConstraint  constraint)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+  gint                      min_x;
+  gint                      max_x;
+
+  if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE)
+    return;
+
+  gimp_tool_rectangle_get_constraints (rectangle,
+                                       &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_tool_rectangle_keep_inside_vertically:
+ * @rectangle:      A #GimpToolRectangle.
+ * @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_tool_rectangle_keep_inside_vertically (GimpToolRectangle       *rectangle,
+                                            GimpRectangleConstraint  constraint)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+  gint                      min_y;
+  gint                      max_y;
+
+  if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE)
+    return;
+
+  gimp_tool_rectangle_get_constraints (rectangle,
+                                       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_tool_rectangle_apply_fixed_width:
+ * @rectangle:      A #GimpToolRectangle.
+ * @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_tool_rectangle_apply_fixed_width (GimpToolRectangle      *rectangle,
+                                       GimpRectangleConstraint constraint,
+                                       gdouble                 width)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+
+  switch (private->function)
+    {
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
+    case GIMP_TOOL_RECTANGLE_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_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
+    case GIMP_TOOL_RECTANGLE_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;
+
+    default:
+      break;
+    }
+
+  /* Width shall be kept even after constraints, so we move the
+   * rectangle sideways rather than adjusting a side.
+   */
+  gimp_tool_rectangle_keep_inside_horizontally (rectangle, constraint);
+}
+
+/**
+ * gimp_tool_rectangle_apply_fixed_height:
+ * @rectangle:      A #GimpToolRectangle.
+ * @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_tool_rectangle_apply_fixed_height (GimpToolRectangle      *rectangle,
+                                        GimpRectangleConstraint constraint,
+                                        gdouble                 height)
+
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+
+  switch (private->function)
+    {
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
+    case GIMP_TOOL_RECTANGLE_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_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
+    case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
+    case GIMP_TOOL_RECTANGLE_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;
+
+    default:
+      break;
+    }
+
+  /* Width shall be kept even after constraints, so we move the
+   * rectangle sideways rather than adjusting a side.
+   */
+  gimp_tool_rectangle_keep_inside_vertically (rectangle, constraint);
+}
+
+/**
+ * gimp_tool_rectangle_apply_aspect:
+ * @rectangle:      A #GimpToolRectangle.
+ * @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_tool_rectangle_apply_aspect (GimpToolRectangle *rectangle,
+                                  gdouble            aspect,
+                                  gint               clamped_sides)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+  gdouble                   current_w;
+  gdouble                   current_h;
+  gdouble                   current_aspect;
+  SideToResize              side_to_resize = SIDE_TO_RESIZE_NONE;
+
+  current_w = private->x2 - private->x1;
+  current_h = private->y2 - private->y1;
+
+  current_aspect = (gdouble) current_w / (gdouble) current_h;
+
+  /* Do we have to do anything? */
+  if (current_aspect == aspect)
+    return;
+
+  if (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_TOOL_RECTANGLE_RESIZING_LEFT:
+            case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
+            case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
+            case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
+            case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
+            case GIMP_TOOL_RECTANGLE_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_TOOL_RECTANGLE_RESIZING_TOP:
+            case GIMP_TOOL_RECTANGLE_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_TOOL_RECTANGLE_RESIZING_TOP:
+            case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
+            case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
+            case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
+            case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
+            case GIMP_TOOL_RECTANGLE_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_TOOL_RECTANGLE_RESIZING_LEFT:
+            case GIMP_TOOL_RECTANGLE_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_TOOL_RECTANGLE_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_TOOL_RECTANGLE_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_TOOL_RECTANGLE_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_TOOL_RECTANGLE_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_TOOL_RECTANGLE_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_TOOL_RECTANGLE_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_TOOL_RECTANGLE_RESIZING_BOTTOM:
+        case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
+          side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY;
+          break;
+
+        case GIMP_TOOL_RECTANGLE_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_TOOL_RECTANGLE_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_TOOL_RECTANGLE_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_TOOL_RECTANGLE_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_TOOL_RECTANGLE_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_TOOL_RECTANGLE_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_TOOL_RECTANGLE_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_TOOL_RECTANGLE_RESIZING_LEFT:
+        case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
+          side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY;
+          break;
+
+        case GIMP_TOOL_RECTANGLE_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_tool_rectangle_update_with_coord:
+ * @rectangle:      A #GimpToolRectangle.
+ * @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_tool_rectangle_update_with_coord (GimpToolRectangle *rectangle,
+                                       gdouble            new_x,
+                                       gdouble            new_y)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+
+  /* Move the corner or edge the user currently has grabbed. */
+  gimp_tool_rectangle_apply_coord (rectangle, new_x, new_y);
+
+  /* Update private->function. The function changes if the user
+   * "flips" the rectangle.
+   */
+  gimp_tool_rectangle_check_function (rectangle);
+
+  /* Clamp the rectangle if necessary */
+  gimp_tool_rectangle_handle_general_clamping (rectangle);
+
+  /* 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_TOOL_RECTANGLE_MOVING)
+    {
+      gimp_tool_rectangle_apply_fixed_rule (rectangle);
+    }
+
+  gimp_tool_rectangle_update_int_rect (rectangle);
+}
+
+static void
+gimp_tool_rectangle_apply_fixed_rule (GimpToolRectangle *rectangle)
+{
+  GimpToolRectanglePrivate    *private = rectangle->private;
+  GimpRectangleConstraint      constraint_to_use;
+  GimpDisplayShell            *shell;
+  GimpImage                   *image;
+
+  shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle));
+  image = gimp_display_get_image (shell->display);
+
+  /* Calculate what constraint to use when needed. */
+  constraint_to_use = gimp_tool_rectangle_get_constraint (rectangle);
+
+  if (private->fixed_rule_active &&
+      private->fixed_rule == GIMP_RECTANGLE_FIXED_ASPECT)
+    {
+      gdouble aspect;
+
+      aspect = CLAMP (private->aspect_numerator /
+                      private->aspect_denominator,
+                      1.0 / gimp_image_get_height (image),
+                      gimp_image_get_width (image));
+
+      if (constraint_to_use == GIMP_RECTANGLE_CONSTRAIN_NONE)
+        {
+          gimp_tool_rectangle_apply_aspect (rectangle,
+                                            aspect,
+                                            CLAMPED_NONE);
+        }
+      else
+        {
+          if (private->function != GIMP_TOOL_RECTANGLE_MOVING)
+            {
+              ClampedSide clamped_sides = CLAMPED_NONE;
+
+              gimp_tool_rectangle_apply_aspect (rectangle,
+                                                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_tool_rectangle_clamp (rectangle,
+                                         &clamped_sides,
+                                         constraint_to_use,
+                                         private->fixed_center);
+
+              gimp_tool_rectangle_apply_aspect (rectangle,
+                                                aspect,
+                                                clamped_sides);
+            }
+          else
+            {
+              gimp_tool_rectangle_apply_aspect (rectangle,
+                                                aspect,
+                                                CLAMPED_NONE);
+
+              gimp_tool_rectangle_keep_inside (rectangle,
+                                               constraint_to_use);
+            }
+        }
+    }
+  else if (private->fixed_rule_active &&
+           private->fixed_rule == GIMP_RECTANGLE_FIXED_SIZE)
+    {
+      gimp_tool_rectangle_apply_fixed_width (rectangle,
+                                             constraint_to_use,
+                                             private->desired_fixed_size_width);
+      gimp_tool_rectangle_apply_fixed_height (rectangle,
+                                              constraint_to_use,
+                                              private->desired_fixed_size_height);
+    }
+  else if (private->fixed_rule_active &&
+           private->fixed_rule == GIMP_RECTANGLE_FIXED_WIDTH)
+    {
+      gimp_tool_rectangle_apply_fixed_width (rectangle,
+                                             constraint_to_use,
+                                             private->desired_fixed_width);
+    }
+  else if (private->fixed_rule_active &&
+           private->fixed_rule == GIMP_RECTANGLE_FIXED_HEIGHT)
+    {
+      gimp_tool_rectangle_apply_fixed_height (rectangle,
+                                              constraint_to_use,
+                                              private->desired_fixed_height);
+    }
+}
+
+/**
+ * gimp_tool_rectangle_get_constraints:
+ * @rectangle:      A #GimpToolRectangle.
+ * @min_x:
+ * @min_y:
+ * @max_x:
+ * @max_y:          Pointers of where to put constraints. NULL allowed.
+ * @constraint:     Whether to return image or layer constraints.
+ *
+ * Calculates constraint coordinates for image or layer.
+ */
+static void
+gimp_tool_rectangle_get_constraints (GimpToolRectangle       *rectangle,
+                                     gint                    *min_x,
+                                     gint                    *min_y,
+                                     gint                    *max_x,
+                                     gint                    *max_y,
+                                     GimpRectangleConstraint  constraint)
+{
+  GimpDisplayShell *shell;
+  GimpImage        *image;
+  gint              min_x_dummy;
+  gint              min_y_dummy;
+  gint              max_x_dummy;
+  gint              max_y_dummy;
+
+  shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle));
+  image = gimp_display_get_image (shell->display);
+
+  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;
+
+  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 (gimp_image_get_active_drawable (image));
+
+        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_tool_rectangle_handle_general_clamping:
+ * @rectangle: A #GimpToolRectangle.
+ *
+ * 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_tool_rectangle_handle_general_clamping (GimpToolRectangle *rectangle)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+  GimpRectangleConstraint   constraint;
+
+  constraint = gimp_tool_rectangle_get_constraint (rectangle);
+
+  /* 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_TOOL_RECTANGLE_MOVING)
+    {
+      gimp_tool_rectangle_clamp (rectangle,
+                                 NULL,
+                                 constraint,
+                                 private->fixed_center);
+    }
+  else
+    {
+      gimp_tool_rectangle_keep_inside (rectangle, constraint);
+    }
+}
+
+/**
+ * gimp_tool_rectangle_update_int_rect:
+ * @rectangle:
+ *
+ * Update integer representation of rectangle.
+ **/
+static void
+gimp_tool_rectangle_update_int_rect (GimpToolRectangle *rectangle)
+{
+  GimpToolRectanglePrivate *private = rectangle->private;
+
+  private->x1_int = SIGNED_ROUND (private->x1);
+  private->y1_int = SIGNED_ROUND (private->y1);
+
+  if (gimp_tool_rectangle_rect_rubber_banding_func (rectangle))
+    {
+      private->width_int  = (gint) SIGNED_ROUND (private->x2) - private->x1_int;
+      private->height_int = (gint) SIGNED_ROUND (private->y2) - private->y1_int;
+    }
+}
+
+/**
+ * gimp_tool_rectangle_adjust_coord:
+ * @rectangle:
+ * @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_tool_rectangle_adjust_coord (GimpToolRectangle *rectangle,
+                                  gdouble            coord_x_input,
+                                  gdouble            coord_y_input,
+                                  gdouble           *coord_x_output,
+                                  gdouble           *coord_y_output)
+{
+  GimpToolRectanglePrivate *priv = rectangle->private;
+
+  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;
+    }
+}
+
+
+/*  public functions  */
+
+GimpToolWidget *
+gimp_tool_rectangle_new (GimpDisplayShell  *shell)
+{
+  g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);
+
+  return g_object_new (GIMP_TYPE_TOOL_RECTANGLE,
+                       "shell",      shell,
+                       NULL);
+}
+
+GimpRectangleFunction
+gimp_tool_rectangle_get_function (GimpToolRectangle *rectangle)
+{
+  g_return_val_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle),
+                        GIMP_TOOL_RECTANGLE_DEAD);
+
+  return rectangle->private->function;
+}
+
+void
+gimp_tool_rectangle_set_function (GimpToolRectangle     *rectangle,
+                                  GimpRectangleFunction  function)
+{
+  GimpToolRectanglePrivate *private;
+
+  g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle));
+
+  private = rectangle->private;
+
+  if (private->function != function)
+    {
+      private->function = function;
+
+      gimp_tool_rectangle_changed (GIMP_TOOL_WIDGET (rectangle));
+    }
+}
+
+void
+gimp_tool_rectangle_set_constraint (GimpToolRectangle       *rectangle,
+                                    GimpRectangleConstraint  constraint)
+{
+  GimpToolRectanglePrivate *private;
+
+  g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle));
+
+  private = rectangle->private;
+
+  if (constraint != private->constraint)
+    {
+      g_object_freeze_notify (G_OBJECT (rectangle));
+
+      private->constraint = constraint;
+      g_object_notify (G_OBJECT (rectangle), "constraint");
+
+      gimp_tool_rectangle_clamp (rectangle, NULL, constraint, FALSE);
+
+      g_object_thaw_notify (G_OBJECT (rectangle));
+
+      gimp_tool_rectangle_change_complete (rectangle);
+    }
+}
+
+GimpRectangleConstraint
+gimp_tool_rectangle_get_constraint (GimpToolRectangle *rectangle)
+{
+  g_return_val_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle), 0);
+
+  return rectangle->private->constraint;
+}
+
+/**
+ * gimp_tool_rectangle_get_public_rect:
+ * @rectangle:
+ * @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).
+ **/
+void
+gimp_tool_rectangle_get_public_rect (GimpToolRectangle *rectangle,
+                                     gdouble           *pub_x1,
+                                     gdouble           *pub_y1,
+                                     gdouble           *pub_x2,
+                                     gdouble           *pub_y2)
+{
+  GimpToolRectanglePrivate *priv;
+
+  g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle));
+  g_return_if_fail (pub_x1 != NULL);
+  g_return_if_fail (pub_y1 != NULL);
+  g_return_if_fail (pub_x2 != NULL);
+  g_return_if_fail (pub_y2 != NULL);
+
+  priv = rectangle->private;
+
+  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_tool_rectangle_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_tool_rectangle_pending_size_set (GimpToolRectangle *rectangle,
+                                      GObject           *object,
+                                      const gchar       *width_property,
+                                      const gchar       *height_property)
+{
+  GimpToolRectanglePrivate *private;
+
+  g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle));
+  g_return_if_fail (width_property  != NULL);
+  g_return_if_fail (height_property != NULL);
+
+  private = rectangle->private;
+
+  g_object_set (object,
+                width_property,  MAX (private->x2 - private->x1, 1.0),
+                height_property, MAX (private->y2 - private->y1, 1.0),
+                NULL);
+}
+
+/**
+ * gimp_tool_rectangle_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_tool_rectangle_constraint_size_set (GimpToolRectangle *rectangle,
+                                         GObject           *object,
+                                         const gchar       *width_property,
+                                         const gchar       *height_property)
+{
+  GimpDisplayShell *shell;
+  GimpContext      *context;
+  GimpImage        *image;
+  gdouble           width;
+  gdouble           height;
+
+  g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle));
+
+  shell   = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle));
+  context = gimp_get_user_context (shell->display->gimp);
+  image   = gimp_context_get_image (context);
+
+  if (! image)
+    {
+      width  = 1.0;
+      height = 1.0;
+    }
+  else
+    {
+      GimpRectangleConstraint constraint;
+
+      constraint = gimp_tool_rectangle_get_constraint (rectangle);
+
+      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:
+          {
+            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_tool_rectangle_rectangle_is_new:
+ * @rectangle:
+ *
+ * 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_tool_rectangle_rectangle_is_new (GimpToolRectangle *rectangle)
+{
+  g_return_val_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle), FALSE);
+
+  return rectangle->private->is_new;
+}
+
+/**
+ * gimp_tool_rectangle_point_in_rectangle:
+ * @rectangle:
+ * @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_tool_rectangle_point_in_rectangle (GimpToolRectangle *rectangle,
+                                        gdouble            x,
+                                        gdouble            y)
+{
+  gdouble  x1, y1, x2, y2;
+
+  g_return_val_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle), FALSE);
+
+  gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2);
+
+  return (x >= x1 && x <= x2 &&
+          y >= y1 && y <= y2);
+}
+
+/**
+ * gimp_tool_rectangle_frame_item:
+ * @rectangle: a #GimpToolRectangle 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_tool_rectangle_frame_item (GimpToolRectangle *rectangle,
+                                GimpItem          *item)
+{
+  GimpDisplayShell *shell;
+  gint              offset_x;
+  gint              offset_y;
+  gint              width;
+  gint              height;
+
+  g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle));
+  g_return_if_fail (GIMP_IS_ITEM (item));
+  g_return_if_fail (gimp_item_is_attached (item));
+
+  shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle));
+
+  g_return_if_fail (gimp_display_get_image (shell->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_tool_rectangle_set_function (rectangle,
+                                    GIMP_TOOL_RECTANGLE_CREATING);
+
+  g_object_set (rectangle,
+                "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_tool_rectangle_set_constraint (rectangle,
+                                      GIMP_RECTANGLE_CONSTRAIN_NONE);
+}
+
+void
+gimp_tool_rectangle_auto_shrink (GimpToolRectangle *rectangle,
+                                 gboolean           shrink_merged)
+{
+  GimpToolRectanglePrivate *private;
+  GimpDisplayShell         *shell;
+  GimpImage                *image;
+  GimpPickable             *pickable;
+  gint                      offset_x = 0;
+  gint                      offset_y = 0;
+  gint                      x1, y1;
+  gint                      x2, y2;
+  gint                      shrunk_x;
+  gint                      shrunk_y;
+  gint                      shrunk_width;
+  gint                      shrunk_height;
+
+  g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle));
+
+  private = rectangle->private;
+
+  shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle));
+  image = gimp_display_get_image (shell->display);
+
+  if (shrink_merged)
+    {
+      pickable = GIMP_PICKABLE (image);
+
+      x1 = private->x1;
+      y1 = private->y1;
+      x2 = private->x2;
+      y2 = private->y2;
+    }
+  else
+    {
+      pickable = GIMP_PICKABLE (gimp_image_get_active_drawable (image));
+
+      if (! pickable)
+        return;
+
+      gimp_item_get_offset (GIMP_ITEM (pickable), &offset_x, &offset_y);
+
+      x1 = private->x1 - offset_x;
+      y1 = private->y1 - offset_y;
+      x2 = private->x2 - offset_x;
+      y2 = private->y2 - offset_y;
+    }
+
+  switch (gimp_pickable_auto_shrink (pickable,
+                                     x1, y1, x2 - x1, y2 - y1,
+                                     &shrunk_x,
+                                     &shrunk_y,
+                                     &shrunk_width,
+                                     &shrunk_height))
+    {
+    case GIMP_AUTO_SHRINK_SHRINK:
+      {
+        GimpRectangleFunction original_function = private->function;
+
+        private->function = GIMP_TOOL_RECTANGLE_AUTO_SHRINK;
+
+        private->x1 = offset_x + shrunk_x;
+        private->y1 = offset_y + shrunk_y;
+        private->x2 = offset_x + shrunk_x + shrunk_width;
+        private->y2 = offset_y + shrunk_y + shrunk_height;
+
+        gimp_tool_rectangle_update_int_rect (rectangle);
+
+        gimp_tool_rectangle_change_complete (rectangle);
+
+        private->function = original_function;
+
+        gimp_tool_rectangle_update_options (rectangle);
+      }
+      break;
+
+    default:
+      break;
+    }
+}
diff --git a/app/display/gimptoolrectangle.h b/app/display/gimptoolrectangle.h
new file mode 100644
index 0000000..b342984
--- /dev/null
+++ b/app/display/gimptoolrectangle.h
@@ -0,0 +1,122 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimptoolrectangle.h
+ * Copyright (C) 2017 Michael Natterer <mitch gimp org>
+ *
+ * Based on GimpRectangleTool
+ * 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/>.
+ */
+
+#ifndef __GIMP_TOOL_RECTANGLE_H__
+#define __GIMP_TOOL_RECTANGLE_H__
+
+
+#include "gimptoolwidget.h"
+
+
+typedef enum
+{
+  GIMP_TOOL_RECTANGLE_DEAD,
+  GIMP_TOOL_RECTANGLE_CREATING,
+  GIMP_TOOL_RECTANGLE_MOVING,
+  GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT,
+  GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT,
+  GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT,
+  GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT,
+  GIMP_TOOL_RECTANGLE_RESIZING_LEFT,
+  GIMP_TOOL_RECTANGLE_RESIZING_RIGHT,
+  GIMP_TOOL_RECTANGLE_RESIZING_TOP,
+  GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM,
+  GIMP_TOOL_RECTANGLE_AUTO_SHRINK,
+  GIMP_TOOL_RECTANGLE_EXECUTING,
+  GIMP_N_TOOL_RECTANGLE_FUNCTIONS
+} GimpRectangleFunction;
+
+
+#define GIMP_TYPE_TOOL_RECTANGLE            (gimp_tool_rectangle_get_type ())
+#define GIMP_TOOL_RECTANGLE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_RECTANGLE, 
GimpToolRectangle))
+#define GIMP_TOOL_RECTANGLE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_RECTANGLE, 
GimpToolRectangleClass))
+#define GIMP_IS_TOOL_RECTANGLE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_RECTANGLE))
+#define GIMP_IS_TOOL_RECTANGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_RECTANGLE))
+#define GIMP_TOOL_RECTANGLE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_RECTANGLE, 
GimpToolRectangleClass))
+
+
+typedef struct _GimpToolRectangle        GimpToolRectangle;
+typedef struct _GimpToolRectanglePrivate GimpToolRectanglePrivate;
+typedef struct _GimpToolRectangleClass   GimpToolRectangleClass;
+
+struct _GimpToolRectangle
+{
+  GimpToolWidget            parent_instance;
+
+  GimpToolRectanglePrivate *private;
+};
+
+struct _GimpToolRectangleClass
+{
+  GimpToolWidgetClass  parent_class;
+
+  /*  signals  */
+
+  gboolean (* change_complete) (GimpToolRectangle *rectangle);
+};
+
+
+GType    gimp_tool_rectangle_get_type         (void) G_GNUC_CONST;
+
+GimpToolWidget * gimp_tool_rectangle_new      (GimpDisplayShell       *shell);
+
+GimpRectangleFunction
+         gimp_tool_rectangle_get_function     (GimpToolRectangle      *rectangle);
+void     gimp_tool_rectangle_set_function     (GimpToolRectangle      *rectangle,
+                                               GimpRectangleFunction   function);
+
+void     gimp_tool_rectangle_set_constraint   (GimpToolRectangle      *rectangle,
+                                               GimpRectangleConstraint constraint);
+GimpRectangleConstraint
+         gimp_tool_rectangle_get_constraint   (GimpToolRectangle      *rectangle);
+
+void     gimp_tool_rectangle_get_public_rect  (GimpToolRectangle      *rectangle,
+                                               gdouble                *pub_x1,
+                                               gdouble                *pub_y1,
+                                               gdouble                *pub_x2,
+                                               gdouble                *pub_y2);
+
+void     gimp_tool_rectangle_pending_size_set (GimpToolRectangle      *rectangle,
+                                               GObject                *object,
+                                               const gchar            *width_property,
+                                               const gchar            *height_property);
+
+void     gimp_tool_rectangle_constraint_size_set
+                                              (GimpToolRectangle      *rectangle,
+                                               GObject                *object,
+                                               const gchar            *width_property,
+                                               const gchar            *height_property);
+
+gboolean gimp_tool_rectangle_rectangle_is_new (GimpToolRectangle      *rectangle);
+gboolean gimp_tool_rectangle_point_in_rectangle
+                                              (GimpToolRectangle      *rectangle,
+                                               gdouble                 x,
+                                               gdouble                 y);
+
+void     gimp_tool_rectangle_frame_item       (GimpToolRectangle      *rectangle,
+                                               GimpItem               *item);
+void     gimp_tool_rectangle_auto_shrink      (GimpToolRectangle      *rectrectangle,
+                                               gboolean                shrink_merged);
+
+
+#endif /* __GIMP_TOOL_RECTANGLE_H__ */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 5d713bf..8a29390 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -258,6 +258,7 @@ app/display/gimptoolcompass.c
 app/display/gimptoolhandlegrid.c
 app/display/gimptoolpath.c
 app/display/gimptoolpolygon.c
+app/display/gimptoolrectangle.c
 app/display/gimptooltransformgrid.c
 
 app/file/file-open.c


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]