[gimp/gimp-2-10] app: add GimpTransformGridTool; derive most transform tools from it



commit 340c4a2309e1abf47efd874e9528594e321095c8
Author: Ell <ell_se yahoo com>
Date:   Sat Jun 9 16:25:03 2018 -0400

    app: add GimpTransformGridTool; derive most transform tools from it
    
    While most of our transform tools use an interactive transform
    grid, and have similar behavior, the flip tool is an odd one out.
    The new "auto straighten" function of the measure tool introduces
    another tool that performs transformations, while not behaving like
    the rest of the transform tools.
    
    Factor out the parts of GimpTransformTool that handle user
    interaction into GimpTransformGridTool (with corresponding
    GimpTransformGridOptions, and GimpTransformGridToolUndo), and only
    leave the basic transform functionality and options in
    GimpTransformTool (and GimpTransformOptions).
    
    Derive all the transform tools (and transform-tool base classes)
    that previously derived from GimpTransformTool, from
    GimpTransformGridTool.  The one exception is GimpFlipTool, which
    still derives from GimpTransformTool directly.  The next commit
    will derive GimpMeasureTool from GimpTransformTool as well.

 app/core/core-enums.c                  |    4 +-
 app/core/core-enums.h                  |    2 +-
 app/tools/Makefile.am                  |    8 +-
 app/tools/gimpflipoptions.c            |   20 +-
 app/tools/gimpfliptool.c               |  137 +--
 app/tools/gimpgenerictransformtool.c   |   47 +-
 app/tools/gimpgenerictransformtool.h   |   16 +-
 app/tools/gimphandletransformoptions.c |    4 +-
 app/tools/gimphandletransformoptions.h |    6 +-
 app/tools/gimphandletransformtool.c    |  248 +++---
 app/tools/gimpperspectivetool.c        |  133 +--
 app/tools/gimprotatetool.c             |  213 ++---
 app/tools/gimprotatetool.h             |   12 +-
 app/tools/gimpscaletool.c              |  186 ++--
 app/tools/gimpscaletool.h              |    8 +-
 app/tools/gimpsheartool.c              |  217 ++---
 app/tools/gimpsheartool.h              |   10 +-
 app/tools/gimptransformgridoptions.c   |  548 ++++++++++++
 app/tools/gimptransformgridoptions.h   |   69 ++
 app/tools/gimptransformgridtool.c      | 1215 ++++++++++++++++++++++++++
 app/tools/gimptransformgridtool.h      |  103 +++
 app/tools/gimptransformgridtoolundo.c  |  220 +++++
 app/tools/gimptransformgridtoolundo.h  |   56 ++
 app/tools/gimptransformoptions.c       |  448 +---------
 app/tools/gimptransformoptions.h       |   33 +-
 app/tools/gimptransformtool.c          | 1494 +++++---------------------------
 app/tools/gimptransformtool.h          |   82 +-
 app/tools/gimptransformtoolundo.c      |  220 -----
 app/tools/gimptransformtoolundo.h      |   56 --
 app/tools/gimpunifiedtransformtool.c   |  145 ++--
 app/tools/tools-types.h                |    1 +
 po/POTFILES.in                         |    2 +
 32 files changed, 3237 insertions(+), 2726 deletions(-)
---
diff --git a/app/core/core-enums.c b/app/core/core-enums.c
index ce5781d468..75137ab66c 100644
--- a/app/core/core-enums.c
+++ b/app/core/core-enums.c
@@ -848,7 +848,7 @@ gimp_undo_type_get_type (void)
     { GIMP_UNDO_VECTORS_REMOVE, "GIMP_UNDO_VECTORS_REMOVE", "vectors-remove" },
     { GIMP_UNDO_VECTORS_MOD, "GIMP_UNDO_VECTORS_MOD", "vectors-mod" },
     { GIMP_UNDO_FS_TO_LAYER, "GIMP_UNDO_FS_TO_LAYER", "fs-to-layer" },
-    { GIMP_UNDO_TRANSFORM, "GIMP_UNDO_TRANSFORM", "transform" },
+    { GIMP_UNDO_TRANSFORM_GRID, "GIMP_UNDO_TRANSFORM_GRID", "transform-grid" },
     { GIMP_UNDO_PAINT, "GIMP_UNDO_PAINT", "paint" },
     { GIMP_UNDO_INK, "GIMP_UNDO_INK", "ink" },
     { GIMP_UNDO_FOREGROUND_SELECT, "GIMP_UNDO_FOREGROUND_SELECT", "foreground-select" },
@@ -946,7 +946,7 @@ gimp_undo_type_get_type (void)
     { GIMP_UNDO_VECTORS_REMOVE, NC_("undo-type", "Delete path"), NULL },
     { GIMP_UNDO_VECTORS_MOD, NC_("undo-type", "Path modification"), NULL },
     { GIMP_UNDO_FS_TO_LAYER, NC_("undo-type", "Floating selection to layer"), NULL },
-    { GIMP_UNDO_TRANSFORM, NC_("undo-type", "Transform"), NULL },
+    { GIMP_UNDO_TRANSFORM_GRID, NC_("undo-type", "Transform grid"), NULL },
     { GIMP_UNDO_PAINT, NC_("undo-type", "Paint"), NULL },
     { GIMP_UNDO_INK, NC_("undo-type", "Ink"), NULL },
     { GIMP_UNDO_FOREGROUND_SELECT, NC_("undo-type", "Select foreground"), NULL },
diff --git a/app/core/core-enums.h b/app/core/core-enums.h
index 5b6a659068..68de446a1b 100644
--- a/app/core/core-enums.h
+++ b/app/core/core-enums.h
@@ -444,7 +444,7 @@ typedef enum /*< pdb-skip >*/
   GIMP_UNDO_VECTORS_REMOVE,              /*< desc="Delete path"                    >*/
   GIMP_UNDO_VECTORS_MOD,                 /*< desc="Path modification"              >*/
   GIMP_UNDO_FS_TO_LAYER,                 /*< desc="Floating selection to layer"    >*/
-  GIMP_UNDO_TRANSFORM,                   /*< desc="Transform"                      >*/
+  GIMP_UNDO_TRANSFORM_GRID,              /*< desc="Transform grid"                 >*/
   GIMP_UNDO_PAINT,                       /*< desc="Paint"                          >*/
   GIMP_UNDO_INK,                         /*< desc="Ink"                            >*/
   GIMP_UNDO_FOREGROUND_SELECT,           /*< desc="Select foreground"              >*/
diff --git a/app/tools/Makefile.am b/app/tools/Makefile.am
index 46770cd7bd..23ce3b288b 100644
--- a/app/tools/Makefile.am
+++ b/app/tools/Makefile.am
@@ -206,12 +206,16 @@ libapptools_a_sources = \
        gimptoolcontrol.h               \
        gimptooloptions-gui.c           \
        gimptooloptions-gui.h           \
+       gimptransformgridoptions.c      \
+       gimptransformgridoptions.h      \
+       gimptransformgridtool.c         \
+       gimptransformgridtool.h         \
+       gimptransformgridtoolundo.c     \
+       gimptransformgridtoolundo.h     \
        gimptransformoptions.c          \
        gimptransformoptions.h          \
        gimptransformtool.c             \
        gimptransformtool.h             \
-       gimptransformtoolundo.c         \
-       gimptransformtoolundo.h         \
        gimpunifiedtransformtool.c      \
        gimpunifiedtransformtool.h      \
        gimpvectoroptions.c             \
diff --git a/app/tools/gimpflipoptions.c b/app/tools/gimpflipoptions.c
index 8a80f5b5c9..b9910bd281 100644
--- a/app/tools/gimpflipoptions.c
+++ b/app/tools/gimpflipoptions.c
@@ -28,7 +28,6 @@
 #include "widgets/gimpwidgets-utils.h"
 
 #include "gimpflipoptions.h"
-#include "gimptooloptions-gui.h"
 
 #include "gimp-intl.h"
 
@@ -120,29 +119,16 @@ gimp_flip_options_gui (GimpToolOptions *tool_options)
   GObject              *config     = G_OBJECT (tool_options);
   GimpFlipOptions      *options    = GIMP_FLIP_OPTIONS (tool_options);
   GimpTransformOptions *tr_options = GIMP_TRANSFORM_OPTIONS (tool_options);
-  GtkWidget            *vbox       = gimp_tool_options_gui (tool_options);
-  GtkWidget            *hbox;
-  GtkWidget            *box;
-  GtkWidget            *label;
+  GtkWidget            *vbox;
   GtkWidget            *frame;
   GtkWidget            *combo;
   gchar                *str;
   GtkListStore         *clip_model;
   GdkModifierType       toggle_mask;
 
-  toggle_mask = gimp_get_toggle_behavior_mask ();
-
-  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
-  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
-  gtk_widget_show (hbox);
+  vbox = gimp_transform_options_gui (tool_options, FALSE, FALSE, FALSE);
 
-  label = gtk_label_new (_("Transform:"));
-  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-  gtk_widget_show (label);
-
-  box = gimp_prop_enum_icon_box_new (config, "type", "gimp", 0, 0);
-  gtk_box_pack_start (GTK_BOX (hbox), box, FALSE, FALSE, 0);
-  gtk_widget_show (box);
+  toggle_mask = gimp_get_toggle_behavior_mask ();
 
   /*  tool toggle  */
   str = g_strdup_printf (_("Direction  (%s)"),
diff --git a/app/tools/gimpfliptool.c b/app/tools/gimpfliptool.c
index 4eb52f791a..db5e5820af 100644
--- a/app/tools/gimpfliptool.c
+++ b/app/tools/gimpfliptool.c
@@ -54,37 +54,38 @@
 
 /*  local function prototypes  */
 
-static GimpDisplay         * gimp_flip_tool_has_image     (GimpTool           *tool,
-                                                           GimpImage          *image);
-static gboolean              gimp_flip_tool_initialize    (GimpTool           *tool,
-                                                           GimpDisplay        *display,
-                                                           GError            **error);
-static void                  gimp_flip_tool_modifier_key  (GimpTool           *tool,
-                                                           GdkModifierType     key,
-                                                           gboolean            press,
-                                                           GdkModifierType     state,
-                                                           GimpDisplay        *display);
-static void                  gimp_flip_tool_oper_update   (GimpTool           *tool,
-                                                           const GimpCoords   *coords,
-                                                           GdkModifierType     state,
-                                                           gboolean            proximity,
-                                                           GimpDisplay        *display);
-static void                  gimp_flip_tool_cursor_update (GimpTool           *tool,
-                                                           const GimpCoords   *coords,
-                                                           GdkModifierType     state,
-                                                           GimpDisplay        *display);
-
-static void                  gimp_flip_tool_draw          (GimpDrawTool       *draw_tool);
-
-static gchar               * gimp_flip_tool_get_undo_desc (GimpTransformTool  *tool);
-static GeglBuffer          * gimp_flip_tool_transform     (GimpTransformTool  *tool,
-                                                           GimpItem           *item,
-                                                           GeglBuffer         *orig_buffer,
-                                                           gint                orig_offset_x,
-                                                           gint                orig_offset_y,
-                                                           GimpColorProfile  **buffer_profile,
-                                                           gint               *new_offset_x,
-                                                           gint               *new_offset_y);
+static void         gimp_flip_tool_button_press  (GimpTool             *tool,
+                                                  const GimpCoords     *coords,
+                                                  guint32               time,
+                                                  GdkModifierType       state,
+                                                  GimpButtonPressType   press_type,
+                                                  GimpDisplay          *display);
+static void         gimp_flip_tool_modifier_key  (GimpTool             *tool,
+                                                  GdkModifierType       key,
+                                                  gboolean              press,
+                                                  GdkModifierType       state,
+                                                  GimpDisplay          *display);
+static void         gimp_flip_tool_oper_update   (GimpTool             *tool,
+                                                  const GimpCoords     *coords,
+                                                  GdkModifierType       state,
+                                                  gboolean              proximity,
+                                                  GimpDisplay          *display);
+static void         gimp_flip_tool_cursor_update (GimpTool             *tool,
+                                                  const GimpCoords     *coords,
+                                                  GdkModifierType       state,
+                                                  GimpDisplay          *display);
+
+static void         gimp_flip_tool_draw          (GimpDrawTool         *draw_tool);
+
+static gchar      * gimp_flip_tool_get_undo_desc (GimpTransformTool    *tr_tool);
+static GeglBuffer * gimp_flip_tool_transform     (GimpTransformTool    *tr_tool,
+                                                  GimpItem             *item,
+                                                  GeglBuffer           *orig_buffer,
+                                                  gint                  orig_offset_x,
+                                                  gint                  orig_offset_y,
+                                                  GimpColorProfile    **buffer_profile,
+                                                  gint                 *new_offset_x,
+                                                  gint                 *new_offset_y);
 
 static GimpOrientationType   gimp_flip_tool_get_flip_type  (GimpFlipTool      *flip);
 
@@ -117,20 +118,19 @@ gimp_flip_tool_class_init (GimpFlipToolClass *klass)
 {
   GimpToolClass          *tool_class      = GIMP_TOOL_CLASS (klass);
   GimpDrawToolClass      *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass);
-  GimpTransformToolClass *trans_class     = GIMP_TRANSFORM_TOOL_CLASS (klass);
+  GimpTransformToolClass *tr_class        = GIMP_TRANSFORM_TOOL_CLASS (klass);
 
-  tool_class->has_image      = gimp_flip_tool_has_image;
-  tool_class->initialize     = gimp_flip_tool_initialize;
-  tool_class->modifier_key   = gimp_flip_tool_modifier_key;
-  tool_class->oper_update    = gimp_flip_tool_oper_update;
-  tool_class->cursor_update  = gimp_flip_tool_cursor_update;
+  tool_class->button_press  = gimp_flip_tool_button_press;
+  tool_class->modifier_key  = gimp_flip_tool_modifier_key;
+  tool_class->oper_update   = gimp_flip_tool_oper_update;
+  tool_class->cursor_update = gimp_flip_tool_cursor_update;
 
-  draw_tool_class->draw      = gimp_flip_tool_draw;
+  draw_tool_class->draw     = gimp_flip_tool_draw;
 
-  trans_class->get_undo_desc = gimp_flip_tool_get_undo_desc;
-  trans_class->transform     = gimp_flip_tool_transform;
+  tr_class->get_undo_desc   = gimp_flip_tool_get_undo_desc;
+  tr_class->transform       = gimp_flip_tool_transform;
 
-  trans_class->ok_button_label = _("_Flip");
+  tr_class->progress_text   = _("Flipping");
 }
 
 static void
@@ -151,26 +151,17 @@ gimp_flip_tool_init (GimpFlipTool *flip_tool)
   flip_tool->guide = NULL;
 }
 
-static GimpDisplay *
-gimp_flip_tool_has_image (GimpTool  *tool,
-                          GimpImage *image)
-{
-  /* avoid committing, and hence flipping, when changing tools */
-  return NULL;
-}
-
-static gboolean
-gimp_flip_tool_initialize (GimpTool     *tool,
-                           GimpDisplay  *display,
-                           GError      **error)
+static void
+gimp_flip_tool_button_press (GimpTool            *tool,
+                             const GimpCoords    *coords,
+                             guint32              time,
+                             GdkModifierType      state,
+                             GimpButtonPressType  press_type,
+                             GimpDisplay         *display)
 {
-  GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (tool);
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
 
-  /* let GimpTransformTool take control over the draw tool while it's active */
-  if (gimp_draw_tool_is_active (draw_tool))
-    gimp_draw_tool_stop (draw_tool);
-
-  return GIMP_TOOL_CLASS (parent_class)->initialize (tool, display, error);
+  gimp_transform_tool_transform (tr_tool, display);
 }
 
 static void
@@ -258,7 +249,17 @@ gimp_flip_tool_cursor_update (GimpTool         *tool,
                               GdkModifierType   state,
                               GimpDisplay      *display)
 {
-  GimpFlipTool *flip = GIMP_FLIP_TOOL (tool);
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
+  GimpFlipTool      *flip    = GIMP_FLIP_TOOL (tool);
+
+  if (! gimp_transform_tool_get_active_item (tr_tool, display, TRUE, NULL))
+    {
+      gimp_tool_set_cursor (tool, display,
+                            gimp_tool_control_get_cursor (tool->control),
+                            gimp_tool_control_get_tool_cursor (tool->control),
+                            GIMP_CURSOR_MODIFIER_BAD);
+      return;
+    }
 
   gimp_tool_control_set_toggled (tool->control,
                                  gimp_flip_tool_get_flip_type (flip) ==
@@ -309,7 +310,7 @@ gimp_flip_tool_get_undo_desc (GimpTransformTool *tr_tool)
 }
 
 static GeglBuffer *
-gimp_flip_tool_transform (GimpTransformTool *trans_tool,
+gimp_flip_tool_transform (GimpTransformTool *tr_tool,
                           GimpItem          *active_item,
                           GeglBuffer        *orig_buffer,
                           gint               orig_offset_x,
@@ -318,9 +319,9 @@ gimp_flip_tool_transform (GimpTransformTool *trans_tool,
                           gint              *new_offset_x,
                           gint              *new_offset_y)
 {
-  GimpFlipTool         *flip        = GIMP_FLIP_TOOL (trans_tool);
-  GimpFlipOptions      *options     = GIMP_FLIP_TOOL_GET_OPTIONS (trans_tool);
-  GimpTransformOptions *tr_options  = GIMP_TRANSFORM_TOOL_GET_OPTIONS (trans_tool);
+  GimpFlipTool         *flip        = GIMP_FLIP_TOOL (tr_tool);
+  GimpFlipOptions      *options     = GIMP_FLIP_TOOL_GET_OPTIONS (tr_tool);
+  GimpTransformOptions *tr_options  = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
   GimpContext          *context     = GIMP_CONTEXT (options);
   GimpOrientationType   flip_type   = GIMP_ORIENTATION_UNKNOWN;
   gdouble               axis        = 0.0;
@@ -338,13 +339,13 @@ gimp_flip_tool_transform (GimpTransformTool *trans_tool,
       switch (flip_type)
         {
         case GIMP_ORIENTATION_HORIZONTAL:
-          axis = ((gdouble) trans_tool->x1 +
-                  (gdouble) (trans_tool->x2 - trans_tool->x1) / 2.0);
+          axis = ((gdouble) tr_tool->x1 +
+                  (gdouble) (tr_tool->x2 - tr_tool->x1) / 2.0);
           break;
 
         case GIMP_ORIENTATION_VERTICAL:
-          axis = ((gdouble) trans_tool->y1 +
-                  (gdouble) (trans_tool->y2 - trans_tool->y1) / 2.0);
+          axis = ((gdouble) tr_tool->y1 +
+                  (gdouble) (tr_tool->y2 - tr_tool->y1) / 2.0);
           break;
 
         default:
diff --git a/app/tools/gimpgenerictransformtool.c b/app/tools/gimpgenerictransformtool.c
index 1eceb86c26..f05a09495c 100644
--- a/app/tools/gimpgenerictransformtool.c
+++ b/app/tools/gimpgenerictransformtool.c
@@ -35,33 +35,33 @@
 
 #include "gimpgenerictransformtool.h"
 #include "gimptoolcontrol.h"
-#include "gimptransformoptions.h"
+#include "gimptransformgridoptions.h"
 
 #include "gimp-intl.h"
 
 
 /*  local function prototypes  */
 
-static void   gimp_generic_transform_tool_dialog        (GimpTransformTool *tr_tool);
-static void   gimp_generic_transform_tool_dialog_update (GimpTransformTool *tr_tool);
-static void   gimp_generic_transform_tool_prepare       (GimpTransformTool *tr_tool);
-static void   gimp_generic_transform_tool_recalc_matrix (GimpTransformTool *tr_tool,
-                                                         GimpToolWidget    *widget);
+static void   gimp_generic_transform_tool_dialog        (GimpTransformGridTool *tg_tool);
+static void   gimp_generic_transform_tool_dialog_update (GimpTransformGridTool *tg_tool);
+static void   gimp_generic_transform_tool_prepare       (GimpTransformGridTool *tg_tool);
+static void   gimp_generic_transform_tool_recalc_matrix (GimpTransformGridTool *tg_tool,
+                                                         GimpToolWidget        *widget);
 
 
 G_DEFINE_TYPE (GimpGenericTransformTool, gimp_generic_transform_tool,
-               GIMP_TYPE_TRANSFORM_TOOL)
+               GIMP_TYPE_TRANSFORM_GRID_TOOL)
 
 
 static void
 gimp_generic_transform_tool_class_init (GimpGenericTransformToolClass *klass)
 {
-  GimpTransformToolClass *trans_class = GIMP_TRANSFORM_TOOL_CLASS (klass);
+  GimpTransformGridToolClass *tg_class = GIMP_TRANSFORM_GRID_TOOL_CLASS (klass);
 
-  trans_class->dialog        = gimp_generic_transform_tool_dialog;
-  trans_class->dialog_update = gimp_generic_transform_tool_dialog_update;
-  trans_class->prepare       = gimp_generic_transform_tool_prepare;
-  trans_class->recalc_matrix = gimp_generic_transform_tool_recalc_matrix;
+  tg_class->dialog        = gimp_generic_transform_tool_dialog;
+  tg_class->dialog_update = gimp_generic_transform_tool_dialog_update;
+  tg_class->prepare       = gimp_generic_transform_tool_prepare;
+  tg_class->recalc_matrix = gimp_generic_transform_tool_recalc_matrix;
 }
 
 static void
@@ -70,9 +70,9 @@ gimp_generic_transform_tool_init (GimpGenericTransformTool *unified_tool)
 }
 
 static void
-gimp_generic_transform_tool_dialog (GimpTransformTool *tr_tool)
+gimp_generic_transform_tool_dialog (GimpTransformGridTool *tg_tool)
 {
-  GimpGenericTransformTool *generic = GIMP_GENERIC_TRANSFORM_TOOL (tr_tool);
+  GimpGenericTransformTool *generic = GIMP_GENERIC_TRANSFORM_TOOL (tg_tool);
   GtkWidget                *frame;
   GtkWidget                *vbox;
   GtkWidget                *table;
@@ -81,7 +81,7 @@ gimp_generic_transform_tool_dialog (GimpTransformTool *tr_tool)
   gint                      x, y;
 
   frame = gimp_frame_new (_("Transform Matrix"));
-  gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (tr_tool->gui)), frame,
+  gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (tg_tool->gui)), frame,
                       FALSE, FALSE, 0);
   gtk_widget_show (frame);
 
@@ -122,9 +122,10 @@ gimp_generic_transform_tool_dialog (GimpTransformTool *tr_tool)
 }
 
 static void
-gimp_generic_transform_tool_dialog_update (GimpTransformTool *tr_tool)
+gimp_generic_transform_tool_dialog_update (GimpTransformGridTool *tg_tool)
 {
-  GimpGenericTransformTool *generic = GIMP_GENERIC_TRANSFORM_TOOL (tr_tool);
+  GimpTransformTool        *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpGenericTransformTool *generic = GIMP_GENERIC_TRANSFORM_TOOL (tg_tool);
 
   if (tr_tool->transform_valid)
     {
@@ -154,9 +155,10 @@ gimp_generic_transform_tool_dialog_update (GimpTransformTool *tr_tool)
 }
 
 static void
-gimp_generic_transform_tool_prepare (GimpTransformTool *tr_tool)
+gimp_generic_transform_tool_prepare (GimpTransformGridTool *tg_tool)
 {
-  GimpGenericTransformTool *generic = GIMP_GENERIC_TRANSFORM_TOOL (tr_tool);
+  GimpTransformTool        *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpGenericTransformTool *generic = GIMP_GENERIC_TRANSFORM_TOOL (tg_tool);
 
   generic->input_points[0] = (GimpVector2) {tr_tool->x1, tr_tool->y1};
   generic->input_points[1] = (GimpVector2) {tr_tool->x2, tr_tool->y1};
@@ -168,10 +170,11 @@ gimp_generic_transform_tool_prepare (GimpTransformTool *tr_tool)
 }
 
 static void
-gimp_generic_transform_tool_recalc_matrix (GimpTransformTool *tr_tool,
-                                           GimpToolWidget    *widget)
+gimp_generic_transform_tool_recalc_matrix (GimpTransformGridTool *tg_tool,
+                                           GimpToolWidget        *widget)
 {
-  GimpGenericTransformTool *generic = GIMP_GENERIC_TRANSFORM_TOOL (tr_tool);
+  GimpTransformTool        *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpGenericTransformTool *generic = GIMP_GENERIC_TRANSFORM_TOOL (tg_tool);
 
   if (GIMP_GENERIC_TRANSFORM_TOOL_GET_CLASS (generic)->recalc_points)
     {
diff --git a/app/tools/gimpgenerictransformtool.h b/app/tools/gimpgenerictransformtool.h
index 6160d4d2d1..b6937ffa8e 100644
--- a/app/tools/gimpgenerictransformtool.h
+++ b/app/tools/gimpgenerictransformtool.h
@@ -19,7 +19,7 @@
 #define __GIMP_GENERIC_TRANSFORM_TOOL_H__
 
 
-#include "gimptransformtool.h"
+#include "gimptransformgridtool.h"
 
 
 #define GIMP_TYPE_GENERIC_TRANSFORM_TOOL            (gimp_generic_transform_tool_get_type ())
@@ -34,19 +34,19 @@ typedef struct _GimpGenericTransformToolClass GimpGenericTransformToolClass;
 
 struct _GimpGenericTransformTool
 {
-  GimpTransformTool  parent_instance;
+  GimpTransformGridTool  parent_instance;
 
-  GimpVector2        input_points[4];
-  GimpVector2        output_points[4];
+  GimpVector2            input_points[4];
+  GimpVector2            output_points[4];
 
-  GtkWidget         *matrix_table;
-  GtkWidget         *matrix_labels[3][3];
-  GtkWidget         *invalid_label;
+  GtkWidget             *matrix_table;
+  GtkWidget             *matrix_labels[3][3];
+  GtkWidget             *invalid_label;
 };
 
 struct _GimpGenericTransformToolClass
 {
-  GimpTransformToolClass  parent_class;
+  GimpTransformGridToolClass  parent_class;
 
   /*  virtual functions  */
   void   (* recalc_points) (GimpGenericTransformTool *generic,
diff --git a/app/tools/gimphandletransformoptions.c b/app/tools/gimphandletransformoptions.c
index 2076e1c1d0..fcb6bf5e6e 100644
--- a/app/tools/gimphandletransformoptions.c
+++ b/app/tools/gimphandletransformoptions.c
@@ -55,7 +55,7 @@ static void   gimp_handle_transform_options_get_property (GObject         *objec
 
 
 G_DEFINE_TYPE (GimpHandleTransformOptions, gimp_handle_transform_options,
-               GIMP_TYPE_TRANSFORM_OPTIONS)
+               GIMP_TYPE_TRANSFORM_GRID_OPTIONS)
 
 #define parent_class gimp_handle_transform_options_parent_class
 
@@ -134,7 +134,7 @@ GtkWidget *
 gimp_handle_transform_options_gui (GimpToolOptions *tool_options)
 {
   GObject   *config = G_OBJECT (tool_options);
-  GtkWidget *vbox   = gimp_transform_options_gui (tool_options);
+  GtkWidget *vbox   = gimp_transform_grid_options_gui (tool_options);
   GtkWidget *frame;
   GtkWidget *button;
   gint       i;
diff --git a/app/tools/gimphandletransformoptions.h b/app/tools/gimphandletransformoptions.h
index 34077404c9..9432ef8d27 100644
--- a/app/tools/gimphandletransformoptions.h
+++ b/app/tools/gimphandletransformoptions.h
@@ -19,7 +19,7 @@
 #define __GIMP_HANDLE_TRANSFORM_OPTIONS_H__
 
 
-#include "gimptransformoptions.h"
+#include "gimptransformgridoptions.h"
 
 
 #define GIMP_TYPE_HANDLE_TRANSFORM_OPTIONS            (gimp_handle_transform_options_get_type ())
@@ -35,14 +35,14 @@ typedef struct _GimpHandleTransformOptionsClass GimpHandleTransformOptionsClass;
 
 struct _GimpHandleTransformOptions
 {
-  GimpTransformOptions      parent_instance;
+  GimpTransformGridOptions  parent_instance;
 
   GimpTransformHandleMode   handle_mode;
 };
 
 struct _GimpHandleTransformOptionsClass
 {
-  GimpTransformOptionsClass  parent_class;
+  GimpTransformGridOptionsClass  parent_class;
 };
 
 
diff --git a/app/tools/gimphandletransformtool.c b/app/tools/gimphandletransformtool.c
index 7e4a843e0e..b3491b9d01 100644
--- a/app/tools/gimphandletransformtool.c
+++ b/app/tools/gimphandletransformtool.c
@@ -83,18 +83,19 @@ static void   gimp_handle_transform_tool_modifier_key   (GimpTool
                                                          GdkModifierType           state,
                                                          GimpDisplay              *display);
 
-static void   gimp_handle_transform_tool_prepare        (GimpTransformTool        *tr_tool);
+static gchar *gimp_handle_transform_tool_get_undo_desc  (GimpTransformTool        *tr_tool);
+
+static void   gimp_handle_transform_tool_prepare        (GimpTransformGridTool    *tg_tool);
 static GimpToolWidget *
-              gimp_handle_transform_tool_get_widget     (GimpTransformTool        *tr_tool);
-static void   gimp_handle_transform_tool_recalc_matrix  (GimpTransformTool        *tr_tool,
+              gimp_handle_transform_tool_get_widget     (GimpTransformGridTool    *tg_tool);
+static void   gimp_handle_transform_tool_recalc_matrix  (GimpTransformGridTool    *tg_tool,
                                                          GimpToolWidget           *widget);
-static gchar *gimp_handle_transform_tool_get_undo_desc  (GimpTransformTool        *tr_tool);
 
 static void   gimp_handle_transform_tool_recalc_points  (GimpGenericTransformTool *generic,
                                                          GimpToolWidget           *widget);
 
 static void   gimp_handle_transform_tool_widget_changed (GimpToolWidget           *widget,
-                                                         GimpTransformTool        *tr_tool);
+                                                         GimpTransformGridTool    *tg_tool);
 
 
 G_DEFINE_TYPE (GimpHandleTransformTool, gimp_handle_transform_tool,
@@ -125,26 +126,26 @@ static void
 gimp_handle_transform_tool_class_init (GimpHandleTransformToolClass *klass)
 {
   GimpToolClass                 *tool_class    = GIMP_TOOL_CLASS (klass);
-  GimpTransformToolClass        *trans_class   = GIMP_TRANSFORM_TOOL_CLASS (klass);
+  GimpTransformToolClass        *tr_class   = GIMP_TRANSFORM_TOOL_CLASS (klass);
+  GimpTransformGridToolClass    *tg_class   = GIMP_TRANSFORM_GRID_TOOL_CLASS (klass);
   GimpGenericTransformToolClass *generic_class = GIMP_GENERIC_TRANSFORM_TOOL_CLASS (klass);
 
   tool_class->modifier_key     = gimp_handle_transform_tool_modifier_key;
 
-  trans_class->prepare         = gimp_handle_transform_tool_prepare;
-  trans_class->get_widget      = gimp_handle_transform_tool_get_widget;
-  trans_class->recalc_matrix   = gimp_handle_transform_tool_recalc_matrix;
-  trans_class->get_undo_desc   = gimp_handle_transform_tool_get_undo_desc;
+  tr_class->get_undo_desc      = gimp_handle_transform_tool_get_undo_desc;
+
+  tg_class->prepare            = gimp_handle_transform_tool_prepare;
+  tg_class->get_widget         = gimp_handle_transform_tool_get_widget;
+  tg_class->recalc_matrix      = gimp_handle_transform_tool_recalc_matrix;
 
   generic_class->recalc_points = gimp_handle_transform_tool_recalc_points;
+
+  tr_class->progress_text      = _("Handle transformation");
 }
 
 static void
 gimp_handle_transform_tool_init (GimpHandleTransformTool *ht_tool)
 {
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (ht_tool);
-
-  tr_tool->progress_text = _("Handle transformation");
-
   ht_tool->saved_handle_mode = GIMP_HANDLE_MODE_ADD_TRANSFORM;
 }
 
@@ -202,39 +203,48 @@ gimp_handle_transform_tool_modifier_key (GimpTool        *tool,
                                                 state, display);
 }
 
+static gchar *
+gimp_handle_transform_tool_get_undo_desc (GimpTransformTool *tr_tool)
+{
+  return g_strdup (C_("undo-type", "Handle transform"));
+}
+
 static void
-gimp_handle_transform_tool_prepare (GimpTransformTool *tr_tool)
+gimp_handle_transform_tool_prepare (GimpTransformGridTool *tg_tool)
 {
-  GIMP_TRANSFORM_TOOL_CLASS (parent_class)->prepare (tr_tool);
-
-  tr_tool->trans_info[X0]        = (gdouble) tr_tool->x1;
-  tr_tool->trans_info[Y0]        = (gdouble) tr_tool->y1;
-  tr_tool->trans_info[X1]        = (gdouble) tr_tool->x2;
-  tr_tool->trans_info[Y1]        = (gdouble) tr_tool->y1;
-  tr_tool->trans_info[X2]        = (gdouble) tr_tool->x1;
-  tr_tool->trans_info[Y2]        = (gdouble) tr_tool->y2;
-  tr_tool->trans_info[X3]        = (gdouble) tr_tool->x2;
-  tr_tool->trans_info[Y3]        = (gdouble) tr_tool->y2;
-  tr_tool->trans_info[OX0]       = (gdouble) tr_tool->x1;
-  tr_tool->trans_info[OY0]       = (gdouble) tr_tool->y1;
-  tr_tool->trans_info[OX1]       = (gdouble) tr_tool->x2;
-  tr_tool->trans_info[OY1]       = (gdouble) tr_tool->y1;
-  tr_tool->trans_info[OX2]       = (gdouble) tr_tool->x1;
-  tr_tool->trans_info[OY2]       = (gdouble) tr_tool->y2;
-  tr_tool->trans_info[OX3]       = (gdouble) tr_tool->x2;
-  tr_tool->trans_info[OY3]       = (gdouble) tr_tool->y2;
-  tr_tool->trans_info[N_HANDLES] = 0;
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+
+  GIMP_TRANSFORM_GRID_TOOL_CLASS (parent_class)->prepare (tg_tool);
+
+  tg_tool->trans_info[X0]        = (gdouble) tr_tool->x1;
+  tg_tool->trans_info[Y0]        = (gdouble) tr_tool->y1;
+  tg_tool->trans_info[X1]        = (gdouble) tr_tool->x2;
+  tg_tool->trans_info[Y1]        = (gdouble) tr_tool->y1;
+  tg_tool->trans_info[X2]        = (gdouble) tr_tool->x1;
+  tg_tool->trans_info[Y2]        = (gdouble) tr_tool->y2;
+  tg_tool->trans_info[X3]        = (gdouble) tr_tool->x2;
+  tg_tool->trans_info[Y3]        = (gdouble) tr_tool->y2;
+  tg_tool->trans_info[OX0]       = (gdouble) tr_tool->x1;
+  tg_tool->trans_info[OY0]       = (gdouble) tr_tool->y1;
+  tg_tool->trans_info[OX1]       = (gdouble) tr_tool->x2;
+  tg_tool->trans_info[OY1]       = (gdouble) tr_tool->y1;
+  tg_tool->trans_info[OX2]       = (gdouble) tr_tool->x1;
+  tg_tool->trans_info[OY2]       = (gdouble) tr_tool->y2;
+  tg_tool->trans_info[OX3]       = (gdouble) tr_tool->x2;
+  tg_tool->trans_info[OY3]       = (gdouble) tr_tool->y2;
+  tg_tool->trans_info[N_HANDLES] = 0;
 }
 
 static GimpToolWidget *
-gimp_handle_transform_tool_get_widget (GimpTransformTool *tr_tool)
+gimp_handle_transform_tool_get_widget (GimpTransformGridTool *tg_tool)
 {
-  GimpTool                   *tool  = GIMP_TOOL (tr_tool);
+  GimpTool                   *tool    = GIMP_TOOL (tg_tool);
+  GimpTransformTool          *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
   GimpHandleTransformOptions *options;
-  GimpDisplayShell           *shell = gimp_display_get_shell (tool->display);
+  GimpDisplayShell           *shell   = gimp_display_get_shell (tool->display);
   GimpToolWidget             *widget;
 
-  options = GIMP_HANDLE_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
+  options = GIMP_HANDLE_TRANSFORM_TOOL_GET_OPTIONS (tg_tool);
 
   widget = gimp_tool_handle_grid_new (shell,
                                       tr_tool->x1,
@@ -243,23 +253,23 @@ gimp_handle_transform_tool_get_widget (GimpTransformTool *tr_tool)
                                       tr_tool->y2);
 
   g_object_set (widget,
-                "n-handles", (gint) tr_tool->trans_info[N_HANDLES],
-                "orig-x1",   tr_tool->trans_info[OX0],
-                "orig-y1",   tr_tool->trans_info[OY0],
-                "orig-x2",   tr_tool->trans_info[OX1],
-                "orig-y2",   tr_tool->trans_info[OY1],
-                "orig-x3",   tr_tool->trans_info[OX2],
-                "orig-y3",   tr_tool->trans_info[OY2],
-                "orig-x4",   tr_tool->trans_info[OX3],
-                "orig-y4",   tr_tool->trans_info[OY3],
-                "trans-x1",  tr_tool->trans_info[X0],
-                "trans-y1",  tr_tool->trans_info[Y0],
-                "trans-x2",  tr_tool->trans_info[X1],
-                "trans-y2",  tr_tool->trans_info[Y1],
-                "trans-x3",  tr_tool->trans_info[X2],
-                "trans-y3",  tr_tool->trans_info[Y2],
-                "trans-x4",  tr_tool->trans_info[X3],
-                "trans-y4",  tr_tool->trans_info[Y3],
+                "n-handles", (gint) tg_tool->trans_info[N_HANDLES],
+                "orig-x1",   tg_tool->trans_info[OX0],
+                "orig-y1",   tg_tool->trans_info[OY0],
+                "orig-x2",   tg_tool->trans_info[OX1],
+                "orig-y2",   tg_tool->trans_info[OY1],
+                "orig-x3",   tg_tool->trans_info[OX2],
+                "orig-y3",   tg_tool->trans_info[OY2],
+                "orig-x4",   tg_tool->trans_info[OX3],
+                "orig-y4",   tg_tool->trans_info[OY3],
+                "trans-x1",  tg_tool->trans_info[X0],
+                "trans-y1",  tg_tool->trans_info[Y0],
+                "trans-x2",  tg_tool->trans_info[X1],
+                "trans-y2",  tg_tool->trans_info[Y1],
+                "trans-x3",  tg_tool->trans_info[X2],
+                "trans-y3",  tg_tool->trans_info[Y2],
+                "trans-x4",  tg_tool->trans_info[X3],
+                "trans-y4",  tg_tool->trans_info[Y3],
                 NULL);
 
   g_object_bind_property (G_OBJECT (options), "handle-mode",
@@ -269,99 +279,95 @@ gimp_handle_transform_tool_get_widget (GimpTransformTool *tr_tool)
 
   g_signal_connect (widget, "changed",
                     G_CALLBACK (gimp_handle_transform_tool_widget_changed),
-                    tr_tool);
+                    tg_tool);
 
   return widget;
 }
 
 static void
-gimp_handle_transform_tool_recalc_matrix (GimpTransformTool *tr_tool,
-                                          GimpToolWidget    *widget)
+gimp_handle_transform_tool_recalc_matrix (GimpTransformGridTool *tg_tool,
+                                          GimpToolWidget        *widget)
 {
-  GIMP_TRANSFORM_TOOL_CLASS (parent_class)->recalc_matrix (tr_tool, widget);
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+
+  GIMP_TRANSFORM_GRID_TOOL_CLASS (parent_class)->recalc_matrix (tg_tool, widget);
 
   if (widget)
     g_object_set (widget,
                   "transform",   &tr_tool->transform,
                   "show-guides", tr_tool->transform_valid,
-                  "n-handles",   (gint) tr_tool->trans_info[N_HANDLES],
-                  "orig-x1",     tr_tool->trans_info[OX0],
-                  "orig-y1",     tr_tool->trans_info[OY0],
-                  "orig-x2",     tr_tool->trans_info[OX1],
-                  "orig-y2",     tr_tool->trans_info[OY1],
-                  "orig-x3",     tr_tool->trans_info[OX2],
-                  "orig-y3",     tr_tool->trans_info[OY2],
-                  "orig-x4",     tr_tool->trans_info[OX3],
-                  "orig-y4",     tr_tool->trans_info[OY3],
-                  "trans-x1",    tr_tool->trans_info[X0],
-                  "trans-y1",    tr_tool->trans_info[Y0],
-                  "trans-x2",    tr_tool->trans_info[X1],
-                  "trans-y2",    tr_tool->trans_info[Y1],
-                  "trans-x3",    tr_tool->trans_info[X2],
-                  "trans-y3",    tr_tool->trans_info[Y2],
-                  "trans-x4",    tr_tool->trans_info[X3],
-                  "trans-y4",    tr_tool->trans_info[Y3],
+                  "n-handles",   (gint) tg_tool->trans_info[N_HANDLES],
+                  "orig-x1",     tg_tool->trans_info[OX0],
+                  "orig-y1",     tg_tool->trans_info[OY0],
+                  "orig-x2",     tg_tool->trans_info[OX1],
+                  "orig-y2",     tg_tool->trans_info[OY1],
+                  "orig-x3",     tg_tool->trans_info[OX2],
+                  "orig-y3",     tg_tool->trans_info[OY2],
+                  "orig-x4",     tg_tool->trans_info[OX3],
+                  "orig-y4",     tg_tool->trans_info[OY3],
+                  "trans-x1",    tg_tool->trans_info[X0],
+                  "trans-y1",    tg_tool->trans_info[Y0],
+                  "trans-x2",    tg_tool->trans_info[X1],
+                  "trans-y2",    tg_tool->trans_info[Y1],
+                  "trans-x3",    tg_tool->trans_info[X2],
+                  "trans-y3",    tg_tool->trans_info[Y2],
+                  "trans-x4",    tg_tool->trans_info[X3],
+                  "trans-y4",    tg_tool->trans_info[Y3],
                   NULL);
 }
 
-static gchar *
-gimp_handle_transform_tool_get_undo_desc (GimpTransformTool *tr_tool)
-{
-  return g_strdup (C_("undo-type", "Handle transform"));
-}
-
 static void
 gimp_handle_transform_tool_recalc_points (GimpGenericTransformTool *generic,
                                           GimpToolWidget           *widget)
 {
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (generic);
-
-  generic->input_points[0]  = (GimpVector2) {tr_tool->trans_info[OX0],
-                                             tr_tool->trans_info[OY0]};
-  generic->input_points[1]  = (GimpVector2) {tr_tool->trans_info[OX1],
-                                             tr_tool->trans_info[OY1]};
-  generic->input_points[2]  = (GimpVector2) {tr_tool->trans_info[OX2],
-                                             tr_tool->trans_info[OY2]};
-  generic->input_points[3]  = (GimpVector2) {tr_tool->trans_info[OX3],
-                                             tr_tool->trans_info[OY3]};
-
-  generic->output_points[0] = (GimpVector2) {tr_tool->trans_info[X0],
-                                             tr_tool->trans_info[Y0]};
-  generic->output_points[1] = (GimpVector2) {tr_tool->trans_info[X1],
-                                             tr_tool->trans_info[Y1]};
-  generic->output_points[2] = (GimpVector2) {tr_tool->trans_info[X2],
-                                             tr_tool->trans_info[Y2]};
-  generic->output_points[3] = (GimpVector2) {tr_tool->trans_info[X3],
-                                             tr_tool->trans_info[Y3]};
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (generic);
+
+  generic->input_points[0]  = (GimpVector2) {tg_tool->trans_info[OX0],
+                                             tg_tool->trans_info[OY0]};
+  generic->input_points[1]  = (GimpVector2) {tg_tool->trans_info[OX1],
+                                             tg_tool->trans_info[OY1]};
+  generic->input_points[2]  = (GimpVector2) {tg_tool->trans_info[OX2],
+                                             tg_tool->trans_info[OY2]};
+  generic->input_points[3]  = (GimpVector2) {tg_tool->trans_info[OX3],
+                                             tg_tool->trans_info[OY3]};
+
+  generic->output_points[0] = (GimpVector2) {tg_tool->trans_info[X0],
+                                             tg_tool->trans_info[Y0]};
+  generic->output_points[1] = (GimpVector2) {tg_tool->trans_info[X1],
+                                             tg_tool->trans_info[Y1]};
+  generic->output_points[2] = (GimpVector2) {tg_tool->trans_info[X2],
+                                             tg_tool->trans_info[Y2]};
+  generic->output_points[3] = (GimpVector2) {tg_tool->trans_info[X3],
+                                             tg_tool->trans_info[Y3]};
 }
 
 static void
-gimp_handle_transform_tool_widget_changed (GimpToolWidget    *widget,
-                                           GimpTransformTool *tr_tool)
+gimp_handle_transform_tool_widget_changed (GimpToolWidget        *widget,
+                                           GimpTransformGridTool *tg_tool)
 {
   gint n_handles;
 
   g_object_get (widget,
                 "n-handles", &n_handles,
-                "orig-x1",   &tr_tool->trans_info[OX0],
-                "orig-y1",   &tr_tool->trans_info[OY0],
-                "orig-x2",   &tr_tool->trans_info[OX1],
-                "orig-y2",   &tr_tool->trans_info[OY1],
-                "orig-x3",   &tr_tool->trans_info[OX2],
-                "orig-y3",   &tr_tool->trans_info[OY2],
-                "orig-x4",   &tr_tool->trans_info[OX3],
-                "orig-y4",   &tr_tool->trans_info[OY3],
-                "trans-x1",  &tr_tool->trans_info[X0],
-                "trans-y1",  &tr_tool->trans_info[Y0],
-                "trans-x2",  &tr_tool->trans_info[X1],
-                "trans-y2",  &tr_tool->trans_info[Y1],
-                "trans-x3",  &tr_tool->trans_info[X2],
-                "trans-y3",  &tr_tool->trans_info[Y2],
-                "trans-x4",  &tr_tool->trans_info[X3],
-                "trans-y4",  &tr_tool->trans_info[Y3],
+                "orig-x1",   &tg_tool->trans_info[OX0],
+                "orig-y1",   &tg_tool->trans_info[OY0],
+                "orig-x2",   &tg_tool->trans_info[OX1],
+                "orig-y2",   &tg_tool->trans_info[OY1],
+                "orig-x3",   &tg_tool->trans_info[OX2],
+                "orig-y3",   &tg_tool->trans_info[OY2],
+                "orig-x4",   &tg_tool->trans_info[OX3],
+                "orig-y4",   &tg_tool->trans_info[OY3],
+                "trans-x1",  &tg_tool->trans_info[X0],
+                "trans-y1",  &tg_tool->trans_info[Y0],
+                "trans-x2",  &tg_tool->trans_info[X1],
+                "trans-y2",  &tg_tool->trans_info[Y1],
+                "trans-x3",  &tg_tool->trans_info[X2],
+                "trans-y3",  &tg_tool->trans_info[Y2],
+                "trans-x4",  &tg_tool->trans_info[X3],
+                "trans-y4",  &tg_tool->trans_info[Y3],
                 NULL);
 
-  tr_tool->trans_info[N_HANDLES] = n_handles;
+  tg_tool->trans_info[N_HANDLES] = n_handles;
 
-  gimp_transform_tool_recalc_matrix (tr_tool, NULL);
+  gimp_transform_grid_tool_recalc_matrix (tg_tool, NULL);
 }
diff --git a/app/tools/gimpperspectivetool.c b/app/tools/gimpperspectivetool.c
index 97e24b8738..f7f238921f 100644
--- a/app/tools/gimpperspectivetool.c
+++ b/app/tools/gimpperspectivetool.c
@@ -33,7 +33,7 @@
 
 #include "gimpperspectivetool.h"
 #include "gimptoolcontrol.h"
-#include "gimptransformoptions.h"
+#include "gimptransformgridoptions.h"
 
 #include "gimp-intl.h"
 
@@ -54,17 +54,18 @@ enum
 
 /*  local function prototypes  */
 
-static void             gimp_perspective_tool_prepare        (GimpTransformTool        *tr_tool);
-static GimpToolWidget * gimp_perspective_tool_get_widget     (GimpTransformTool        *tr_tool);
-static void             gimp_perspective_tool_recalc_matrix  (GimpTransformTool        *tr_tool,
-                                                              GimpToolWidget           *widget);
 static gchar          * gimp_perspective_tool_get_undo_desc  (GimpTransformTool        *tr_tool);
 
+static void             gimp_perspective_tool_prepare        (GimpTransformGridTool    *tg_tool);
+static GimpToolWidget * gimp_perspective_tool_get_widget     (GimpTransformGridTool    *tg_tool);
+static void             gimp_perspective_tool_recalc_matrix  (GimpTransformGridTool    *tg_tool,
+                                                              GimpToolWidget           *widget);
+
 static void             gimp_perspective_tool_recalc_points  (GimpGenericTransformTool *generic,
                                                               GimpToolWidget           *widget);
 
 static void             gimp_perspective_tool_widget_changed (GimpToolWidget           *widget,
-                                                              GimpTransformTool        *tr_tool);
+                                                              GimpTransformGridTool    *tg_tool);
 
 
 G_DEFINE_TYPE (GimpPerspectiveTool, gimp_perspective_tool,
@@ -78,8 +79,8 @@ gimp_perspective_tool_register (GimpToolRegisterCallback  callback,
                                 gpointer                  data)
 {
   (* callback) (GIMP_TYPE_PERSPECTIVE_TOOL,
-                GIMP_TYPE_TRANSFORM_OPTIONS,
-                gimp_transform_options_gui,
+                GIMP_TYPE_TRANSFORM_GRID_OPTIONS,
+                gimp_transform_grid_options_gui,
                 GIMP_CONTEXT_PROP_MASK_BACKGROUND,
                 "gimp-perspective-tool",
                 _("Perspective"),
@@ -94,50 +95,60 @@ gimp_perspective_tool_register (GimpToolRegisterCallback  callback,
 static void
 gimp_perspective_tool_class_init (GimpPerspectiveToolClass *klass)
 {
-  GimpTransformToolClass        *trans_class   = GIMP_TRANSFORM_TOOL_CLASS (klass);
+  GimpTransformToolClass        *tr_class      = GIMP_TRANSFORM_TOOL_CLASS (klass);
+  GimpTransformGridToolClass    *tg_class      = GIMP_TRANSFORM_GRID_TOOL_CLASS (klass);
   GimpGenericTransformToolClass *generic_class = GIMP_GENERIC_TRANSFORM_TOOL_CLASS (klass);
 
-  trans_class->prepare         = gimp_perspective_tool_prepare;
-  trans_class->get_widget      = gimp_perspective_tool_get_widget;
-  trans_class->recalc_matrix   = gimp_perspective_tool_recalc_matrix;
-  trans_class->get_undo_desc   = gimp_perspective_tool_get_undo_desc;
+  tr_class->get_undo_desc      = gimp_perspective_tool_get_undo_desc;
+
+  tg_class->prepare            = gimp_perspective_tool_prepare;
+  tg_class->get_widget         = gimp_perspective_tool_get_widget;
+  tg_class->recalc_matrix      = gimp_perspective_tool_recalc_matrix;
 
   generic_class->recalc_points = gimp_perspective_tool_recalc_points;
+
+  tr_class->progress_text      = _("Perspective transformation");
 }
 
 static void
 gimp_perspective_tool_init (GimpPerspectiveTool *perspective_tool)
 {
-  GimpTool          *tool    = GIMP_TOOL (perspective_tool);
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (perspective_tool);
+  GimpTool *tool = GIMP_TOOL (perspective_tool);
 
   gimp_tool_control_set_tool_cursor (tool->control,
                                      GIMP_TOOL_CURSOR_PERSPECTIVE);
+}
 
-  tr_tool->progress_text = _("Perspective transformation");
+static gchar *
+gimp_perspective_tool_get_undo_desc (GimpTransformTool *tr_tool)
+{
+  return g_strdup (C_("undo-type", "Perspective"));
 }
 
 static void
-gimp_perspective_tool_prepare (GimpTransformTool  *tr_tool)
+gimp_perspective_tool_prepare (GimpTransformGridTool *tg_tool)
 {
-  GIMP_TRANSFORM_TOOL_CLASS (parent_class)->prepare (tr_tool);
-
-  tr_tool->trans_info[X0] = (gdouble) tr_tool->x1;
-  tr_tool->trans_info[Y0] = (gdouble) tr_tool->y1;
-  tr_tool->trans_info[X1] = (gdouble) tr_tool->x2;
-  tr_tool->trans_info[Y1] = (gdouble) tr_tool->y1;
-  tr_tool->trans_info[X2] = (gdouble) tr_tool->x1;
-  tr_tool->trans_info[Y2] = (gdouble) tr_tool->y2;
-  tr_tool->trans_info[X3] = (gdouble) tr_tool->x2;
-  tr_tool->trans_info[Y3] = (gdouble) tr_tool->y2;
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+
+  GIMP_TRANSFORM_GRID_TOOL_CLASS (parent_class)->prepare (tg_tool);
+
+  tg_tool->trans_info[X0] = (gdouble) tr_tool->x1;
+  tg_tool->trans_info[Y0] = (gdouble) tr_tool->y1;
+  tg_tool->trans_info[X1] = (gdouble) tr_tool->x2;
+  tg_tool->trans_info[Y1] = (gdouble) tr_tool->y1;
+  tg_tool->trans_info[X2] = (gdouble) tr_tool->x1;
+  tg_tool->trans_info[Y2] = (gdouble) tr_tool->y2;
+  tg_tool->trans_info[X3] = (gdouble) tr_tool->x2;
+  tg_tool->trans_info[Y3] = (gdouble) tr_tool->y2;
 }
 
 static GimpToolWidget *
-gimp_perspective_tool_get_widget (GimpTransformTool *tr_tool)
+gimp_perspective_tool_get_widget (GimpTransformGridTool *tg_tool)
 {
-  GimpTool         *tool  = GIMP_TOOL (tr_tool);
-  GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
-  GimpToolWidget   *widget;
+  GimpTool          *tool    = GIMP_TOOL (tg_tool);
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpDisplayShell  *shell   = gimp_display_get_shell (tool->display);
+  GimpToolWidget    *widget;
 
   widget = gimp_tool_transform_grid_new (shell,
                                          &tr_tool->transform,
@@ -155,16 +166,18 @@ gimp_perspective_tool_get_widget (GimpTransformTool *tr_tool)
 
   g_signal_connect (widget, "changed",
                     G_CALLBACK (gimp_perspective_tool_widget_changed),
-                    tr_tool);
+                    tg_tool);
 
   return widget;
 }
 
 static void
-gimp_perspective_tool_recalc_matrix (GimpTransformTool *tr_tool,
-                                     GimpToolWidget    *widget)
+gimp_perspective_tool_recalc_matrix (GimpTransformGridTool *tg_tool,
+                                     GimpToolWidget        *widget)
 {
-  GIMP_TRANSFORM_TOOL_CLASS (parent_class)->recalc_matrix (tr_tool, widget);
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+
+  GIMP_TRANSFORM_GRID_TOOL_CLASS (parent_class)->recalc_matrix (tg_tool, widget);
 
   if (widget)
     g_object_set (widget,
@@ -176,38 +189,34 @@ gimp_perspective_tool_recalc_matrix (GimpTransformTool *tr_tool,
                   NULL);
 }
 
-static gchar *
-gimp_perspective_tool_get_undo_desc (GimpTransformTool *tr_tool)
-{
-  return g_strdup (C_("undo-type", "Perspective"));
-}
-
 static void
 gimp_perspective_tool_recalc_points (GimpGenericTransformTool *generic,
                                      GimpToolWidget           *widget)
 {
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (generic);
+  GimpTransformTool     *tr_tool = GIMP_TRANSFORM_TOOL (generic);
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (generic);
 
   generic->input_points[0]  = (GimpVector2) {tr_tool->x1, tr_tool->y1};
   generic->input_points[1]  = (GimpVector2) {tr_tool->x2, tr_tool->y1};
   generic->input_points[2]  = (GimpVector2) {tr_tool->x1, tr_tool->y2};
   generic->input_points[3]  = (GimpVector2) {tr_tool->x2, tr_tool->y2};
 
-  generic->output_points[0] = (GimpVector2) {tr_tool->trans_info[X0],
-                                             tr_tool->trans_info[Y0]};
-  generic->output_points[1] = (GimpVector2) {tr_tool->trans_info[X1],
-                                             tr_tool->trans_info[Y1]};
-  generic->output_points[2] = (GimpVector2) {tr_tool->trans_info[X2],
-                                             tr_tool->trans_info[Y2]};
-  generic->output_points[3] = (GimpVector2) {tr_tool->trans_info[X3],
-                                             tr_tool->trans_info[Y3]};
+  generic->output_points[0] = (GimpVector2) {tg_tool->trans_info[X0],
+                                             tg_tool->trans_info[Y0]};
+  generic->output_points[1] = (GimpVector2) {tg_tool->trans_info[X1],
+                                             tg_tool->trans_info[Y1]};
+  generic->output_points[2] = (GimpVector2) {tg_tool->trans_info[X2],
+                                             tg_tool->trans_info[Y2]};
+  generic->output_points[3] = (GimpVector2) {tg_tool->trans_info[X3],
+                                             tg_tool->trans_info[Y3]};
 }
 
 static void
-gimp_perspective_tool_widget_changed (GimpToolWidget    *widget,
-                                      GimpTransformTool *tr_tool)
+gimp_perspective_tool_widget_changed (GimpToolWidget        *widget,
+                                      GimpTransformGridTool *tg_tool)
 {
-  GimpMatrix3 *transform;
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpMatrix3       *transform;
 
   g_object_get (widget,
                 "transform", &transform,
@@ -215,22 +224,22 @@ gimp_perspective_tool_widget_changed (GimpToolWidget    *widget,
 
   gimp_matrix3_transform_point (transform,
                                 tr_tool->x1, tr_tool->y1,
-                                &tr_tool->trans_info[X0],
-                                &tr_tool->trans_info[Y0]);
+                                &tg_tool->trans_info[X0],
+                                &tg_tool->trans_info[Y0]);
   gimp_matrix3_transform_point (transform,
                                 tr_tool->x2, tr_tool->y1,
-                                &tr_tool->trans_info[X1],
-                                &tr_tool->trans_info[Y1]);
+                                &tg_tool->trans_info[X1],
+                                &tg_tool->trans_info[Y1]);
   gimp_matrix3_transform_point (transform,
                                 tr_tool->x1, tr_tool->y2,
-                                &tr_tool->trans_info[X2],
-                                &tr_tool->trans_info[Y2]);
+                                &tg_tool->trans_info[X2],
+                                &tg_tool->trans_info[Y2]);
   gimp_matrix3_transform_point (transform,
                                 tr_tool->x2, tr_tool->y2,
-                                &tr_tool->trans_info[X3],
-                                &tr_tool->trans_info[Y3]);
+                                &tg_tool->trans_info[X3],
+                                &tg_tool->trans_info[Y3]);
 
   g_free (transform);
 
-  gimp_transform_tool_recalc_matrix (tr_tool, NULL);
+  gimp_transform_grid_tool_recalc_matrix (tg_tool, NULL);
 }
diff --git a/app/tools/gimprotatetool.c b/app/tools/gimprotatetool.c
index f60d794af9..9a9feca351 100644
--- a/app/tools/gimprotatetool.c
+++ b/app/tools/gimprotatetool.c
@@ -38,7 +38,7 @@
 
 #include "gimprotatetool.h"
 #include "gimptoolcontrol.h"
-#include "gimptransformoptions.h"
+#include "gimptransformgridoptions.h"
 
 #include "gimp-intl.h"
 
@@ -57,28 +57,29 @@ enum
 
 /*  local function prototypes  */
 
-static gboolean         gimp_rotate_tool_key_press      (GimpTool           *tool,
-                                                         GdkEventKey        *kevent,
-                                                         GimpDisplay        *display);
+static gboolean         gimp_rotate_tool_key_press      (GimpTool              *tool,
+                                                         GdkEventKey           *kevent,
+                                                         GimpDisplay           *display);
 
-static void             gimp_rotate_tool_dialog         (GimpTransformTool  *tr_tool);
-static void             gimp_rotate_tool_dialog_update  (GimpTransformTool  *tr_tool);
-static void             gimp_rotate_tool_prepare        (GimpTransformTool  *tr_tool);
-static GimpToolWidget * gimp_rotate_tool_get_widget     (GimpTransformTool *tr_tool);
-static void             gimp_rotate_tool_recalc_matrix  (GimpTransformTool  *tr_tool,
-                                                         GimpToolWidget     *widget);
-static gchar          * gimp_rotate_tool_get_undo_desc  (GimpTransformTool  *tr_tool);
+static gchar          * gimp_rotate_tool_get_undo_desc  (GimpTransformTool     *tr_tool);
 
-static void             gimp_rotate_tool_widget_changed (GimpToolWidget    *widget,
-                                                         GimpTransformTool *tr_tool);
+static void             gimp_rotate_tool_dialog         (GimpTransformGridTool *tg_tool);
+static void             gimp_rotate_tool_dialog_update  (GimpTransformGridTool *tg_tool);
+static void             gimp_rotate_tool_prepare        (GimpTransformGridTool *tg_tool);
+static GimpToolWidget * gimp_rotate_tool_get_widget     (GimpTransformGridTool *tg_tool);
+static void             gimp_rotate_tool_recalc_matrix  (GimpTransformGridTool *tg_tool,
+                                                         GimpToolWidget        *widget);
 
-static void             rotate_angle_changed            (GtkAdjustment      *adj,
-                                                         GimpTransformTool  *tr_tool);
-static void             rotate_center_changed           (GtkWidget          *entry,
-                                                         GimpTransformTool   *tr_tool);
+static void             gimp_rotate_tool_widget_changed (GimpToolWidget        *widget,
+                                                         GimpTransformGridTool *tg_tool);
 
+static void             rotate_angle_changed            (GtkAdjustment         *adj,
+                                                         GimpTransformGridTool *tg_tool);
+static void             rotate_center_changed           (GtkWidget             *entry,
+                                                         GimpTransformGridTool *tg_tool);
 
-G_DEFINE_TYPE (GimpRotateTool, gimp_rotate_tool, GIMP_TYPE_TRANSFORM_TOOL)
+
+G_DEFINE_TYPE (GimpRotateTool, gimp_rotate_tool, GIMP_TYPE_TRANSFORM_GRID_TOOL)
 
 #define parent_class gimp_rotate_tool_parent_class
 
@@ -88,8 +89,8 @@ gimp_rotate_tool_register (GimpToolRegisterCallback  callback,
                            gpointer                  data)
 {
   (* callback) (GIMP_TYPE_ROTATE_TOOL,
-                GIMP_TYPE_TRANSFORM_OPTIONS,
-                gimp_transform_options_gui,
+                GIMP_TYPE_TRANSFORM_GRID_OPTIONS,
+                gimp_transform_grid_options_gui,
                 GIMP_CONTEXT_PROP_MASK_BACKGROUND,
                 "gimp-rotate-tool",
                 _("Rotate"),
@@ -103,30 +104,30 @@ gimp_rotate_tool_register (GimpToolRegisterCallback  callback,
 static void
 gimp_rotate_tool_class_init (GimpRotateToolClass *klass)
 {
-  GimpToolClass          *tool_class  = GIMP_TOOL_CLASS (klass);
-  GimpTransformToolClass *trans_class = GIMP_TRANSFORM_TOOL_CLASS (klass);
+  GimpToolClass              *tool_class = GIMP_TOOL_CLASS (klass);
+  GimpTransformToolClass     *tr_class   = GIMP_TRANSFORM_TOOL_CLASS (klass);
+  GimpTransformGridToolClass *tg_class   = GIMP_TRANSFORM_GRID_TOOL_CLASS (klass);
+
+  tool_class->key_press     = gimp_rotate_tool_key_press;
 
-  tool_class->key_press        = gimp_rotate_tool_key_press;
+  tr_class->get_undo_desc   = gimp_rotate_tool_get_undo_desc;
 
-  trans_class->dialog          = gimp_rotate_tool_dialog;
-  trans_class->dialog_update   = gimp_rotate_tool_dialog_update;
-  trans_class->prepare         = gimp_rotate_tool_prepare;
-  trans_class->get_widget      = gimp_rotate_tool_get_widget;
-  trans_class->recalc_matrix   = gimp_rotate_tool_recalc_matrix;
-  trans_class->get_undo_desc   = gimp_rotate_tool_get_undo_desc;
+  tg_class->dialog          = gimp_rotate_tool_dialog;
+  tg_class->dialog_update   = gimp_rotate_tool_dialog_update;
+  tg_class->prepare         = gimp_rotate_tool_prepare;
+  tg_class->get_widget      = gimp_rotate_tool_get_widget;
+  tg_class->recalc_matrix   = gimp_rotate_tool_recalc_matrix;
 
-  trans_class->ok_button_label = _("R_otate");
+  tr_class->progress_text   = _("Rotating");
+  tg_class->ok_button_label = _("R_otate");
 }
 
 static void
 gimp_rotate_tool_init (GimpRotateTool *rotate_tool)
 {
-  GimpTool          *tool    = GIMP_TOOL (rotate_tool);
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (rotate_tool);
+  GimpTool *tool = GIMP_TOOL (rotate_tool);
 
   gimp_tool_control_set_tool_cursor (tool->control, GIMP_TOOL_CURSOR_ROTATE);
-
-  tr_tool->progress_text = _("Rotating");
 }
 
 static gboolean
@@ -167,10 +168,22 @@ gimp_rotate_tool_key_press (GimpTool    *tool,
   return GIMP_TOOL_CLASS (parent_class)->key_press (tool, kevent, display);
 }
 
+static gchar *
+gimp_rotate_tool_get_undo_desc (GimpTransformTool *tr_tool)
+{
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tr_tool);
+
+  return g_strdup_printf (C_("undo-type",
+                             "Rotate by %-3.3g° around (%g, %g)"),
+                          gimp_rad_to_deg (tg_tool->trans_info[ANGLE]),
+                          tg_tool->trans_info[PIVOT_X],
+                          tg_tool->trans_info[PIVOT_Y]);
+}
+
 static void
-gimp_rotate_tool_dialog (GimpTransformTool *tr_tool)
+gimp_rotate_tool_dialog (GimpTransformGridTool *tg_tool)
 {
-  GimpRotateTool *rotate = GIMP_ROTATE_TOOL (tr_tool);
+  GimpRotateTool *rotate = GIMP_ROTATE_TOOL (tg_tool);
   GtkWidget      *table;
   GtkWidget      *button;
   GtkWidget      *scale;
@@ -180,7 +193,7 @@ gimp_rotate_tool_dialog (GimpTransformTool *tr_tool)
   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
   gtk_table_set_row_spacing (GTK_TABLE (table), 1, 6);
-  gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (tr_tool->gui)), table,
+  gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (tg_tool->gui)), table,
                       FALSE, FALSE, 0);
   gtk_widget_show (table);
 
@@ -196,7 +209,7 @@ gimp_rotate_tool_dialog (GimpTransformTool *tr_tool)
 
   g_signal_connect (rotate->angle_adj, "value-changed",
                     G_CALLBACK (rotate_angle_changed),
-                    tr_tool);
+                    tg_tool);
 
   scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, rotate->angle_adj);
   gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
@@ -222,49 +235,50 @@ gimp_rotate_tool_dialog (GimpTransformTool *tr_tool)
 
   g_signal_connect (rotate->sizeentry, "value-changed",
                     G_CALLBACK (rotate_center_changed),
-                    tr_tool);
+                    tg_tool);
 }
 
 static void
-gimp_rotate_tool_dialog_update (GimpTransformTool *tr_tool)
+gimp_rotate_tool_dialog_update (GimpTransformGridTool *tg_tool)
 {
-  GimpRotateTool *rotate = GIMP_ROTATE_TOOL (tr_tool);
+  GimpRotateTool *rotate = GIMP_ROTATE_TOOL (tg_tool);
 
   gtk_adjustment_set_value (rotate->angle_adj,
-                            gimp_rad_to_deg (tr_tool->trans_info[ANGLE]));
+                            gimp_rad_to_deg (tg_tool->trans_info[ANGLE]));
 
   g_signal_handlers_block_by_func (rotate->sizeentry,
                                    rotate_center_changed,
-                                   tr_tool);
+                                   tg_tool);
 
   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (rotate->sizeentry), 0,
-                              tr_tool->trans_info[PIVOT_X]);
+                              tg_tool->trans_info[PIVOT_X]);
   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (rotate->sizeentry), 1,
-                              tr_tool->trans_info[PIVOT_Y]);
+                              tg_tool->trans_info[PIVOT_Y]);
 
   g_signal_handlers_unblock_by_func (rotate->sizeentry,
                                      rotate_center_changed,
-                                     tr_tool);
+                                     tg_tool);
 }
 
 static void
-gimp_rotate_tool_prepare (GimpTransformTool *tr_tool)
+gimp_rotate_tool_prepare (GimpTransformGridTool *tg_tool)
 {
-  GimpRotateTool *rotate  = GIMP_ROTATE_TOOL (tr_tool);
-  GimpDisplay    *display = GIMP_TOOL (tr_tool)->display;
-  GimpImage      *image   = gimp_display_get_image (display);
-  gdouble         xres;
-  gdouble         yres;
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpRotateTool    *rotate  = GIMP_ROTATE_TOOL (tg_tool);
+  GimpDisplay       *display = GIMP_TOOL (tg_tool)->display;
+  GimpImage         *image   = gimp_display_get_image (display);
+  gdouble            xres;
+  gdouble            yres;
 
-  tr_tool->trans_info[ANGLE]   = 0.0;
-  tr_tool->trans_info[PIVOT_X] = (gdouble) (tr_tool->x1 + tr_tool->x2) / 2.0;
-  tr_tool->trans_info[PIVOT_Y] = (gdouble) (tr_tool->y1 + tr_tool->y2) / 2.0;
+  tg_tool->trans_info[ANGLE]   = 0.0;
+  tg_tool->trans_info[PIVOT_X] = (gdouble) (tr_tool->x1 + tr_tool->x2) / 2.0;
+  tg_tool->trans_info[PIVOT_Y] = (gdouble) (tr_tool->y1 + tr_tool->y2) / 2.0;
 
   gimp_image_get_resolution (image, &xres, &yres);
 
   g_signal_handlers_block_by_func (rotate->sizeentry,
                                    rotate_center_changed,
-                                   tr_tool);
+                                   tg_tool);
 
   gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (rotate->sizeentry),
                             gimp_display_get_shell (display)->unit);
@@ -290,24 +304,25 @@ gimp_rotate_tool_prepare (GimpTransformTool *tr_tool)
 
   g_signal_handlers_unblock_by_func (rotate->sizeentry,
                                      rotate_center_changed,
-                                     tr_tool);
+                                     tg_tool);
 }
 
 static GimpToolWidget *
-gimp_rotate_tool_get_widget (GimpTransformTool *tr_tool)
+gimp_rotate_tool_get_widget (GimpTransformGridTool *tg_tool)
 {
-  GimpTool         *tool  = GIMP_TOOL (tr_tool);
-  GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
-  GimpToolWidget   *widget;
+  GimpTool          *tool    = GIMP_TOOL (tg_tool);
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpDisplayShell  *shell   = gimp_display_get_shell (tool->display);
+  GimpToolWidget    *widget;
 
   widget = gimp_tool_rotate_grid_new (shell,
                                       tr_tool->x1,
                                       tr_tool->y1,
                                       tr_tool->x2,
                                       tr_tool->y2,
-                                      tr_tool->trans_info[PIVOT_X],
-                                      tr_tool->trans_info[PIVOT_Y],
-                                      tr_tool->trans_info[ANGLE]);
+                                      tg_tool->trans_info[PIVOT_X],
+                                      tg_tool->trans_info[PIVOT_Y],
+                                      tg_tool->trans_info[ANGLE]);
 
   g_object_set (widget,
                 "inside-function",  GIMP_TRANSFORM_FUNCTION_ROTATE,
@@ -317,88 +332,80 @@ gimp_rotate_tool_get_widget (GimpTransformTool *tr_tool)
 
   g_signal_connect (widget, "changed",
                     G_CALLBACK (gimp_rotate_tool_widget_changed),
-                    tr_tool);
+                    tg_tool);
 
   return widget;
 }
 
 static void
-gimp_rotate_tool_recalc_matrix (GimpTransformTool *tr_tool,
-                                GimpToolWidget    *widget)
+gimp_rotate_tool_recalc_matrix (GimpTransformGridTool *tg_tool,
+                                GimpToolWidget        *widget)
 {
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+
   gimp_matrix3_identity (&tr_tool->transform);
   gimp_transform_matrix_rotate_center (&tr_tool->transform,
-                                       tr_tool->trans_info[PIVOT_X],
-                                       tr_tool->trans_info[PIVOT_Y],
-                                       tr_tool->trans_info[ANGLE]);
+                                       tg_tool->trans_info[PIVOT_X],
+                                       tg_tool->trans_info[PIVOT_Y],
+                                       tg_tool->trans_info[ANGLE]);
 
   if (widget)
     g_object_set (widget,
                   "transform", &tr_tool->transform,
-                  "angle",     tr_tool->trans_info[ANGLE],
-                  "pivot-x",   tr_tool->trans_info[PIVOT_X],
-                  "pivot-y",   tr_tool->trans_info[PIVOT_Y],
+                  "angle",     tg_tool->trans_info[ANGLE],
+                  "pivot-x",   tg_tool->trans_info[PIVOT_X],
+                  "pivot-y",   tg_tool->trans_info[PIVOT_Y],
                   NULL);
 }
 
-static gchar *
-gimp_rotate_tool_get_undo_desc (GimpTransformTool  *tr_tool)
-{
-  return g_strdup_printf (C_("undo-type",
-                             "Rotate by %-3.3g° around (%g, %g)"),
-                          gimp_rad_to_deg (tr_tool->trans_info[ANGLE]),
-                          tr_tool->trans_info[PIVOT_X],
-                          tr_tool->trans_info[PIVOT_Y]);
-}
-
 static void
-gimp_rotate_tool_widget_changed (GimpToolWidget    *widget,
-                                 GimpTransformTool *tr_tool)
+gimp_rotate_tool_widget_changed (GimpToolWidget        *widget,
+                                 GimpTransformGridTool *tg_tool)
 {
   g_object_get (widget,
-                "angle",   &tr_tool->trans_info[ANGLE],
-                "pivot-x", &tr_tool->trans_info[PIVOT_X],
-                "pivot-y", &tr_tool->trans_info[PIVOT_Y],
+                "angle",   &tg_tool->trans_info[ANGLE],
+                "pivot-x", &tg_tool->trans_info[PIVOT_X],
+                "pivot-y", &tg_tool->trans_info[PIVOT_Y],
                 NULL);
 
-  gimp_transform_tool_recalc_matrix (tr_tool, NULL);
+  gimp_transform_grid_tool_recalc_matrix (tg_tool, NULL);
 }
 
 static void
-rotate_angle_changed (GtkAdjustment     *adj,
-                      GimpTransformTool *tr_tool)
+rotate_angle_changed (GtkAdjustment         *adj,
+                      GimpTransformGridTool *tg_tool)
 {
   gdouble value = gimp_deg_to_rad (gtk_adjustment_get_value (adj));
 
 #define ANGLE_EPSILON 0.0001
 
-  if (ABS (value - tr_tool->trans_info[ANGLE]) > ANGLE_EPSILON)
+  if (ABS (value - tg_tool->trans_info[ANGLE]) > ANGLE_EPSILON)
     {
-      tr_tool->trans_info[ANGLE] = value;
+      tg_tool->trans_info[ANGLE] = value;
 
-      gimp_transform_tool_push_internal_undo (tr_tool);
+      gimp_transform_grid_tool_push_internal_undo (tg_tool);
 
-      gimp_transform_tool_recalc_matrix (tr_tool, tr_tool->widget);
+      gimp_transform_grid_tool_recalc_matrix (tg_tool, tg_tool->widget);
     }
 
 #undef ANGLE_EPSILON
 }
 
 static void
-rotate_center_changed (GtkWidget         *widget,
-                       GimpTransformTool *tr_tool)
+rotate_center_changed (GtkWidget             *widget,
+                       GimpTransformGridTool *tg_tool)
 {
   gdouble px = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
   gdouble py = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1);
 
-  if ((px != tr_tool->trans_info[PIVOT_X]) ||
-      (py != tr_tool->trans_info[PIVOT_Y]))
+  if ((px != tg_tool->trans_info[PIVOT_X]) ||
+      (py != tg_tool->trans_info[PIVOT_Y]))
     {
-      tr_tool->trans_info[PIVOT_X] = px;
-      tr_tool->trans_info[PIVOT_Y] = py;
+      tg_tool->trans_info[PIVOT_X] = px;
+      tg_tool->trans_info[PIVOT_Y] = py;
 
-      gimp_transform_tool_push_internal_undo (tr_tool);
+      gimp_transform_grid_tool_push_internal_undo (tg_tool);
 
-      gimp_transform_tool_recalc_matrix (tr_tool, tr_tool->widget);
+      gimp_transform_grid_tool_recalc_matrix (tg_tool, tg_tool->widget);
     }
 }
diff --git a/app/tools/gimprotatetool.h b/app/tools/gimprotatetool.h
index e7e02d1b62..290c4302f7 100644
--- a/app/tools/gimprotatetool.h
+++ b/app/tools/gimprotatetool.h
@@ -19,7 +19,7 @@
 #define __GIMP_ROTATE_TOOL_H__
 
 
-#include "gimptransformtool.h"
+#include "gimptransformgridtool.h"
 
 
 #define GIMP_TYPE_ROTATE_TOOL            (gimp_rotate_tool_get_type ())
@@ -35,16 +35,16 @@ typedef struct _GimpRotateToolClass GimpRotateToolClass;
 
 struct _GimpRotateTool
 {
-  GimpTransformTool  parent_instance;
+  GimpTransformGridTool  parent_instance;
 
-  GtkAdjustment     *angle_adj;
-  GtkWidget         *angle_spin_button;
-  GtkWidget         *sizeentry;
+  GtkAdjustment         *angle_adj;
+  GtkWidget             *angle_spin_button;
+  GtkWidget             *sizeentry;
 };
 
 struct _GimpRotateToolClass
 {
-  GimpTransformToolClass  parent_class;
+  GimpTransformGridToolClass  parent_class;
 };
 
 
diff --git a/app/tools/gimpscaletool.c b/app/tools/gimpscaletool.c
index f01e4cfe09..9b75e50585 100644
--- a/app/tools/gimpscaletool.c
+++ b/app/tools/gimpscaletool.c
@@ -40,7 +40,7 @@
 
 #include "gimpscaletool.h"
 #include "gimptoolcontrol.h"
-#include "gimptransformoptions.h"
+#include "gimptransformgridoptions.h"
 
 #include "gimp-intl.h"
 
@@ -57,23 +57,24 @@ enum
 
 /*  local function prototypes  */
 
-static void             gimp_scale_tool_dialog         (GimpTransformTool *tr_tool);
-static void             gimp_scale_tool_dialog_update  (GimpTransformTool *tr_tool);
-static void             gimp_scale_tool_prepare        (GimpTransformTool *tr_tool);
-static GimpToolWidget * gimp_scale_tool_get_widget     (GimpTransformTool *tr_tool);
-static void             gimp_scale_tool_recalc_matrix  (GimpTransformTool *tr_tool,
-                                                        GimpToolWidget    *widget);
-static gchar          * gimp_scale_tool_get_undo_desc  (GimpTransformTool *tr_tool);
+static gchar          * gimp_scale_tool_get_undo_desc  (GimpTransformTool     *tr_tool);
 
-static void             gimp_scale_tool_widget_changed (GimpToolWidget    *widget,
-                                                        GimpTransformTool *tr_tool);
+static void             gimp_scale_tool_dialog         (GimpTransformGridTool *tg_tool);
+static void             gimp_scale_tool_dialog_update  (GimpTransformGridTool *tg_tool);
+static void             gimp_scale_tool_prepare        (GimpTransformGridTool *tg_tool);
+static GimpToolWidget * gimp_scale_tool_get_widget     (GimpTransformGridTool *tg_tool);
+static void             gimp_scale_tool_recalc_matrix  (GimpTransformGridTool *tg_tool,
+                                                        GimpToolWidget        *widget);
 
-static void             gimp_scale_tool_size_notify    (GtkWidget         *box,
-                                                        GParamSpec        *pspec,
-                                                        GimpTransformTool *tr_tool);
+static void             gimp_scale_tool_widget_changed (GimpToolWidget        *widget,
+                                                        GimpTransformGridTool *tg_tool);
 
+static void             gimp_scale_tool_size_notify    (GtkWidget             *box,
+                                                        GParamSpec            *pspec,
+                                                        GimpTransformGridTool *tg_tool);
 
-G_DEFINE_TYPE (GimpScaleTool, gimp_scale_tool, GIMP_TYPE_TRANSFORM_TOOL)
+
+G_DEFINE_TYPE (GimpScaleTool, gimp_scale_tool, GIMP_TYPE_TRANSFORM_GRID_TOOL)
 
 #define parent_class gimp_scale_tool_parent_class
 
@@ -83,8 +84,8 @@ gimp_scale_tool_register (GimpToolRegisterCallback  callback,
                           gpointer                  data)
 {
   (* callback) (GIMP_TYPE_SCALE_TOOL,
-                GIMP_TYPE_TRANSFORM_OPTIONS,
-                gimp_transform_options_gui,
+                GIMP_TYPE_TRANSFORM_GRID_OPTIONS,
+                gimp_transform_grid_options_gui,
                 GIMP_CONTEXT_PROP_MASK_BACKGROUND,
                 "gimp-scale-tool",
                 _("Scale"),
@@ -98,45 +99,59 @@ gimp_scale_tool_register (GimpToolRegisterCallback  callback,
 static void
 gimp_scale_tool_class_init (GimpScaleToolClass *klass)
 {
-  GimpTransformToolClass *trans_class = GIMP_TRANSFORM_TOOL_CLASS (klass);
+  GimpTransformToolClass     *tr_class = GIMP_TRANSFORM_TOOL_CLASS (klass);
+  GimpTransformGridToolClass *tg_class = GIMP_TRANSFORM_GRID_TOOL_CLASS (klass);
+
+  tr_class->get_undo_desc   = gimp_scale_tool_get_undo_desc;
 
-  trans_class->dialog          = gimp_scale_tool_dialog;
-  trans_class->dialog_update   = gimp_scale_tool_dialog_update;
-  trans_class->prepare         = gimp_scale_tool_prepare;
-  trans_class->get_widget      = gimp_scale_tool_get_widget;
-  trans_class->recalc_matrix   = gimp_scale_tool_recalc_matrix;
-  trans_class->get_undo_desc   = gimp_scale_tool_get_undo_desc;
+  tg_class->dialog          = gimp_scale_tool_dialog;
+  tg_class->dialog_update   = gimp_scale_tool_dialog_update;
+  tg_class->prepare         = gimp_scale_tool_prepare;
+  tg_class->get_widget      = gimp_scale_tool_get_widget;
+  tg_class->recalc_matrix   = gimp_scale_tool_recalc_matrix;
 
-  trans_class->ok_button_label = _("_Scale");
+  tr_class->progress_text   = _("Scaling");
+  tg_class->ok_button_label = _("_Scale");
 }
 
 static void
 gimp_scale_tool_init (GimpScaleTool *scale_tool)
 {
-  GimpTool          *tool    = GIMP_TOOL (scale_tool);
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (scale_tool);
+  GimpTool *tool  = GIMP_TOOL (scale_tool);
 
   gimp_tool_control_set_tool_cursor (tool->control, GIMP_TOOL_CURSOR_RESIZE);
+}
+
+static gchar *
+gimp_scale_tool_get_undo_desc (GimpTransformTool *tr_tool)
+{
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tr_tool);
+  gint                   width;
+  gint                   height;
+
+  width  = ROUND (tg_tool->trans_info[X1] - tg_tool->trans_info[X0]);
+  height = ROUND (tg_tool->trans_info[Y1] - tg_tool->trans_info[Y0]);
 
-  tr_tool->progress_text = _("Scaling");
+  return g_strdup_printf (C_("undo-type", "Scale to %d x %d"),
+                          width, height);
 }
 
 static void
-gimp_scale_tool_dialog (GimpTransformTool *tr_tool)
+gimp_scale_tool_dialog (GimpTransformGridTool *tg_tool)
 {
 }
 
 static void
-gimp_scale_tool_dialog_update (GimpTransformTool *tr_tool)
+gimp_scale_tool_dialog_update (GimpTransformGridTool *tg_tool)
 {
-  GimpTransformOptions *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
-  gint                  width;
-  gint                  height;
+  GimpTransformGridOptions *options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
+  gint                      width;
+  gint                      height;
 
-  width  = ROUND (tr_tool->trans_info[X1] - tr_tool->trans_info[X0]);
-  height = ROUND (tr_tool->trans_info[Y1] - tr_tool->trans_info[Y0]);
+  width  = ROUND (tg_tool->trans_info[X1] - tg_tool->trans_info[X0]);
+  height = ROUND (tg_tool->trans_info[Y1] - tg_tool->trans_info[Y0]);
 
-  g_object_set (GIMP_SCALE_TOOL (tr_tool)->box,
+  g_object_set (GIMP_SCALE_TOOL (tg_tool)->box,
                 "width",       width,
                 "height",      height,
                 "keep-aspect", options->constrain_scale,
@@ -144,18 +159,19 @@ gimp_scale_tool_dialog_update (GimpTransformTool *tr_tool)
 }
 
 static void
-gimp_scale_tool_prepare (GimpTransformTool *tr_tool)
+gimp_scale_tool_prepare (GimpTransformGridTool *tg_tool)
 {
-  GimpScaleTool        *scale   = GIMP_SCALE_TOOL (tr_tool);
-  GimpTransformOptions *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
-  GimpDisplay          *display = GIMP_TOOL (tr_tool)->display;
-  gdouble               xres;
-  gdouble               yres;
-
-  tr_tool->trans_info[X0] = (gdouble) tr_tool->x1;
-  tr_tool->trans_info[Y0] = (gdouble) tr_tool->y1;
-  tr_tool->trans_info[X1] = (gdouble) tr_tool->x2;
-  tr_tool->trans_info[Y1] = (gdouble) tr_tool->y2;
+  GimpScaleTool            *scale   = GIMP_SCALE_TOOL (tg_tool);
+  GimpTransformTool        *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpTransformGridOptions *options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
+  GimpDisplay              *display = GIMP_TOOL (tg_tool)->display;
+  gdouble                   xres;
+  gdouble                   yres;
+
+  tg_tool->trans_info[X0] = (gdouble) tr_tool->x1;
+  tg_tool->trans_info[Y0] = (gdouble) tr_tool->y1;
+  tg_tool->trans_info[X1] = (gdouble) tr_tool->x2;
+  tg_tool->trans_info[Y1] = (gdouble) tr_tool->y2;
 
   gimp_image_get_resolution (gimp_display_get_image (display),
                              &xres, &yres);
@@ -164,7 +180,7 @@ gimp_scale_tool_prepare (GimpTransformTool *tr_tool)
     {
       g_signal_handlers_disconnect_by_func (scale->box,
                                             gimp_scale_tool_size_notify,
-                                            tr_tool);
+                                            tg_tool);
       gtk_widget_destroy (scale->box);
     }
 
@@ -181,21 +197,22 @@ gimp_scale_tool_prepare (GimpTransformTool *tr_tool)
                   "yresolution", yres,
                   NULL);
 
-  gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (tr_tool->gui)),
+  gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (tg_tool->gui)),
                       scale->box, FALSE, FALSE, 0);
   gtk_widget_show (scale->box);
 
   g_signal_connect (scale->box, "notify",
                     G_CALLBACK (gimp_scale_tool_size_notify),
-                    tr_tool);
+                    tg_tool);
 }
 
 static GimpToolWidget *
-gimp_scale_tool_get_widget (GimpTransformTool *tr_tool)
+gimp_scale_tool_get_widget (GimpTransformGridTool *tg_tool)
 {
-  GimpTool         *tool  = GIMP_TOOL (tr_tool);
-  GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
-  GimpToolWidget   *widget;
+  GimpTool          *tool    = GIMP_TOOL (tg_tool);
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpDisplayShell  *shell   = gimp_display_get_shell (tool->display);
+  GimpToolWidget    *widget;
 
   widget = gimp_tool_transform_grid_new (shell,
                                          &tr_tool->transform,
@@ -216,25 +233,27 @@ gimp_scale_tool_get_widget (GimpTransformTool *tr_tool)
 
   g_signal_connect (widget, "changed",
                     G_CALLBACK (gimp_scale_tool_widget_changed),
-                    tr_tool);
+                    tg_tool);
 
   return widget;
 }
 
 static void
-gimp_scale_tool_recalc_matrix (GimpTransformTool *tr_tool,
-                               GimpToolWidget    *widget)
+gimp_scale_tool_recalc_matrix (GimpTransformGridTool *tg_tool,
+                               GimpToolWidget        *widget)
 {
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+
   gimp_matrix3_identity (&tr_tool->transform);
   gimp_transform_matrix_scale (&tr_tool->transform,
                                tr_tool->x1,
                                tr_tool->y1,
                                tr_tool->x2 - tr_tool->x1,
                                tr_tool->y2 - tr_tool->y1,
-                               tr_tool->trans_info[X0],
-                               tr_tool->trans_info[Y0],
-                               tr_tool->trans_info[X1] - tr_tool->trans_info[X0],
-                               tr_tool->trans_info[Y1] - tr_tool->trans_info[Y0]);
+                               tg_tool->trans_info[X0],
+                               tg_tool->trans_info[Y0],
+                               tg_tool->trans_info[X1] - tg_tool->trans_info[X0],
+                               tg_tool->trans_info[Y1] - tg_tool->trans_info[Y0]);
 
   if (widget)
     g_object_set (widget,
@@ -248,21 +267,12 @@ gimp_scale_tool_recalc_matrix (GimpTransformTool *tr_tool,
                   NULL);
 }
 
-static gchar *
-gimp_scale_tool_get_undo_desc (GimpTransformTool *tr_tool)
-{
-  gint width  = ROUND (tr_tool->trans_info[X1] - tr_tool->trans_info[X0]);
-  gint height = ROUND (tr_tool->trans_info[Y1] - tr_tool->trans_info[Y0]);
-
-  return g_strdup_printf (C_("undo-type", "Scale to %d x %d"),
-                          width, height);
-}
-
 static void
-gimp_scale_tool_widget_changed (GimpToolWidget    *widget,
-                                GimpTransformTool *tr_tool)
+gimp_scale_tool_widget_changed (GimpToolWidget        *widget,
+                                GimpTransformGridTool *tg_tool)
 {
-  GimpMatrix3 *transform;
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpMatrix3       *transform;
 
   g_object_get (widget,
                 "transform", &transform,
@@ -270,24 +280,24 @@ gimp_scale_tool_widget_changed (GimpToolWidget    *widget,
 
   gimp_matrix3_transform_point (transform,
                                 tr_tool->x1, tr_tool->y1,
-                                &tr_tool->trans_info[X0],
-                                &tr_tool->trans_info[Y0]);
+                                &tg_tool->trans_info[X0],
+                                &tg_tool->trans_info[Y0]);
   gimp_matrix3_transform_point (transform,
                                 tr_tool->x2, tr_tool->y2,
-                                &tr_tool->trans_info[X1],
-                                &tr_tool->trans_info[Y1]);
+                                &tg_tool->trans_info[X1],
+                                &tg_tool->trans_info[Y1]);
 
   g_free (transform);
 
-  gimp_transform_tool_recalc_matrix (tr_tool, NULL);
+  gimp_transform_grid_tool_recalc_matrix (tg_tool, NULL);
 }
 
 static void
-gimp_scale_tool_size_notify (GtkWidget         *box,
-                             GParamSpec        *pspec,
-                             GimpTransformTool *tr_tool)
+gimp_scale_tool_size_notify (GtkWidget             *box,
+                             GParamSpec            *pspec,
+                             GimpTransformGridTool *tg_tool)
 {
-  GimpTransformOptions *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
+  GimpTransformGridOptions *options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
 
   if (! strcmp (pspec->name, "width") ||
       ! strcmp (pspec->name, "height"))
@@ -302,17 +312,17 @@ gimp_scale_tool_size_notify (GtkWidget         *box,
                     "height", &height,
                     NULL);
 
-      old_width  = ROUND (tr_tool->trans_info[X1] - tr_tool->trans_info[X0]);
-      old_height = ROUND (tr_tool->trans_info[Y1] - tr_tool->trans_info[Y0]);
+      old_width  = ROUND (tg_tool->trans_info[X1] - tg_tool->trans_info[X0]);
+      old_height = ROUND (tg_tool->trans_info[Y1] - tg_tool->trans_info[Y0]);
 
       if ((width != old_width) || (height != old_height))
         {
-          tr_tool->trans_info[X1] = tr_tool->trans_info[X0] + width;
-          tr_tool->trans_info[Y1] = tr_tool->trans_info[Y0] + height;
+          tg_tool->trans_info[X1] = tg_tool->trans_info[X0] + width;
+          tg_tool->trans_info[Y1] = tg_tool->trans_info[Y0] + height;
 
-          gimp_transform_tool_push_internal_undo (tr_tool);
+          gimp_transform_grid_tool_push_internal_undo (tg_tool);
 
-          gimp_transform_tool_recalc_matrix (tr_tool, tr_tool->widget);
+          gimp_transform_grid_tool_recalc_matrix (tg_tool, tg_tool->widget);
         }
     }
   else if (! strcmp (pspec->name, "keep-aspect"))
diff --git a/app/tools/gimpscaletool.h b/app/tools/gimpscaletool.h
index e0b5aaf3c6..5e6e3d3ae8 100644
--- a/app/tools/gimpscaletool.h
+++ b/app/tools/gimpscaletool.h
@@ -19,7 +19,7 @@
 #define __GIMP_SCALE_TOOL_H__
 
 
-#include "gimptransformtool.h"
+#include "gimptransformgridtool.h"
 
 
 #define GIMP_TYPE_SCALE_TOOL            (gimp_scale_tool_get_type ())
@@ -34,14 +34,14 @@ typedef struct _GimpScaleToolClass GimpScaleToolClass;
 
 struct _GimpScaleTool
 {
-  GimpTransformTool  parent_instance;
+  GimpTransformGridTool  parent_instance;
 
-  GtkWidget         *box;
+  GtkWidget             *box;
 };
 
 struct _GimpScaleToolClass
 {
-  GimpTransformToolClass parent_class;
+  GimpTransformGridToolClass parent_class;
 };
 
 
diff --git a/app/tools/gimpsheartool.c b/app/tools/gimpsheartool.c
index 17ae565384..d06b148368 100644
--- a/app/tools/gimpsheartool.c
+++ b/app/tools/gimpsheartool.c
@@ -36,7 +36,7 @@
 
 #include "gimpsheartool.h"
 #include "gimptoolcontrol.h"
-#include "gimptransformoptions.h"
+#include "gimptransformgridoptions.h"
 
 #include "gimp-intl.h"
 
@@ -55,25 +55,25 @@ enum
 
 /*  local function prototypes  */
 
-static void             gimp_shear_tool_dialog         (GimpTransformTool *tr_tool);
-static void             gimp_shear_tool_dialog_update  (GimpTransformTool *tr_tool);
+static gchar          * gimp_shear_tool_get_undo_desc  (GimpTransformTool     *tr_tool);
 
-static void             gimp_shear_tool_prepare        (GimpTransformTool *tr_tool);
-static GimpToolWidget * gimp_shear_tool_get_widget     (GimpTransformTool *tr_tool);
-static void             gimp_shear_tool_recalc_matrix  (GimpTransformTool *tr_tool,
-                                                        GimpToolWidget    *widget);
-static gchar          * gimp_shear_tool_get_undo_desc  (GimpTransformTool *tr_tool);
+static void             gimp_shear_tool_dialog         (GimpTransformGridTool *tg_tool);
+static void             gimp_shear_tool_dialog_update  (GimpTransformGridTool *tg_tool);
+static void             gimp_shear_tool_prepare        (GimpTransformGridTool *tg_tool);
+static GimpToolWidget * gimp_shear_tool_get_widget     (GimpTransformGridTool *tg_tool);
+static void             gimp_shear_tool_recalc_matrix  (GimpTransformGridTool *tg_tool,
+                                                        GimpToolWidget        *widget);
 
-static void             gimp_shear_tool_widget_changed (GimpToolWidget    *widget,
-                                                        GimpTransformTool *tr_tool);
+static void             gimp_shear_tool_widget_changed (GimpToolWidget        *widget,
+                                                        GimpTransformGridTool *tg_tool);
 
-static void             shear_x_mag_changed            (GtkAdjustment     *adj,
-                                                        GimpTransformTool *tr_tool);
-static void             shear_y_mag_changed            (GtkAdjustment     *adj,
-                                                        GimpTransformTool *tr_tool);
+static void             shear_x_mag_changed            (GtkAdjustment         *adj,
+                                                        GimpTransformGridTool *tg_tool);
+static void             shear_y_mag_changed            (GtkAdjustment         *adj,
+                                                        GimpTransformGridTool *tg_tool);
 
 
-G_DEFINE_TYPE (GimpShearTool, gimp_shear_tool, GIMP_TYPE_TRANSFORM_TOOL)
+G_DEFINE_TYPE (GimpShearTool, gimp_shear_tool, GIMP_TYPE_TRANSFORM_GRID_TOOL)
 
 
 void
@@ -81,8 +81,8 @@ gimp_shear_tool_register (GimpToolRegisterCallback  callback,
                           gpointer                  data)
 {
   (* callback) (GIMP_TYPE_SHEAR_TOOL,
-                GIMP_TYPE_TRANSFORM_OPTIONS,
-                gimp_transform_options_gui,
+                GIMP_TYPE_TRANSFORM_GRID_OPTIONS,
+                gimp_transform_grid_options_gui,
                 0,
                 "gimp-shear-tool",
                 _("Shear"),
@@ -96,38 +96,62 @@ gimp_shear_tool_register (GimpToolRegisterCallback  callback,
 static void
 gimp_shear_tool_class_init (GimpShearToolClass *klass)
 {
-  GimpTransformToolClass *trans_class = GIMP_TRANSFORM_TOOL_CLASS (klass);
+  GimpTransformToolClass     *tr_class = GIMP_TRANSFORM_TOOL_CLASS (klass);
+  GimpTransformGridToolClass *tg_class = GIMP_TRANSFORM_GRID_TOOL_CLASS (klass);
 
-  trans_class->dialog          = gimp_shear_tool_dialog;
-  trans_class->dialog_update   = gimp_shear_tool_dialog_update;
-  trans_class->prepare         = gimp_shear_tool_prepare;
-  trans_class->get_widget      = gimp_shear_tool_get_widget;
-  trans_class->recalc_matrix   = gimp_shear_tool_recalc_matrix;
-  trans_class->get_undo_desc   = gimp_shear_tool_get_undo_desc;
+  tr_class->get_undo_desc   = gimp_shear_tool_get_undo_desc;
 
-  trans_class->ok_button_label = _("_Shear");
+  tg_class->dialog          = gimp_shear_tool_dialog;
+  tg_class->dialog_update   = gimp_shear_tool_dialog_update;
+  tg_class->prepare         = gimp_shear_tool_prepare;
+  tg_class->get_widget      = gimp_shear_tool_get_widget;
+  tg_class->recalc_matrix   = gimp_shear_tool_recalc_matrix;
+
+  tr_class->progress_text   = _("Shearing");
+  tg_class->ok_button_label = _("_Shear");
 }
 
 static void
 gimp_shear_tool_init (GimpShearTool *shear_tool)
 {
-  GimpTool          *tool    = GIMP_TOOL (shear_tool);
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (shear_tool);
+  GimpTool *tool = GIMP_TOOL (shear_tool);
 
   gimp_tool_control_set_tool_cursor (tool->control, GIMP_TOOL_CURSOR_SHEAR);
+}
+
+static gchar *
+gimp_shear_tool_get_undo_desc (GimpTransformTool *tr_tool)
+{
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tr_tool);
+  gdouble                x       = tg_tool->trans_info[SHEAR_X];
+  gdouble                y       = tg_tool->trans_info[SHEAR_Y];
+
+  switch ((gint) tg_tool->trans_info[ORIENTATION])
+    {
+    case GIMP_ORIENTATION_HORIZONTAL:
+      return g_strdup_printf (C_("undo-type", "Shear horizontally by %-3.3g"),
+                              x);
+
+    case GIMP_ORIENTATION_VERTICAL:
+      return g_strdup_printf (C_("undo-type", "Shear vertically by %-3.3g"),
+                              y);
 
-  tr_tool->progress_text = _("Shearing");
+    default:
+      /* e.g. user entered numbers but no notification callback */
+      return g_strdup_printf (C_("undo-type", "Shear horizontally by %-3.3g, vertically by %-3.3g"),
+                              x, y);
+    }
 }
 
 static void
-gimp_shear_tool_dialog (GimpTransformTool *tr_tool)
+gimp_shear_tool_dialog (GimpTransformGridTool *tg_tool)
 {
-  GimpShearTool *shear = GIMP_SHEAR_TOOL (tr_tool);
+  GimpShearTool *shear = GIMP_SHEAR_TOOL (tg_tool);
   GtkWidget     *vbox;
   GtkWidget     *scale;
 
   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
-  gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (tr_tool->gui)), vbox,
+  gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (tg_tool->gui)), vbox,
                       FALSE, FALSE, 0);
   gtk_widget_show (vbox);
 
@@ -140,7 +164,7 @@ gimp_shear_tool_dialog (GimpTransformTool *tr_tool)
 
   g_signal_connect (shear->x_adj, "value-changed",
                     G_CALLBACK (shear_x_mag_changed),
-                    tr_tool);
+                    tg_tool);
 
   shear->y_adj = (GtkAdjustment *)
     gtk_adjustment_new (0, -65536, 65536, 1, 10, 0);
@@ -151,41 +175,42 @@ gimp_shear_tool_dialog (GimpTransformTool *tr_tool)
 
   g_signal_connect (shear->y_adj, "value-changed",
                     G_CALLBACK (shear_y_mag_changed),
-                    tr_tool);
+                    tg_tool);
 }
 
 static void
-gimp_shear_tool_dialog_update (GimpTransformTool *tr_tool)
+gimp_shear_tool_dialog_update (GimpTransformGridTool *tg_tool)
 {
-  GimpShearTool *shear = GIMP_SHEAR_TOOL (tr_tool);
+  GimpShearTool *shear = GIMP_SHEAR_TOOL (tg_tool);
 
-  gtk_adjustment_set_value (shear->x_adj, tr_tool->trans_info[SHEAR_X]);
-  gtk_adjustment_set_value (shear->y_adj, tr_tool->trans_info[SHEAR_Y]);
+  gtk_adjustment_set_value (shear->x_adj, tg_tool->trans_info[SHEAR_X]);
+  gtk_adjustment_set_value (shear->y_adj, tg_tool->trans_info[SHEAR_Y]);
 }
 
 static void
-gimp_shear_tool_prepare (GimpTransformTool *tr_tool)
+gimp_shear_tool_prepare (GimpTransformGridTool *tg_tool)
 {
-  tr_tool->trans_info[ORIENTATION] = GIMP_ORIENTATION_UNKNOWN;
-  tr_tool->trans_info[SHEAR_X]      = 0.0;
-  tr_tool->trans_info[SHEAR_Y]      = 0.0;
+  tg_tool->trans_info[ORIENTATION] = GIMP_ORIENTATION_UNKNOWN;
+  tg_tool->trans_info[SHEAR_X]     = 0.0;
+  tg_tool->trans_info[SHEAR_Y]     = 0.0;
 }
 
 static GimpToolWidget *
-gimp_shear_tool_get_widget (GimpTransformTool *tr_tool)
+gimp_shear_tool_get_widget (GimpTransformGridTool *tg_tool)
 {
-  GimpTool         *tool  = GIMP_TOOL (tr_tool);
-  GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
-  GimpToolWidget   *widget;
+  GimpTool          *tool    = GIMP_TOOL (tg_tool);
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpDisplayShell  *shell   = gimp_display_get_shell (tool->display);
+  GimpToolWidget    *widget;
 
   widget = gimp_tool_shear_grid_new (shell,
                                      tr_tool->x1,
                                      tr_tool->y1,
                                      tr_tool->x2,
                                      tr_tool->y2,
-                                     tr_tool->trans_info[ORIENTATION],
-                                     tr_tool->trans_info[SHEAR_X],
-                                     tr_tool->trans_info[SHEAR_Y]);
+                                     tg_tool->trans_info[ORIENTATION],
+                                     tg_tool->trans_info[SHEAR_X],
+                                     tg_tool->trans_info[SHEAR_Y]);
 
   g_object_set (widget,
                 "inside-function",  GIMP_TRANSFORM_FUNCTION_SHEAR,
@@ -195,27 +220,28 @@ gimp_shear_tool_get_widget (GimpTransformTool *tr_tool)
 
   g_signal_connect (widget, "changed",
                     G_CALLBACK (gimp_shear_tool_widget_changed),
-                    tr_tool);
+                    tg_tool);
 
   return widget;
 }
 
 static void
-gimp_shear_tool_recalc_matrix (GimpTransformTool *tr_tool,
-                               GimpToolWidget    *widget)
+gimp_shear_tool_recalc_matrix (GimpTransformGridTool *tg_tool,
+                               GimpToolWidget        *widget)
 {
-  gdouble amount;
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  gdouble            amount;
 
-  if (tr_tool->trans_info[SHEAR_X] == 0.0 &&
-      tr_tool->trans_info[SHEAR_Y] == 0.0)
+  if (tg_tool->trans_info[SHEAR_X] == 0.0 &&
+      tg_tool->trans_info[SHEAR_Y] == 0.0)
     {
-      tr_tool->trans_info[ORIENTATION] = GIMP_ORIENTATION_UNKNOWN;
+      tg_tool->trans_info[ORIENTATION] = GIMP_ORIENTATION_UNKNOWN;
     }
 
-  if (tr_tool->trans_info[ORIENTATION] == GIMP_ORIENTATION_HORIZONTAL)
-    amount = tr_tool->trans_info[SHEAR_X];
+  if (tg_tool->trans_info[ORIENTATION] == GIMP_ORIENTATION_HORIZONTAL)
+    amount = tg_tool->trans_info[SHEAR_X];
   else
-    amount = tr_tool->trans_info[SHEAR_Y];
+    amount = tg_tool->trans_info[SHEAR_Y];
 
   gimp_matrix3_identity (&tr_tool->transform);
   gimp_transform_matrix_shear (&tr_tool->transform,
@@ -223,7 +249,7 @@ gimp_shear_tool_recalc_matrix (GimpTransformTool *tr_tool,
                                tr_tool->y1,
                                tr_tool->x2 - tr_tool->x1,
                                tr_tool->y2 - tr_tool->y1,
-                               tr_tool->trans_info[ORIENTATION],
+                               tg_tool->trans_info[ORIENTATION],
                                amount);
 
   if (widget)
@@ -233,86 +259,63 @@ gimp_shear_tool_recalc_matrix (GimpTransformTool *tr_tool,
                   "y1",          (gdouble) tr_tool->y1,
                   "x2",          (gdouble) tr_tool->x2,
                   "y2",          (gdouble) tr_tool->y2,
-                  "orientation", (gint) tr_tool->trans_info[ORIENTATION],
-                  "shear-x",     tr_tool->trans_info[SHEAR_X],
-                  "shear-y",     tr_tool->trans_info[SHEAR_Y],
+                  "orientation", (gint) tg_tool->trans_info[ORIENTATION],
+                  "shear-x",     tg_tool->trans_info[SHEAR_X],
+                  "shear-y",     tg_tool->trans_info[SHEAR_Y],
                   NULL);
 }
 
-static gchar *
-gimp_shear_tool_get_undo_desc (GimpTransformTool *tr_tool)
-{
-  gdouble x = tr_tool->trans_info[SHEAR_X];
-  gdouble y = tr_tool->trans_info[SHEAR_Y];
-
-  switch ((gint) tr_tool->trans_info[ORIENTATION])
-    {
-    case GIMP_ORIENTATION_HORIZONTAL:
-      return g_strdup_printf (C_("undo-type", "Shear horizontally by %-3.3g"),
-                              x);
-
-    case GIMP_ORIENTATION_VERTICAL:
-      return g_strdup_printf (C_("undo-type", "Shear vertically by %-3.3g"),
-                              y);
-
-    default:
-      /* e.g. user entered numbers but no notification callback */
-      return g_strdup_printf (C_("undo-type", "Shear horizontally by %-3.3g, vertically by %-3.3g"),
-                              x, y);
-    }
-}
-
 static void
-gimp_shear_tool_widget_changed (GimpToolWidget    *widget,
-                                GimpTransformTool *tr_tool)
+gimp_shear_tool_widget_changed (GimpToolWidget        *widget,
+                                GimpTransformGridTool *tg_tool)
 {
   GimpOrientationType orientation;
 
   g_object_get (widget,
                 "orientation", &orientation,
-                "shear-x",     &tr_tool->trans_info[SHEAR_X],
-                "shear-y",     &tr_tool->trans_info[SHEAR_Y],
+                "shear-x",     &tg_tool->trans_info[SHEAR_X],
+                "shear-y",     &tg_tool->trans_info[SHEAR_Y],
                 NULL);
 
-  tr_tool->trans_info[ORIENTATION] = orientation;
+  tg_tool->trans_info[ORIENTATION] = orientation;
 
-  gimp_transform_tool_recalc_matrix (tr_tool, NULL);
+  gimp_transform_grid_tool_recalc_matrix (tg_tool, NULL);
 }
 
 static void
-shear_x_mag_changed (GtkAdjustment     *adj,
-                     GimpTransformTool *tr_tool)
+shear_x_mag_changed (GtkAdjustment         *adj,
+                     GimpTransformGridTool *tg_tool)
 {
   gdouble value = gtk_adjustment_get_value (adj);
 
-  if (value != tr_tool->trans_info[SHEAR_X])
+  if (value != tg_tool->trans_info[SHEAR_X])
     {
-      tr_tool->trans_info[ORIENTATION] = GIMP_ORIENTATION_HORIZONTAL;
+      tg_tool->trans_info[ORIENTATION] = GIMP_ORIENTATION_HORIZONTAL;
 
-      tr_tool->trans_info[SHEAR_X] = value;
-      tr_tool->trans_info[SHEAR_Y] = 0.0;  /* can only shear in one axis */
+      tg_tool->trans_info[SHEAR_X] = value;
+      tg_tool->trans_info[SHEAR_Y] = 0.0;  /* can only shear in one axis */
 
-      gimp_transform_tool_push_internal_undo (tr_tool);
+      gimp_transform_grid_tool_push_internal_undo (tg_tool);
 
-      gimp_transform_tool_recalc_matrix (tr_tool, tr_tool->widget);
+      gimp_transform_grid_tool_recalc_matrix (tg_tool, tg_tool->widget);
     }
 }
 
 static void
-shear_y_mag_changed (GtkAdjustment     *adj,
-                     GimpTransformTool *tr_tool)
+shear_y_mag_changed (GtkAdjustment         *adj,
+                     GimpTransformGridTool *tg_tool)
 {
   gdouble value = gtk_adjustment_get_value (adj);
 
-  if (value != tr_tool->trans_info[SHEAR_Y])
+  if (value != tg_tool->trans_info[SHEAR_Y])
     {
-      tr_tool->trans_info[ORIENTATION] = GIMP_ORIENTATION_VERTICAL;
+      tg_tool->trans_info[ORIENTATION] = GIMP_ORIENTATION_VERTICAL;
 
-      tr_tool->trans_info[SHEAR_Y] = value;
-      tr_tool->trans_info[SHEAR_X] = 0.0;  /* can only shear in one axis */
+      tg_tool->trans_info[SHEAR_Y] = value;
+      tg_tool->trans_info[SHEAR_X] = 0.0;  /* can only shear in one axis */
 
-      gimp_transform_tool_push_internal_undo (tr_tool);
+      gimp_transform_grid_tool_push_internal_undo (tg_tool);
 
-      gimp_transform_tool_recalc_matrix (tr_tool, tr_tool->widget);
+      gimp_transform_grid_tool_recalc_matrix (tg_tool, tg_tool->widget);
     }
 }
diff --git a/app/tools/gimpsheartool.h b/app/tools/gimpsheartool.h
index 32d278a02e..a26a4a0b83 100644
--- a/app/tools/gimpsheartool.h
+++ b/app/tools/gimpsheartool.h
@@ -19,7 +19,7 @@
 #define __GIMP_SHEAR_TOOL_H__
 
 
-#include "gimptransformtool.h"
+#include "gimptransformgridtool.h"
 
 
 #define GIMP_TYPE_SHEAR_TOOL            (gimp_shear_tool_get_type ())
@@ -35,15 +35,15 @@ typedef struct _GimpShearToolClass GimpShearToolClass;
 
 struct _GimpShearTool
 {
-  GimpTransformTool  parent_instance;
+  GimpTransformGridTool  parent_instance;
 
-  GtkAdjustment     *x_adj;
-  GtkAdjustment     *y_adj;
+  GtkAdjustment         *x_adj;
+  GtkAdjustment         *y_adj;
 };
 
 struct _GimpShearToolClass
 {
-  GimpTransformToolClass  parent_class;
+  GimpTransformGridToolClass  parent_class;
 };
 
 
diff --git a/app/tools/gimptransformgridoptions.c b/app/tools/gimptransformgridoptions.c
new file mode 100644
index 0000000000..4e1802662d
--- /dev/null
+++ b/app/tools/gimptransformgridoptions.c
@@ -0,0 +1,548 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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 "libgimpconfig/gimpconfig.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "tools-types.h"
+
+#include "core/gimp.h"
+#include "core/gimptoolinfo.h"
+
+#include "widgets/gimppropwidgets.h"
+#include "widgets/gimpspinscale.h"
+#include "widgets/gimpwidgets-utils.h"
+
+#include "gimprotatetool.h"
+#include "gimpscaletool.h"
+#include "gimpunifiedtransformtool.h"
+#include "gimptooloptions-gui.h"
+#include "gimptransformgridoptions.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+  PROP_0,
+  PROP_DIRECTION,
+  PROP_SHOW_PREVIEW,
+  PROP_PREVIEW_OPACITY,
+  PROP_GRID_TYPE,
+  PROP_GRID_SIZE,
+  PROP_CONSTRAIN_MOVE,
+  PROP_CONSTRAIN_SCALE,
+  PROP_CONSTRAIN_ROTATE,
+  PROP_CONSTRAIN_SHEAR,
+  PROP_CONSTRAIN_PERSPECTIVE,
+  PROP_FROMPIVOT_SCALE,
+  PROP_FROMPIVOT_SHEAR,
+  PROP_FROMPIVOT_PERSPECTIVE,
+  PROP_CORNERSNAP,
+  PROP_FIXEDPIVOT,
+};
+
+
+static void       gimp_transform_grid_options_set_property (GObject      *object,
+                                                            guint         property_id,
+                                                            const GValue *value,
+                                                            GParamSpec   *pspec);
+static void       gimp_transform_grid_options_get_property (GObject      *object,
+                                                            guint         property_id,
+                                                            GValue       *value,
+                                                            GParamSpec   *pspec);
+
+static gboolean   gimp_transform_grid_options_sync_grid    (GBinding     *binding,
+                                                            const GValue *source_value,
+                                                            GValue       *target_value,
+                                                            gpointer      user_data);
+
+
+G_DEFINE_TYPE (GimpTransformGridOptions, gimp_transform_grid_options,
+               GIMP_TYPE_TRANSFORM_OPTIONS)
+
+#define parent_class gimp_transform_grid_options_parent_class
+
+
+static void
+gimp_transform_grid_options_class_init (GimpTransformGridOptionsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->set_property = gimp_transform_grid_options_set_property;
+  object_class->get_property = gimp_transform_grid_options_get_property;
+
+  g_object_class_override_property (object_class, PROP_DIRECTION,
+                                    "direction");
+
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_PREVIEW,
+                            "show-preview",
+                            _("Show image preview"),
+                            _("Show a preview of the transform_grided image"),
+                            TRUE,
+                            GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_PREVIEW_OPACITY,
+                           "preview-opacity",
+                           _("Image opacity"),
+                           _("Opacity of the preview image"),
+                           0.0, 1.0, 1.0,
+                           GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_ENUM (object_class, PROP_GRID_TYPE,
+                         "grid-type",
+                         _("Guides"),
+                         _("Composition guides such as rule of thirds"),
+                         GIMP_TYPE_GUIDES_TYPE,
+                         GIMP_GUIDES_NONE,
+                         GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_INT (object_class, PROP_GRID_SIZE,
+                        "grid-size",
+                        NULL,
+                        _("Size of a grid cell for variable number "
+                          "of composition guides"),
+                        1, 128, 15,
+                        GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CONSTRAIN_MOVE,
+                            "constrain-move",
+                            NULL, NULL,
+                            FALSE,
+                            GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CONSTRAIN_SCALE,
+                            "constrain-scale",
+                            NULL, NULL,
+                            FALSE,
+                            GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CONSTRAIN_ROTATE,
+                            "constrain-rotate",
+                            NULL, NULL,
+                            FALSE,
+                            GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CONSTRAIN_SHEAR,
+                            "constrain-shear",
+                            NULL, NULL,
+                            TRUE,
+                            GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CONSTRAIN_PERSPECTIVE,
+                            "constrain-perspective",
+                            NULL, NULL,
+                            FALSE,
+                            GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_FROMPIVOT_SCALE,
+                            "frompivot-scale",
+                            NULL, NULL,
+                            FALSE,
+                            GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_FROMPIVOT_SHEAR,
+                            "frompivot-shear",
+                            NULL, NULL,
+                            FALSE,
+                            GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_FROMPIVOT_PERSPECTIVE,
+                            "frompivot-perspective",
+                            NULL, NULL,
+                            FALSE,
+                            GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CORNERSNAP,
+                            "cornersnap",
+                            NULL, NULL,
+                            FALSE,
+                            GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_FIXEDPIVOT,
+                            "fixedpivot",
+                            NULL, NULL,
+                            FALSE,
+                            GIMP_PARAM_STATIC_STRINGS);
+}
+
+static void
+gimp_transform_grid_options_init (GimpTransformGridOptions *options)
+{
+}
+
+static void
+gimp_transform_grid_options_set_property (GObject      *object,
+                                     guint         property_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+  GimpTransformGridOptions *options           = GIMP_TRANSFORM_GRID_OPTIONS (object);
+  GimpTransformOptions     *transform_options = GIMP_TRANSFORM_OPTIONS (object);
+
+  switch (property_id)
+    {
+    case PROP_DIRECTION:
+      transform_options->direction = g_value_get_enum (value);
+
+      /* Expected default for corrective transform_grid is to see the
+       * original image only.
+       */
+      g_object_set (options,
+                    "show-preview",
+                    transform_options->direction != GIMP_TRANSFORM_BACKWARD,
+                    NULL);
+      break;
+    case PROP_SHOW_PREVIEW:
+      options->show_preview = g_value_get_boolean (value);
+      break;
+    case PROP_PREVIEW_OPACITY:
+      options->preview_opacity = g_value_get_double (value);
+      break;
+    case PROP_GRID_TYPE:
+      options->grid_type = g_value_get_enum (value);
+      break;
+    case PROP_GRID_SIZE:
+      options->grid_size = g_value_get_int (value);
+      break;
+    case PROP_CONSTRAIN_MOVE:
+      options->constrain_move = g_value_get_boolean (value);
+      break;
+    case PROP_CONSTRAIN_SCALE:
+      options->constrain_scale = g_value_get_boolean (value);
+      break;
+    case PROP_CONSTRAIN_ROTATE:
+      options->constrain_rotate = g_value_get_boolean (value);
+      break;
+    case PROP_CONSTRAIN_SHEAR:
+      options->constrain_shear = g_value_get_boolean (value);
+      break;
+    case PROP_CONSTRAIN_PERSPECTIVE:
+      options->constrain_perspective = g_value_get_boolean (value);
+      break;
+    case PROP_FROMPIVOT_SCALE:
+      options->frompivot_scale = g_value_get_boolean (value);
+      break;
+    case PROP_FROMPIVOT_SHEAR:
+      options->frompivot_shear = g_value_get_boolean (value);
+      break;
+    case PROP_FROMPIVOT_PERSPECTIVE:
+      options->frompivot_perspective = g_value_get_boolean (value);
+      break;
+    case PROP_CORNERSNAP:
+      options->cornersnap = g_value_get_boolean (value);
+      break;
+    case PROP_FIXEDPIVOT:
+      options->fixedpivot = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_transform_grid_options_get_property (GObject    *object,
+                                          guint       property_id,
+                                          GValue     *value,
+                                          GParamSpec *pspec)
+{
+  GimpTransformGridOptions *options           = GIMP_TRANSFORM_GRID_OPTIONS (object);
+  GimpTransformOptions     *transform_options = GIMP_TRANSFORM_OPTIONS (object);
+
+  switch (property_id)
+    {
+    case PROP_DIRECTION:
+      g_value_set_enum (value, transform_options->direction);
+      break;
+    case PROP_SHOW_PREVIEW:
+      g_value_set_boolean (value, options->show_preview);
+      break;
+    case PROP_PREVIEW_OPACITY:
+      g_value_set_double (value, options->preview_opacity);
+      break;
+    case PROP_GRID_TYPE:
+      g_value_set_enum (value, options->grid_type);
+      break;
+    case PROP_GRID_SIZE:
+      g_value_set_int (value, options->grid_size);
+      break;
+    case PROP_CONSTRAIN_MOVE:
+      g_value_set_boolean (value, options->constrain_move);
+      break;
+    case PROP_CONSTRAIN_SCALE:
+      g_value_set_boolean (value, options->constrain_scale);
+      break;
+    case PROP_CONSTRAIN_ROTATE:
+      g_value_set_boolean (value, options->constrain_rotate);
+      break;
+    case PROP_CONSTRAIN_SHEAR:
+      g_value_set_boolean (value, options->constrain_shear);
+      break;
+    case PROP_CONSTRAIN_PERSPECTIVE:
+      g_value_set_boolean (value, options->constrain_perspective);
+      break;
+    case PROP_FROMPIVOT_SCALE:
+      g_value_set_boolean (value, options->frompivot_scale);
+      break;
+    case PROP_FROMPIVOT_SHEAR:
+      g_value_set_boolean (value, options->frompivot_shear);
+      break;
+    case PROP_FROMPIVOT_PERSPECTIVE:
+      g_value_set_boolean (value, options->frompivot_perspective);
+      break;
+    case PROP_CORNERSNAP:
+      g_value_set_boolean (value, options->cornersnap);
+      break;
+    case PROP_FIXEDPIVOT:
+      g_value_set_boolean (value, options->fixedpivot);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+/**
+ * gimp_transform_grid_options_gui:
+ * @tool_options: a #GimpToolOptions
+ *
+ * Build the TransformGrid Tool Options.
+ *
+ * Return value: a container holding the transform_grid tool options
+ **/
+GtkWidget *
+gimp_transform_grid_options_gui (GimpToolOptions *tool_options)
+{
+  GObject         *config = G_OBJECT (tool_options);
+  GtkWidget       *vbox;
+  GtkWidget       *frame;
+  GtkWidget       *combo;
+  GtkWidget       *scale;
+  GtkWidget       *grid_box;
+  GdkModifierType  extend_mask    = gimp_get_extend_selection_mask ();
+  GdkModifierType  constrain_mask = gimp_get_constrain_behavior_mask ();
+
+  vbox = gimp_transform_options_gui (tool_options, TRUE, TRUE, TRUE);
+
+  /*  the preview frame  */
+  scale = gimp_prop_spin_scale_new (config, "preview-opacity", NULL,
+                                    0.01, 0.1, 0);
+  gimp_prop_widget_set_factor (scale, 100.0, 0.0, 0.0, 1);
+  frame = gimp_prop_expanding_frame_new (config, "show-preview", NULL,
+                                         scale, NULL);
+  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+  gtk_widget_show (frame);
+
+  /*  the guides frame  */
+  frame = gimp_frame_new (NULL);
+  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+  gtk_widget_show (frame);
+
+  /*  the guides type menu  */
+  combo = gimp_prop_enum_combo_box_new (config, "grid-type", 0, 0);
+  gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (combo), _("Guides"));
+  g_object_set (combo, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+  gtk_frame_set_label_widget (GTK_FRAME (frame), combo);
+  gtk_widget_show (combo);
+
+  /*  the grid density scale  */
+  scale = gimp_prop_spin_scale_new (config, "grid-size", NULL,
+                                    1.8, 8.0, 0);
+  gimp_spin_scale_set_label (GIMP_SPIN_SCALE (scale), NULL);
+  gtk_container_add (GTK_CONTAINER (frame), scale);
+
+  g_object_bind_property_full (config, "grid-type",
+                               scale,  "visible",
+                               G_BINDING_SYNC_CREATE,
+                               gimp_transform_grid_options_sync_grid,
+                               NULL,
+                               NULL, NULL);
+
+  if (tool_options->tool_info->tool_type == GIMP_TYPE_ROTATE_TOOL)
+    {
+      GtkWidget *button;
+      gchar     *label;
+
+      label = g_strdup_printf (_("15 degrees (%s)"),
+                               gimp_get_mod_string (extend_mask));
+
+      button = gimp_prop_check_button_new (config, "constrain-rotate", label);
+      gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+      gtk_widget_show (button);
+
+      gimp_help_set_help_data (button, _("Limit rotation steps to 15 degrees"),
+                               NULL);
+
+      g_free (label);
+    }
+  else if (tool_options->tool_info->tool_type == GIMP_TYPE_SCALE_TOOL)
+    {
+      GtkWidget *button;
+      gchar     *label;
+
+      label = g_strdup_printf (_("Keep aspect (%s)"),
+                               gimp_get_mod_string (extend_mask));
+
+      button = gimp_prop_check_button_new (config, "constrain-scale", label);
+      gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+      gtk_widget_show (button);
+
+      gimp_help_set_help_data (button, _("Keep the original aspect ratio"),
+                               NULL);
+
+      g_free (label);
+
+      label = g_strdup_printf (_("Around center (%s)"),
+                               gimp_get_mod_string (constrain_mask));
+
+      button = gimp_prop_check_button_new (config, "frompivot-scale", label);
+      gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+      gtk_widget_show (button);
+
+      gimp_help_set_help_data (button, _("Scale around the center point"),
+                               NULL);
+
+      g_free (label);
+    }
+  else if (tool_options->tool_info->tool_type == GIMP_TYPE_UNIFIED_TRANSFORM_TOOL)
+    {
+      struct
+      {
+        GdkModifierType  mod;
+        gchar           *name;
+        gchar           *desc;
+        gchar           *tip;
+      }
+      opt_list[] =
+      {
+        { extend_mask, NULL, N_("Constrain (%s)") },
+        { extend_mask, "constrain-move", N_("Move"),
+          N_("Constrain movement to 45 degree angles from center (%s)") },
+        { extend_mask, "constrain-scale", N_("Scale"),
+          N_("Maintain aspect ratio when scaling (%s)") },
+        { extend_mask, "constrain-rotate", N_("Rotate"),
+          N_("Constrain rotation to 15 degree increments (%s)") },
+        { extend_mask, "constrain-shear", N_("Shear"),
+          N_("Shear along edge direction only (%s)") },
+        { extend_mask, "constrain-perspective", N_("Perspective"),
+          N_("Constrain perspective handles to move along edges and diagonal (%s)") },
+
+        { constrain_mask, NULL,
+          N_("From pivot  (%s)") },
+        { constrain_mask, "frompivot-scale", N_("Scale"),
+          N_("Scale from pivot point (%s)") },
+        { constrain_mask, "frompivot-shear", N_("Shear"),
+          N_("Shear opposite edge by same amount (%s)") },
+        { constrain_mask, "frompivot-perspective", N_("Perspective"),
+          N_("Maintain position of pivot while changing perspective (%s)") },
+
+        { 0, NULL,
+          N_("Pivot") },
+        { extend_mask, "cornersnap", N_("Snap (%s)"),
+          N_("Snap pivot to corners and center (%s)") },
+        { 0, "fixedpivot", N_("Lock"),
+          N_("Lock pivot position to canvas") },
+      };
+
+      GtkWidget *button;
+      gchar     *label;
+      gint       i;
+
+      frame = NULL;
+
+      for (i = 0; i < G_N_ELEMENTS (opt_list); i++)
+        {
+          if (! opt_list[i].name && ! opt_list[i].desc)
+            {
+              frame = NULL;
+              continue;
+            }
+
+          label = g_strdup_printf (gettext (opt_list[i].desc),
+                                   gimp_get_mod_string (opt_list[i].mod));
+
+          if (opt_list[i].name)
+            {
+              button = gimp_prop_check_button_new (config, opt_list[i].name,
+                                                   label);
+
+              gtk_box_pack_start (GTK_BOX (frame ? grid_box : vbox),
+                                  button, FALSE, FALSE, 0);
+
+              gtk_widget_show (button);
+
+              g_free (label);
+              label = g_strdup_printf (gettext (opt_list[i].tip),
+                                       gimp_get_mod_string (opt_list[i].mod));
+
+              gimp_help_set_help_data (button, label, NULL);
+            }
+          else
+            {
+              frame = gimp_frame_new (label);
+              gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+              gtk_widget_show (frame);
+
+              grid_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+              gtk_container_add (GTK_CONTAINER (frame), grid_box);
+              gtk_widget_show (grid_box);
+            }
+
+          g_free (label);
+        }
+    }
+
+  return vbox;
+}
+
+gboolean
+gimp_transform_grid_options_show_preview (GimpTransformGridOptions *options)
+{
+  GimpTransformOptions *transform_options;
+
+  g_return_val_if_fail (GIMP_IS_TRANSFORM_GRID_OPTIONS (options), FALSE);
+
+  transform_options = GIMP_TRANSFORM_OPTIONS (options);
+
+  return (options->show_preview &&
+          transform_options->type == GIMP_TRANSFORM_TYPE_LAYER);
+}
+
+
+/*  private functions  */
+
+static gboolean
+gimp_transform_grid_options_sync_grid (GBinding     *binding,
+                                       const GValue *source_value,
+                                       GValue       *target_value,
+                                       gpointer      user_data)
+{
+  GimpGuidesType type = g_value_get_enum (source_value);
+
+  g_value_set_boolean (target_value,
+                       type == GIMP_GUIDES_N_LINES ||
+                       type == GIMP_GUIDES_SPACING);
+
+  return TRUE;
+}
+
diff --git a/app/tools/gimptransformgridoptions.h b/app/tools/gimptransformgridoptions.h
new file mode 100644
index 0000000000..94664d349b
--- /dev/null
+++ b/app/tools/gimptransformgridoptions.h
@@ -0,0 +1,69 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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_TRANSFORM_GRID_OPTIONS_H__
+#define __GIMP_TRANSFORM_GRID_OPTIONS_H__
+
+
+#include "gimptransformoptions.h"
+
+
+#define GIMP_TYPE_TRANSFORM_GRID_OPTIONS            (gimp_transform_grid_options_get_type ())
+#define GIMP_TRANSFORM_GRID_OPTIONS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GIMP_TYPE_TRANSFORM_GRID_OPTIONS, GimpTransformGridOptions))
+#define GIMP_TRANSFORM_GRID_OPTIONS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
GIMP_TYPE_TRANSFORM_GRID_OPTIONS, GimpTransformGridOptionsClass))
+#define GIMP_IS_TRANSFORM_GRID_OPTIONS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GIMP_TYPE_TRANSFORM_GRID_OPTIONS))
+#define GIMP_IS_TRANSFORM_GRID_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GIMP_TYPE_TRANSFORM_GRID_OPTIONS))
+#define GIMP_TRANSFORM_GRID_OPTIONS_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GIMP_TYPE_TRANSFORM_GRID_OPTIONS, GimpTransformGridOptionsClass))
+
+
+typedef struct _GimpTransformGridOptions      GimpTransformGridOptions;
+typedef struct _GimpTransformGridOptionsClass GimpTransformGridOptionsClass;
+
+struct _GimpTransformGridOptions
+{
+  GimpTransformOptions  parent_instance;
+
+  gboolean              show_preview;
+  gdouble               preview_opacity;
+  GimpGuidesType        grid_type;
+  gint                  grid_size;
+  gboolean              constrain_move;
+  gboolean              constrain_scale;
+  gboolean              constrain_rotate;
+  gboolean              constrain_shear;
+  gboolean              constrain_perspective;
+  gboolean              frompivot_scale;
+  gboolean              frompivot_shear;
+  gboolean              frompivot_perspective;
+  gboolean              cornersnap;
+  gboolean              fixedpivot;
+};
+
+struct _GimpTransformGridOptionsClass
+{
+  GimpTransformOptionsClass  parent_class;
+};
+
+
+GType       gimp_transform_grid_options_get_type     (void) G_GNUC_CONST;
+
+GtkWidget * gimp_transform_grid_options_gui          (GimpToolOptions          *tool_options);
+
+gboolean    gimp_transform_grid_options_show_preview (GimpTransformGridOptions *options);
+
+
+#endif /* __GIMP_TRANSFORM_GRID_OPTIONS_H__ */
diff --git a/app/tools/gimptransformgridtool.c b/app/tools/gimptransformgridtool.c
new file mode 100644
index 0000000000..26bcd176bb
--- /dev/null
+++ b/app/tools/gimptransformgridtool.c
@@ -0,0 +1,1215 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others
+ *
+ * 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 "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "tools-types.h"
+
+#include "core/gimp.h"
+#include "core/gimpboundary.h"
+#include "core/gimperror.h"
+#include "core/gimpimage.h"
+#include "core/gimpimage-undo.h"
+#include "core/gimpimage-undo-push.h"
+#include "core/gimplayer.h"
+#include "core/gimplayermask.h"
+#include "core/gimpprojection.h"
+#include "core/gimptoolinfo.h"
+
+#include "vectors/gimpvectors.h"
+#include "vectors/gimpstroke.h"
+
+#include "widgets/gimpwidgets-utils.h"
+
+#include "display/gimpcanvasitem.h"
+#include "display/gimpdisplay.h"
+#include "display/gimptoolgui.h"
+#include "display/gimptoolwidget.h"
+
+#include "gimptoolcontrol.h"
+#include "gimptransformgridoptions.h"
+#include "gimptransformgridtool.h"
+#include "gimptransformgridtoolundo.h"
+#include "gimptransformoptions.h"
+
+#include "gimp-intl.h"
+
+
+#define RESPONSE_RESET 1
+
+
+static void      gimp_transform_grid_tool_finalize           (GObject                *object);
+
+static gboolean  gimp_transform_grid_tool_initialize         (GimpTool               *tool,
+                                                              GimpDisplay            *display,
+                                                              GError                **error);
+static void      gimp_transform_grid_tool_control            (GimpTool               *tool,
+                                                              GimpToolAction          action,
+                                                              GimpDisplay            *display);
+static void      gimp_transform_grid_tool_button_press       (GimpTool               *tool,
+                                                              const GimpCoords       *coords,
+                                                              guint32                 time,
+                                                              GdkModifierType         state,
+                                                              GimpButtonPressType     press_type,
+                                                              GimpDisplay            *display);
+static void      gimp_transform_grid_tool_button_release     (GimpTool               *tool,
+                                                              const GimpCoords       *coords,
+                                                              guint32                 time,
+                                                              GdkModifierType         state,
+                                                              GimpButtonReleaseType   release_type,
+                                                              GimpDisplay            *display);
+static void      gimp_transform_grid_tool_motion             (GimpTool               *tool,
+                                                              const GimpCoords       *coords,
+                                                              guint32                 time,
+                                                              GdkModifierType         state,
+                                                              GimpDisplay            *display);
+static void      gimp_transform_grid_tool_modifier_key       (GimpTool               *tool,
+                                                              GdkModifierType         key,
+                                                              gboolean                press,
+                                                              GdkModifierType         state,
+                                                              GimpDisplay            *display);
+static void      gimp_transform_grid_tool_cursor_update      (GimpTool               *tool,
+                                                              const GimpCoords       *coords,
+                                                              GdkModifierType         state,
+                                                              GimpDisplay            *display);
+static const gchar * gimp_transform_grid_tool_can_undo       (GimpTool               *tool,
+                                                              GimpDisplay            *display);
+static const gchar * gimp_transform_grid_tool_can_redo       (GimpTool               *tool,
+                                                              GimpDisplay            *display);
+static gboolean  gimp_transform_grid_tool_undo               (GimpTool               *tool,
+                                                              GimpDisplay            *display);
+static gboolean  gimp_transform_grid_tool_redo               (GimpTool               *tool,
+                                                              GimpDisplay            *display);
+static void      gimp_transform_grid_tool_options_notify     (GimpTool               *tool,
+                                                              GimpToolOptions        *options,
+                                                              const GParamSpec       *pspec);
+
+static void      gimp_transform_grid_tool_draw               (GimpDrawTool           *draw_tool);
+
+static void gimp_transform_grid_tool_transform_recalc_matrix (GimpTransformTool      *tr_tool);
+static GeglBuffer * gimp_transform_grid_tool_transform       (GimpTransformTool      *tr_tool,
+                                                              GimpItem               *item,
+                                                              GeglBuffer             *orig_buffer,
+                                                              gint                    orig_offset_x,
+                                                              gint                    orig_offset_y,
+                                                              GimpColorProfile      **buffer_profile,
+                                                              gint                   *new_offset_x,
+                                                              gint                   *new_offset_y);
+
+static GeglBuffer * gimp_transform_grid_tool_real_transform  (GimpTransformGridTool  *tg_tool,
+                                                              GimpItem               *item,
+                                                              GeglBuffer             *orig_buffer,
+                                                              gint                    orig_offset_x,
+                                                              gint                    orig_offset_y,
+                                                              GimpColorProfile      **buffer_profile,
+                                                              gint                   *new_offset_x,
+                                                              gint                   *new_offset_y);
+
+static void      gimp_transform_grid_tool_widget_changed     (GimpToolWidget         *widget,
+                                                              GimpTransformGridTool  *tg_tool);
+static void      gimp_transform_grid_tool_widget_response    (GimpToolWidget         *widget,
+                                                              gint                    response_id,
+                                                              GimpTransformGridTool  *tg_tool);
+
+static void      gimp_transform_grid_tool_halt               (GimpTransformGridTool  *tg_tool);
+static void      gimp_transform_grid_tool_commit             (GimpTransformGridTool  *tg_tool);
+
+static void      gimp_transform_grid_tool_dialog             (GimpTransformGridTool  *tg_tool);
+static void      gimp_transform_grid_tool_dialog_update      (GimpTransformGridTool  *tg_tool);
+static void      gimp_transform_grid_tool_prepare            (GimpTransformGridTool  *tg_tool,
+                                                              GimpDisplay            *display);
+static GimpToolWidget * gimp_transform_grid_tool_get_widget  (GimpTransformGridTool  *tg_tool);
+
+static void      gimp_transform_grid_tool_response           (GimpToolGui            *gui,
+                                                              gint                    response_id,
+                                                              GimpTransformGridTool  *tg_tool);
+
+static void      gimp_transform_grid_tool_update_sensitivity (GimpTransformGridTool  *tg_tool);
+static void      gimp_transform_grid_tool_hide_active_item   (GimpTransformGridTool  *tg_tool,
+                                                              GimpItem               *item);
+static void      gimp_transform_grid_tool_show_active_item   (GimpTransformGridTool  *tg_tool);
+
+static TransInfo * trans_info_new  (void);
+static void        trans_info_free (TransInfo *info);
+
+
+G_DEFINE_TYPE (GimpTransformGridTool, gimp_transform_grid_tool, GIMP_TYPE_TRANSFORM_TOOL)
+
+#define parent_class gimp_transform_grid_tool_parent_class
+
+
+static void
+gimp_transform_grid_tool_class_init (GimpTransformGridToolClass *klass)
+{
+  GObjectClass           *object_class = G_OBJECT_CLASS (klass);
+  GimpToolClass          *tool_class   = GIMP_TOOL_CLASS (klass);
+  GimpDrawToolClass      *draw_class   = GIMP_DRAW_TOOL_CLASS (klass);
+  GimpTransformToolClass *tr_class     = GIMP_TRANSFORM_TOOL_CLASS (klass);
+
+  object_class->finalize     = gimp_transform_grid_tool_finalize;
+
+  tool_class->initialize     = gimp_transform_grid_tool_initialize;
+  tool_class->control        = gimp_transform_grid_tool_control;
+  tool_class->button_press   = gimp_transform_grid_tool_button_press;
+  tool_class->button_release = gimp_transform_grid_tool_button_release;
+  tool_class->motion         = gimp_transform_grid_tool_motion;
+  tool_class->modifier_key   = gimp_transform_grid_tool_modifier_key;
+  tool_class->cursor_update  = gimp_transform_grid_tool_cursor_update;
+  tool_class->can_undo       = gimp_transform_grid_tool_can_undo;
+  tool_class->can_redo       = gimp_transform_grid_tool_can_redo;
+  tool_class->undo           = gimp_transform_grid_tool_undo;
+  tool_class->redo           = gimp_transform_grid_tool_redo;
+  tool_class->options_notify = gimp_transform_grid_tool_options_notify;
+
+  draw_class->draw           = gimp_transform_grid_tool_draw;
+
+  tr_class->recalc_matrix    = gimp_transform_grid_tool_transform_recalc_matrix;
+  tr_class->transform        = gimp_transform_grid_tool_transform;
+
+  klass->dialog              = NULL;
+  klass->dialog_update       = NULL;
+  klass->prepare             = NULL;
+  klass->recalc_matrix       = NULL;
+  klass->transform           = gimp_transform_grid_tool_real_transform;
+
+  klass->ok_button_label     = _("_Transform");
+}
+
+static void
+gimp_transform_grid_tool_init (GimpTransformGridTool *tg_tool)
+{
+  GimpTool *tool = GIMP_TOOL (tg_tool);
+
+  gimp_tool_control_set_scroll_lock      (tool->control, TRUE);
+  gimp_tool_control_set_preserve         (tool->control, FALSE);
+  gimp_tool_control_set_dirty_mask       (tool->control,
+                                          GIMP_DIRTY_IMAGE_SIZE |
+                                          GIMP_DIRTY_DRAWABLE   |
+                                          GIMP_DIRTY_SELECTION  |
+                                          GIMP_DIRTY_ACTIVE_DRAWABLE);
+  gimp_tool_control_set_active_modifiers (tool->control,
+                                          GIMP_TOOL_ACTIVE_MODIFIERS_SAME);
+  gimp_tool_control_set_precision        (tool->control,
+                                          GIMP_CURSOR_PRECISION_SUBPIXEL);
+  gimp_tool_control_set_cursor           (tool->control,
+                                          GIMP_CURSOR_CROSSHAIR_SMALL);
+  gimp_tool_control_set_action_opacity   (tool->control,
+                                          "tools/tools-transform-preview-opacity-set");
+
+  tg_tool->strokes = g_ptr_array_new ();
+}
+
+static void
+gimp_transform_grid_tool_finalize (GObject *object)
+{
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (object);
+
+  g_clear_object (&tg_tool->gui);
+  g_clear_pointer (&tg_tool->strokes, g_ptr_array_unref);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+gimp_transform_grid_tool_initialize (GimpTool     *tool,
+                                     GimpDisplay  *display,
+                                     GError      **error)
+{
+  GimpTransformTool     *tr_tool  = GIMP_TRANSFORM_TOOL (tool);
+  GimpTransformGridTool *tg_tool  = GIMP_TRANSFORM_GRID_TOOL (tool);
+  GimpImage             *image    = gimp_display_get_image (display);
+  GimpDrawable          *drawable = gimp_image_get_active_drawable (image);
+  GimpItem              *item;
+
+  item = gimp_transform_tool_get_active_item (tr_tool, display, FALSE, error);
+
+  if (! item)
+    return FALSE;
+
+  tool->display  = display;
+  tool->drawable = drawable;
+
+  /*  Initialize the transform_grid tool dialog  */
+  if (! tg_tool->gui)
+    gimp_transform_grid_tool_dialog (tg_tool);
+
+  /*  Find the transform bounds for some tools (like scale,
+   *  perspective) that actually need the bounds for initializing
+   */
+  gimp_transform_tool_bounds (tr_tool, display);
+
+  /*  Initialize the tool-specific trans_info, and adjust the tool dialog  */
+  gimp_transform_grid_tool_prepare (tg_tool, display);
+
+  /*  Recalculate the transform_grid tool  */
+  gimp_transform_grid_tool_recalc_matrix (tg_tool, NULL);
+
+  /*  Get the on-canvas gui  */
+  tg_tool->widget = gimp_transform_grid_tool_get_widget (tg_tool);
+
+  gimp_transform_grid_tool_hide_active_item (tg_tool, item);
+
+  /*  start drawing the bounding box and handles...  */
+  gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
+
+  /* Initialize undo and redo lists */
+  tg_tool->undo_list = g_list_prepend (NULL, trans_info_new ());
+  tg_tool->redo_list = NULL;
+  tg_tool->old_trans_info = g_list_last (tg_tool->undo_list)->data;
+  tg_tool->prev_trans_info = g_list_first (tg_tool->undo_list)->data;
+  gimp_transform_grid_tool_update_sensitivity (tg_tool);
+
+  /*  Save the current transformation info  */
+  memcpy (tg_tool->prev_trans_info, tg_tool->trans_info,
+          sizeof (TransInfo));
+
+  return TRUE;
+}
+
+static void
+gimp_transform_grid_tool_control (GimpTool       *tool,
+                                  GimpToolAction  action,
+                                  GimpDisplay    *display)
+{
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
+
+  switch (action)
+    {
+    case GIMP_TOOL_ACTION_PAUSE:
+      break;
+
+    case GIMP_TOOL_ACTION_RESUME:
+      gimp_transform_grid_tool_recalc_matrix (tg_tool, tg_tool->widget);
+      break;
+
+    case GIMP_TOOL_ACTION_HALT:
+      gimp_transform_grid_tool_halt (tg_tool);
+     break;
+
+    case GIMP_TOOL_ACTION_COMMIT:
+      if (tool->display)
+        gimp_transform_grid_tool_commit (tg_tool);
+      break;
+    }
+
+  GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
+}
+
+static void
+gimp_transform_grid_tool_button_press (GimpTool            *tool,
+                                       const GimpCoords    *coords,
+                                       guint32              time,
+                                       GdkModifierType      state,
+                                       GimpButtonPressType  press_type,
+                                       GimpDisplay         *display)
+{
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
+
+  if (tg_tool->widget)
+    {
+      gimp_tool_widget_hover (tg_tool->widget, coords, state, TRUE);
+
+      if (gimp_tool_widget_button_press (tg_tool->widget, coords, time, state,
+                                         press_type))
+        {
+          tg_tool->grab_widget = tg_tool->widget;
+        }
+    }
+
+  gimp_tool_control_activate (tool->control);
+}
+
+static void
+gimp_transform_grid_tool_button_release (GimpTool              *tool,
+                                         const GimpCoords      *coords,
+                                         guint32                time,
+                                         GdkModifierType        state,
+                                         GimpButtonReleaseType  release_type,
+                                         GimpDisplay           *display)
+{
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
+
+  gimp_tool_control_halt (tool->control);
+
+  if (tg_tool->grab_widget)
+    {
+      gimp_tool_widget_button_release (tg_tool->grab_widget,
+                                       coords, time, state, release_type);
+      tg_tool->grab_widget = NULL;
+    }
+
+  if (release_type != GIMP_BUTTON_RELEASE_CANCEL)
+    {
+      /* This hack is to perform the flip immediately with the flip tool */
+      if (! tg_tool->widget)
+        {
+          gimp_transform_grid_tool_response (NULL, GTK_RESPONSE_OK, tg_tool);
+          return;
+        }
+
+      /* We're done with an interaction, save it on the undo list */
+      gimp_transform_grid_tool_push_internal_undo (tg_tool);
+    }
+  else
+    {
+      /*  Restore the last saved state  */
+      memcpy (tg_tool->trans_info, tg_tool->prev_trans_info,
+              sizeof (TransInfo));
+
+      /*  recalculate the tool's transformation matrix  */
+      gimp_transform_grid_tool_recalc_matrix (tg_tool, tg_tool->widget);
+    }
+}
+
+static void
+gimp_transform_grid_tool_motion (GimpTool         *tool,
+                                 const GimpCoords *coords,
+                                 guint32           time,
+                                 GdkModifierType   state,
+                                 GimpDisplay      *display)
+{
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
+
+  if (tg_tool->grab_widget)
+    {
+      gimp_tool_widget_motion (tg_tool->grab_widget, coords, time, state);
+    }
+}
+
+static void
+gimp_transform_grid_tool_modifier_key (GimpTool        *tool,
+                                       GdkModifierType  key,
+                                       gboolean         press,
+                                       GdkModifierType  state,
+                                       GimpDisplay     *display)
+{
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
+
+  if (tg_tool->widget)
+    {
+      GIMP_TOOL_CLASS (parent_class)->modifier_key (tool, key, press,
+                                                    state, display);
+    }
+  else
+    {
+      GimpTransformGridOptions *options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tool);
+
+      if (key == gimp_get_constrain_behavior_mask ())
+        {
+          g_object_set (options,
+                        "frompivot-scale",       ! options->frompivot_scale,
+                        "frompivot-shear",       ! options->frompivot_shear,
+                        "frompivot-perspective", ! options->frompivot_perspective,
+                        NULL);
+        }
+      else if (key == gimp_get_extend_selection_mask ())
+        {
+          g_object_set (options,
+                        "cornersnap",            ! options->cornersnap,
+                        "constrain-move",        ! options->constrain_move,
+                        "constrain-scale",       ! options->constrain_scale,
+                        "constrain-rotate",      ! options->constrain_rotate,
+                        "constrain-shear",       ! options->constrain_shear,
+                        "constrain-perspective", ! options->constrain_perspective,
+                        NULL);
+        }
+    }
+}
+
+static void
+gimp_transform_grid_tool_cursor_update (GimpTool         *tool,
+                                        const GimpCoords *coords,
+                                        GdkModifierType   state,
+                                        GimpDisplay      *display)
+{
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
+
+  if (! gimp_transform_tool_get_active_item (tr_tool, display, TRUE, NULL))
+    {
+      gimp_tool_set_cursor (tool, display,
+                            gimp_tool_control_get_cursor (tool->control),
+                            gimp_tool_control_get_tool_cursor (tool->control),
+                            GIMP_CURSOR_MODIFIER_BAD);
+      return;
+    }
+
+  GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
+}
+
+static const gchar *
+gimp_transform_grid_tool_can_undo (GimpTool    *tool,
+                                   GimpDisplay *display)
+{
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
+
+  if (! tg_tool->undo_list || ! tg_tool->undo_list->next)
+    return NULL;
+
+  return _("Transform Step");
+}
+
+static const gchar *
+gimp_transform_grid_tool_can_redo (GimpTool    *tool,
+                                   GimpDisplay *display)
+{
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
+
+  if (! tg_tool->redo_list)
+    return NULL;
+
+  return _("Transform Step");
+}
+
+static gboolean
+gimp_transform_grid_tool_undo (GimpTool    *tool,
+                               GimpDisplay *display)
+{
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
+  GList                 *item;
+
+  item = g_list_next (tg_tool->undo_list);
+
+  /* Move prev_trans_info from undo_list to redo_list */
+  tg_tool->redo_list = g_list_prepend (tg_tool->redo_list,
+                                       tg_tool->prev_trans_info);
+  tg_tool->undo_list = g_list_remove (tg_tool->undo_list,
+                                      tg_tool->prev_trans_info);
+
+  tg_tool->prev_trans_info = item->data;
+
+  /*  Restore the previous transformation info  */
+  memcpy (tg_tool->trans_info, tg_tool->prev_trans_info,
+          sizeof (TransInfo));
+
+  /*  recalculate the tool's transformation matrix  */
+  gimp_transform_grid_tool_recalc_matrix (tg_tool, tg_tool->widget);
+
+  return TRUE;
+}
+
+static gboolean
+gimp_transform_grid_tool_redo (GimpTool    *tool,
+                               GimpDisplay *display)
+{
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tool);
+  GList                 *item;
+
+  item = tg_tool->redo_list;
+
+  /* Move prev_trans_info from redo_list to undo_list */
+  tg_tool->prev_trans_info = item->data;
+
+  tg_tool->undo_list = g_list_prepend (tg_tool->undo_list,
+                                       tg_tool->prev_trans_info);
+  tg_tool->redo_list = g_list_remove (tg_tool->redo_list,
+                                      tg_tool->prev_trans_info);
+
+  /*  Restore the previous transformation info  */
+  memcpy (tg_tool->trans_info, tg_tool->prev_trans_info,
+          sizeof (TransInfo));
+
+  /*  recalculate the tool's transformation matrix  */
+  gimp_transform_grid_tool_recalc_matrix (tg_tool, tg_tool->widget);
+
+  return TRUE;
+}
+
+static void
+gimp_transform_grid_tool_options_notify (GimpTool         *tool,
+                                         GimpToolOptions  *options,
+                                         const GParamSpec *pspec)
+{
+  GimpTransformTool        *tr_tool    = GIMP_TRANSFORM_TOOL (tool);
+  GimpTransformGridTool    *tg_tool    = GIMP_TRANSFORM_GRID_TOOL (tool);
+  GimpTransformGridOptions *tg_options = GIMP_TRANSFORM_GRID_OPTIONS (options);
+
+  GIMP_TOOL_CLASS (parent_class)->options_notify (tool, options, pspec);
+
+  if (! strcmp (pspec->name, "type"))
+    {
+      gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
+      return;
+    }
+
+  if (! tg_tool->widget)
+    return;
+
+  if (! strcmp (pspec->name, "direction"))
+    {
+      /*  recalculate the tool's transformation matrix  */
+      gimp_transform_grid_tool_recalc_matrix (tg_tool, tg_tool->widget);
+    }
+  else if (! strcmp (pspec->name, "show-preview"))
+    {
+      if (tg_tool->preview)
+        {
+          GimpDisplay *display;
+          GimpItem    *item;
+          gboolean     show_preview;
+
+          show_preview = gimp_transform_grid_options_show_preview (tg_options) &&
+                         tr_tool->transform_valid;
+
+          gimp_canvas_item_set_visible (tg_tool->preview, show_preview);
+
+          display = tool->display;
+          item    = gimp_transform_tool_get_active_item (tr_tool,
+                                                         display, TRUE, NULL);
+          if (item)
+            {
+              if (show_preview)
+                gimp_transform_grid_tool_hide_active_item (tg_tool, item);
+              else
+                gimp_transform_grid_tool_show_active_item (tg_tool);
+            }
+        }
+    }
+  else if (g_str_has_prefix (pspec->name, "constrain-") ||
+           g_str_has_prefix (pspec->name, "frompivot-") ||
+           ! strcmp (pspec->name, "fixedpivot") ||
+           ! strcmp (pspec->name, "cornersnap"))
+    {
+      gimp_transform_grid_tool_dialog_update (tg_tool);
+    }
+}
+
+static void
+gimp_transform_grid_tool_draw (GimpDrawTool *draw_tool)
+{
+  GimpTool                 *tool       = GIMP_TOOL (draw_tool);
+  GimpTransformTool        *tr_tool    = GIMP_TRANSFORM_TOOL (draw_tool);
+  GimpTransformGridTool    *tg_tool    = GIMP_TRANSFORM_GRID_TOOL (draw_tool);
+  GimpTransformGridOptions *options    = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tool);
+  GimpTransformOptions     *tr_options = GIMP_TRANSFORM_OPTIONS (options);
+  GimpImage                *image      = gimp_display_get_image (tool->display);
+  GimpMatrix3               matrix     = tr_tool->transform;
+  GimpCanvasItem           *item;
+
+  if (tr_options->direction == GIMP_TRANSFORM_BACKWARD)
+    gimp_matrix3_invert (&matrix);
+
+  if (tg_tool->widget)
+    {
+      gboolean show_preview = gimp_transform_grid_options_show_preview (options) &&
+                              tr_tool->transform_valid;
+
+      tg_tool->preview =
+        gimp_draw_tool_add_transform_preview (draw_tool,
+                                              tool->drawable,
+                                              &matrix,
+                                              tr_tool->x1,
+                                              tr_tool->y1,
+                                              tr_tool->x2,
+                                              tr_tool->y2);
+      g_object_add_weak_pointer (G_OBJECT (tg_tool->preview),
+                                 (gpointer) &tg_tool->preview);
+
+      gimp_canvas_item_set_visible (tg_tool->preview, show_preview);
+
+      g_object_bind_property (G_OBJECT (options),          "preview-opacity",
+                              G_OBJECT (tg_tool->preview), "opacity",
+                              G_BINDING_SYNC_CREATE |
+                              G_BINDING_BIDIRECTIONAL);
+
+      GIMP_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool);
+    }
+
+  if (tr_options->type == GIMP_TRANSFORM_TYPE_SELECTION)
+    {
+      const GimpBoundSeg *segs_in;
+      const GimpBoundSeg *segs_out;
+      gint                n_segs_in;
+      gint                n_segs_out;
+
+      gimp_channel_boundary (gimp_image_get_mask (image),
+                             &segs_in, &segs_out,
+                             &n_segs_in, &n_segs_out,
+                             0, 0, 0, 0);
+
+      if (segs_in)
+        {
+          tg_tool->boundary_in =
+            gimp_draw_tool_add_boundary (draw_tool,
+                                         segs_in, n_segs_in,
+                                         &matrix,
+                                         0, 0);
+          g_object_add_weak_pointer (G_OBJECT (tg_tool->boundary_in),
+                                     (gpointer) &tg_tool->boundary_in);
+
+          gimp_canvas_item_set_visible (tg_tool->boundary_in,
+                                        tr_tool->transform_valid);
+        }
+
+      if (segs_out)
+        {
+          tg_tool->boundary_out =
+            gimp_draw_tool_add_boundary (draw_tool,
+                                         segs_out, n_segs_out,
+                                         &matrix,
+                                         0, 0);
+          g_object_add_weak_pointer (G_OBJECT (tg_tool->boundary_in),
+                                     (gpointer) &tg_tool->boundary_out);
+
+          gimp_canvas_item_set_visible (tg_tool->boundary_out,
+                                        tr_tool->transform_valid);
+        }
+    }
+  else if (tr_options->type == GIMP_TRANSFORM_TYPE_PATH)
+    {
+      GimpVectors *vectors = gimp_image_get_active_vectors (image);
+
+      if (vectors)
+        {
+          GimpStroke *stroke = NULL;
+
+          while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke)))
+            {
+              GArray   *coords;
+              gboolean  closed;
+
+              coords = gimp_stroke_interpolate (stroke, 1.0, &closed);
+
+              if (coords && coords->len)
+                {
+                  item =
+                    gimp_draw_tool_add_strokes (draw_tool,
+                                                &g_array_index (coords,
+                                                                GimpCoords, 0),
+                                                coords->len, &matrix, FALSE);
+
+                  g_ptr_array_add (tg_tool->strokes, item);
+                  g_object_weak_ref (G_OBJECT (item),
+                                     (GWeakNotify) g_ptr_array_remove,
+                                     tg_tool->strokes);
+
+                  gimp_canvas_item_set_visible (item, tr_tool->transform_valid);
+                }
+
+              if (coords)
+                g_array_free (coords, TRUE);
+            }
+        }
+    }
+}
+
+static void
+gimp_transform_grid_tool_transform_recalc_matrix (GimpTransformTool *tr_tool)
+{
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tr_tool);
+
+  gimp_transform_grid_tool_recalc_matrix (tg_tool, NULL);
+}
+
+static GeglBuffer *
+gimp_transform_grid_tool_transform (GimpTransformTool  *tr_tool,
+                                    GimpItem           *item,
+                                    GeglBuffer         *orig_buffer,
+                                    gint                orig_offset_x,
+                                    gint                orig_offset_y,
+                                    GimpColorProfile  **buffer_profile,
+                                    gint               *new_offset_x,
+                                    gint               *new_offset_y)
+{
+  GimpTool              *tool    = GIMP_TOOL (tr_tool);
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (tr_tool);
+  GimpDisplay           *display = tool->display;
+  GimpImage             *image   = gimp_display_get_image (display);
+  GeglBuffer            *new_buffer;
+
+  /*  Send the request for the transformation to the tool...
+   */
+  new_buffer =
+    GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->transform (tg_tool,
+                                                             item,
+                                                             orig_buffer,
+                                                             orig_offset_x,
+                                                             orig_offset_y,
+                                                             buffer_profile,
+                                                             new_offset_x,
+                                                             new_offset_y);
+
+  gimp_image_undo_push (image, GIMP_TYPE_TRANSFORM_GRID_TOOL_UNDO,
+                        GIMP_UNDO_TRANSFORM_GRID, NULL,
+                        0,
+                        "transform-tool", tg_tool,
+                        NULL);
+
+  return new_buffer;
+}
+
+static GeglBuffer *
+gimp_transform_grid_tool_real_transform (GimpTransformGridTool  *tg_tool,
+                                         GimpItem               *item,
+                                         GeglBuffer             *orig_buffer,
+                                         gint                    orig_offset_x,
+                                         gint                    orig_offset_y,
+                                         GimpColorProfile      **buffer_profile,
+                                         gint                   *new_offset_x,
+                                         gint                   *new_offset_y)
+{
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+
+  return GIMP_TRANSFORM_TOOL_CLASS (parent_class)->transform (tr_tool,
+                                                              item,
+                                                              orig_buffer,
+                                                              orig_offset_x,
+                                                              orig_offset_y,
+                                                              buffer_profile,
+                                                              new_offset_x,
+                                                              new_offset_y);
+}
+
+static void
+gimp_transform_grid_tool_widget_changed (GimpToolWidget        *widget,
+                                         GimpTransformGridTool *tg_tool)
+{
+  GimpTransformTool        *tr_tool    = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpTransformGridOptions *options    = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
+  GimpTransformOptions     *tr_options = GIMP_TRANSFORM_OPTIONS (options);
+  GimpMatrix3               matrix     = tr_tool->transform;
+  gint                      i;
+
+  if (tr_options->direction == GIMP_TRANSFORM_BACKWARD)
+    gimp_matrix3_invert (&matrix);
+
+  if (tg_tool->preview)
+    {
+      gboolean show_preview = gimp_transform_grid_options_show_preview (options) &&
+                              tr_tool->transform_valid;
+
+      gimp_canvas_item_begin_change (tg_tool->preview);
+      gimp_canvas_item_set_visible (tg_tool->preview, show_preview);
+      g_object_set (tg_tool->preview,
+                    "transform", &matrix,
+                    NULL);
+      gimp_canvas_item_end_change (tg_tool->preview);
+    }
+
+  if (tg_tool->boundary_in)
+    {
+      gimp_canvas_item_begin_change (tg_tool->boundary_in);
+      gimp_canvas_item_set_visible (tg_tool->boundary_in,
+                                    tr_tool->transform_valid);
+      g_object_set (tg_tool->boundary_in,
+                    "transform", &matrix,
+                    NULL);
+      gimp_canvas_item_end_change (tg_tool->boundary_in);
+    }
+
+  if (tg_tool->boundary_out)
+    {
+      gimp_canvas_item_begin_change (tg_tool->boundary_out);
+      gimp_canvas_item_set_visible (tg_tool->boundary_out,
+                                    tr_tool->transform_valid);
+      g_object_set (tg_tool->boundary_out,
+                    "transform", &matrix,
+                    NULL);
+      gimp_canvas_item_end_change (tg_tool->boundary_out);
+    }
+
+  for (i = 0; i < tg_tool->strokes->len; i++)
+    {
+      GimpCanvasItem *item = g_ptr_array_index (tg_tool->strokes, i);
+
+      gimp_canvas_item_begin_change (item);
+      gimp_canvas_item_set_visible (item, tr_tool->transform_valid);
+      g_object_set (item,
+                    "transform", &matrix,
+                    NULL);
+      gimp_canvas_item_end_change (item);
+    }
+}
+
+static void
+gimp_transform_grid_tool_widget_response (GimpToolWidget        *widget,
+                                          gint                   response_id,
+                                          GimpTransformGridTool *tg_tool)
+{
+  switch (response_id)
+    {
+    case GIMP_TOOL_WIDGET_RESPONSE_CONFIRM:
+      gimp_transform_grid_tool_response (NULL, GTK_RESPONSE_OK, tg_tool);
+      break;
+
+    case GIMP_TOOL_WIDGET_RESPONSE_CANCEL:
+      gimp_transform_grid_tool_response (NULL, GTK_RESPONSE_CANCEL, tg_tool);
+      break;
+
+    case GIMP_TOOL_WIDGET_RESPONSE_RESET:
+      gimp_transform_grid_tool_response (NULL, RESPONSE_RESET, tg_tool);
+      break;
+    }
+}
+
+static void
+gimp_transform_grid_tool_halt (GimpTransformGridTool *tg_tool)
+{
+  GimpTool *tool = GIMP_TOOL (tg_tool);
+
+  if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tg_tool)))
+    gimp_draw_tool_stop (GIMP_DRAW_TOOL (tg_tool));
+
+  gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tg_tool), NULL);
+  g_clear_object (&tg_tool->widget);
+
+  if (tg_tool->gui)
+    gimp_tool_gui_hide (tg_tool->gui);
+
+  if (tg_tool->redo_list)
+    {
+      g_list_free_full (tg_tool->redo_list, (GDestroyNotify) trans_info_free);
+      tg_tool->redo_list = NULL;
+    }
+
+  if (tg_tool->undo_list)
+    {
+      g_list_free_full (tg_tool->undo_list, (GDestroyNotify) trans_info_free);
+      tg_tool->undo_list = NULL;
+      tg_tool->prev_trans_info = NULL;
+    }
+
+  gimp_transform_grid_tool_show_active_item (tg_tool);
+
+  tool->display  = NULL;
+  tool->drawable = NULL;
+}
+
+static void
+gimp_transform_grid_tool_commit (GimpTransformGridTool *tg_tool)
+{
+  GimpTool          *tool    = GIMP_TOOL (tg_tool);
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpDisplay       *display = tool->display;
+
+  /* undraw the tool before we muck around with the transform matrix */
+  gimp_draw_tool_stop (GIMP_DRAW_TOOL (tg_tool));
+
+  gimp_transform_tool_transform (tr_tool, display);
+}
+
+static void
+gimp_transform_grid_tool_dialog (GimpTransformGridTool *tg_tool)
+{
+  GimpTool         *tool      = GIMP_TOOL (tg_tool);
+  GimpToolInfo     *tool_info = tool->tool_info;
+  GimpDisplayShell *shell;
+  const gchar      *ok_button_label;
+
+  if (! GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->dialog)
+    return;
+
+  g_return_if_fail (tool->display != NULL);
+
+  shell = gimp_display_get_shell (tool->display);
+
+  ok_button_label = GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->ok_button_label;
+
+  tg_tool->gui = gimp_tool_gui_new (tool_info,
+                                    NULL, NULL, NULL, NULL,
+                                    gtk_widget_get_screen (GTK_WIDGET (shell)),
+                                    gimp_widget_get_monitor (GTK_WIDGET (shell)),
+                                    TRUE,
+
+                                    _("_Reset"),     RESPONSE_RESET,
+                                    _("_Cancel"),    GTK_RESPONSE_CANCEL,
+                                    ok_button_label, GTK_RESPONSE_OK,
+
+                                    NULL);
+
+  gimp_tool_gui_set_auto_overlay (tg_tool->gui, TRUE);
+  gimp_tool_gui_set_default_response (tg_tool->gui, GTK_RESPONSE_OK);
+
+  gimp_tool_gui_set_alternative_button_order (tg_tool->gui,
+                                              RESPONSE_RESET,
+                                              GTK_RESPONSE_OK,
+                                              GTK_RESPONSE_CANCEL,
+                                              -1);
+
+  g_signal_connect (tg_tool->gui, "response",
+                    G_CALLBACK (gimp_transform_grid_tool_response),
+                    tg_tool);
+
+  GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->dialog (tg_tool);
+}
+
+static void
+gimp_transform_grid_tool_dialog_update (GimpTransformGridTool *tg_tool)
+{
+  if (tg_tool->gui &&
+      GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->dialog_update)
+    {
+      GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->dialog_update (tg_tool);
+    }
+}
+
+static void
+gimp_transform_grid_tool_prepare (GimpTransformGridTool *tg_tool,
+                                  GimpDisplay           *display)
+{
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+
+  if (tg_tool->gui)
+    {
+      GimpItem *item = gimp_transform_tool_get_active_item (tr_tool,
+                                                            display, TRUE, NULL);
+
+      g_return_if_fail (item != NULL);
+
+      gimp_tool_gui_set_shell (tg_tool->gui, gimp_display_get_shell (display));
+      gimp_tool_gui_set_viewable (tg_tool->gui, GIMP_VIEWABLE (item));
+    }
+
+  if (GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->prepare)
+    GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->prepare (tg_tool);
+}
+
+static GimpToolWidget *
+gimp_transform_grid_tool_get_widget (GimpTransformGridTool *tg_tool)
+{
+  static const gchar *properties[] =
+  {
+    "constrain-move",
+    "constrain-scale",
+    "constrain-rotate",
+    "constrain-shear",
+    "constrain-perspective",
+    "frompivot-scale",
+    "frompivot-shear",
+    "frompivot-perspective",
+    "cornersnap",
+    "fixedpivot"
+  };
+
+  GimpToolWidget *widget = NULL;
+
+  if (GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->get_widget)
+    {
+      GimpTransformGridOptions *options = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
+      gint                      i;
+
+      widget = GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->get_widget (tg_tool);
+
+      gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tg_tool), widget);
+
+      g_object_bind_property (G_OBJECT (options), "grid-type",
+                              G_OBJECT (widget),  "guide-type",
+                              G_BINDING_SYNC_CREATE |
+                              G_BINDING_BIDIRECTIONAL);
+      g_object_bind_property (G_OBJECT (options), "grid-size",
+                              G_OBJECT (widget),  "n-guides",
+                              G_BINDING_SYNC_CREATE |
+                              G_BINDING_BIDIRECTIONAL);
+
+      for (i = 0; i < G_N_ELEMENTS (properties); i++)
+        g_object_bind_property (G_OBJECT (options), properties[i],
+                                G_OBJECT (widget),  properties[i],
+                                G_BINDING_SYNC_CREATE |
+                                G_BINDING_BIDIRECTIONAL);
+
+      g_signal_connect (widget, "changed",
+                        G_CALLBACK (gimp_transform_grid_tool_widget_changed),
+                        tg_tool);
+      g_signal_connect (widget, "response",
+                        G_CALLBACK (gimp_transform_grid_tool_widget_response),
+                        tg_tool);
+    }
+
+  return widget;
+}
+
+static void
+gimp_transform_grid_tool_response (GimpToolGui           *gui,
+                                   gint                   response_id,
+                                   GimpTransformGridTool *tg_tool)
+{
+  GimpTool    *tool    = GIMP_TOOL (tg_tool);
+  GimpDisplay *display = tool->display;
+
+  switch (response_id)
+    {
+    case RESPONSE_RESET:
+      /* Move all undo events to redo, and pop off the first
+       * one as that's the current one, which always sits on
+       * the undo_list
+       */
+      tg_tool->redo_list =
+        g_list_remove (g_list_concat (g_list_reverse (tg_tool->undo_list),
+                                      tg_tool->redo_list),
+                       tg_tool->old_trans_info);
+      tg_tool->prev_trans_info = tg_tool->old_trans_info;
+      tg_tool->undo_list = g_list_prepend (NULL,
+                                           tg_tool->prev_trans_info);
+
+      gimp_transform_grid_tool_update_sensitivity (tg_tool);
+
+      /*  Restore the previous transformation info  */
+      memcpy (tg_tool->trans_info, tg_tool->prev_trans_info,
+              sizeof (TransInfo));
+
+      /*  recalculate the tool's transformtion matrix  */
+      gimp_transform_grid_tool_recalc_matrix (tg_tool, tg_tool->widget);
+
+      /*  update the undo actions / menu items  */
+      gimp_image_flush (gimp_display_get_image (display));
+      break;
+
+    case GTK_RESPONSE_OK:
+      g_return_if_fail (display != NULL);
+      gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, display);
+      break;
+
+    default:
+      gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
+
+      /*  update the undo actions / menu items  */
+      if (display)
+        gimp_image_flush (gimp_display_get_image (display));
+      break;
+    }
+}
+
+static void
+gimp_transform_grid_tool_update_sensitivity (GimpTransformGridTool *tg_tool)
+{
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+
+  if (! tg_tool->gui)
+    return;
+
+  gimp_tool_gui_set_response_sensitive (tg_tool->gui, GTK_RESPONSE_OK,
+                                        tr_tool->transform_valid);
+  gimp_tool_gui_set_response_sensitive (tg_tool->gui, RESPONSE_RESET,
+                                        g_list_next (tg_tool->undo_list) != NULL);
+}
+
+static void
+gimp_transform_grid_tool_hide_active_item (GimpTransformGridTool *tg_tool,
+                                           GimpItem          *item)
+{
+  GimpTransformGridOptions *options    = GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS (tg_tool);
+  GimpTransformOptions     *tr_options = GIMP_TRANSFORM_OPTIONS (options);
+  GimpDisplay              *display    = GIMP_TOOL (tg_tool)->display;
+  GimpImage                *image      = gimp_display_get_image (display);
+
+  /*  hide only complete layers and channels, not layer masks  */
+  if (tr_options->type == GIMP_TRANSFORM_TYPE_LAYER &&
+      options->show_preview                         &&
+      GIMP_IS_DRAWABLE (item)                       &&
+      ! GIMP_IS_LAYER_MASK (item)                   &&
+      gimp_item_get_visible (item)                  &&
+      gimp_channel_is_empty (gimp_image_get_mask (image)))
+    {
+      tg_tool->hidden_item = item;
+      gimp_item_set_visible (item, FALSE, FALSE);
+
+      gimp_projection_flush (gimp_image_get_projection (image));
+    }
+}
+
+static void
+gimp_transform_grid_tool_show_active_item (GimpTransformGridTool *tg_tool)
+{
+  if (tg_tool->hidden_item)
+    {
+      GimpDisplay *display = GIMP_TOOL (tg_tool)->display;
+      GimpImage   *image   = gimp_display_get_image (display);
+
+      gimp_item_set_visible (tg_tool->hidden_item, TRUE, FALSE);
+      tg_tool->hidden_item = NULL;
+
+      gimp_image_flush (image);
+    }
+}
+
+static TransInfo *
+trans_info_new (void)
+{
+  return g_slice_new0 (TransInfo);
+}
+
+static void
+trans_info_free (TransInfo *info)
+{
+  g_slice_free (TransInfo, info);
+}
+
+void
+gimp_transform_grid_tool_recalc_matrix (GimpTransformGridTool *tg_tool,
+                                        GimpToolWidget        *widget)
+{
+  GimpTool          *tool;
+  GimpTransformTool *tr_tool;
+
+  g_return_if_fail (GIMP_IS_TRANSFORM_GRID_TOOL (tg_tool));
+  g_return_if_fail (widget == NULL || GIMP_IS_TOOL_WIDGET (widget));
+
+  tool    = GIMP_TOOL (tg_tool);
+  tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+
+  gimp_transform_tool_bounds (tr_tool, tool->display);
+
+  if (GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->recalc_matrix)
+    {
+      GIMP_TRANSFORM_GRID_TOOL_GET_CLASS (tg_tool)->recalc_matrix (tg_tool,
+                                                                   widget);
+    }
+
+  gimp_transform_grid_tool_dialog_update (tg_tool);
+  gimp_transform_grid_tool_update_sensitivity (tg_tool);
+
+  if (tg_tool->gui)
+    gimp_tool_gui_show (tg_tool->gui);
+}
+
+void
+gimp_transform_grid_tool_push_internal_undo (GimpTransformGridTool *tg_tool)
+{
+  g_return_if_fail (GIMP_IS_TRANSFORM_GRID_TOOL (tg_tool));
+  g_return_if_fail (tg_tool->prev_trans_info != NULL);
+
+  /* push current state on the undo list and set this state as the
+   * current state, but avoid doing this if there were no changes
+   */
+  if (memcmp (tg_tool->prev_trans_info, tg_tool->trans_info,
+              sizeof (TransInfo)) != 0)
+    {
+      tg_tool->prev_trans_info = trans_info_new ();
+      memcpy (tg_tool->prev_trans_info, tg_tool->trans_info,
+              sizeof (TransInfo));
+
+      tg_tool->undo_list = g_list_prepend (tg_tool->undo_list,
+                                           tg_tool->prev_trans_info);
+
+      /* If we undid anything and started interacting, we have to
+       * discard the redo history
+       */
+      g_list_free_full (tg_tool->redo_list, (GDestroyNotify) trans_info_free);
+      tg_tool->redo_list = NULL;
+
+      gimp_transform_grid_tool_update_sensitivity (tg_tool);
+
+      /*  update the undo actions / menu items  */
+      gimp_image_flush (gimp_display_get_image (GIMP_TOOL (tg_tool)->display));
+    }
+}
diff --git a/app/tools/gimptransformgridtool.h b/app/tools/gimptransformgridtool.h
new file mode 100644
index 0000000000..da383ddeec
--- /dev/null
+++ b/app/tools/gimptransformgridtool.h
@@ -0,0 +1,103 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others
+ *
+ * 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_TRANSFORM_GRID_TOOL_H__
+#define __GIMP_TRANSFORM_GRID_TOOL_H__
+
+
+#include "gimptransformtool.h"
+
+
+/* This is not the number of items in the enum above, but the max size
+ * of the enums at the top of each transformation tool, stored in
+ * trans_info and related
+ */
+#define TRANS_INFO_SIZE 17
+
+typedef gdouble TransInfo[TRANS_INFO_SIZE];
+
+
+#define GIMP_TYPE_TRANSFORM_GRID_TOOL            (gimp_transform_grid_tool_get_type ())
+#define GIMP_TRANSFORM_GRID_TOOL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GIMP_TYPE_TRANSFORM_GRID_TOOL, GimpTransformGridTool))
+#define GIMP_TRANSFORM_GRID_TOOL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
GIMP_TYPE_TRANSFORM_GRID_TOOL, GimpTransformGridToolClass))
+#define GIMP_IS_TRANSFORM_GRID_TOOL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GIMP_TYPE_TRANSFORM_GRID_TOOL))
+#define GIMP_IS_TRANSFORM_GRID_TOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GIMP_TYPE_TRANSFORM_GRID_TOOL))
+#define GIMP_TRANSFORM_GRID_TOOL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GIMP_TYPE_TRANSFORM_GRID_TOOL, GimpTransformGridToolClass))
+
+#define GIMP_TRANSFORM_GRID_TOOL_GET_OPTIONS(t)  (GIMP_TRANSFORM_GRID_OPTIONS (gimp_tool_get_options 
(GIMP_TOOL (t))))
+
+
+typedef struct _GimpTransformGridToolClass GimpTransformGridToolClass;
+
+struct _GimpTransformGridTool
+{
+  GimpTransformTool  parent_instance;
+
+  TransInfo          trans_info;       /*  transformation info                */
+  TransInfo         *old_trans_info;   /*  for resetting everything           */
+  TransInfo         *prev_trans_info;  /*  the current finished state         */
+  GList             *undo_list;        /*  list of all states,
+                                           head is current == prev_trans_info,
+                                           tail is original == old_trans_info */
+  GList             *redo_list;        /*  list of all undone states,
+                                           NULL when nothing undone */
+
+  GimpItem          *hidden_item;      /*  the item that was hidden during
+                                           the transform                      */
+
+  GimpToolWidget    *widget;
+  GimpToolWidget    *grab_widget;
+  GimpCanvasItem    *preview;
+  GimpCanvasItem    *boundary_in;
+  GimpCanvasItem    *boundary_out;
+  GPtrArray         *strokes;
+
+  GimpToolGui       *gui;
+};
+
+struct _GimpTransformGridToolClass
+{
+  GimpTransformToolClass  parent_class;
+
+  /*  virtual functions  */
+  void             (* dialog)        (GimpTransformGridTool  *tg_tool);
+  void             (* dialog_update) (GimpTransformGridTool  *tg_tool);
+  void             (* prepare)       (GimpTransformGridTool  *tg_tool);
+  GimpToolWidget * (* get_widget)    (GimpTransformGridTool  *tg_tool);
+  void             (* recalc_matrix) (GimpTransformGridTool  *tg_tool,
+                                      GimpToolWidget         *widget);
+  GeglBuffer     * (* transform)     (GimpTransformGridTool  *tg_tool,
+                                      GimpItem               *item,
+                                      GeglBuffer             *orig_buffer,
+                                      gint                    orig_offset_x,
+                                      gint                    orig_offset_y,
+                                      GimpColorProfile      **buffer_profile,
+                                      gint                   *new_offset_x,
+                                      gint                   *new_offset_y);
+
+  const gchar *ok_button_label;
+};
+
+
+GType   gimp_transform_grid_tool_get_type           (void) G_GNUC_CONST;
+
+void    gimp_transform_grid_tool_recalc_matrix      (GimpTransformGridTool *tg_tool,
+                                                     GimpToolWidget        *widget);
+void    gimp_transform_grid_tool_push_internal_undo (GimpTransformGridTool *tg_tool);
+
+
+#endif  /*  __GIMP_TRANSFORM_GRID_TOOL_H__  */
diff --git a/app/tools/gimptransformgridtoolundo.c b/app/tools/gimptransformgridtoolundo.c
new file mode 100644
index 0000000000..3c9490c889
--- /dev/null
+++ b/app/tools/gimptransformgridtoolundo.c
@@ -0,0 +1,220 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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 "tools-types.h"
+
+#include "gimptoolcontrol.h"
+#include "gimptransformgridtool.h"
+#include "gimptransformgridtoolundo.h"
+
+
+enum
+{
+  PROP_0,
+  PROP_TRANSFORM_TOOL
+};
+
+
+static void   gimp_transform_grid_tool_undo_constructed  (GObject             *object);
+static void   gimp_transform_grid_tool_undo_set_property (GObject             *object,
+                                                          guint                property_id,
+                                                          const GValue        *value,
+                                                          GParamSpec          *pspec);
+static void   gimp_transform_grid_tool_undo_get_property (GObject             *object,
+                                                          guint                property_id,
+                                                          GValue              *value,
+                                                          GParamSpec          *pspec);
+
+static void   gimp_transform_grid_tool_undo_pop          (GimpUndo            *undo,
+                                                          GimpUndoMode         undo_mode,
+                                                          GimpUndoAccumulator *accum);
+static void   gimp_transform_grid_tool_undo_free         (GimpUndo            *undo,
+                                                          GimpUndoMode         undo_mode);
+
+
+G_DEFINE_TYPE (GimpTransformGridToolUndo, gimp_transform_grid_tool_undo, GIMP_TYPE_UNDO)
+
+#define parent_class gimp_transform_grid_tool_undo_parent_class
+
+
+static void
+gimp_transform_grid_tool_undo_class_init (GimpTransformGridToolUndoClass *klass)
+{
+  GObjectClass  *object_class = G_OBJECT_CLASS (klass);
+  GimpUndoClass *undo_class   = GIMP_UNDO_CLASS (klass);
+
+  object_class->constructed  = gimp_transform_grid_tool_undo_constructed;
+  object_class->set_property = gimp_transform_grid_tool_undo_set_property;
+  object_class->get_property = gimp_transform_grid_tool_undo_get_property;
+
+  undo_class->pop            = gimp_transform_grid_tool_undo_pop;
+  undo_class->free           = gimp_transform_grid_tool_undo_free;
+
+  g_object_class_install_property (object_class, PROP_TRANSFORM_TOOL,
+                                   g_param_spec_object ("transform-tool",
+                                                        NULL, NULL,
+                                                        GIMP_TYPE_TRANSFORM_GRID_TOOL,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gimp_transform_grid_tool_undo_init (GimpTransformGridToolUndo *undo)
+{
+}
+
+static void
+gimp_transform_grid_tool_undo_constructed (GObject *object)
+{
+  GimpTransformGridToolUndo *tg_tool_undo = GIMP_TRANSFORM_GRID_TOOL_UNDO (object);
+  GimpTransformGridTool     *tg_tool;
+  gint                       i;
+
+  G_OBJECT_CLASS (parent_class)->constructed (object);
+
+  gimp_assert (GIMP_IS_TRANSFORM_GRID_TOOL (tg_tool_undo->tg_tool));
+
+  tg_tool = tg_tool_undo->tg_tool;
+
+  for (i = 0; i < TRANS_INFO_SIZE; i++)
+    tg_tool_undo->trans_info[i] = (*tg_tool->old_trans_info)[i];
+
+#if 0
+  if (tg_tool->original)
+    tg_tool_undo->original = tile_manager_ref (tg_tool->original);
+#endif
+
+  g_object_add_weak_pointer (G_OBJECT (tg_tool_undo->tg_tool),
+                             (gpointer) &tg_tool_undo->tg_tool);
+}
+
+static void
+gimp_transform_grid_tool_undo_set_property (GObject      *object,
+                                            guint         property_id,
+                                            const GValue *value,
+                                            GParamSpec   *pspec)
+{
+  GimpTransformGridToolUndo *tg_tool_undo = GIMP_TRANSFORM_GRID_TOOL_UNDO (object);
+
+  switch (property_id)
+    {
+    case PROP_TRANSFORM_TOOL:
+      tg_tool_undo->tg_tool = g_value_get_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_transform_grid_tool_undo_get_property (GObject    *object,
+                                            guint       property_id,
+                                            GValue     *value,
+                                            GParamSpec *pspec)
+{
+  GimpTransformGridToolUndo *tg_tool_undo = GIMP_TRANSFORM_GRID_TOOL_UNDO (object);
+
+  switch (property_id)
+    {
+    case PROP_TRANSFORM_TOOL:
+      g_value_set_object (value, tg_tool_undo->tg_tool);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_transform_grid_tool_undo_pop (GimpUndo            *undo,
+                                   GimpUndoMode         undo_mode,
+                                   GimpUndoAccumulator *accum)
+{
+  GimpTransformGridToolUndo *tg_tool_undo = GIMP_TRANSFORM_GRID_TOOL_UNDO (undo);
+
+  GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum);
+
+  if (tg_tool_undo->tg_tool)
+    {
+      GimpTransformGridTool *tg_tool;
+#if 0
+      TileManager           *temp;
+#endif
+      gdouble                d;
+      gint                   i;
+
+      tg_tool = tg_tool_undo->tg_tool;
+
+      /*  swap the transformation information arrays  */
+      for (i = 0; i < TRANS_INFO_SIZE; i++)
+        {
+          d = tg_tool_undo->trans_info[i];
+          tg_tool_undo->trans_info[i] = tg_tool->trans_info[i];
+          tg_tool->trans_info[i] = d;
+        }
+
+#if 0
+      /*  swap the original buffer--the source buffer for repeated transform_grids
+       */
+      temp                   = tg_tool_undo->original;
+      tg_tool_undo->original = tg_tool->original;
+      tg_tool->original      = temp;
+#endif
+
+#if 0
+      /*  If we're re-implementing the first transform_grid, reactivate tool  */
+      if (undo_mode == GIMP_UNDO_MODE_REDO && tg_tool->original)
+        {
+          gimp_tool_control_activate (GIMP_TOOL (tg_tool)->control);
+
+          gimp_draw_tool_resume (GIMP_DRAW_TOOL (tg_tool));
+        }
+#endif
+    }
+ }
+
+static void
+gimp_transform_grid_tool_undo_free (GimpUndo     *undo,
+                                    GimpUndoMode  undo_mode)
+{
+  GimpTransformGridToolUndo *tg_tool_undo = GIMP_TRANSFORM_GRID_TOOL_UNDO (undo);
+
+  if (tg_tool_undo->tg_tool)
+    {
+      g_object_remove_weak_pointer (G_OBJECT (tg_tool_undo->tg_tool),
+                                    (gpointer) &tg_tool_undo->tg_tool);
+      tg_tool_undo->tg_tool = NULL;
+    }
+
+#if 0
+  if (tg_tool_undo->original)
+    {
+      tile_manager_unref (tg_tool_undo->original);
+      tg_tool_undo->original = NULL;
+    }
+#endif
+
+  GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode);
+}
diff --git a/app/tools/gimptransformgridtoolundo.h b/app/tools/gimptransformgridtoolundo.h
new file mode 100644
index 0000000000..d4fd99ab79
--- /dev/null
+++ b/app/tools/gimptransformgridtoolundo.h
@@ -0,0 +1,56 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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_TRANSFORM_GRID_TOOL_UNDO_H__
+#define __GIMP_TRANSFORM_GRID_TOOL_UNDO_H__
+
+
+#include "core/gimpundo.h"
+
+
+#define GIMP_TYPE_TRANSFORM_GRID_TOOL_UNDO            (gimp_transform_grid_tool_undo_get_type ())
+#define GIMP_TRANSFORM_GRID_TOOL_UNDO(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GIMP_TYPE_TRANSFORM_GRID_TOOL_UNDO, GimpTransformGridToolUndo))
+#define GIMP_TRANSFORM_GRID_TOOL_UNDO_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
GIMP_TYPE_TRANSFORM_GRID_TOOL_UNDO, GimpTransformGridToolUndoClass))
+#define GIMP_IS_TRANSFORM_GRID_TOOL_UNDO(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GIMP_TYPE_TRANSFORM_GRID_TOOL_UNDO))
+#define GIMP_IS_TRANSFORM_GRID_TOOL_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GIMP_TYPE_TRANSFORM_GRID_TOOL_UNDO))
+#define GIMP_TRANSFORM_GRID_TOOL_UNDO_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GIMP_TYPE_TRANSFORM_GRID_TOOL_UNDO, GimpTransformGridToolUndoClass))
+
+
+typedef struct _GimpTransformGridToolUndo      GimpTransformGridToolUndo;
+typedef struct _GimpTransformGridToolUndoClass GimpTransformGridToolUndoClass;
+
+struct _GimpTransformGridToolUndo
+{
+  GimpUndo               parent_instance;
+
+  GimpTransformGridTool *tg_tool;
+  TransInfo              trans_info;
+#if 0                   
+  TileManager           *original;
+#endif
+};
+
+struct _GimpTransformGridToolUndoClass
+{
+  GimpUndoClass  parent_class;
+};
+
+
+GType   gimp_transform_grid_tool_undo_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_TRANSFORM_GRID_TOOL_UNDO_H__ */
diff --git a/app/tools/gimptransformoptions.c b/app/tools/gimptransformoptions.c
index 5fd7a1e05f..a0366ee2f3 100644
--- a/app/tools/gimptransformoptions.c
+++ b/app/tools/gimptransformoptions.c
@@ -31,12 +31,8 @@
 #include "core/gimptoolinfo.h"
 
 #include "widgets/gimppropwidgets.h"
-#include "widgets/gimpspinscale.h"
 #include "widgets/gimpwidgets-utils.h"
 
-#include "gimprotatetool.h"
-#include "gimpscaletool.h"
-#include "gimpunifiedtransformtool.h"
 #include "gimptooloptions-gui.h"
 #include "gimptransformoptions.h"
 
@@ -49,21 +45,7 @@ enum
   PROP_TYPE,
   PROP_DIRECTION,
   PROP_INTERPOLATION,
-  PROP_CLIP,
-  PROP_SHOW_PREVIEW,
-  PROP_PREVIEW_OPACITY,
-  PROP_GRID_TYPE,
-  PROP_GRID_SIZE,
-  PROP_CONSTRAIN_MOVE,
-  PROP_CONSTRAIN_SCALE,
-  PROP_CONSTRAIN_ROTATE,
-  PROP_CONSTRAIN_SHEAR,
-  PROP_CONSTRAIN_PERSPECTIVE,
-  PROP_FROMPIVOT_SCALE,
-  PROP_FROMPIVOT_SHEAR,
-  PROP_FROMPIVOT_PERSPECTIVE,
-  PROP_CORNERSNAP,
-  PROP_FIXEDPIVOT,
+  PROP_CLIP
 };
 
 
@@ -80,12 +62,6 @@ static void     gimp_transform_options_get_property (GObject         *object,
 
 static void     gimp_transform_options_reset        (GimpConfig      *config);
 
-static gboolean gimp_transform_options_sync_grid    (GBinding        *binding,
-                                                     const GValue    *source_value,
-                                                     GValue          *target_value,
-                                                     gpointer         user_data);
-
-
 G_DEFINE_TYPE_WITH_CODE (GimpTransformOptions, gimp_transform_options,
                          GIMP_TYPE_TOOL_OPTIONS,
                          G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
@@ -134,96 +110,6 @@ gimp_transform_options_class_init (GimpTransformOptionsClass *klass)
                          GIMP_TYPE_TRANSFORM_RESIZE,
                          GIMP_TRANSFORM_RESIZE_ADJUST,
                          GIMP_PARAM_STATIC_STRINGS);
-
-  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_PREVIEW,
-                            "show-preview",
-                            _("Show image preview"),
-                            _("Show a preview of the transformed image"),
-                            TRUE,
-                            GIMP_PARAM_STATIC_STRINGS);
-
-  GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_PREVIEW_OPACITY,
-                           "preview-opacity",
-                           _("Image opacity"),
-                           _("Opacity of the preview image"),
-                           0.0, 1.0, 1.0,
-                           GIMP_PARAM_STATIC_STRINGS);
-
-  GIMP_CONFIG_PROP_ENUM (object_class, PROP_GRID_TYPE,
-                         "grid-type",
-                         _("Guides"),
-                         _("Composition guides such as rule of thirds"),
-                         GIMP_TYPE_GUIDES_TYPE,
-                         GIMP_GUIDES_NONE,
-                         GIMP_PARAM_STATIC_STRINGS);
-
-  GIMP_CONFIG_PROP_INT (object_class, PROP_GRID_SIZE,
-                        "grid-size",
-                        NULL,
-                        _("Size of a grid cell for variable number "
-                          "of composition guides"),
-                        1, 128, 15,
-                        GIMP_PARAM_STATIC_STRINGS);
-
-  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CONSTRAIN_MOVE,
-                            "constrain-move",
-                            NULL, NULL,
-                            FALSE,
-                            GIMP_PARAM_STATIC_STRINGS);
-
-  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CONSTRAIN_SCALE,
-                            "constrain-scale",
-                            NULL, NULL,
-                            FALSE,
-                            GIMP_PARAM_STATIC_STRINGS);
-
-  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CONSTRAIN_ROTATE,
-                            "constrain-rotate",
-                            NULL, NULL,
-                            FALSE,
-                            GIMP_PARAM_STATIC_STRINGS);
-
-  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CONSTRAIN_SHEAR,
-                            "constrain-shear",
-                            NULL, NULL,
-                            TRUE,
-                            GIMP_PARAM_STATIC_STRINGS);
-
-  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CONSTRAIN_PERSPECTIVE,
-                            "constrain-perspective",
-                            NULL, NULL,
-                            FALSE,
-                            GIMP_PARAM_STATIC_STRINGS);
-
-  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_FROMPIVOT_SCALE,
-                            "frompivot-scale",
-                            NULL, NULL,
-                            FALSE,
-                            GIMP_PARAM_STATIC_STRINGS);
-
-  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_FROMPIVOT_SHEAR,
-                            "frompivot-shear",
-                            NULL, NULL,
-                            FALSE,
-                            GIMP_PARAM_STATIC_STRINGS);
-
-  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_FROMPIVOT_PERSPECTIVE,
-                            "frompivot-perspective",
-                            NULL, NULL,
-                            FALSE,
-                            GIMP_PARAM_STATIC_STRINGS);
-
-  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CORNERSNAP,
-                            "cornersnap",
-                            NULL, NULL,
-                            FALSE,
-                            GIMP_PARAM_STATIC_STRINGS);
-
-  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_FIXEDPIVOT,
-                            "fixedpivot",
-                            NULL, NULL,
-                            FALSE,
-                            GIMP_PARAM_STATIC_STRINGS);
 }
 
 static void
@@ -254,14 +140,6 @@ gimp_transform_options_set_property (GObject      *object,
       break;
     case PROP_DIRECTION:
       options->direction = g_value_get_enum (value);
-
-      /* Expected default for corrective transform is to see the
-       * original image only.
-       */
-      g_object_set (options,
-                    "show-preview",
-                    options->direction != GIMP_TRANSFORM_BACKWARD,
-                    NULL);
       break;
     case PROP_INTERPOLATION:
       options->interpolation = g_value_get_enum (value);
@@ -269,48 +147,6 @@ gimp_transform_options_set_property (GObject      *object,
     case PROP_CLIP:
       options->clip = g_value_get_enum (value);
       break;
-    case PROP_SHOW_PREVIEW:
-      options->show_preview = g_value_get_boolean (value);
-      break;
-    case PROP_PREVIEW_OPACITY:
-      options->preview_opacity = g_value_get_double (value);
-      break;
-    case PROP_GRID_TYPE:
-      options->grid_type = g_value_get_enum (value);
-      break;
-    case PROP_GRID_SIZE:
-      options->grid_size = g_value_get_int (value);
-      break;
-    case PROP_CONSTRAIN_MOVE:
-      options->constrain_move = g_value_get_boolean (value);
-      break;
-    case PROP_CONSTRAIN_SCALE:
-      options->constrain_scale = g_value_get_boolean (value);
-      break;
-    case PROP_CONSTRAIN_ROTATE:
-      options->constrain_rotate = g_value_get_boolean (value);
-      break;
-    case PROP_CONSTRAIN_SHEAR:
-      options->constrain_shear = g_value_get_boolean (value);
-      break;
-    case PROP_CONSTRAIN_PERSPECTIVE:
-      options->constrain_perspective = g_value_get_boolean (value);
-      break;
-    case PROP_FROMPIVOT_SCALE:
-      options->frompivot_scale = g_value_get_boolean (value);
-      break;
-    case PROP_FROMPIVOT_SHEAR:
-      options->frompivot_shear = g_value_get_boolean (value);
-      break;
-    case PROP_FROMPIVOT_PERSPECTIVE:
-      options->frompivot_perspective = g_value_get_boolean (value);
-      break;
-    case PROP_CORNERSNAP:
-      options->cornersnap = g_value_get_boolean (value);
-      break;
-    case PROP_FIXEDPIVOT:
-      options->fixedpivot = g_value_get_boolean (value);
-      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -339,48 +175,6 @@ gimp_transform_options_get_property (GObject    *object,
     case PROP_CLIP:
       g_value_set_enum (value, options->clip);
       break;
-    case PROP_SHOW_PREVIEW:
-      g_value_set_boolean (value, options->show_preview);
-      break;
-    case PROP_PREVIEW_OPACITY:
-      g_value_set_double (value, options->preview_opacity);
-      break;
-    case PROP_GRID_TYPE:
-      g_value_set_enum (value, options->grid_type);
-      break;
-    case PROP_GRID_SIZE:
-      g_value_set_int (value, options->grid_size);
-      break;
-    case PROP_CONSTRAIN_MOVE:
-      g_value_set_boolean (value, options->constrain_move);
-      break;
-    case PROP_CONSTRAIN_SCALE:
-      g_value_set_boolean (value, options->constrain_scale);
-      break;
-    case PROP_CONSTRAIN_ROTATE:
-      g_value_set_boolean (value, options->constrain_rotate);
-      break;
-    case PROP_CONSTRAIN_SHEAR:
-      g_value_set_boolean (value, options->constrain_shear);
-      break;
-    case PROP_CONSTRAIN_PERSPECTIVE:
-      g_value_set_boolean (value, options->constrain_perspective);
-      break;
-    case PROP_FROMPIVOT_SCALE:
-      g_value_set_boolean (value, options->frompivot_scale);
-      break;
-    case PROP_FROMPIVOT_SHEAR:
-      g_value_set_boolean (value, options->frompivot_shear);
-      break;
-    case PROP_FROMPIVOT_PERSPECTIVE:
-      g_value_set_boolean (value, options->frompivot_perspective);
-      break;
-    case PROP_CORNERSNAP:
-      g_value_set_boolean (value, options->cornersnap);
-      break;
-    case PROP_FIXEDPIVOT:
-      g_value_set_boolean (value, options->fixedpivot);
-      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -405,14 +199,20 @@ gimp_transform_options_reset (GimpConfig *config)
 
 /**
  * gimp_transform_options_gui:
- * @tool_options: a #GimpToolOptions
+ * @tool_options:  a #GimpToolOptions
+ * @direction:     whether to show the direction frame
+ * @interpolation: whether to show the interpolation menu
+ * @clipping:      whether to show the clipping menu
  *
  * Build the Transform Tool Options.
  *
  * Return value: a container holding the transform tool options
  **/
 GtkWidget *
-gimp_transform_options_gui (GimpToolOptions *tool_options)
+gimp_transform_options_gui (GimpToolOptions *tool_options,
+                            gboolean         direction,
+                            gboolean         interpolation,
+                            gboolean         clipping)
 {
   GObject         *config = G_OBJECT (tool_options);
   GtkWidget       *vbox   = gimp_tool_options_gui (tool_options);
@@ -421,10 +221,6 @@ gimp_transform_options_gui (GimpToolOptions *tool_options)
   GtkWidget       *label;
   GtkWidget       *frame;
   GtkWidget       *combo;
-  GtkWidget       *scale;
-  GtkWidget       *grid_box;
-  GdkModifierType  extend_mask    = gimp_get_extend_selection_mask ();
-  GdkModifierType  constrain_mask = gimp_get_constrain_behavior_mask ();
 
   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
@@ -438,219 +234,33 @@ gimp_transform_options_gui (GimpToolOptions *tool_options)
   gtk_box_pack_start (GTK_BOX (hbox), box, FALSE, FALSE, 0);
   gtk_widget_show (box);
 
-  frame = gimp_prop_enum_radio_frame_new (config, "direction", NULL,
-                                          0, 0);
-  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
-  gtk_widget_show (frame);
+  if (direction)
+    {
+      frame = gimp_prop_enum_radio_frame_new (config, "direction", NULL,
+                                              0, 0);
+      gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+      gtk_widget_show (frame);
+    }
 
   /*  the interpolation menu  */
-  combo = gimp_prop_enum_combo_box_new (config, "interpolation", 0, 0);
-  gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (combo), _("Interpolation"));
-  g_object_set (combo, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-  gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0);
-  gtk_widget_show (combo);
-
-  /*  the clipping menu  */
-  combo = gimp_prop_enum_combo_box_new (config, "clip", 0, 0);
-  gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (combo), _("Clipping"));
-  g_object_set (combo, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-  gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0);
-  gtk_widget_show (combo);
-
-  /*  the preview frame  */
-  scale = gimp_prop_spin_scale_new (config, "preview-opacity", NULL,
-                                    0.01, 0.1, 0);
-  gimp_prop_widget_set_factor (scale, 100.0, 0.0, 0.0, 1);
-  frame = gimp_prop_expanding_frame_new (config, "show-preview", NULL,
-                                         scale, NULL);
-  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
-  gtk_widget_show (frame);
-
-  /*  the guides frame  */
-  frame = gimp_frame_new (NULL);
-  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
-  gtk_widget_show (frame);
-
-  /*  the guides type menu  */
-  combo = gimp_prop_enum_combo_box_new (config, "grid-type", 0, 0);
-  gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (combo), _("Guides"));
-  g_object_set (combo, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-  gtk_frame_set_label_widget (GTK_FRAME (frame), combo);
-  gtk_widget_show (combo);
-
-  /*  the grid density scale  */
-  scale = gimp_prop_spin_scale_new (config, "grid-size", NULL,
-                                    1.8, 8.0, 0);
-  gimp_spin_scale_set_label (GIMP_SPIN_SCALE (scale), NULL);
-  gtk_container_add (GTK_CONTAINER (frame), scale);
-
-  g_object_bind_property_full (config, "grid-type",
-                               scale,  "visible",
-                               G_BINDING_SYNC_CREATE,
-                               gimp_transform_options_sync_grid,
-                               NULL,
-                               NULL, NULL);
-
-  if (tool_options->tool_info->tool_type == GIMP_TYPE_ROTATE_TOOL)
+  if (interpolation)
     {
-      GtkWidget *button;
-      gchar     *label;
-
-      label = g_strdup_printf (_("15 degrees (%s)"),
-                               gimp_get_mod_string (extend_mask));
-
-      button = gimp_prop_check_button_new (config, "constrain-rotate", label);
-      gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
-      gtk_widget_show (button);
-
-      gimp_help_set_help_data (button, _("Limit rotation steps to 15 degrees"),
-                               NULL);
-
-      g_free (label);
+      combo = gimp_prop_enum_combo_box_new (config, "interpolation", 0, 0);
+      gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (combo), _("Interpolation"));
+      g_object_set (combo, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+      gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0);
+      gtk_widget_show (combo);
     }
-  else if (tool_options->tool_info->tool_type == GIMP_TYPE_SCALE_TOOL)
-    {
-      GtkWidget *button;
-      gchar     *label;
-
-      label = g_strdup_printf (_("Keep aspect (%s)"),
-                               gimp_get_mod_string (extend_mask));
-
-      button = gimp_prop_check_button_new (config, "constrain-scale", label);
-      gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
-      gtk_widget_show (button);
-
-      gimp_help_set_help_data (button, _("Keep the original aspect ratio"),
-                               NULL);
-
-      g_free (label);
-
-      label = g_strdup_printf (_("Around center (%s)"),
-                               gimp_get_mod_string (constrain_mask));
 
-      button = gimp_prop_check_button_new (config, "frompivot-scale", label);
-      gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
-      gtk_widget_show (button);
-
-      gimp_help_set_help_data (button, _("Scale around the center point"),
-                               NULL);
-
-      g_free (label);
-    }
-  else if (tool_options->tool_info->tool_type == GIMP_TYPE_UNIFIED_TRANSFORM_TOOL)
+  /*  the clipping menu  */
+  if (clipping)
     {
-      struct
-      {
-        GdkModifierType  mod;
-        gchar           *name;
-        gchar           *desc;
-        gchar           *tip;
-      }
-      opt_list[] =
-      {
-        { extend_mask, NULL, N_("Constrain (%s)") },
-        { extend_mask, "constrain-move", N_("Move"),
-          N_("Constrain movement to 45 degree angles from center (%s)") },
-        { extend_mask, "constrain-scale", N_("Scale"),
-          N_("Maintain aspect ratio when scaling (%s)") },
-        { extend_mask, "constrain-rotate", N_("Rotate"),
-          N_("Constrain rotation to 15 degree increments (%s)") },
-        { extend_mask, "constrain-shear", N_("Shear"),
-          N_("Shear along edge direction only (%s)") },
-        { extend_mask, "constrain-perspective", N_("Perspective"),
-          N_("Constrain perspective handles to move along edges and diagonal (%s)") },
-
-        { constrain_mask, NULL,
-          N_("From pivot  (%s)") },
-        { constrain_mask, "frompivot-scale", N_("Scale"),
-          N_("Scale from pivot point (%s)") },
-        { constrain_mask, "frompivot-shear", N_("Shear"),
-          N_("Shear opposite edge by same amount (%s)") },
-        { constrain_mask, "frompivot-perspective", N_("Perspective"),
-          N_("Maintain position of pivot while changing perspective (%s)") },
-
-        { 0, NULL,
-          N_("Pivot") },
-        { extend_mask, "cornersnap", N_("Snap (%s)"),
-          N_("Snap pivot to corners and center (%s)") },
-        { 0, "fixedpivot", N_("Lock"),
-          N_("Lock pivot position to canvas") },
-      };
-
-      GtkWidget *button;
-      gchar     *label;
-      gint       i;
-
-      frame = NULL;
-
-      for (i = 0; i < G_N_ELEMENTS (opt_list); i++)
-        {
-          if (! opt_list[i].name && ! opt_list[i].desc)
-            {
-              frame = NULL;
-              continue;
-            }
-
-          label = g_strdup_printf (gettext (opt_list[i].desc),
-                                   gimp_get_mod_string (opt_list[i].mod));
-
-          if (opt_list[i].name)
-            {
-              button = gimp_prop_check_button_new (config, opt_list[i].name,
-                                                   label);
-
-              gtk_box_pack_start (GTK_BOX (frame ? grid_box : vbox),
-                                  button, FALSE, FALSE, 0);
-
-              gtk_widget_show (button);
-
-              g_free (label);
-              label = g_strdup_printf (gettext (opt_list[i].tip),
-                                       gimp_get_mod_string (opt_list[i].mod));
-
-              gimp_help_set_help_data (button, label, NULL);
-            }
-          else
-            {
-              frame = gimp_frame_new (label);
-              gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
-              gtk_widget_show (frame);
-
-              grid_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
-              gtk_container_add (GTK_CONTAINER (frame), grid_box);
-              gtk_widget_show (grid_box);
-            }
-
-          g_free (label);
-        }
+      combo = gimp_prop_enum_combo_box_new (config, "clip", 0, 0);
+      gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (combo), _("Clipping"));
+      g_object_set (combo, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+      gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0);
+      gtk_widget_show (combo);
     }
 
   return vbox;
 }
-
-gboolean
-gimp_transform_options_show_preview (GimpTransformOptions *options)
-{
-  g_return_val_if_fail (GIMP_IS_TRANSFORM_OPTIONS (options), FALSE);
-
-  return (options->show_preview &&
-          options->type == GIMP_TRANSFORM_TYPE_LAYER);
-}
-
-
-/*  private functions  */
-
-static gboolean
-gimp_transform_options_sync_grid (GBinding     *binding,
-                                  const GValue *source_value,
-                                  GValue       *target_value,
-                                  gpointer      user_data)
-{
-  GimpGuidesType type = g_value_get_enum (source_value);
-
-  g_value_set_boolean (target_value,
-                       type == GIMP_GUIDES_N_LINES ||
-                       type == GIMP_GUIDES_SPACING);
-
-  return TRUE;
-}
diff --git a/app/tools/gimptransformoptions.h b/app/tools/gimptransformoptions.h
index 94c6b63c01..86cbdff396 100644
--- a/app/tools/gimptransformoptions.h
+++ b/app/tools/gimptransformoptions.h
@@ -35,26 +35,12 @@ typedef struct _GimpTransformOptionsClass GimpTransformOptionsClass;
 
 struct _GimpTransformOptions
 {
-  GimpToolOptions           parent_instance;
+  GimpToolOptions        parent_instance;
 
-  GimpTransformType         type;
-  GimpTransformDirection    direction;
-  GimpInterpolationType     interpolation;
-  GimpTransformResize       clip;
-  gboolean                  show_preview;
-  gdouble                   preview_opacity;
-  GimpGuidesType            grid_type;
-  gint                      grid_size;
-  gboolean                  constrain_move;
-  gboolean                  constrain_scale;
-  gboolean                  constrain_rotate;
-  gboolean                  constrain_shear;
-  gboolean                  constrain_perspective;
-  gboolean                  frompivot_scale;
-  gboolean                  frompivot_shear;
-  gboolean                  frompivot_perspective;
-  gboolean                  cornersnap;
-  gboolean                  fixedpivot;
+  GimpTransformType      type;
+  GimpTransformDirection direction;
+  GimpInterpolationType  interpolation;
+  GimpTransformResize    clip;
 };
 
 struct _GimpTransformOptionsClass
@@ -63,11 +49,12 @@ struct _GimpTransformOptionsClass
 };
 
 
-GType       gimp_transform_options_get_type     (void) G_GNUC_CONST;
+GType       gimp_transform_options_get_type (void) G_GNUC_CONST;
 
-GtkWidget * gimp_transform_options_gui          (GimpToolOptions *tool_options);
-
-gboolean    gimp_transform_options_show_preview (GimpTransformOptions *options);
+GtkWidget * gimp_transform_options_gui      (GimpToolOptions *tool_options,
+                                             gboolean         direction,
+                                             gboolean         interpolation,
+                                             gboolean         clipping);
 
 
 #endif /* __GIMP_TRANSFORM_OPTIONS_H__ */
diff --git a/app/tools/gimptransformtool.c b/app/tools/gimptransformtool.c
index e43337c3df..2b50e4594e 100644
--- a/app/tools/gimptransformtool.c
+++ b/app/tools/gimptransformtool.c
@@ -19,141 +19,40 @@
 
 #include <gegl.h>
 #include <gtk/gtk.h>
-#include <gdk/gdkkeysyms.h>
 
 #include "libgimpmath/gimpmath.h"
-#include "libgimpwidgets/gimpwidgets.h"
 
 #include "tools-types.h"
 
 #include "core/gimp.h"
-#include "core/gimpboundary.h"
 #include "core/gimpdrawable-transform.h"
 #include "core/gimperror.h"
 #include "core/gimpimage.h"
 #include "core/gimpimage-undo.h"
-#include "core/gimpimage-undo-push.h"
 #include "core/gimpitem-linked.h"
 #include "core/gimplayer.h"
 #include "core/gimplayermask.h"
 #include "core/gimpprogress.h"
-#include "core/gimpprojection.h"
-#include "core/gimptoolinfo.h"
 
 #include "vectors/gimpvectors.h"
-#include "vectors/gimpstroke.h"
 
-#include "widgets/gimpwidgets-utils.h"
-
-#include "display/gimpcanvasitem.h"
 #include "display/gimpdisplay.h"
-#include "display/gimptoolgui.h"
-#include "display/gimptoolwidget.h"
 
 #include "gimptoolcontrol.h"
 #include "gimptransformoptions.h"
 #include "gimptransformtool.h"
-#include "gimptransformtoolundo.h"
 
 #include "gimp-intl.h"
 
 
-#define RESPONSE_RESET 1
-
-
-static void      gimp_transform_tool_finalize            (GObject               *object);
-
-static gboolean  gimp_transform_tool_initialize          (GimpTool              *tool,
-                                                          GimpDisplay           *display,
-                                                          GError               **error);
-static void      gimp_transform_tool_control             (GimpTool              *tool,
-                                                          GimpToolAction         action,
-                                                          GimpDisplay           *display);
-static void      gimp_transform_tool_button_press        (GimpTool              *tool,
-                                                          const GimpCoords      *coords,
-                                                          guint32                time,
-                                                          GdkModifierType        state,
-                                                          GimpButtonPressType    press_type,
-                                                          GimpDisplay           *display);
-static void      gimp_transform_tool_button_release      (GimpTool              *tool,
-                                                          const GimpCoords      *coords,
-                                                          guint32                time,
-                                                          GdkModifierType        state,
-                                                          GimpButtonReleaseType  release_type,
-                                                          GimpDisplay           *display);
-static void      gimp_transform_tool_motion              (GimpTool              *tool,
-                                                          const GimpCoords      *coords,
-                                                          guint32                time,
-                                                          GdkModifierType        state,
-                                                          GimpDisplay           *display);
-static void      gimp_transform_tool_modifier_key        (GimpTool              *tool,
-                                                          GdkModifierType        key,
-                                                          gboolean               press,
-                                                          GdkModifierType        state,
-                                                          GimpDisplay           *display);
-static void      gimp_transform_tool_cursor_update       (GimpTool              *tool,
-                                                          const GimpCoords      *coords,
-                                                          GdkModifierType        state,
-                                                          GimpDisplay           *display);
-static const gchar * gimp_transform_tool_can_undo        (GimpTool              *tool,
-                                                          GimpDisplay           *display);
-static const gchar * gimp_transform_tool_can_redo        (GimpTool              *tool,
-                                                          GimpDisplay           *display);
-static gboolean  gimp_transform_tool_undo                (GimpTool              *tool,
-                                                          GimpDisplay           *display);
-static gboolean  gimp_transform_tool_redo                (GimpTool              *tool,
-                                                          GimpDisplay           *display);
-static void      gimp_transform_tool_options_notify      (GimpTool              *tool,
-                                                          GimpToolOptions       *options,
-                                                          const GParamSpec      *pspec);
-
-static void      gimp_transform_tool_draw                (GimpDrawTool          *draw_tool);
-
-static GeglBuffer *
-                 gimp_transform_tool_real_transform      (GimpTransformTool     *tr_tool,
-                                                          GimpItem              *item,
-                                                          GeglBuffer            *orig_buffer,
-                                                          gint                   orig_offset_x,
-                                                          gint                   orig_offset_y,
-                                                          GimpColorProfile     **buffer_profile,
-                                                          gint                  *new_offset_x,
-                                                          gint                  *new_offset_y);
-
-static void      gimp_transform_tool_widget_changed      (GimpToolWidget        *widget,
-                                                          GimpTransformTool     *tr_tool);
-static void      gimp_transform_tool_widget_response     (GimpToolWidget        *widget,
-                                                          gint                   response_id,
-                                                          GimpTransformTool     *tr_tool);
-
-static void      gimp_transform_tool_halt                (GimpTransformTool     *tr_tool);
-static void      gimp_transform_tool_commit              (GimpTransformTool     *tr_tool);
-
-static gboolean  gimp_transform_tool_bounds              (GimpTransformTool     *tr_tool,
-                                                          GimpDisplay           *display);
-static void      gimp_transform_tool_dialog              (GimpTransformTool     *tr_tool);
-static void      gimp_transform_tool_dialog_update       (GimpTransformTool     *tr_tool);
-static void      gimp_transform_tool_prepare             (GimpTransformTool     *tr_tool,
-                                                          GimpDisplay           *display);
-static GimpToolWidget *
-                 gimp_transform_tool_get_widget          (GimpTransformTool     *tr_tool);
-
-static void      gimp_transform_tool_response            (GimpToolGui           *gui,
-                                                          gint                   response_id,
-                                                          GimpTransformTool     *tr_tool);
-
-static void      gimp_transform_tool_update_sensitivity  (GimpTransformTool     *tr_tool);
-static GimpItem *gimp_transform_tool_get_active_item     (GimpTransformTool     *tr_tool,
-                                                          GimpImage             *image);
-static GimpItem *gimp_transform_tool_check_active_item   (GimpTransformTool     *tr_tool,
-                                                          GimpImage             *display,
-                                                          gboolean               invisible_layer_ok,
-                                                          GError               **error);
-static void      gimp_transform_tool_hide_active_item    (GimpTransformTool     *tr_tool,
-                                                          GimpItem              *item);
-static void      gimp_transform_tool_show_active_item    (GimpTransformTool     *tr_tool);
-
-static TransInfo * trans_info_new  (void);
-static void        trans_info_free (TransInfo *info);
+static GeglBuffer * gimp_transform_tool_real_transform (GimpTransformTool  *tr_tool,
+                                                        GimpItem           *item,
+                                                        GeglBuffer         *orig_buffer,
+                                                        gint                orig_offset_x,
+                                                        gint                orig_offset_y,
+                                                        GimpColorProfile  **buffer_profile,
+                                                        gint               *new_offset_x,
+                                                        gint               *new_offset_y);
 
 
 G_DEFINE_TYPE (GimpTransformTool, gimp_transform_tool, GIMP_TYPE_DRAW_TOOL)
@@ -164,605 +63,18 @@ G_DEFINE_TYPE (GimpTransformTool, gimp_transform_tool, GIMP_TYPE_DRAW_TOOL)
 static void
 gimp_transform_tool_class_init (GimpTransformToolClass *klass)
 {
-  GObjectClass      *object_class = G_OBJECT_CLASS (klass);
-  GimpToolClass     *tool_class   = GIMP_TOOL_CLASS (klass);
-  GimpDrawToolClass *draw_class   = GIMP_DRAW_TOOL_CLASS (klass);
-
-  object_class->finalize          = gimp_transform_tool_finalize;
-
-  tool_class->initialize          = gimp_transform_tool_initialize;
-  tool_class->control             = gimp_transform_tool_control;
-  tool_class->button_press        = gimp_transform_tool_button_press;
-  tool_class->button_release      = gimp_transform_tool_button_release;
-  tool_class->motion              = gimp_transform_tool_motion;
-  tool_class->modifier_key        = gimp_transform_tool_modifier_key;
-  tool_class->cursor_update       = gimp_transform_tool_cursor_update;
-  tool_class->can_undo            = gimp_transform_tool_can_undo;
-  tool_class->can_redo            = gimp_transform_tool_can_redo;
-  tool_class->undo                = gimp_transform_tool_undo;
-  tool_class->redo                = gimp_transform_tool_redo;
-  tool_class->options_notify      = gimp_transform_tool_options_notify;
-
-  draw_class->draw                = gimp_transform_tool_draw;
-
-  klass->dialog                   = NULL;
-  klass->dialog_update            = NULL;
-  klass->prepare                  = NULL;
-  klass->recalc_matrix            = NULL;
-  klass->get_undo_desc            = NULL;
-  klass->transform                = gimp_transform_tool_real_transform;
-
-  klass->ok_button_label          = _("_Transform");
+  klass->recalc_matrix = NULL;
+  klass->get_undo_desc = NULL;
+  klass->transform     = gimp_transform_tool_real_transform;
+
+  klass->progress_text = _("Transforming");
 }
 
 static void
 gimp_transform_tool_init (GimpTransformTool *tr_tool)
 {
-  GimpTool *tool = GIMP_TOOL (tr_tool);
-
-  gimp_tool_control_set_scroll_lock      (tool->control, TRUE);
-  gimp_tool_control_set_preserve         (tool->control, FALSE);
-  gimp_tool_control_set_dirty_mask       (tool->control,
-                                          GIMP_DIRTY_IMAGE_SIZE |
-                                          GIMP_DIRTY_DRAWABLE   |
-                                          GIMP_DIRTY_SELECTION  |
-                                          GIMP_DIRTY_ACTIVE_DRAWABLE);
-  gimp_tool_control_set_active_modifiers (tool->control,
-                                          GIMP_TOOL_ACTIVE_MODIFIERS_SAME);
-  gimp_tool_control_set_precision        (tool->control,
-                                          GIMP_CURSOR_PRECISION_SUBPIXEL);
-  gimp_tool_control_set_cursor           (tool->control,
-                                          GIMP_CURSOR_CROSSHAIR_SMALL);
-  gimp_tool_control_set_action_opacity   (tool->control,
-                                          "tools/tools-transform-preview-opacity-set");
-
-  tr_tool->progress_text = _("Transforming");
-
   gimp_matrix3_identity (&tr_tool->transform);
   tr_tool->transform_valid = TRUE;
-
-  tr_tool->strokes = g_ptr_array_new ();
-}
-
-static void
-gimp_transform_tool_finalize (GObject *object)
-{
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (object);
-
-  g_clear_object (&tr_tool->gui);
-  g_clear_pointer (&tr_tool->strokes, g_ptr_array_unref);
-
-  G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static gboolean
-gimp_transform_tool_initialize (GimpTool     *tool,
-                                GimpDisplay  *display,
-                                GError      **error)
-{
-  GimpTransformTool *tr_tool  = GIMP_TRANSFORM_TOOL (tool);
-  GimpImage         *image    = gimp_display_get_image (display);
-  GimpDrawable      *drawable = gimp_image_get_active_drawable (image);
-  GimpItem          *item;
-
-  item = gimp_transform_tool_check_active_item (tr_tool, image, FALSE, error);
-
-  if (! item)
-    return FALSE;
-
-  /*  Find the transform bounds for some tools (like scale,
-   *  perspective) that actually need the bounds for initializing
-   */
-  if (! gimp_transform_tool_bounds (tr_tool, display))
-    {
-      g_set_error (error, GIMP_ERROR, GIMP_FAILED,
-                   _("The selection does not intersect with the layer."));
-      return FALSE;
-    }
-
-  tool->display  = display;
-  tool->drawable = drawable;
-
-  /*  Initialize the transform tool dialog  */
-  if (! tr_tool->gui)
-    gimp_transform_tool_dialog (tr_tool);
-
-  /*  Initialize the tool-specific trans_info, and adjust the tool dialog  */
-  gimp_transform_tool_prepare (tr_tool, display);
-
-  /*  Recalculate the transform tool  */
-  gimp_transform_tool_recalc_matrix (tr_tool, NULL);
-
-  /*  Get the on-canvas gui  */
-  tr_tool->widget = gimp_transform_tool_get_widget (tr_tool);
-
-  gimp_transform_tool_hide_active_item (tr_tool, item);
-
-  /*  start drawing the bounding box and handles...  */
-  gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
-
-  /* Initialize undo and redo lists */
-  tr_tool->undo_list = g_list_prepend (NULL, trans_info_new ());
-  tr_tool->redo_list = NULL;
-  tr_tool->old_trans_info = g_list_last (tr_tool->undo_list)->data;
-  tr_tool->prev_trans_info = g_list_first (tr_tool->undo_list)->data;
-  gimp_transform_tool_update_sensitivity (tr_tool);
-
-  /*  Save the current transformation info  */
-  memcpy (tr_tool->prev_trans_info, tr_tool->trans_info,
-          sizeof (TransInfo));
-
-  return TRUE;
-}
-
-static void
-gimp_transform_tool_control (GimpTool       *tool,
-                             GimpToolAction  action,
-                             GimpDisplay    *display)
-{
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
-
-  switch (action)
-    {
-    case GIMP_TOOL_ACTION_PAUSE:
-      break;
-
-    case GIMP_TOOL_ACTION_RESUME:
-      gimp_transform_tool_bounds (tr_tool, display);
-      gimp_transform_tool_recalc_matrix (tr_tool, tr_tool->widget);
-      break;
-
-    case GIMP_TOOL_ACTION_HALT:
-      gimp_transform_tool_halt (tr_tool);
-     break;
-
-    case GIMP_TOOL_ACTION_COMMIT:
-      if (tool->display)
-        gimp_transform_tool_commit (tr_tool);
-      break;
-    }
-
-  GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
-}
-
-static void
-gimp_transform_tool_button_press (GimpTool            *tool,
-                                  const GimpCoords    *coords,
-                                  guint32              time,
-                                  GdkModifierType      state,
-                                  GimpButtonPressType  press_type,
-                                  GimpDisplay         *display)
-{
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
-
-  if (tr_tool->widget)
-    {
-      gimp_tool_widget_hover (tr_tool->widget, coords, state, TRUE);
-
-      if (gimp_tool_widget_button_press (tr_tool->widget, coords, time, state,
-                                         press_type))
-        {
-          tr_tool->grab_widget = tr_tool->widget;
-        }
-    }
-
-  gimp_tool_control_activate (tool->control);
-}
-
-void
-gimp_transform_tool_push_internal_undo (GimpTransformTool *tr_tool)
-{
-  g_return_if_fail (GIMP_IS_TRANSFORM_TOOL (tr_tool));
-  g_return_if_fail (tr_tool->prev_trans_info != NULL);
-
-  /* push current state on the undo list and set this state as the
-   * current state, but avoid doing this if there were no changes
-   */
-  if (memcmp (tr_tool->prev_trans_info, tr_tool->trans_info,
-              sizeof (TransInfo)) != 0)
-    {
-      tr_tool->prev_trans_info = trans_info_new ();
-      memcpy (tr_tool->prev_trans_info, tr_tool->trans_info,
-              sizeof (TransInfo));
-
-      tr_tool->undo_list = g_list_prepend (tr_tool->undo_list,
-                                           tr_tool->prev_trans_info);
-
-      /* If we undid anything and started interacting, we have to
-       * discard the redo history
-       */
-      g_list_free_full (tr_tool->redo_list, (GDestroyNotify) trans_info_free);
-      tr_tool->redo_list = NULL;
-
-      gimp_transform_tool_update_sensitivity (tr_tool);
-
-      /*  update the undo actions / menu items  */
-      gimp_image_flush (gimp_display_get_image (GIMP_TOOL (tr_tool)->display));
-    }
-}
-
-static void
-gimp_transform_tool_button_release (GimpTool              *tool,
-                                    const GimpCoords      *coords,
-                                    guint32                time,
-                                    GdkModifierType        state,
-                                    GimpButtonReleaseType  release_type,
-                                    GimpDisplay           *display)
-{
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
-
-  gimp_tool_control_halt (tool->control);
-
-  if (tr_tool->grab_widget)
-    {
-      gimp_tool_widget_button_release (tr_tool->grab_widget,
-                                       coords, time, state, release_type);
-      tr_tool->grab_widget = NULL;
-    }
-
-  if (release_type != GIMP_BUTTON_RELEASE_CANCEL)
-    {
-      /* This hack is to perform the flip immediately with the flip tool */
-      if (! tr_tool->widget)
-        {
-          gimp_transform_tool_response (NULL, GTK_RESPONSE_OK, tr_tool);
-          return;
-        }
-
-      /* We're done with an interaction, save it on the undo list */
-      gimp_transform_tool_push_internal_undo (tr_tool);
-    }
-  else
-    {
-      /*  Restore the last saved state  */
-      memcpy (tr_tool->trans_info, tr_tool->prev_trans_info,
-              sizeof (TransInfo));
-
-      /*  reget the selection bounds  */
-      gimp_transform_tool_bounds (tr_tool, display);
-
-      /*  recalculate the tool's transformation matrix  */
-      gimp_transform_tool_recalc_matrix (tr_tool, tr_tool->widget);
-    }
-}
-
-static void
-gimp_transform_tool_motion (GimpTool         *tool,
-                            const GimpCoords *coords,
-                            guint32           time,
-                            GdkModifierType   state,
-                            GimpDisplay      *display)
-{
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
-
-  if (tr_tool->grab_widget)
-    {
-      gimp_tool_widget_motion (tr_tool->grab_widget, coords, time, state);
-    }
-}
-
-static void
-gimp_transform_tool_modifier_key (GimpTool        *tool,
-                                  GdkModifierType  key,
-                                  gboolean         press,
-                                  GdkModifierType  state,
-                                  GimpDisplay     *display)
-{
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
-
-  if (tr_tool->widget)
-    {
-      GIMP_TOOL_CLASS (parent_class)->modifier_key (tool, key, press,
-                                                    state, display);
-    }
-  else
-    {
-      GimpTransformOptions *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
-
-      if (key == gimp_get_constrain_behavior_mask ())
-        {
-          g_object_set (options,
-                        "frompivot-scale",       ! options->frompivot_scale,
-                        "frompivot-shear",       ! options->frompivot_shear,
-                        "frompivot-perspective", ! options->frompivot_perspective,
-                        NULL);
-        }
-      else if (key == gimp_get_extend_selection_mask ())
-        {
-          g_object_set (options,
-                        "cornersnap",            ! options->cornersnap,
-                        "constrain-move",        ! options->constrain_move,
-                        "constrain-scale",       ! options->constrain_scale,
-                        "constrain-rotate",      ! options->constrain_rotate,
-                        "constrain-shear",       ! options->constrain_shear,
-                        "constrain-perspective", ! options->constrain_perspective,
-                        NULL);
-        }
-    }
-}
-
-static void
-gimp_transform_tool_cursor_update (GimpTool         *tool,
-                                   const GimpCoords *coords,
-                                   GdkModifierType   state,
-                                   GimpDisplay      *display)
-{
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
-  GimpImage         *image   = gimp_display_get_image (display);
-
-  if (! gimp_transform_tool_check_active_item (tr_tool, image, TRUE, NULL))
-    {
-      gimp_tool_set_cursor (tool, display,
-                            gimp_tool_control_get_cursor (tool->control),
-                            gimp_tool_control_get_tool_cursor (tool->control),
-                            GIMP_CURSOR_MODIFIER_BAD);
-      return;
-    }
-
-  GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
-}
-
-static const gchar *
-gimp_transform_tool_can_undo (GimpTool    *tool,
-                              GimpDisplay *display)
-{
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
-
-  if (! tr_tool->undo_list || ! tr_tool->undo_list->next)
-    return NULL;
-
-  return _("Transform Step");
-}
-
-static const gchar *
-gimp_transform_tool_can_redo (GimpTool    *tool,
-                              GimpDisplay *display)
-{
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
-
-  if (! tr_tool->redo_list)
-    return NULL;
-
-  return _("Transform Step");
-}
-
-static gboolean
-gimp_transform_tool_undo (GimpTool    *tool,
-                          GimpDisplay *display)
-{
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
-  GList             *item;
-
-  item = g_list_next (tr_tool->undo_list);
-
-  /* Move prev_trans_info from undo_list to redo_list */
-  tr_tool->redo_list = g_list_prepend (tr_tool->redo_list,
-                                       tr_tool->prev_trans_info);
-  tr_tool->undo_list = g_list_remove (tr_tool->undo_list,
-                                      tr_tool->prev_trans_info);
-
-  tr_tool->prev_trans_info = item->data;
-
-  /*  Restore the previous transformation info  */
-  memcpy (tr_tool->trans_info, tr_tool->prev_trans_info,
-          sizeof (TransInfo));
-
-  /*  reget the selection bounds  */
-  gimp_transform_tool_bounds (tr_tool, tool->display);
-
-  /*  recalculate the tool's transformation matrix  */
-  gimp_transform_tool_recalc_matrix (tr_tool, tr_tool->widget);
-
-  return TRUE;
-}
-
-static gboolean
-gimp_transform_tool_redo (GimpTool    *tool,
-                          GimpDisplay *display)
-{
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
-  GList             *item;
-
-  item = tr_tool->redo_list;
-
-  /* Move prev_trans_info from redo_list to undo_list */
-  tr_tool->prev_trans_info = item->data;
-
-  tr_tool->undo_list = g_list_prepend (tr_tool->undo_list,
-                                       tr_tool->prev_trans_info);
-  tr_tool->redo_list = g_list_remove (tr_tool->redo_list,
-                                      tr_tool->prev_trans_info);
-
-  /*  Restore the previous transformation info  */
-  memcpy (tr_tool->trans_info, tr_tool->prev_trans_info,
-          sizeof (TransInfo));
-
-  /*  reget the selection bounds  */
-  gimp_transform_tool_bounds (tr_tool, tool->display);
-
-  /*  recalculate the tool's transformation matrix  */
-  gimp_transform_tool_recalc_matrix (tr_tool, tr_tool->widget);
-
-  return TRUE;
-}
-
-static void
-gimp_transform_tool_options_notify (GimpTool         *tool,
-                                    GimpToolOptions  *options,
-                                    const GParamSpec *pspec)
-{
-  GimpTransformTool    *tr_tool    = GIMP_TRANSFORM_TOOL (tool);
-  GimpTransformOptions *tr_options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
-
-  GIMP_TOOL_CLASS (parent_class)->options_notify (tool, options, pspec);
-
-  if (! strcmp (pspec->name, "type"))
-    {
-      gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
-      return;
-    }
-
-  if (! tr_tool->widget)
-    return;
-
-  if (! strcmp (pspec->name, "direction"))
-    {
-      /*  reget the selection bounds  */
-      gimp_transform_tool_bounds (tr_tool, tool->display);
-
-      /*  recalculate the tool's transformation matrix  */
-      gimp_transform_tool_recalc_matrix (tr_tool, tr_tool->widget);
-    }
-  else if (! strcmp (pspec->name, "show-preview"))
-    {
-      if (tr_tool->preview)
-        {
-          GimpDisplay *display;
-          GimpImage   *image;
-          GimpItem    *item;
-          gboolean     show_preview;
-
-          show_preview = gimp_transform_options_show_preview (tr_options) &&
-                         tr_tool->transform_valid;
-
-          gimp_canvas_item_set_visible (tr_tool->preview, show_preview);
-
-          display = tool->display;
-          image   = gimp_display_get_image (display);
-          item    = gimp_transform_tool_check_active_item (tr_tool, image, TRUE, NULL);
-          if (item)
-            {
-              if (show_preview)
-                gimp_transform_tool_hide_active_item (tr_tool, item);
-              else
-                gimp_transform_tool_show_active_item (tr_tool);
-            }
-        }
-    }
-  else if (g_str_has_prefix (pspec->name, "constrain-") ||
-           g_str_has_prefix (pspec->name, "frompivot-") ||
-           ! strcmp (pspec->name, "fixedpivot") ||
-           ! strcmp (pspec->name, "cornersnap"))
-    {
-      gimp_transform_tool_dialog_update (tr_tool);
-    }
-}
-
-static void
-gimp_transform_tool_draw (GimpDrawTool *draw_tool)
-{
-  GimpTool             *tool    = GIMP_TOOL (draw_tool);
-  GimpTransformTool    *tr_tool = GIMP_TRANSFORM_TOOL (draw_tool);
-  GimpTransformOptions *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
-  GimpImage            *image   = gimp_display_get_image (tool->display);
-  GimpMatrix3           matrix  = tr_tool->transform;
-  GimpCanvasItem       *item;
-
-  if (options->direction == GIMP_TRANSFORM_BACKWARD)
-    gimp_matrix3_invert (&matrix);
-
-  if (tr_tool->widget)
-    {
-      gboolean show_preview = gimp_transform_options_show_preview (options) &&
-                              tr_tool->transform_valid;
-
-      tr_tool->preview =
-        gimp_draw_tool_add_transform_preview (draw_tool,
-                                              tool->drawable,
-                                              &matrix,
-                                              tr_tool->x1,
-                                              tr_tool->y1,
-                                              tr_tool->x2,
-                                              tr_tool->y2);
-      g_object_add_weak_pointer (G_OBJECT (tr_tool->preview),
-                                 (gpointer) &tr_tool->preview);
-
-      gimp_canvas_item_set_visible (tr_tool->preview, show_preview);
-
-      g_object_bind_property (G_OBJECT (options),          "preview-opacity",
-                              G_OBJECT (tr_tool->preview), "opacity",
-                              G_BINDING_SYNC_CREATE |
-                              G_BINDING_BIDIRECTIONAL);
-
-      GIMP_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool);
-    }
-
-  if (options->type == GIMP_TRANSFORM_TYPE_SELECTION)
-    {
-      const GimpBoundSeg *segs_in;
-      const GimpBoundSeg *segs_out;
-      gint                n_segs_in;
-      gint                n_segs_out;
-
-      gimp_channel_boundary (gimp_image_get_mask (image),
-                             &segs_in, &segs_out,
-                             &n_segs_in, &n_segs_out,
-                             0, 0, 0, 0);
-
-      if (segs_in)
-        {
-          tr_tool->boundary_in =
-            gimp_draw_tool_add_boundary (draw_tool,
-                                         segs_in, n_segs_in,
-                                         &matrix,
-                                         0, 0);
-          g_object_add_weak_pointer (G_OBJECT (tr_tool->boundary_in),
-                                     (gpointer) &tr_tool->boundary_in);
-
-          gimp_canvas_item_set_visible (tr_tool->boundary_in,
-                                        tr_tool->transform_valid);
-        }
-
-      if (segs_out)
-        {
-          tr_tool->boundary_out =
-            gimp_draw_tool_add_boundary (draw_tool,
-                                         segs_out, n_segs_out,
-                                         &matrix,
-                                         0, 0);
-          g_object_add_weak_pointer (G_OBJECT (tr_tool->boundary_in),
-                                     (gpointer) &tr_tool->boundary_out);
-
-          gimp_canvas_item_set_visible (tr_tool->boundary_out,
-                                        tr_tool->transform_valid);
-        }
-    }
-  else if (options->type == GIMP_TRANSFORM_TYPE_PATH)
-    {
-      GimpVectors *vectors = gimp_image_get_active_vectors (image);
-
-      if (vectors)
-        {
-          GimpStroke *stroke = NULL;
-
-          while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke)))
-            {
-              GArray   *coords;
-              gboolean  closed;
-
-              coords = gimp_stroke_interpolate (stroke, 1.0, &closed);
-
-              if (coords && coords->len)
-                {
-                  item =
-                    gimp_draw_tool_add_strokes (draw_tool,
-                                                &g_array_index (coords,
-                                                                GimpCoords, 0),
-                                                coords->len, &matrix, FALSE);
-
-                  g_ptr_array_add (tr_tool->strokes, item);
-                  g_object_weak_ref (G_OBJECT (item),
-                                     (GWeakNotify) g_ptr_array_remove,
-                                     tr_tool->strokes);
-
-                  gimp_canvas_item_set_visible (item, tr_tool->transform_valid);
-                }
-
-              if (coords)
-                g_array_free (coords, TRUE);
-            }
-        }
-    }
 }
 
 static GeglBuffer *
@@ -775,15 +87,16 @@ gimp_transform_tool_real_transform (GimpTransformTool *tr_tool,
                                     gint              *new_offset_x,
                                     gint              *new_offset_y)
 {
-  GimpTool             *tool    = GIMP_TOOL (tr_tool);
-  GimpTransformOptions *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
-  GimpContext          *context = GIMP_CONTEXT (options);
-  GeglBuffer           *ret     = NULL;
-  GimpTransformResize   clip    = options->clip;
-  GimpProgress         *progress;
+  GimpTransformToolClass *klass   = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool);
+  GimpTool               *tool    = GIMP_TOOL (tr_tool);
+  GimpTransformOptions   *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
+  GimpContext            *context = GIMP_CONTEXT (options);
+  GeglBuffer             *ret     = NULL;
+  GimpTransformResize     clip    = options->clip;
+  GimpProgress           *progress;
 
   progress = gimp_progress_start (GIMP_PROGRESS (tool), FALSE,
-                                  "%s", tr_tool->progress_text);
+                                  "%s", klass->progress_text);
 
   if (orig_buffer)
     {
@@ -812,307 +125,56 @@ gimp_transform_tool_real_transform (GimpTransformTool *tr_tool,
                                                    new_offset_y,
                                                    progress);
     }
-  else
-    {
-      /*  this happens for entire drawables, paths and layer groups  */
-
-      if (gimp_item_get_linked (active_item))
-        {
-          gimp_item_linked_transform (active_item, context,
-                                      &tr_tool->transform,
-                                      options->direction,
-                                      options->interpolation,
-                                      clip,
-                                      progress);
-        }
-      else
-        {
-          /*  always clip layer masks so they keep their size
-           */
-          if (GIMP_IS_CHANNEL (active_item))
-            clip = GIMP_TRANSFORM_RESIZE_CLIP;
-
-          gimp_item_transform (active_item, context,
-                               &tr_tool->transform,
-                               options->direction,
-                               options->interpolation,
-                               clip,
-                               progress);
-        }
-    }
-
-  if (progress)
-    gimp_progress_end (progress);
-
-  return ret;
-}
-
-static void
-gimp_transform_tool_widget_changed (GimpToolWidget    *widget,
-                                    GimpTransformTool *tr_tool)
-{
-  GimpTransformOptions *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
-  GimpMatrix3           matrix  = tr_tool->transform;
-  gint                  i;
-
-  if (options->direction == GIMP_TRANSFORM_BACKWARD)
-    gimp_matrix3_invert (&matrix);
-
-  if (tr_tool->preview)
-    {
-      gboolean show_preview = gimp_transform_options_show_preview (options) &&
-                              tr_tool->transform_valid;
-
-      gimp_canvas_item_begin_change (tr_tool->preview);
-      gimp_canvas_item_set_visible (tr_tool->preview, show_preview);
-      g_object_set (tr_tool->preview,
-                    "transform", &matrix,
-                    NULL);
-      gimp_canvas_item_end_change (tr_tool->preview);
-    }
-
-  if (tr_tool->boundary_in)
-    {
-      gimp_canvas_item_begin_change (tr_tool->boundary_in);
-      gimp_canvas_item_set_visible (tr_tool->boundary_in,
-                                    tr_tool->transform_valid);
-      g_object_set (tr_tool->boundary_in,
-                    "transform", &matrix,
-                    NULL);
-      gimp_canvas_item_end_change (tr_tool->boundary_in);
-    }
-
-  if (tr_tool->boundary_out)
-    {
-      gimp_canvas_item_begin_change (tr_tool->boundary_out);
-      gimp_canvas_item_set_visible (tr_tool->boundary_out,
-                                    tr_tool->transform_valid);
-      g_object_set (tr_tool->boundary_out,
-                    "transform", &matrix,
-                    NULL);
-      gimp_canvas_item_end_change (tr_tool->boundary_out);
-    }
-
-  for (i = 0; i < tr_tool->strokes->len; i++)
-    {
-      GimpCanvasItem *item = g_ptr_array_index (tr_tool->strokes, i);
-
-      gimp_canvas_item_begin_change (item);
-      gimp_canvas_item_set_visible (item, tr_tool->transform_valid);
-      g_object_set (item,
-                    "transform", &matrix,
-                    NULL);
-      gimp_canvas_item_end_change (item);
-    }
-}
-
-static void
-gimp_transform_tool_widget_response (GimpToolWidget    *widget,
-                                     gint               response_id,
-                                     GimpTransformTool *tr_tool)
-{
-  switch (response_id)
-    {
-    case GIMP_TOOL_WIDGET_RESPONSE_CONFIRM:
-      gimp_transform_tool_response (NULL, GTK_RESPONSE_OK, tr_tool);
-      break;
-
-    case GIMP_TOOL_WIDGET_RESPONSE_CANCEL:
-      gimp_transform_tool_response (NULL, GTK_RESPONSE_CANCEL, tr_tool);
-      break;
-
-    case GIMP_TOOL_WIDGET_RESPONSE_RESET:
-      gimp_transform_tool_response (NULL, RESPONSE_RESET, tr_tool);
-      break;
-    }
-}
-
-static void
-gimp_transform_tool_halt (GimpTransformTool *tr_tool)
-{
-  GimpTool *tool = GIMP_TOOL (tr_tool);
-
-  if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tr_tool)))
-    gimp_draw_tool_stop (GIMP_DRAW_TOOL (tr_tool));
-
-  gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tr_tool), NULL);
-  g_clear_object (&tr_tool->widget);
-
-  if (tr_tool->gui)
-    gimp_tool_gui_hide (tr_tool->gui);
-
-  if (tr_tool->redo_list)
-    {
-      g_list_free_full (tr_tool->redo_list, (GDestroyNotify) trans_info_free);
-      tr_tool->redo_list = NULL;
-    }
-
-  if (tr_tool->undo_list)
-    {
-      g_list_free_full (tr_tool->undo_list, (GDestroyNotify) trans_info_free);
-      tr_tool->undo_list = NULL;
-      tr_tool->prev_trans_info = NULL;
-    }
-
-  gimp_transform_tool_show_active_item (tr_tool);
-
-  tool->display  = NULL;
-  tool->drawable = NULL;
- }
-
-static void
-gimp_transform_tool_commit (GimpTransformTool *tr_tool)
-{
-  GimpTool             *tool           = GIMP_TOOL (tr_tool);
-  GimpTransformOptions *options        = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
-  GimpContext          *context        = GIMP_CONTEXT (options);
-  GimpDisplay          *display        = tool->display;
-  GimpImage            *image          = gimp_display_get_image (display);
-  GimpItem             *active_item;
-  GeglBuffer           *orig_buffer    = NULL;
-  gint                  orig_offset_x  = 0;
-  gint                  orig_offset_y  = 0;
-  GeglBuffer           *new_buffer;
-  gint                  new_offset_x;
-  gint                  new_offset_y;
-  GimpColorProfile     *buffer_profile;
-  gchar                *undo_desc      = NULL;
-  gboolean              new_layer      = FALSE;
-  GError               *error          = NULL;
-
-  active_item = gimp_transform_tool_check_active_item (tr_tool, image,
-                                                       TRUE, &error);
-
-  if (! active_item)
-    {
-      gimp_tool_message_literal (tool, display, error->message);
-      g_clear_error (&error);
-      return;
-    }
-
-  if (! tr_tool->transform_valid)
-    {
-      gimp_tool_message_literal (tool, display,
-                                 _("The current transform is invalid"));
-      return;
-    }
-
-  if (tr_tool->gui)
-    gimp_tool_gui_hide (tr_tool->gui);
-
-  if (GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->recalc_matrix &&
-      gimp_matrix3_is_identity (&tr_tool->transform))
-    {
-      /* No need to commit an identity transformation! */
-      return;
-    }
-
-  gimp_set_busy (display->gimp);
-
-  /* undraw the tool before we muck around with the transform matrix */
-  gimp_draw_tool_stop (GIMP_DRAW_TOOL (tr_tool));
-
-  /*  We're going to dirty this image, but we want to keep the tool around  */
-  gimp_tool_control_push_preserve (tool->control, TRUE);
-
-  undo_desc = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->get_undo_desc (tr_tool);
-  gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM, undo_desc);
-  g_free (undo_desc);
-
-  switch (options->type)
-    {
-    case GIMP_TRANSFORM_TYPE_LAYER:
-      if (! gimp_viewable_get_children (GIMP_VIEWABLE (tool->drawable)) &&
-          ! gimp_channel_is_empty (gimp_image_get_mask (image)))
-        {
-          orig_buffer = gimp_drawable_transform_cut (tool->drawable,
-                                                     context,
-                                                     &orig_offset_x,
-                                                     &orig_offset_y,
-                                                     &new_layer);
-        }
-      break;
-
-    case GIMP_TRANSFORM_TYPE_SELECTION:
-      orig_buffer = g_object_ref (gimp_drawable_get_buffer (GIMP_DRAWABLE (active_item)));
-      break;
-
-    case GIMP_TRANSFORM_TYPE_PATH:
-      break;
-    }
-
-  /*  Send the request for the transformation to the tool...
-   */
-  new_buffer = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->transform (tr_tool,
-                                                                   active_item,
-                                                                   orig_buffer,
-                                                                   orig_offset_x,
-                                                                   orig_offset_y,
-                                                                   &buffer_profile,
-                                                                   &new_offset_x,
-                                                                   &new_offset_y);
-
-  if (orig_buffer)
-    g_object_unref (orig_buffer);
-
-  switch (options->type)
-    {
-    case GIMP_TRANSFORM_TYPE_LAYER:
-      if (new_buffer)
-        {
-          /*  paste the new transformed image to the image...also implement
-           *  undo...
-           */
-          gimp_drawable_transform_paste (tool->drawable,
-                                         new_buffer, buffer_profile,
-                                         new_offset_x, new_offset_y,
-                                         new_layer);
-          g_object_unref (new_buffer);
-        }
-      break;
+  else
+    {
+      /*  this happens for entire drawables, paths and layer groups  */
 
-     case GIMP_TRANSFORM_TYPE_SELECTION:
-      if (new_buffer)
+      if (gimp_item_get_linked (active_item))
         {
-          gimp_channel_push_undo (GIMP_CHANNEL (active_item), NULL);
-
-          gimp_drawable_set_buffer (GIMP_DRAWABLE (active_item),
-                                    FALSE, NULL, new_buffer);
-          g_object_unref (new_buffer);
+          gimp_item_linked_transform (active_item, context,
+                                      &tr_tool->transform,
+                                      options->direction,
+                                      options->interpolation,
+                                      clip,
+                                      progress);
         }
-      break;
+      else
+        {
+          /*  always clip layer masks so they keep their size
+           */
+          if (GIMP_IS_CHANNEL (active_item))
+            clip = GIMP_TRANSFORM_RESIZE_CLIP;
 
-    case GIMP_TRANSFORM_TYPE_PATH:
-      /*  Nothing to be done  */
-      break;
+          gimp_item_transform (active_item, context,
+                               &tr_tool->transform,
+                               options->direction,
+                               options->interpolation,
+                               clip,
+                               progress);
+        }
     }
 
-  gimp_image_undo_push (image, GIMP_TYPE_TRANSFORM_TOOL_UNDO,
-                        GIMP_UNDO_TRANSFORM, NULL,
-                        0,
-                        "transform-tool", tr_tool,
-                        NULL);
-
-  gimp_image_undo_group_end (image);
-
-  /*  We're done dirtying the image, and would like to be restarted if
-   *  the image gets dirty while the tool exists
-   */
-  gimp_tool_control_pop_preserve (tool->control);
-
-  gimp_unset_busy (display->gimp);
+  if (progress)
+    gimp_progress_end (progress);
 
-  gimp_image_flush (image);
+  return ret;
 }
 
-static gboolean
+gboolean
 gimp_transform_tool_bounds (GimpTransformTool *tr_tool,
                             GimpDisplay       *display)
 {
-  GimpTransformOptions *options   = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
-  GimpImage            *image     = gimp_display_get_image (display);
+  GimpTransformOptions *options;
+  GimpImage            *image;
   gboolean              non_empty = TRUE;
 
+  g_return_val_if_fail (GIMP_IS_TRANSFORM_TOOL (tr_tool), FALSE);
+
+  options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
+  image   = gimp_display_get_image (display);
+
+  g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
+
   switch (options->type)
     {
     case GIMP_TRANSFORM_TYPE_LAYER:
@@ -1188,259 +250,45 @@ gimp_transform_tool_bounds (GimpTransformTool *tr_tool,
   return non_empty;
 }
 
-static void
-gimp_transform_tool_dialog (GimpTransformTool *tr_tool)
-{
-  GimpTool         *tool      = GIMP_TOOL (tr_tool);
-  GimpToolInfo     *tool_info = tool->tool_info;
-  GimpDisplayShell *shell;
-  const gchar      *ok_button_label;
-
-  if (! GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->dialog)
-    return;
-
-  g_return_if_fail (tool->display != NULL);
-
-  shell = gimp_display_get_shell (tool->display);
-
-  ok_button_label = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->ok_button_label;
-
-  tr_tool->gui = gimp_tool_gui_new (tool_info,
-                                    NULL, NULL, NULL, NULL,
-                                    gtk_widget_get_screen (GTK_WIDGET (shell)),
-                                    gimp_widget_get_monitor (GTK_WIDGET (shell)),
-                                    TRUE,
-
-                                    _("_Reset"),     RESPONSE_RESET,
-                                    _("_Cancel"),    GTK_RESPONSE_CANCEL,
-                                    ok_button_label, GTK_RESPONSE_OK,
-
-                                    NULL);
-
-  gimp_tool_gui_set_auto_overlay (tr_tool->gui, TRUE);
-  gimp_tool_gui_set_default_response (tr_tool->gui, GTK_RESPONSE_OK);
-
-  gimp_tool_gui_set_alternative_button_order (tr_tool->gui,
-                                              RESPONSE_RESET,
-                                              GTK_RESPONSE_OK,
-                                              GTK_RESPONSE_CANCEL,
-                                              -1);
-
-  g_signal_connect (tr_tool->gui, "response",
-                    G_CALLBACK (gimp_transform_tool_response),
-                    tr_tool);
-
-  GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->dialog (tr_tool);
-}
-
-static void
-gimp_transform_tool_dialog_update (GimpTransformTool *tr_tool)
-{
-  if (tr_tool->gui &&
-      GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->dialog_update)
-    {
-      GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->dialog_update (tr_tool);
-    }
-}
-
-static void
-gimp_transform_tool_prepare (GimpTransformTool *tr_tool,
-                             GimpDisplay       *display)
-{
-  if (tr_tool->gui)
-    {
-      GimpImage *image = gimp_display_get_image (display);
-      GimpItem  *item  = gimp_transform_tool_get_active_item (tr_tool, image);
-
-      gimp_tool_gui_set_shell (tr_tool->gui, gimp_display_get_shell (display));
-      gimp_tool_gui_set_viewable (tr_tool->gui, GIMP_VIEWABLE (item));
-    }
-
-  if (GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->prepare)
-    GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->prepare (tr_tool);
-}
-
-static GimpToolWidget *
-gimp_transform_tool_get_widget (GimpTransformTool *tr_tool)
-{
-  static const gchar *properties[] =
-  {
-    "constrain-move",
-    "constrain-scale",
-    "constrain-rotate",
-    "constrain-shear",
-    "constrain-perspective",
-    "frompivot-scale",
-    "frompivot-shear",
-    "frompivot-perspective",
-    "cornersnap",
-    "fixedpivot"
-  };
-
-  GimpToolWidget *widget = NULL;
-
-  if (GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->get_widget)
-    {
-      GimpTransformOptions *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
-      gint                  i;
-
-      widget = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->get_widget (tr_tool);
-
-      gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tr_tool), widget);
-
-      g_object_bind_property (G_OBJECT (options), "grid-type",
-                              G_OBJECT (widget),  "guide-type",
-                              G_BINDING_SYNC_CREATE |
-                              G_BINDING_BIDIRECTIONAL);
-      g_object_bind_property (G_OBJECT (options), "grid-size",
-                              G_OBJECT (widget),  "n-guides",
-                              G_BINDING_SYNC_CREATE |
-                              G_BINDING_BIDIRECTIONAL);
-
-      for (i = 0; i < G_N_ELEMENTS (properties); i++)
-        g_object_bind_property (G_OBJECT (options), properties[i],
-                                G_OBJECT (widget),  properties[i],
-                                G_BINDING_SYNC_CREATE |
-                                G_BINDING_BIDIRECTIONAL);
-
-      g_signal_connect (widget, "changed",
-                        G_CALLBACK (gimp_transform_tool_widget_changed),
-                        tr_tool);
-      g_signal_connect (widget, "response",
-                        G_CALLBACK (gimp_transform_tool_widget_response),
-                        tr_tool);
-    }
-
-  return widget;
-}
-
 void
 gimp_transform_tool_recalc_matrix (GimpTransformTool *tr_tool,
-                                   GimpToolWidget    *widget)
+                                   GimpDisplay       *display)
 {
   g_return_if_fail (GIMP_IS_TRANSFORM_TOOL (tr_tool));
-  g_return_if_fail (widget == NULL || GIMP_IS_TOOL_WIDGET (widget));
-
-  if (GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->recalc_matrix)
-    GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->recalc_matrix (tr_tool, widget);
-
-  gimp_transform_tool_dialog_update (tr_tool);
-  gimp_transform_tool_update_sensitivity (tr_tool);
-
-  if (tr_tool->gui)
-    gimp_tool_gui_show (tr_tool->gui);
-}
-
-static void
-gimp_transform_tool_response (GimpToolGui       *gui,
-                              gint               response_id,
-                              GimpTransformTool *tr_tool)
-{
-  GimpTool    *tool    = GIMP_TOOL (tr_tool);
-  GimpDisplay *display = tool->display;
-
-  switch (response_id)
-    {
-    case RESPONSE_RESET:
-      /* Move all undo events to redo, and pop off the first
-       * one as that's the current one, which always sits on
-       * the undo_list
-       */
-      tr_tool->redo_list =
-        g_list_remove (g_list_concat (g_list_reverse (tr_tool->undo_list),
-                                      tr_tool->redo_list),
-                       tr_tool->old_trans_info);
-      tr_tool->prev_trans_info = tr_tool->old_trans_info;
-      tr_tool->undo_list = g_list_prepend (NULL,
-                                           tr_tool->prev_trans_info);
+  g_return_if_fail (GIMP_IS_DISPLAY (display));
 
-      gimp_transform_tool_update_sensitivity (tr_tool);
+  gimp_transform_tool_bounds (tr_tool, display);
 
-      /*  Restore the previous transformation info  */
-      memcpy (tr_tool->trans_info, tr_tool->prev_trans_info,
-              sizeof (TransInfo));
-
-      /*  reget the selection bounds  */
-      gimp_transform_tool_bounds (tr_tool, display);
-
-      /*  recalculate the tool's transformation matrix  */
-      gimp_transform_tool_recalc_matrix (tr_tool, tr_tool->widget);
-
-      /*  update the undo actions / menu items  */
-      gimp_image_flush (gimp_display_get_image (display));
-      break;
-
-    case GTK_RESPONSE_OK:
-      g_return_if_fail (display != NULL);
-      gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, display);
-      break;
-
-    default:
-      gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
-
-      /*  update the undo actions / menu items  */
-      if (display)
-        gimp_image_flush (gimp_display_get_image (display));
-      break;
-    }
-}
-
-static void
-gimp_transform_tool_update_sensitivity (GimpTransformTool *tr_tool)
-{
-  if (! tr_tool->gui)
-    return;
-
-  gimp_tool_gui_set_response_sensitive (tr_tool->gui, GTK_RESPONSE_OK,
-                                        tr_tool->transform_valid);
-  gimp_tool_gui_set_response_sensitive (tr_tool->gui, RESPONSE_RESET,
-                                        g_list_next (tr_tool->undo_list) != NULL);
+  if (GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->recalc_matrix)
+    GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->recalc_matrix (tr_tool);
 }
 
-static GimpItem *
-gimp_transform_tool_get_active_item (GimpTransformTool *tr_tool,
-                                     GimpImage         *image)
+GimpItem *
+gimp_transform_tool_get_active_item (GimpTransformTool  *tr_tool,
+                                     GimpDisplay        *display,
+                                     gboolean            invisible_layer_ok,
+                                     GError            **error)
 {
-  GimpTransformOptions *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
-
-  switch (options->type)
-    {
-    case GIMP_TRANSFORM_TYPE_LAYER:
-      return GIMP_ITEM (gimp_image_get_active_drawable (image));
-
-    case GIMP_TRANSFORM_TYPE_SELECTION:
-      {
-        GimpChannel *selection = gimp_image_get_mask (image);
-
-        if (gimp_channel_is_empty (selection))
-          return NULL;
-        else
-          return GIMP_ITEM (selection);
-      }
+  GimpTransformOptions *options;
+  GimpImage            *image;
+  GimpItem             *item           = NULL;
+  const gchar          *null_message   = NULL;
+  const gchar          *locked_message = NULL;
 
-    case GIMP_TRANSFORM_TYPE_PATH:
-      return GIMP_ITEM (gimp_image_get_active_vectors (image));
-    }
+  g_return_val_if_fail (GIMP_IS_TRANSFORM_TOOL (tr_tool), NULL);
+  g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-  return NULL;
-}
+  options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
 
-static GimpItem *
-gimp_transform_tool_check_active_item (GimpTransformTool  *tr_tool,
-                                       GimpImage          *image,
-                                       gboolean            invisible_layer_ok,
-                                       GError            **error)
-{
-  GimpTransformOptions *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
-  GimpItem             *item;
-  const gchar          *null_message   = NULL;
-  const gchar          *locked_message = NULL;
+  image = gimp_display_get_image (display);
 
-  item = gimp_transform_tool_get_active_item (tr_tool, image);
+  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
 
   switch (options->type)
     {
     case GIMP_TRANSFORM_TYPE_LAYER:
+      item         = GIMP_ITEM (gimp_image_get_active_drawable (image));
       null_message = _("There is no layer to transform.");
 
       if (item)
@@ -1457,12 +305,23 @@ gimp_transform_tool_check_active_item (GimpTransformTool  *tr_tool,
                                    _("The active layer is not visible."));
               return NULL;
             }
+
+          if (! gimp_transform_tool_bounds (tr_tool, display))
+            {
+              g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
+                                   _("The selection does not intersect with the layer."));
+              return NULL;
+            }
         }
       break;
 
     case GIMP_TRANSFORM_TYPE_SELECTION:
+      item         = GIMP_ITEM (gimp_image_get_mask (image));
       null_message = _("There is no selection to transform.");
 
+      if (gimp_channel_is_empty (GIMP_CHANNEL (item)))
+        item = NULL;
+
       if (item)
         {
           /* cannot happen, so don't translate these messages */
@@ -1474,6 +333,7 @@ gimp_transform_tool_check_active_item (GimpTransformTool  *tr_tool,
       break;
 
     case GIMP_TRANSFORM_TYPE_PATH:
+      item         = GIMP_ITEM (gimp_image_get_active_vectors (image));
       null_message = _("There is no path to transform.");
 
       if (item)
@@ -1503,53 +363,149 @@ gimp_transform_tool_check_active_item (GimpTransformTool  *tr_tool,
   return item;
 }
 
-static void
-gimp_transform_tool_hide_active_item (GimpTransformTool *tr_tool,
-                                      GimpItem          *item)
+gboolean
+gimp_transform_tool_transform (GimpTransformTool *tr_tool,
+                               GimpDisplay       *display)
 {
-  GimpTransformOptions *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
-  GimpDisplay          *display = GIMP_TOOL (tr_tool)->display;
-  GimpImage            *image   = gimp_display_get_image (display);
-
-  /*  hide only complete layers and channels, not layer masks  */
-  if (options->type == GIMP_TRANSFORM_TYPE_LAYER &&
-      options->show_preview                      &&
-      tr_tool->widget /* not for flip */         &&
-      GIMP_IS_DRAWABLE (item)                    &&
-      ! GIMP_IS_LAYER_MASK (item)                &&
-      gimp_item_get_visible (item)               &&
-      gimp_channel_is_empty (gimp_image_get_mask (image)))
+  GimpTool             *tool;
+  GimpTransformOptions *options;
+  GimpContext          *context;
+  GimpImage            *image;
+  GimpItem             *active_item;
+  GeglBuffer           *orig_buffer   = NULL;
+  gint                  orig_offset_x = 0;
+  gint                  orig_offset_y = 0;
+  GeglBuffer           *new_buffer;
+  gint                  new_offset_x;
+  gint                  new_offset_y;
+  GimpColorProfile     *buffer_profile;
+  gchar                *undo_desc     = NULL;
+  gboolean              new_layer     = FALSE;
+  GError               *error         = NULL;
+
+  g_return_val_if_fail (GIMP_IS_TRANSFORM_TOOL (tr_tool), FALSE);
+  g_return_val_if_fail (GIMP_IS_DISPLAY (display), FALSE);
+
+  tool    = GIMP_TOOL (tr_tool);
+  options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
+  context = GIMP_CONTEXT (options);
+  image   = gimp_display_get_image (display);
+
+  g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
+
+  active_item = gimp_transform_tool_get_active_item (tr_tool, display, TRUE,
+                                                     &error);
+
+  if (! active_item)
     {
-      tr_tool->hidden_item = item;
-      gimp_item_set_visible (item, FALSE, FALSE);
+      gimp_tool_message_literal (tool, display, error->message);
+      g_clear_error (&error);
+      return FALSE;
+    }
+
+  gimp_transform_tool_recalc_matrix (tr_tool, display);
 
-      gimp_projection_flush (gimp_image_get_projection (image));
+  if (! tr_tool->transform_valid)
+    {
+      gimp_tool_message_literal (tool, display,
+                                 _("The current transform is invalid"));
+      return FALSE;
     }
-}
 
-static void
-gimp_transform_tool_show_active_item (GimpTransformTool *tr_tool)
-{
-  if (tr_tool->hidden_item)
+  if (GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->recalc_matrix &&
+      gimp_matrix3_is_identity (&tr_tool->transform))
+    {
+      /* No need to commit an identity transformation! */
+      return TRUE;
+    }
+
+  gimp_set_busy (display->gimp);
+
+  /*  We're going to dirty this image, but we want to keep the tool around  */
+  gimp_tool_control_push_preserve (tool->control, TRUE);
+
+  undo_desc = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->get_undo_desc (tr_tool);
+  gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM, undo_desc);
+  g_free (undo_desc);
+
+  switch (options->type)
+    {
+    case GIMP_TRANSFORM_TYPE_LAYER:
+      if (! gimp_viewable_get_children (GIMP_VIEWABLE (tool->drawable)) &&
+          ! gimp_channel_is_empty (gimp_image_get_mask (image)))
+        {
+          orig_buffer = gimp_drawable_transform_cut (tool->drawable,
+                                                     context,
+                                                     &orig_offset_x,
+                                                     &orig_offset_y,
+                                                     &new_layer);
+        }
+      break;
+
+    case GIMP_TRANSFORM_TYPE_SELECTION:
+      orig_buffer = g_object_ref (gimp_drawable_get_buffer (GIMP_DRAWABLE (active_item)));
+      break;
+
+    case GIMP_TRANSFORM_TYPE_PATH:
+      break;
+    }
+
+  /*  Send the request for the transformation to the tool...
+   */
+  new_buffer = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->transform (tr_tool,
+                                                                   active_item,
+                                                                   orig_buffer,
+                                                                   orig_offset_x,
+                                                                   orig_offset_y,
+                                                                   &buffer_profile,
+                                                                   &new_offset_x,
+                                                                   &new_offset_y);
+
+  if (orig_buffer)
+    g_object_unref (orig_buffer);
+
+  switch (options->type)
     {
-      GimpDisplay *display = GIMP_TOOL (tr_tool)->display;
-      GimpImage   *image   = gimp_display_get_image (display);
+    case GIMP_TRANSFORM_TYPE_LAYER:
+      if (new_buffer)
+        {
+          /*  paste the new transformed image to the image...also implement
+           *  undo...
+           */
+          gimp_drawable_transform_paste (tool->drawable,
+                                         new_buffer, buffer_profile,
+                                         new_offset_x, new_offset_y,
+                                         new_layer);
+          g_object_unref (new_buffer);
+        }
+      break;
+
+     case GIMP_TRANSFORM_TYPE_SELECTION:
+      if (new_buffer)
+        {
+          gimp_channel_push_undo (GIMP_CHANNEL (active_item), NULL);
 
-      gimp_item_set_visible (tr_tool->hidden_item, TRUE, FALSE);
-      tr_tool->hidden_item = NULL;
+          gimp_drawable_set_buffer (GIMP_DRAWABLE (active_item),
+                                    FALSE, NULL, new_buffer);
+          g_object_unref (new_buffer);
+        }
+      break;
 
-      gimp_image_flush (image);
+    case GIMP_TRANSFORM_TYPE_PATH:
+      /*  Nothing to be done  */
+      break;
     }
-}
 
-static TransInfo *
-trans_info_new (void)
-{
-  return g_slice_new0 (TransInfo);
-}
+  gimp_image_undo_group_end (image);
 
-static void
-trans_info_free (TransInfo *info)
-{
-  g_slice_free (TransInfo, info);
+  /*  We're done dirtying the image, and would like to be restarted if
+   *  the image gets dirty while the tool exists
+   */
+  gimp_tool_control_pop_preserve (tool->control);
+
+  gimp_unset_busy (display->gimp);
+
+  gimp_image_flush (image);
+
+  return TRUE;
 }
diff --git a/app/tools/gimptransformtool.h b/app/tools/gimptransformtool.h
index 6273d86ee5..8d0b6d3874 100644
--- a/app/tools/gimptransformtool.h
+++ b/app/tools/gimptransformtool.h
@@ -45,35 +45,13 @@ typedef struct _GimpTransformToolClass GimpTransformToolClass;
 
 struct _GimpTransformTool
 {
-  GimpDrawTool    parent_instance;
-
-  gint            x1, y1;             /*  upper left hand coordinate         */
-  gint            x2, y2;             /*  lower right hand coords            */
-
-  GimpMatrix3     transform;          /*  transformation matrix              */
-  gboolean        transform_valid;    /*  whether the matrix is valid        */
-  TransInfo       trans_info;         /*  transformation info                */
-  TransInfo      *old_trans_info;     /*  for resetting everything           */
-  TransInfo      *prev_trans_info;    /*  the current finished state         */
-  GList          *undo_list;          /*  list of all states,
-                                          head is current == prev_trans_info,
-                                          tail is original == old_trans_info */
-  GList          *redo_list;          /*  list of all undone states,
-                                          NULL when nothing undone */
-
-  GimpItem       *hidden_item;        /*  the item that was hidden during
-                                          the transform                      */
-
-  GimpToolWidget *widget;
-  GimpToolWidget *grab_widget;
-  GimpCanvasItem *preview;
-  GimpCanvasItem *boundary_in;
-  GimpCanvasItem *boundary_out;
-  GPtrArray      *strokes;
-
-  const gchar    *progress_text;
-
-  GimpToolGui    *gui;
+  GimpDrawTool  parent_instance;
+
+  gint          x1, y1;             /*  upper left hand coordinate         */
+  gint          x2, y2;             /*  lower right hand coords            */
+
+  GimpMatrix3   transform;          /*  transformation matrix              */
+  gboolean      transform_valid;    /*  whether the matrix is valid        */
 };
 
 struct _GimpTransformToolClass
@@ -81,31 +59,35 @@ struct _GimpTransformToolClass
   GimpDrawToolClass  parent_class;
 
   /*  virtual functions  */
-  void             (* dialog)        (GimpTransformTool *tool);
-  void             (* dialog_update) (GimpTransformTool *tool);
-  void             (* prepare)       (GimpTransformTool *tool);
-  GimpToolWidget * (* get_widget)    (GimpTransformTool *tool);
-  void             (* recalc_matrix) (GimpTransformTool *tool,
-                                      GimpToolWidget    *widget);
-  gchar          * (* get_undo_desc) (GimpTransformTool *tool);
-  GeglBuffer     * (* transform)     (GimpTransformTool *tool,
-                                      GimpItem          *item,
-                                      GeglBuffer        *orig_buffer,
-                                      gint               orig_offset_x,
-                                      gint               orig_offset_y,
-                                      GimpColorProfile **buffer_profile,
-                                      gint              *new_offset_x,
-                                      gint              *new_offset_y);
-
-  const gchar *ok_button_label;
+  void         (* recalc_matrix) (GimpTransformTool  *tr_tool);
+  gchar      * (* get_undo_desc) (GimpTransformTool  *tr_tool);
+  GeglBuffer * (* transform)     (GimpTransformTool  *tr_tool,
+                                  GimpItem           *item,
+                                  GeglBuffer         *orig_buffer,
+                                  gint                orig_offset_x,
+                                  gint                orig_offset_y,
+                                  GimpColorProfile  **buffer_profile,
+                                  gint               *new_offset_x,
+                                  gint               *new_offset_y);
+
+  const gchar *progress_text;
 };
 
 
-GType   gimp_transform_tool_get_type           (void) G_GNUC_CONST;
+GType      gimp_transform_tool_get_type        (void) G_GNUC_CONST;
+
+GimpItem * gimp_transform_tool_get_active_item (GimpTransformTool  *tr_tool,
+                                                GimpDisplay        *display,
+                                                gboolean            invisible_layer_ok,
+                                                GError            **error);
+
+gboolean   gimp_transform_tool_bounds          (GimpTransformTool  *tr_tool,
+                                                GimpDisplay        *display);
+void       gimp_transform_tool_recalc_matrix   (GimpTransformTool  *tr_tool,
+                                                GimpDisplay        *display);
 
-void    gimp_transform_tool_recalc_matrix      (GimpTransformTool *tr_tool,
-                                                GimpToolWidget    *widget);
-void    gimp_transform_tool_push_internal_undo (GimpTransformTool *tr_tool);
+gboolean   gimp_transform_tool_transform       (GimpTransformTool  *tr_tool,
+                                                GimpDisplay        *display);
 
 
 #endif  /*  __GIMP_TRANSFORM_TOOL_H__  */
diff --git a/app/tools/gimpunifiedtransformtool.c b/app/tools/gimpunifiedtransformtool.c
index b76d2f7718..2d5bd98cf6 100644
--- a/app/tools/gimpunifiedtransformtool.c
+++ b/app/tools/gimpunifiedtransformtool.c
@@ -32,7 +32,7 @@
 #include "display/gimptooltransformgrid.h"
 
 #include "gimptoolcontrol.h"
-#include "gimptransformoptions.h"
+#include "gimptransformgridoptions.h"
 #include "gimpunifiedtransformtool.h"
 
 #include "gimp-intl.h"
@@ -56,17 +56,18 @@ enum
 
 /*  local function prototypes  */
 
-static void             gimp_unified_transform_tool_prepare        (GimpTransformTool        *tr_tool);
-static GimpToolWidget * gimp_unified_transform_tool_get_widget     (GimpTransformTool        *tr_tool);
-static void             gimp_unified_transform_tool_recalc_matrix  (GimpTransformTool        *tr_tool,
-                                                                    GimpToolWidget           *widget);
 static gchar          * gimp_unified_transform_tool_get_undo_desc  (GimpTransformTool        *tr_tool);
 
+static void             gimp_unified_transform_tool_prepare        (GimpTransformGridTool    *tg_tool);
+static GimpToolWidget * gimp_unified_transform_tool_get_widget     (GimpTransformGridTool    *tg_tool);
+static void             gimp_unified_transform_tool_recalc_matrix  (GimpTransformGridTool    *tg_tool,
+                                                                    GimpToolWidget           *widget);
+
 static void             gimp_unified_transform_tool_recalc_points  (GimpGenericTransformTool *generic,
                                                                     GimpToolWidget           *widget);
 
 static void             gimp_unified_transform_tool_widget_changed (GimpToolWidget           *widget,
-                                                                    GimpTransformTool        *tr_tool);
+                                                                    GimpTransformGridTool    *tg_tool);
 
 
 G_DEFINE_TYPE (GimpUnifiedTransformTool, gimp_unified_transform_tool,
@@ -80,8 +81,8 @@ gimp_unified_transform_tool_register (GimpToolRegisterCallback  callback,
                                       gpointer                  data)
 {
   (* callback) (GIMP_TYPE_UNIFIED_TRANSFORM_TOOL,
-                GIMP_TYPE_TRANSFORM_OPTIONS,
-                gimp_transform_options_gui,
+                GIMP_TYPE_TRANSFORM_GRID_OPTIONS,
+                gimp_transform_grid_options_gui,
                 GIMP_CONTEXT_PROP_MASK_BACKGROUND,
                 "gimp-unified-transform-tool",
                 _("Unified Transform"),
@@ -96,49 +97,59 @@ gimp_unified_transform_tool_register (GimpToolRegisterCallback  callback,
 static void
 gimp_unified_transform_tool_class_init (GimpUnifiedTransformToolClass *klass)
 {
-  GimpTransformToolClass        *trans_class   = GIMP_TRANSFORM_TOOL_CLASS (klass);
+  GimpTransformToolClass        *tr_class      = GIMP_TRANSFORM_TOOL_CLASS (klass);
+  GimpTransformGridToolClass    *tg_class      = GIMP_TRANSFORM_GRID_TOOL_CLASS (klass);
   GimpGenericTransformToolClass *generic_class = GIMP_GENERIC_TRANSFORM_TOOL_CLASS (klass);
 
-  trans_class->prepare         = gimp_unified_transform_tool_prepare;
-  trans_class->get_widget      = gimp_unified_transform_tool_get_widget;
-  trans_class->recalc_matrix   = gimp_unified_transform_tool_recalc_matrix;
-  trans_class->get_undo_desc   = gimp_unified_transform_tool_get_undo_desc;
+  tr_class->get_undo_desc      = gimp_unified_transform_tool_get_undo_desc;
+
+  tg_class->prepare            = gimp_unified_transform_tool_prepare;
+  tg_class->get_widget         = gimp_unified_transform_tool_get_widget;
+  tg_class->recalc_matrix      = gimp_unified_transform_tool_recalc_matrix;
 
   generic_class->recalc_points = gimp_unified_transform_tool_recalc_points;
+
+  tr_class->progress_text      = _("Unified transform");
 }
 
 static void
 gimp_unified_transform_tool_init (GimpUnifiedTransformTool *unified_tool)
 {
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (unified_tool);
+}
 
-  tr_tool->progress_text = _("Unified transform");
+static gchar *
+gimp_unified_transform_tool_get_undo_desc (GimpTransformTool *tr_tool)
+{
+  return g_strdup (C_("undo-type", "Unified Transform"));
 }
 
 static void
-gimp_unified_transform_tool_prepare (GimpTransformTool *tr_tool)
+gimp_unified_transform_tool_prepare (GimpTransformGridTool *tg_tool)
 {
-  GIMP_TRANSFORM_TOOL_CLASS (parent_class)->prepare (tr_tool);
-
-  tr_tool->trans_info[PIVOT_X] = (gdouble) (tr_tool->x1 + tr_tool->x2) / 2.0;
-  tr_tool->trans_info[PIVOT_Y] = (gdouble) (tr_tool->y1 + tr_tool->y2) / 2.0;
-
-  tr_tool->trans_info[X0] = (gdouble) tr_tool->x1;
-  tr_tool->trans_info[Y0] = (gdouble) tr_tool->y1;
-  tr_tool->trans_info[X1] = (gdouble) tr_tool->x2;
-  tr_tool->trans_info[Y1] = (gdouble) tr_tool->y1;
-  tr_tool->trans_info[X2] = (gdouble) tr_tool->x1;
-  tr_tool->trans_info[Y2] = (gdouble) tr_tool->y2;
-  tr_tool->trans_info[X3] = (gdouble) tr_tool->x2;
-  tr_tool->trans_info[Y3] = (gdouble) tr_tool->y2;
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+
+  GIMP_TRANSFORM_GRID_TOOL_CLASS (parent_class)->prepare (tg_tool);
+
+  tg_tool->trans_info[PIVOT_X] = (gdouble) (tr_tool->x1 + tr_tool->x2) / 2.0;
+  tg_tool->trans_info[PIVOT_Y] = (gdouble) (tr_tool->y1 + tr_tool->y2) / 2.0;
+
+  tg_tool->trans_info[X0] = (gdouble) tr_tool->x1;
+  tg_tool->trans_info[Y0] = (gdouble) tr_tool->y1;
+  tg_tool->trans_info[X1] = (gdouble) tr_tool->x2;
+  tg_tool->trans_info[Y1] = (gdouble) tr_tool->y1;
+  tg_tool->trans_info[X2] = (gdouble) tr_tool->x1;
+  tg_tool->trans_info[Y2] = (gdouble) tr_tool->y2;
+  tg_tool->trans_info[X3] = (gdouble) tr_tool->x2;
+  tg_tool->trans_info[Y3] = (gdouble) tr_tool->y2;
 }
 
 static GimpToolWidget *
-gimp_unified_transform_tool_get_widget (GimpTransformTool *tr_tool)
+gimp_unified_transform_tool_get_widget (GimpTransformGridTool *tg_tool)
 {
-  GimpTool         *tool  = GIMP_TOOL (tr_tool);
-  GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
-  GimpToolWidget   *widget;
+  GimpTool          *tool    = GIMP_TOOL (tg_tool);
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpDisplayShell  *shell   = gimp_display_get_shell (tool->display);
+  GimpToolWidget    *widget;
 
   widget = gimp_tool_transform_grid_new (shell,
                                          &tr_tool->transform,
@@ -161,16 +172,18 @@ gimp_unified_transform_tool_get_widget (GimpTransformTool *tr_tool)
 
   g_signal_connect (widget, "changed",
                     G_CALLBACK (gimp_unified_transform_tool_widget_changed),
-                    tr_tool);
+                    tg_tool);
 
   return widget;
 }
 
 static void
-gimp_unified_transform_tool_recalc_matrix (GimpTransformTool *tr_tool,
-                                           GimpToolWidget    *widget)
+gimp_unified_transform_tool_recalc_matrix (GimpTransformGridTool *tg_tool,
+                                           GimpToolWidget        *widget)
 {
-  GIMP_TRANSFORM_TOOL_CLASS (parent_class)->recalc_matrix (tr_tool, widget);
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+
+  GIMP_TRANSFORM_GRID_TOOL_CLASS (parent_class)->recalc_matrix (tg_tool, widget);
 
   if (widget)
     g_object_set (widget,
@@ -179,68 +192,64 @@ gimp_unified_transform_tool_recalc_matrix (GimpTransformTool *tr_tool,
                   "y1",        (gdouble) tr_tool->y1,
                   "x2",        (gdouble) tr_tool->x2,
                   "y2",        (gdouble) tr_tool->y2,
-                  "pivot-x",   tr_tool->trans_info[PIVOT_X],
-                  "pivot-y",   tr_tool->trans_info[PIVOT_Y],
+                  "pivot-x",   tg_tool->trans_info[PIVOT_X],
+                  "pivot-y",   tg_tool->trans_info[PIVOT_Y],
                   NULL);
 }
 
-static gchar *
-gimp_unified_transform_tool_get_undo_desc (GimpTransformTool *tr_tool)
-{
-  return g_strdup (C_("undo-type", "Unified Transform"));
-}
-
 static void
 gimp_unified_transform_tool_recalc_points (GimpGenericTransformTool *generic,
                                            GimpToolWidget           *widget)
 {
-  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (generic);
+  GimpTransformTool     *tr_tool = GIMP_TRANSFORM_TOOL (generic);
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (generic);
 
   generic->input_points[0]  = (GimpVector2) {tr_tool->x1, tr_tool->y1};
   generic->input_points[1]  = (GimpVector2) {tr_tool->x2, tr_tool->y1};
   generic->input_points[2]  = (GimpVector2) {tr_tool->x1, tr_tool->y2};
   generic->input_points[3]  = (GimpVector2) {tr_tool->x2, tr_tool->y2};
 
-  generic->output_points[0] = (GimpVector2) {tr_tool->trans_info[X0],
-                                             tr_tool->trans_info[Y0]};
-  generic->output_points[1] = (GimpVector2) {tr_tool->trans_info[X1],
-                                             tr_tool->trans_info[Y1]};
-  generic->output_points[2] = (GimpVector2) {tr_tool->trans_info[X2],
-                                             tr_tool->trans_info[Y2]};
-  generic->output_points[3] = (GimpVector2) {tr_tool->trans_info[X3],
-                                             tr_tool->trans_info[Y3]};
+  generic->output_points[0] = (GimpVector2) {tg_tool->trans_info[X0],
+                                             tg_tool->trans_info[Y0]};
+  generic->output_points[1] = (GimpVector2) {tg_tool->trans_info[X1],
+                                             tg_tool->trans_info[Y1]};
+  generic->output_points[2] = (GimpVector2) {tg_tool->trans_info[X2],
+                                             tg_tool->trans_info[Y2]};
+  generic->output_points[3] = (GimpVector2) {tg_tool->trans_info[X3],
+                                             tg_tool->trans_info[Y3]};
 }
 
 static void
-gimp_unified_transform_tool_widget_changed (GimpToolWidget    *widget,
-                                            GimpTransformTool *tr_tool)
+gimp_unified_transform_tool_widget_changed (GimpToolWidget        *widget,
+                                            GimpTransformGridTool *tg_tool)
 {
-  GimpMatrix3 *transform;
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpMatrix3       *transform;
 
   g_object_get (widget,
                 "transform", &transform,
-                "pivot-x",   &tr_tool->trans_info[PIVOT_X],
-                "pivot-y",   &tr_tool->trans_info[PIVOT_Y],
+                "pivot-x",   &tg_tool->trans_info[PIVOT_X],
+                "pivot-y",   &tg_tool->trans_info[PIVOT_Y],
                 NULL);
 
   gimp_matrix3_transform_point (transform,
                                 tr_tool->x1, tr_tool->y1,
-                                &tr_tool->trans_info[X0],
-                                &tr_tool->trans_info[Y0]);
+                                &tg_tool->trans_info[X0],
+                                &tg_tool->trans_info[Y0]);
   gimp_matrix3_transform_point (transform,
                                 tr_tool->x2, tr_tool->y1,
-                                &tr_tool->trans_info[X1],
-                                &tr_tool->trans_info[Y1]);
+                                &tg_tool->trans_info[X1],
+                                &tg_tool->trans_info[Y1]);
   gimp_matrix3_transform_point (transform,
                                 tr_tool->x1, tr_tool->y2,
-                                &tr_tool->trans_info[X2],
-                                &tr_tool->trans_info[Y2]);
+                                &tg_tool->trans_info[X2],
+                                &tg_tool->trans_info[Y2]);
   gimp_matrix3_transform_point (transform,
                                 tr_tool->x2, tr_tool->y2,
-                                &tr_tool->trans_info[X3],
-                                &tr_tool->trans_info[Y3]);
+                                &tg_tool->trans_info[X3],
+                                &tg_tool->trans_info[Y3]);
 
   g_free (transform);
 
-  gimp_transform_tool_recalc_matrix (tr_tool, NULL);
+  gimp_transform_grid_tool_recalc_matrix (tg_tool, NULL);
 }
diff --git a/app/tools/tools-types.h b/app/tools/tools-types.h
index 1986e57b24..aa9624afcb 100644
--- a/app/tools/tools-types.h
+++ b/app/tools/tools-types.h
@@ -36,6 +36,7 @@ typedef struct _GimpDrawTool                 GimpDrawTool;
 typedef struct _GimpFilterTool               GimpFilterTool;
 typedef struct _GimpGenericTransformTool     GimpGenericTransformTool;
 typedef struct _GimpPaintTool                GimpPaintTool;
+typedef struct _GimpTransformGridTool        GimpTransformGridTool;
 typedef struct _GimpTransformTool            GimpTransformTool;
 
 typedef struct _GimpColorOptions             GimpColorOptions;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9347f2fc7a..21f74ad6ab 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -490,6 +490,8 @@ app/tools/gimptexttool.c
 app/tools/gimptexttool-editor.c
 app/tools/gimpthresholdtool.c
 app/tools/gimptool.c
+app/tools/gimptransformgridoptions.c
+app/tools/gimptransformgridtool.c
 app/tools/gimptransformoptions.c
 app/tools/gimptransformtool.c
 app/tools/gimpunifiedtransformtool.c


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