[gimp/gimp-2-10] app: factor-out common free/fg-select logic into GimpPolygonSelectTool



commit 9cc8d2403d89a910716c8141e8c61e4a93e49db0
Author: Ell <ell_se yahoo com>
Date:   Thu Apr 25 05:40:24 2019 -0400

    app: factor-out common free/fg-select logic into GimpPolygonSelectTool
    
    We currently derive GimpForegroundSelectTool from
    GimpFreeSelectTool, which prevents us from making changes that are
    limited to the free-select tool.
    
    Factor out the common free-select and foreground-select logic into
    a new GimpPolygonSelectTool base-class, and derive both from this
    class.
    
    (cherry picked from commit afab7deaa3321dee4cb366cbe1d35ecf0ffe68a9)

 app/tools/Makefile.am                |   6 +-
 app/tools/gimpforegroundselecttool.c | 110 +++----
 app/tools/gimpforegroundselecttool.h |  25 +-
 app/tools/gimpfreeselecttool.c       | 466 ++++-------------------------
 app/tools/gimpfreeselecttool.h       |  21 +-
 app/tools/gimppolygonselecttool.c    | 558 +++++++++++++++++++++++++++++++++++
 app/tools/gimppolygonselecttool.h    |  69 +++++
 7 files changed, 756 insertions(+), 499 deletions(-)
---
diff --git a/app/tools/Makefile.am b/app/tools/Makefile.am
index c3308cfbcd..cd98150c10 100644
--- a/app/tools/Makefile.am
+++ b/app/tools/Makefile.am
@@ -84,14 +84,14 @@ libapptools_a_sources = \
        gimpflipoptions.h               \
        gimpfliptool.c                  \
        gimpfliptool.h                  \
-       gimpfreeselecttool.c            \
-       gimpfreeselecttool.h            \
        gimpforegroundselectoptions.c   \
        gimpforegroundselectoptions.h   \
        gimpforegroundselecttool.c      \
        gimpforegroundselecttool.h      \
        gimpforegroundselecttoolundo.c  \
        gimpforegroundselecttoolundo.h  \
+       gimpfreeselecttool.c            \
+       gimpfreeselecttool.h            \
        gimpfuzzyselecttool.c           \
        gimpfuzzyselecttool.h           \
        gimpgegltool.c                  \
@@ -160,6 +160,8 @@ libapptools_a_sources = \
        gimpperspectiveclonetool.h      \
        gimpperspectivetool.c           \
        gimpperspectivetool.h           \
+       gimppolygonselecttool.c         \
+       gimppolygonselecttool.h         \
        gimprectangleselecttool.c       \
        gimprectangleselecttool.h       \
        gimprectangleselectoptions.c    \
diff --git a/app/tools/gimpforegroundselecttool.c b/app/tools/gimpforegroundselecttool.c
index 1b9f5e1b0e..f500e0ba4d 100644
--- a/app/tools/gimpforegroundselecttool.c
+++ b/app/tools/gimpforegroundselecttool.c
@@ -139,10 +139,8 @@ static void   gimp_foreground_select_tool_options_notify (GimpTool         *tool
 
 static void   gimp_foreground_select_tool_draw           (GimpDrawTool     *draw_tool);
 
-static void   gimp_foreground_select_tool_select         (GimpFreeSelectTool *free_sel,
-                                                          GimpDisplay        *display,
-                                                          const GimpVector2  *points,
-                                                          gint                n_points);
+static void   gimp_foreground_select_tool_confirm        (GimpPolygonSelectTool *poly_sel,
+                                                          GimpDisplay           *display);
 
 static void   gimp_foreground_select_tool_halt           (GimpForegroundSelectTool *fg_select);
 static void   gimp_foreground_select_tool_commit         (GimpForegroundSelectTool *fg_select);
@@ -172,7 +170,7 @@ static void         gimp_foreground_select_undo_free     (StrokeUndo
 
 
 G_DEFINE_TYPE (GimpForegroundSelectTool, gimp_foreground_select_tool,
-               GIMP_TYPE_FREE_SELECT_TOOL)
+               GIMP_TYPE_POLYGON_SELECT_TOOL)
 
 #define parent_class gimp_foreground_select_tool_parent_class
 
@@ -198,34 +196,34 @@ gimp_foreground_select_tool_register (GimpToolRegisterCallback  callback,
 static void
 gimp_foreground_select_tool_class_init (GimpForegroundSelectToolClass *klass)
 {
-  GObjectClass            *object_class    = G_OBJECT_CLASS (klass);
-  GimpToolClass           *tool_class      = GIMP_TOOL_CLASS (klass);
-  GimpDrawToolClass       *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass);
-  GimpFreeSelectToolClass *free_select_tool_class;
-
-  free_select_tool_class = GIMP_FREE_SELECT_TOOL_CLASS (klass);
-
-  object_class->finalize          = gimp_foreground_select_tool_finalize;
-
-  tool_class->initialize          = gimp_foreground_select_tool_initialize;
-  tool_class->control             = gimp_foreground_select_tool_control;
-  tool_class->button_press        = gimp_foreground_select_tool_button_press;
-  tool_class->button_release      = gimp_foreground_select_tool_button_release;
-  tool_class->motion              = gimp_foreground_select_tool_motion;
-  tool_class->key_press           = gimp_foreground_select_tool_key_press;
-  tool_class->modifier_key        = gimp_foreground_select_tool_modifier_key;
-  tool_class->active_modifier_key = gimp_foreground_select_tool_active_modifier_key;
-  tool_class->oper_update         = gimp_foreground_select_tool_oper_update;
-  tool_class->cursor_update       = gimp_foreground_select_tool_cursor_update;
-  tool_class->can_undo            = gimp_foreground_select_tool_can_undo;
-  tool_class->can_redo            = gimp_foreground_select_tool_can_redo;
-  tool_class->undo                = gimp_foreground_select_tool_undo;
-  tool_class->redo                = gimp_foreground_select_tool_redo;
-  tool_class->options_notify      = gimp_foreground_select_tool_options_notify;
-
-  draw_tool_class->draw           = gimp_foreground_select_tool_draw;
-
-  free_select_tool_class->select  = gimp_foreground_select_tool_select;
+  GObjectClass               *object_class    = G_OBJECT_CLASS (klass);
+  GimpToolClass              *tool_class      = GIMP_TOOL_CLASS (klass);
+  GimpDrawToolClass          *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass);
+  GimpPolygonSelectToolClass *polygon_select_tool_class;
+
+  polygon_select_tool_class = GIMP_POLYGON_SELECT_TOOL_CLASS (klass);
+
+  object_class->finalize             = gimp_foreground_select_tool_finalize;
+
+  tool_class->initialize             = gimp_foreground_select_tool_initialize;
+  tool_class->control                = gimp_foreground_select_tool_control;
+  tool_class->button_press           = gimp_foreground_select_tool_button_press;
+  tool_class->button_release         = gimp_foreground_select_tool_button_release;
+  tool_class->motion                 = gimp_foreground_select_tool_motion;
+  tool_class->key_press              = gimp_foreground_select_tool_key_press;
+  tool_class->modifier_key           = gimp_foreground_select_tool_modifier_key;
+  tool_class->active_modifier_key    = gimp_foreground_select_tool_active_modifier_key;
+  tool_class->oper_update            = gimp_foreground_select_tool_oper_update;
+  tool_class->cursor_update          = gimp_foreground_select_tool_cursor_update;
+  tool_class->can_undo               = gimp_foreground_select_tool_can_undo;
+  tool_class->can_redo               = gimp_foreground_select_tool_can_redo;
+  tool_class->undo                   = gimp_foreground_select_tool_undo;
+  tool_class->redo                   = gimp_foreground_select_tool_redo;
+  tool_class->options_notify         = gimp_foreground_select_tool_options_notify;
+
+  draw_tool_class->draw              = gimp_foreground_select_tool_draw;
+
+  polygon_select_tool_class->confirm = gimp_foreground_select_tool_confirm;
 }
 
 static void
@@ -431,14 +429,6 @@ gimp_foreground_select_tool_button_release (GimpTool              *tool,
     {
       GIMP_TOOL_CLASS (parent_class)->button_release (tool, coords, time, state,
                                                       release_type, display);
-
-      /*  see comment in gimp_foreground_select_tool_select()  */
-      if (fg_select->in_double_click)
-        {
-          gimp_foreground_select_tool_set_trimap (fg_select);
-
-          fg_select->in_double_click = FALSE;
-        }
     }
   else
     {
@@ -611,7 +601,12 @@ gimp_foreground_select_tool_oper_update (GimpTool         *tool,
     {
       if (GIMP_SELECTION_TOOL (tool)->function == SELECTION_SELECT)
         {
-          if (gimp_free_select_tool_get_n_points (GIMP_FREE_SELECT_TOOL (tool)) > 2)
+          gint n_points;
+
+          gimp_polygon_select_tool_get_points (GIMP_POLYGON_SELECT_TOOL (tool),
+                                               NULL, &n_points);
+
+          if (n_points > 2)
             {
               status_mode = _("Roughly outline the object to extract");
               status_stage = _("press Enter to refine.");
@@ -899,18 +894,20 @@ gimp_foreground_select_tool_draw (GimpDrawTool *draw_tool)
 }
 
 static void
-gimp_foreground_select_tool_select (GimpFreeSelectTool *free_sel,
-                                    GimpDisplay        *display,
-                                    const GimpVector2  *points,
-                                    gint                n_points)
+gimp_foreground_select_tool_confirm (GimpPolygonSelectTool *poly_sel,
+                                     GimpDisplay           *display)
 {
-  GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (free_sel);
+  GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (poly_sel);
   GimpImage                *image     = gimp_display_get_image (display);
   GimpDrawable             *drawable  = gimp_image_get_active_drawable (image);
 
   if (drawable && fg_select->state == MATTING_STATE_FREE_SELECT)
     {
-      GimpScanConvert *scan_convert = gimp_scan_convert_new ();
+      GimpScanConvert   *scan_convert = gimp_scan_convert_new ();
+      const GimpVector2 *points;
+      gint               n_points;
+
+      gimp_polygon_select_tool_get_points (poly_sel, &points, &n_points);
 
       gimp_scan_convert_add_polyline (scan_convert, n_points, points, TRUE);
 
@@ -924,20 +921,7 @@ gimp_foreground_select_tool_select (GimpFreeSelectTool *free_sel,
                                       0, 0, 0.5);
       gimp_scan_convert_free (scan_convert);
 
-      if (! gimp_tool_control_is_active (GIMP_TOOL (fg_select)->control))
-        {
-          gimp_foreground_select_tool_set_trimap (fg_select);
-        }
-      else
-        {
-          /*  if the tool is active we got here by double click
-           *  detected in the parent class. We can't switch to trimap
-           *  mode in the middle of a click. Set a flag and let
-           *  button_release() forward the release to the parent class
-           *  so it can conclude its operation
-           */
-          fg_select->in_double_click = TRUE;
-        }
+      gimp_foreground_select_tool_set_trimap (fg_select);
     }
 }
 
@@ -1032,7 +1016,7 @@ gimp_foreground_select_tool_set_trimap (GimpForegroundSelectTool *fg_select)
 
   options = GIMP_FOREGROUND_SELECT_TOOL_GET_OPTIONS (tool);
 
-  gimp_free_select_tool_halt (GIMP_FREE_SELECT_TOOL (fg_select));
+  gimp_polygon_select_tool_halt (GIMP_POLYGON_SELECT_TOOL (fg_select));
 
   gimp_foreground_select_options_get_mask_color (options, &color);
   gimp_display_shell_set_mask (gimp_display_get_shell (tool->display),
diff --git a/app/tools/gimpforegroundselecttool.h b/app/tools/gimpforegroundselecttool.h
index 900b74f2c0..0dcd19ae13 100644
--- a/app/tools/gimpforegroundselecttool.h
+++ b/app/tools/gimpforegroundselecttool.h
@@ -19,7 +19,7 @@
 #define __GIMP_FOREGROUND_SELECT_TOOL_H__
 
 
-#include "gimpfreeselecttool.h"
+#include "gimppolygonselecttool.h"
 
 
 typedef enum
@@ -45,26 +45,25 @@ typedef struct _GimpForegroundSelectToolClass GimpForegroundSelectToolClass;
 
 struct _GimpForegroundSelectTool
 {
-  GimpFreeSelectTool  parent_instance;
+  GimpPolygonSelectTool  parent_instance;
 
-  MattingState        state;
-  gboolean            in_double_click;
+  MattingState           state;
 
-  GimpCoords          last_coords;
-  GArray             *stroke;
-  GeglBuffer         *trimap;
-  GeglBuffer         *mask;
+  GimpCoords             last_coords;
+  GArray                *stroke;
+  GeglBuffer            *trimap;
+  GeglBuffer            *mask;
 
-  GList              *undo_stack;
-  GList              *redo_stack;
+  GList                 *undo_stack;
+  GList                 *redo_stack;
 
-  GimpToolGui        *gui;
-  GtkWidget          *preview_toggle;
+  GimpToolGui           *gui;
+  GtkWidget             *preview_toggle;
 };
 
 struct _GimpForegroundSelectToolClass
 {
-  GimpFreeSelectToolClass  parent_class;
+  GimpPolygonSelectToolClass  parent_class;
 };
 
 
diff --git a/app/tools/gimpfreeselecttool.c b/app/tools/gimpfreeselecttool.c
index 5509a26392..96eb4ad7a1 100644
--- a/app/tools/gimpfreeselecttool.c
+++ b/app/tools/gimpfreeselecttool.c
@@ -46,72 +46,30 @@
 
 struct _GimpFreeSelectToolPrivate
 {
+  gboolean        started;
+
   /* The selection operation active when the tool was started */
   GimpChannelOps  operation_at_start;
-
-  GimpToolWidget *widget;
-  GimpToolWidget *grab_widget;
 };
 
 
-static void     gimp_free_select_tool_finalize            (GObject               *object);
-static void     gimp_free_select_tool_control             (GimpTool              *tool,
-                                                           GimpToolAction         action,
-                                                           GimpDisplay           *display);
-static void     gimp_free_select_tool_oper_update         (GimpTool              *tool,
-                                                           const GimpCoords      *coords,
-                                                           GdkModifierType        state,
-                                                           gboolean               proximity,
-                                                           GimpDisplay           *display);
-static void     gimp_free_select_tool_cursor_update       (GimpTool              *tool,
-                                                           const GimpCoords      *coords,
-                                                           GdkModifierType        state,
-                                                           GimpDisplay           *display);
-static void     gimp_free_select_tool_button_press        (GimpTool              *tool,
-                                                           const GimpCoords      *coords,
-                                                           guint32                time,
-                                                           GdkModifierType        state,
-                                                           GimpButtonPressType    press_type,
-                                                           GimpDisplay           *display);
-static void     gimp_free_select_tool_button_release      (GimpTool              *tool,
-                                                           const GimpCoords      *coords,
-                                                           guint32                time,
-                                                           GdkModifierType        state,
-                                                           GimpButtonReleaseType  release_type,
-                                                           GimpDisplay           *display);
-static void     gimp_free_select_tool_motion              (GimpTool              *tool,
-                                                           const GimpCoords      *coords,
-                                                           guint32                time,
-                                                           GdkModifierType        state,
-                                                           GimpDisplay           *display);
-static gboolean gimp_free_select_tool_key_press           (GimpTool              *tool,
-                                                           GdkEventKey           *kevent,
-                                                           GimpDisplay           *display);
-static void     gimp_free_select_tool_modifier_key        (GimpTool              *tool,
-                                                           GdkModifierType        key,
-                                                           gboolean               press,
-                                                           GdkModifierType        state,
-                                                           GimpDisplay           *display);
-static void     gimp_free_select_tool_active_modifier_key (GimpTool              *tool,
-                                                           GdkModifierType        key,
-                                                           gboolean               press,
-                                                           GdkModifierType        state,
-                                                           GimpDisplay           *display);
-
-static void     gimp_free_select_tool_real_select         (GimpFreeSelectTool    *fst,
-                                                           GimpDisplay           *display,
-                                                           const GimpVector2     *points,
-                                                           gint                   n_points);
-
-static void     gimp_free_select_tool_polygon_changed     (GimpToolWidget        *polygon,
-                                                           GimpFreeSelectTool    *fst);
-static void     gimp_free_select_tool_polygon_response    (GimpToolWidget        *polygon,
-                                                           gint                   response_id,
-                                                           GimpFreeSelectTool    *fst);
+static void   gimp_free_select_tool_control      (GimpTool              *tool,
+                                                  GimpToolAction         action,
+                                                  GimpDisplay           *display);
+static void   gimp_free_select_tool_button_press (GimpTool              *tool,
+                                                  const GimpCoords      *coords,
+                                                  guint32                time,
+                                                  GdkModifierType        state,
+                                                  GimpButtonPressType    press_type,
+                                                  GimpDisplay           *display);
+
+static void   gimp_free_select_tool_commit       (GimpFreeSelectTool    *free_sel,
+                                                  GimpDisplay           *display);
+static void   gimp_free_select_tool_halt         (GimpFreeSelectTool    *free_sel);
 
 
 G_DEFINE_TYPE_WITH_PRIVATE (GimpFreeSelectTool, gimp_free_select_tool,
-                            GIMP_TYPE_SELECTION_TOOL)
+                            GIMP_TYPE_POLYGON_SELECT_TOOL)
 
 #define parent_class gimp_free_select_tool_parent_class
 
@@ -134,132 +92,24 @@ gimp_free_select_tool_register (GimpToolRegisterCallback  callback,
                 data);
 }
 
-gint
-gimp_free_select_tool_get_n_points (GimpFreeSelectTool *tool)
-{
-  GimpFreeSelectToolPrivate *private = tool->private;
-  const GimpVector2         *points;
-  gint                       n_points = 0;
-
-  if (private->widget)
-    gimp_tool_polygon_get_points (GIMP_TOOL_POLYGON (private->widget),
-                                  &points, &n_points);
-
-  return n_points;
-}
-
 static void
 gimp_free_select_tool_class_init (GimpFreeSelectToolClass *klass)
 {
-  GObjectClass  *object_class = G_OBJECT_CLASS (klass);
-  GimpToolClass *tool_class   = GIMP_TOOL_CLASS (klass);
-
-  object_class->finalize          = gimp_free_select_tool_finalize;
-
-  tool_class->control             = gimp_free_select_tool_control;
-  tool_class->oper_update         = gimp_free_select_tool_oper_update;
-  tool_class->cursor_update       = gimp_free_select_tool_cursor_update;
-  tool_class->button_press        = gimp_free_select_tool_button_press;
-  tool_class->button_release      = gimp_free_select_tool_button_release;
-  tool_class->motion              = gimp_free_select_tool_motion;
-  tool_class->key_press           = gimp_free_select_tool_key_press;
-  tool_class->modifier_key        = gimp_free_select_tool_modifier_key;
-  tool_class->active_modifier_key = gimp_free_select_tool_active_modifier_key;
-
-  klass->select                   = gimp_free_select_tool_real_select;
-}
+  GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
 
-static void
-gimp_free_select_tool_init (GimpFreeSelectTool *fst)
-{
-  GimpTool *tool = GIMP_TOOL (fst);
-
-  fst->private = gimp_free_select_tool_get_instance_private (fst);
-
-  gimp_tool_control_set_motion_mode        (tool->control,
-                                            GIMP_MOTION_MODE_EXACT);
-  gimp_tool_control_set_wants_click        (tool->control, TRUE);
-  gimp_tool_control_set_wants_double_click (tool->control, TRUE);
-  gimp_tool_control_set_active_modifiers   (tool->control,
-                                            GIMP_TOOL_ACTIVE_MODIFIERS_SEPARATE);
-  gimp_tool_control_set_precision          (tool->control,
-                                            GIMP_CURSOR_PRECISION_SUBPIXEL);
-  gimp_tool_control_set_tool_cursor        (tool->control,
-                                            GIMP_TOOL_CURSOR_FREE_SELECT);
+  tool_class->control      = gimp_free_select_tool_control;
+  tool_class->button_press = gimp_free_select_tool_button_press;
 }
 
 static void
-gimp_free_select_tool_finalize (GObject *object)
+gimp_free_select_tool_init (GimpFreeSelectTool *free_sel)
 {
-  GimpFreeSelectTool        *fst  = GIMP_FREE_SELECT_TOOL (object);
-  GimpFreeSelectToolPrivate *priv = fst->private;
+  GimpTool *tool = GIMP_TOOL (free_sel);
 
-  g_clear_object (&priv->widget);
+  free_sel->priv = gimp_free_select_tool_get_instance_private (free_sel);
 
-  G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static void
-gimp_free_select_tool_start (GimpFreeSelectTool *fst,
-                             GimpDisplay        *display)
-{
-  GimpTool                  *tool    = GIMP_TOOL (fst);
-  GimpFreeSelectToolPrivate *private = fst->private;
-  GimpSelectionOptions      *options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool);
-  GimpDisplayShell          *shell   = gimp_display_get_shell (display);
-
-  tool->display = display;
-
-  /* We want to apply the selection operation that was current when
-   * the tool was started, so we save this information
-   */
-  private->operation_at_start = options->operation;
-
-  private->widget = gimp_tool_polygon_new (shell);
-
-  gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tool), private->widget);
-
-  g_signal_connect (private->widget, "changed",
-                    G_CALLBACK (gimp_free_select_tool_polygon_changed),
-                    fst);
-  g_signal_connect (private->widget, "response",
-                    G_CALLBACK (gimp_free_select_tool_polygon_response),
-                    fst);
-
-  gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
-}
-
-void
-gimp_free_select_tool_halt (GimpFreeSelectTool *fst)
-{
-  GimpFreeSelectToolPrivate *private = fst->private;
-
-  gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (fst), NULL);
-  g_clear_object (&private->widget);
-}
-
-static void
-gimp_free_select_tool_commit (GimpFreeSelectTool *fst,
-                              GimpDisplay        *display)
-{
-  GimpFreeSelectToolPrivate *private = fst->private;
-
-  if (private->widget)
-    {
-      const GimpVector2 *points;
-      gint               n_points;
-
-      gimp_tool_polygon_get_points (GIMP_TOOL_POLYGON (private->widget),
-                                    &points, &n_points);
-
-      if (n_points >= 3)
-        {
-          GIMP_FREE_SELECT_TOOL_GET_CLASS (fst)->select (fst, display,
-                                                         points, n_points);
-        }
-    }
-
-  gimp_image_flush (gimp_display_get_image (display));
+  gimp_tool_control_set_tool_cursor (tool->control,
+                                     GIMP_TOOL_CURSOR_FREE_SELECT);
 }
 
 static void
@@ -267,7 +117,7 @@ gimp_free_select_tool_control (GimpTool       *tool,
                                GimpToolAction  action,
                                GimpDisplay    *display)
 {
-  GimpFreeSelectTool *fst = GIMP_FREE_SELECT_TOOL (tool);
+  GimpFreeSelectTool *free_sel = GIMP_FREE_SELECT_TOOL (tool);
 
   switch (action)
     {
@@ -276,69 +126,17 @@ gimp_free_select_tool_control (GimpTool       *tool,
       break;
 
     case GIMP_TOOL_ACTION_HALT:
-      gimp_free_select_tool_halt (fst);
+      gimp_free_select_tool_halt (free_sel);
       break;
 
     case GIMP_TOOL_ACTION_COMMIT:
-      gimp_free_select_tool_commit (fst, display);
+      gimp_free_select_tool_commit (free_sel, display);
       break;
     }
 
   GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
 }
 
-static void
-gimp_free_select_tool_oper_update (GimpTool         *tool,
-                                   const GimpCoords *coords,
-                                   GdkModifierType   state,
-                                   gboolean          proximity,
-                                   GimpDisplay      *display)
-{
-  GimpFreeSelectTool        *fst  = GIMP_FREE_SELECT_TOOL (tool);
-  GimpFreeSelectToolPrivate *priv = fst->private;
-
-  if (display != tool->display)
-    {
-      GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state,
-                                                   proximity, display);
-      return;
-    }
-
-  if (priv->widget)
-    {
-      gimp_tool_widget_hover (priv->widget, coords, state, proximity);
-    }
-}
-
-static void
-gimp_free_select_tool_cursor_update (GimpTool         *tool,
-                                     const GimpCoords *coords,
-                                     GdkModifierType   state,
-                                     GimpDisplay      *display)
-{
-  GimpFreeSelectTool        *fst      = GIMP_FREE_SELECT_TOOL (tool);
-  GimpFreeSelectToolPrivate *priv     = fst->private;
-  GimpCursorModifier         modifier = GIMP_CURSOR_MODIFIER_NONE;
-
-  if (tool->display == NULL)
-    {
-      GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state,
-                                                     display);
-      return;
-    }
-
-  if (priv->widget && display == tool->display)
-    {
-      gimp_tool_widget_get_cursor (priv->widget, coords, state,
-                                   NULL, NULL, &modifier);
-    }
-
-  gimp_tool_set_cursor (tool, display,
-                        gimp_tool_control_get_cursor (tool->control),
-                        gimp_tool_control_get_tool_cursor (tool->control),
-                        modifier);
-}
-
 static void
 gimp_free_select_tool_button_press (GimpTool            *tool,
                                     const GimpCoords    *coords,
@@ -347,199 +145,59 @@ gimp_free_select_tool_button_press (GimpTool            *tool,
                                     GimpButtonPressType  press_type,
                                     GimpDisplay         *display)
 {
-  GimpFreeSelectTool        *fst     = GIMP_FREE_SELECT_TOOL (tool);
-  GimpFreeSelectToolPrivate *private = fst->private;
+  GimpSelectionOptions      *options  = GIMP_SELECTION_TOOL_GET_OPTIONS (tool);
+  GimpFreeSelectTool        *free_sel = GIMP_FREE_SELECT_TOOL (tool);
+  GimpPolygonSelectTool     *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
+  GimpFreeSelectToolPrivate *priv     = free_sel->priv;
 
-  if (tool->display && tool->display != display)
-    gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, tool->display);
+  GIMP_TOOL_CLASS (parent_class)->button_press (tool, coords, time, state,
+                                                press_type, display);
 
-  if (! private->widget) /* not tool->display, we have a subclass */
+  if (press_type == GIMP_BUTTON_PRESS_NORMAL &&
+      gimp_polygon_select_tool_is_grabbed (poly_sel))
     {
-      /* First of all handle delegation to the selection mask edit logic
-       * if appropriate.
-       */
-      if (gimp_selection_tool_start_edit (GIMP_SELECTION_TOOL (fst),
-                                          display, coords))
+      if (! priv->started)
         {
-          return;
+          priv->started            = TRUE;
+          priv->operation_at_start = options->operation;
         }
-
-      gimp_free_select_tool_start (fst, display);
-
-      gimp_tool_widget_hover (private->widget, coords, state, TRUE);
-    }
-
-  if (gimp_tool_widget_button_press (private->widget, coords, time, state,
-                                     press_type))
-    {
-      private->grab_widget = private->widget;
-    }
-
-  if (press_type == GIMP_BUTTON_PRESS_NORMAL)
-    gimp_tool_control_activate (tool->control);
-}
-
-static void
-gimp_free_select_tool_button_release (GimpTool              *tool,
-                                      const GimpCoords      *coords,
-                                      guint32                time,
-                                      GdkModifierType        state,
-                                      GimpButtonReleaseType  release_type,
-                                      GimpDisplay           *display)
-{
-  GimpFreeSelectTool        *fst   = GIMP_FREE_SELECT_TOOL (tool);
-  GimpFreeSelectToolPrivate *priv  = fst->private;
-  GimpImage                 *image = gimp_display_get_image (display);
-
-  gimp_tool_control_halt (tool->control);
-
-  switch (release_type)
-    {
-    case GIMP_BUTTON_RELEASE_CLICK:
-    case GIMP_BUTTON_RELEASE_NO_MOTION:
-      /*  If there is a floating selection, anchor it  */
-      if (gimp_image_get_floating_selection (image))
-        {
-          floating_sel_anchor (gimp_image_get_floating_selection (image));
-
-          gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
-
-          return;
-        }
-
-      /* fallthru */
-
-    default:
-      if (priv->grab_widget)
-        {
-          gimp_tool_widget_button_release (priv->grab_widget,
-                                           coords, time, state, release_type);
-          priv->grab_widget = NULL;
-        }
-    }
-}
-
-static void
-gimp_free_select_tool_motion (GimpTool         *tool,
-                              const GimpCoords *coords,
-                              guint32           time,
-                              GdkModifierType   state,
-                              GimpDisplay      *display)
-{
-  GimpFreeSelectTool        *fst  = GIMP_FREE_SELECT_TOOL (tool);
-  GimpFreeSelectToolPrivate *priv = fst->private;
-
-  if (priv->grab_widget)
-    {
-      gimp_tool_widget_motion (priv->grab_widget, coords, time, state);
-    }
-}
-
-static gboolean
-gimp_free_select_tool_key_press (GimpTool    *tool,
-                                 GdkEventKey *kevent,
-                                 GimpDisplay *display)
-{
-  GimpFreeSelectTool        *fst  = GIMP_FREE_SELECT_TOOL (tool);
-  GimpFreeSelectToolPrivate *priv = fst->private;
-
-  if (priv->widget && display == tool->display)
-    {
-      return gimp_tool_widget_key_press (priv->widget, kevent);
     }
-
-  return FALSE;
 }
 
 static void
-gimp_free_select_tool_modifier_key (GimpTool        *tool,
-                                    GdkModifierType  key,
-                                    gboolean         press,
-                                    GdkModifierType  state,
-                                    GimpDisplay     *display)
+gimp_free_select_tool_halt (GimpFreeSelectTool *free_sel)
 {
-  GimpFreeSelectTool        *fst  = GIMP_FREE_SELECT_TOOL (tool);
-  GimpFreeSelectToolPrivate *priv = fst->private;
-
-  if (priv->widget && display == tool->display)
-    {
-      gimp_tool_widget_hover_modifier (priv->widget, key, press, state);
-    }
-
-  GIMP_TOOL_CLASS (parent_class)->modifier_key (tool, key, press, state,
-                                                display);
-}
-
-static void
-gimp_free_select_tool_active_modifier_key (GimpTool        *tool,
-                                           GdkModifierType  key,
-                                           gboolean         press,
-                                           GdkModifierType  state,
-                                           GimpDisplay     *display)
-{
-  GimpFreeSelectTool        *fst  = GIMP_FREE_SELECT_TOOL (tool);
-  GimpFreeSelectToolPrivate *priv = fst->private;
-
-  if (priv->widget)
-    {
-      gimp_tool_widget_motion_modifier (priv->widget, key, press, state);
+  GimpFreeSelectToolPrivate *priv = free_sel->priv;
 
-      GIMP_TOOL_CLASS (parent_class)->active_modifier_key (tool,
-                                                           key, press, state,
-                                                           display);
-    }
+  priv->started = FALSE;
 }
 
 static void
-gimp_free_select_tool_real_select (GimpFreeSelectTool *fst,
-                                   GimpDisplay        *display,
-                                   const GimpVector2  *points,
-                                   gint                n_points)
+gimp_free_select_tool_commit (GimpFreeSelectTool *free_sel,
+                              GimpDisplay        *display)
 {
-  GimpSelectionOptions      *options = GIMP_SELECTION_TOOL_GET_OPTIONS (fst);
-  GimpFreeSelectToolPrivate *priv    = fst->private;
+  GimpSelectionOptions      *options = GIMP_SELECTION_TOOL_GET_OPTIONS (free_sel);
+  GimpFreeSelectToolPrivate *priv    = free_sel->priv;
   GimpImage                 *image   = gimp_display_get_image (display);
+  const GimpVector2         *points;
+  gint                       n_points;
 
-  gimp_channel_select_polygon (gimp_image_get_mask (image),
-                               C_("command", "Free Select"),
-                               n_points,
-                               points,
-                               priv->operation_at_start,
-                               options->antialias,
-                               options->feather,
-                               options->feather_radius,
-                               options->feather_radius,
-                               TRUE);
-
-  gimp_tool_control (GIMP_TOOL (fst), GIMP_TOOL_ACTION_HALT, display);
-}
-
-static void
-gimp_free_select_tool_polygon_changed (GimpToolWidget     *polygon,
-                                       GimpFreeSelectTool *fst)
-{
-}
-
-static void
-gimp_free_select_tool_polygon_response (GimpToolWidget     *polygon,
-                                        gint                response_id,
-                                        GimpFreeSelectTool *fst)
-{
-  GimpTool *tool = GIMP_TOOL (fst);
+  gimp_polygon_select_tool_get_points (GIMP_POLYGON_SELECT_TOOL (free_sel),
+                                       &points, &n_points);
 
-  switch (response_id)
+  if (n_points > 2)
     {
-    case GIMP_TOOL_WIDGET_RESPONSE_CONFIRM:
-      /*  don't gimp_tool_control(COMMIT) here because we don't always
-       *  want to HALT the tool after committing because we have a
-       *  subclass, we do that in the default implementation of
-       *  select().
-       */
-      gimp_free_select_tool_commit (fst, tool->display);
-      break;
-
-    case GIMP_TOOL_WIDGET_RESPONSE_CANCEL:
-      gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
-      break;
+      gimp_channel_select_polygon (gimp_image_get_mask (image),
+                                   C_("command", "Free Select"),
+                                   n_points,
+                                   points,
+                                   priv->operation_at_start,
+                                   options->antialias,
+                                   options->feather,
+                                   options->feather_radius,
+                                   options->feather_radius,
+                                   TRUE);
+
+      gimp_image_flush (image);
     }
 }
diff --git a/app/tools/gimpfreeselecttool.h b/app/tools/gimpfreeselecttool.h
index a7833fdc42..0212040cb9 100644
--- a/app/tools/gimpfreeselecttool.h
+++ b/app/tools/gimpfreeselecttool.h
@@ -19,7 +19,7 @@
 #define __GIMP_FREE_SELECT_TOOL_H__
 
 
-#include "gimpselectiontool.h"
+#include "gimppolygonselecttool.h"
 
 
 #define GIMP_TYPE_FREE_SELECT_TOOL            (gimp_free_select_tool_get_type ())
@@ -36,21 +36,14 @@ typedef struct _GimpFreeSelectToolClass   GimpFreeSelectToolClass;
 
 struct _GimpFreeSelectTool
 {
-  GimpSelectionTool          parent_instance;
+  GimpPolygonSelectTool      parent_instance;
 
-  GimpFreeSelectToolPrivate *private;
+  GimpFreeSelectToolPrivate *priv;
 };
 
 struct _GimpFreeSelectToolClass
 {
-  GimpSelectionToolClass  parent_class;
-
-  /*  virtual function  */
-
-  void (* select) (GimpFreeSelectTool *free_select_tool,
-                   GimpDisplay        *display,
-                   const GimpVector2  *points,
-                   gint                n_points);
+  GimpPolygonSelectToolClass  parent_class;
 };
 
 
@@ -59,11 +52,5 @@ void    gimp_free_select_tool_register     (GimpToolRegisterCallback  callback,
 
 GType   gimp_free_select_tool_get_type     (void) G_GNUC_CONST;
 
-gint    gimp_free_select_tool_get_n_points (GimpFreeSelectTool *tool);
-
-/*  protected functions */
-
-void    gimp_free_select_tool_halt         (GimpFreeSelectTool *tool);
-
 
 #endif  /*  __GIMP_FREE_SELECT_TOOL_H__  */
diff --git a/app/tools/gimppolygonselecttool.c b/app/tools/gimppolygonselecttool.c
new file mode 100644
index 0000000000..af3bfb5eb4
--- /dev/null
+++ b/app/tools/gimppolygonselecttool.c
@@ -0,0 +1,558 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Major improvement to support polygonal segments
+ * Copyright (C) 2008 Martin Nordholts
+ *
+ * This program is polygon software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Polygon 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "tools-types.h"
+
+#include "core/gimpchannel.h"
+#include "core/gimpchannel-select.h"
+#include "core/gimpimage.h"
+#include "core/gimplayer-floating-selection.h"
+
+#include "widgets/gimphelp-ids.h"
+
+#include "display/gimpdisplay.h"
+#include "display/gimptoolpolygon.h"
+
+#include "gimppolygonselecttool.h"
+#include "gimpselectionoptions.h"
+#include "gimptoolcontrol.h"
+
+
+struct _GimpPolygonSelectToolPrivate
+{
+  GimpToolWidget *widget;
+  GimpToolWidget *grab_widget;
+
+  gboolean        pending_response;
+  gint            pending_response_id;
+};
+
+
+/*  local function prototypes  */
+
+static void       gimp_polygon_select_tool_finalize                (GObject               *object);
+
+static void       gimp_polygon_select_tool_control                 (GimpTool              *tool,
+                                                                    GimpToolAction         action,
+                                                                    GimpDisplay           *display);
+static void       gimp_polygon_select_tool_oper_update             (GimpTool              *tool,
+                                                                    const GimpCoords      *coords,
+                                                                    GdkModifierType        state,
+                                                                    gboolean               proximity,
+                                                                    GimpDisplay           *display);
+static void       gimp_polygon_select_tool_cursor_update           (GimpTool              *tool,
+                                                                    const GimpCoords      *coords,
+                                                                    GdkModifierType        state,
+                                                                    GimpDisplay           *display);
+static void       gimp_polygon_select_tool_button_press            (GimpTool              *tool,
+                                                                    const GimpCoords      *coords,
+                                                                    guint32                time,
+                                                                    GdkModifierType        state,
+                                                                    GimpButtonPressType    press_type,
+                                                                    GimpDisplay           *display);
+static void       gimp_polygon_select_tool_button_release          (GimpTool              *tool,
+                                                                    const GimpCoords      *coords,
+                                                                    guint32                time,
+                                                                    GdkModifierType        state,
+                                                                    GimpButtonReleaseType  release_type,
+                                                                    GimpDisplay           *display);
+static void       gimp_polygon_select_tool_motion                  (GimpTool              *tool,
+                                                                    const GimpCoords      *coords,
+                                                                    guint32                time,
+                                                                    GdkModifierType        state,
+                                                                    GimpDisplay           *display);
+static gboolean   gimp_polygon_select_tool_key_press               (GimpTool              *tool,
+                                                                    GdkEventKey           *kevent,
+                                                                    GimpDisplay           *display);
+static void       gimp_polygon_select_tool_modifier_key            (GimpTool              *tool,
+                                                                    GdkModifierType        key,
+                                                                    gboolean               press,
+                                                                    GdkModifierType        state,
+                                                                    GimpDisplay           *display);
+static void       gimp_polygon_select_tool_active_modifier_key     (GimpTool              *tool,
+                                                                    GdkModifierType        key,
+                                                                    gboolean               press,
+                                                                    GdkModifierType        state,
+                                                                    GimpDisplay           *display);
+
+static void       gimp_polygon_select_tool_real_confirm            (GimpPolygonSelectTool *poly_sel,
+                                                                    GimpDisplay           *display);
+
+static void       gimp_polygon_select_tool_polygon_change_complete (GimpToolWidget        *polygon,
+                                                                    GimpPolygonSelectTool *poly_sel);
+static void       gimp_polygon_select_tool_polygon_response        (GimpToolWidget        *polygon,
+                                                                    gint                   response_id,
+                                                                    GimpPolygonSelectTool *poly_sel);
+
+static void       gimp_polygon_select_tool_start                   (GimpPolygonSelectTool *poly_sel,
+                                                                    GimpDisplay           *display);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpPolygonSelectTool, gimp_polygon_select_tool,
+                            GIMP_TYPE_SELECTION_TOOL)
+
+#define parent_class gimp_polygon_select_tool_parent_class
+
+
+/*  private functions  */
+
+static void
+gimp_polygon_select_tool_class_init (GimpPolygonSelectToolClass *klass)
+{
+  GObjectClass  *object_class = G_OBJECT_CLASS (klass);
+  GimpToolClass *tool_class   = GIMP_TOOL_CLASS (klass);
+
+  object_class->finalize          = gimp_polygon_select_tool_finalize;
+
+  tool_class->control             = gimp_polygon_select_tool_control;
+  tool_class->oper_update         = gimp_polygon_select_tool_oper_update;
+  tool_class->cursor_update       = gimp_polygon_select_tool_cursor_update;
+  tool_class->button_press        = gimp_polygon_select_tool_button_press;
+  tool_class->button_release      = gimp_polygon_select_tool_button_release;
+  tool_class->motion              = gimp_polygon_select_tool_motion;
+  tool_class->key_press           = gimp_polygon_select_tool_key_press;
+  tool_class->modifier_key        = gimp_polygon_select_tool_modifier_key;
+  tool_class->active_modifier_key = gimp_polygon_select_tool_active_modifier_key;
+
+  klass->change_complete          = NULL;
+  klass->confirm                  = gimp_polygon_select_tool_real_confirm;
+}
+
+static void
+gimp_polygon_select_tool_init (GimpPolygonSelectTool *poly_sel)
+{
+  GimpTool *tool = GIMP_TOOL (poly_sel);
+
+  poly_sel->priv = gimp_polygon_select_tool_get_instance_private (poly_sel);
+
+  gimp_tool_control_set_motion_mode        (tool->control,
+                                            GIMP_MOTION_MODE_EXACT);
+  gimp_tool_control_set_wants_click        (tool->control, TRUE);
+  gimp_tool_control_set_wants_double_click (tool->control, TRUE);
+  gimp_tool_control_set_active_modifiers   (tool->control,
+                                            GIMP_TOOL_ACTIVE_MODIFIERS_SEPARATE);
+  gimp_tool_control_set_precision          (tool->control,
+                                            GIMP_CURSOR_PRECISION_SUBPIXEL);
+}
+
+static void
+gimp_polygon_select_tool_finalize (GObject *object)
+{
+  GimpPolygonSelectTool        *poly_sel = GIMP_POLYGON_SELECT_TOOL (object);
+  GimpPolygonSelectToolPrivate *priv     = poly_sel->priv;
+
+  g_clear_object (&priv->widget);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_polygon_select_tool_control (GimpTool       *tool,
+                                  GimpToolAction  action,
+                                  GimpDisplay    *display)
+{
+  GimpPolygonSelectTool *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
+
+  switch (action)
+    {
+    case GIMP_TOOL_ACTION_PAUSE:
+    case GIMP_TOOL_ACTION_RESUME:
+      break;
+
+    case GIMP_TOOL_ACTION_HALT:
+      gimp_polygon_select_tool_halt (poly_sel);
+      break;
+
+    case GIMP_TOOL_ACTION_COMMIT:
+      break;
+    }
+
+  GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
+}
+
+static void
+gimp_polygon_select_tool_oper_update (GimpTool         *tool,
+                                      const GimpCoords *coords,
+                                      GdkModifierType   state,
+                                      gboolean          proximity,
+                                      GimpDisplay      *display)
+{
+  GimpPolygonSelectTool        *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
+  GimpPolygonSelectToolPrivate *priv     = poly_sel->priv;
+
+  if (display != tool->display)
+    {
+      GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state,
+                                                   proximity, display);
+      return;
+    }
+
+  if (priv->widget)
+    {
+      gimp_tool_widget_hover (priv->widget, coords, state, proximity);
+    }
+}
+
+static void
+gimp_polygon_select_tool_cursor_update (GimpTool         *tool,
+                                        const GimpCoords *coords,
+                                        GdkModifierType   state,
+                                        GimpDisplay      *display)
+{
+  GimpPolygonSelectTool        *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
+  GimpPolygonSelectToolPrivate *priv     = poly_sel->priv;
+  GimpCursorModifier            modifier = GIMP_CURSOR_MODIFIER_NONE;
+
+  if (tool->display == NULL)
+    {
+      GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state,
+                                                     display);
+      return;
+    }
+
+  if (priv->widget && display == tool->display)
+    {
+      gimp_tool_widget_get_cursor (priv->widget, coords, state,
+                                   NULL, NULL, &modifier);
+    }
+
+  gimp_tool_set_cursor (tool, display,
+                        gimp_tool_control_get_cursor (tool->control),
+                        gimp_tool_control_get_tool_cursor (tool->control),
+                        modifier);
+}
+
+static void
+gimp_polygon_select_tool_button_press (GimpTool            *tool,
+                                       const GimpCoords    *coords,
+                                       guint32              time,
+                                       GdkModifierType      state,
+                                       GimpButtonPressType  press_type,
+                                       GimpDisplay         *display)
+{
+  GimpPolygonSelectTool        *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
+  GimpPolygonSelectToolPrivate *priv     = poly_sel->priv;
+
+  if (tool->display && tool->display != display)
+    gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, tool->display);
+
+  if (! priv->widget) /* not tool->display, we have a subclass */
+    {
+      /* First of all handle delegation to the selection mask edit logic
+       * if appropriate.
+       */
+      if (gimp_selection_tool_start_edit (GIMP_SELECTION_TOOL (poly_sel),
+                                          display, coords))
+        {
+          return;
+        }
+
+      gimp_polygon_select_tool_start (poly_sel, display);
+
+      gimp_tool_widget_hover (priv->widget, coords, state, TRUE);
+    }
+
+  if (gimp_tool_widget_button_press (priv->widget, coords, time, state,
+                                     press_type))
+    {
+      priv->grab_widget = priv->widget;
+    }
+
+  if (press_type == GIMP_BUTTON_PRESS_NORMAL)
+    gimp_tool_control_activate (tool->control);
+}
+
+static void
+gimp_polygon_select_tool_button_release (GimpTool              *tool,
+                                         const GimpCoords      *coords,
+                                         guint32                time,
+                                         GdkModifierType        state,
+                                         GimpButtonReleaseType  release_type,
+                                         GimpDisplay           *display)
+{
+  GimpPolygonSelectTool        *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
+  GimpPolygonSelectToolPrivate *priv     = poly_sel->priv;
+  GimpImage                    *image    = gimp_display_get_image (display);
+
+  gimp_tool_control_halt (tool->control);
+
+  switch (release_type)
+    {
+    case GIMP_BUTTON_RELEASE_CLICK:
+    case GIMP_BUTTON_RELEASE_NO_MOTION:
+      /*  If there is a floating selection, anchor it  */
+      if (gimp_image_get_floating_selection (image))
+        {
+          floating_sel_anchor (gimp_image_get_floating_selection (image));
+
+          gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
+
+          return;
+        }
+
+      /* fallthru */
+
+    default:
+      if (priv->grab_widget)
+        {
+          gimp_tool_widget_button_release (priv->grab_widget,
+                                           coords, time, state, release_type);
+          priv->grab_widget = NULL;
+        }
+    }
+
+  if (priv->pending_response)
+    {
+      gimp_polygon_select_tool_polygon_response (priv->widget,
+                                                 priv->pending_response_id,
+                                                 poly_sel);
+
+      priv->pending_response = FALSE;
+    }
+}
+
+static void
+gimp_polygon_select_tool_motion (GimpTool         *tool,
+                                 const GimpCoords *coords,
+                                 guint32           time,
+                                 GdkModifierType   state,
+                                 GimpDisplay      *display)
+{
+  GimpPolygonSelectTool        *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
+  GimpPolygonSelectToolPrivate *priv     = poly_sel->priv;
+
+  if (priv->grab_widget)
+    {
+      gimp_tool_widget_motion (priv->grab_widget, coords, time, state);
+    }
+}
+
+static gboolean
+gimp_polygon_select_tool_key_press (GimpTool    *tool,
+                                    GdkEventKey *kevent,
+                                    GimpDisplay *display)
+{
+  GimpPolygonSelectTool        *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
+  GimpPolygonSelectToolPrivate *priv     = poly_sel->priv;
+
+  if (priv->widget && display == tool->display)
+    {
+      return gimp_tool_widget_key_press (priv->widget, kevent);
+    }
+
+  return FALSE;
+}
+
+static void
+gimp_polygon_select_tool_modifier_key (GimpTool        *tool,
+                                       GdkModifierType  key,
+                                       gboolean         press,
+                                       GdkModifierType  state,
+                                       GimpDisplay     *display)
+{
+  GimpPolygonSelectTool        *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
+  GimpPolygonSelectToolPrivate *priv     = poly_sel->priv;
+
+  if (priv->widget && display == tool->display)
+    {
+      gimp_tool_widget_hover_modifier (priv->widget, key, press, state);
+    }
+
+  GIMP_TOOL_CLASS (parent_class)->modifier_key (tool, key, press, state,
+                                                display);
+}
+
+static void
+gimp_polygon_select_tool_active_modifier_key (GimpTool        *tool,
+                                              GdkModifierType  key,
+                                              gboolean         press,
+                                              GdkModifierType  state,
+                                              GimpDisplay     *display)
+{
+  GimpPolygonSelectTool        *poly_sel = GIMP_POLYGON_SELECT_TOOL (tool);
+  GimpPolygonSelectToolPrivate *priv     = poly_sel->priv;
+
+  if (priv->widget)
+    {
+      gimp_tool_widget_motion_modifier (priv->widget, key, press, state);
+
+      GIMP_TOOL_CLASS (parent_class)->active_modifier_key (tool,
+                                                           key, press, state,
+                                                           display);
+    }
+}
+
+static void
+gimp_polygon_select_tool_real_confirm (GimpPolygonSelectTool *poly_sel,
+                                       GimpDisplay           *display)
+{
+  gimp_tool_control (GIMP_TOOL (poly_sel), GIMP_TOOL_ACTION_COMMIT, display);
+}
+
+static void
+gimp_polygon_select_tool_polygon_change_complete (GimpToolWidget        *polygon,
+                                                  GimpPolygonSelectTool *poly_sel)
+{
+  if (GIMP_POLYGON_SELECT_TOOL_GET_CLASS (poly_sel)->change_complete)
+    {
+      GIMP_POLYGON_SELECT_TOOL_GET_CLASS (poly_sel)->change_complete (
+        poly_sel, GIMP_TOOL (poly_sel)->display);
+    }
+}
+
+static void
+gimp_polygon_select_tool_polygon_response (GimpToolWidget        *polygon,
+                                           gint                   response_id,
+                                           GimpPolygonSelectTool *poly_sel)
+{
+  GimpTool                     *tool = GIMP_TOOL (poly_sel);
+  GimpPolygonSelectToolPrivate *priv = poly_sel->priv;
+
+  /* if we're in the middle of a click, defer the response to the
+   * button_release() event
+   */
+  if (gimp_tool_control_is_active (tool->control))
+    {
+      priv->pending_response    = TRUE;
+      priv->pending_response_id = response_id;
+
+      return;
+    }
+
+  switch (response_id)
+    {
+    case GIMP_TOOL_WIDGET_RESPONSE_CONFIRM:
+      /*  don't gimp_tool_control(COMMIT) here because we don't always
+       *  want to HALT the tool after committing because we have a
+       *  subclass, we do that in the default implementation of
+       *  confirm().
+       */
+      if (GIMP_POLYGON_SELECT_TOOL_GET_CLASS (poly_sel)->confirm)
+        {
+          GIMP_POLYGON_SELECT_TOOL_GET_CLASS (poly_sel)->confirm (
+            poly_sel, tool->display);
+        }
+      break;
+
+    case GIMP_TOOL_WIDGET_RESPONSE_CANCEL:
+      gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
+      break;
+    }
+}
+
+static void
+gimp_polygon_select_tool_start (GimpPolygonSelectTool *poly_sel,
+                                GimpDisplay           *display)
+{
+  GimpTool                     *tool  = GIMP_TOOL (poly_sel);
+  GimpPolygonSelectToolPrivate *priv  = poly_sel->priv;
+  GimpDisplayShell             *shell = gimp_display_get_shell (display);
+
+  tool->display = display;
+
+  priv->widget = gimp_tool_polygon_new (shell);
+
+  gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tool), priv->widget);
+
+  g_signal_connect (priv->widget, "change-complete",
+                    G_CALLBACK (gimp_polygon_select_tool_polygon_change_complete),
+                    poly_sel);
+  g_signal_connect (priv->widget, "response",
+                    G_CALLBACK (gimp_polygon_select_tool_polygon_response),
+                    poly_sel);
+
+  gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
+}
+
+
+/*  public functions  */
+
+gboolean
+gimp_polygon_select_tool_is_closed (GimpPolygonSelectTool *poly_sel)
+{
+  GimpPolygonSelectToolPrivate *priv;
+
+  g_return_val_if_fail (GIMP_IS_POLYGON_SELECT_TOOL (poly_sel), FALSE);
+
+  priv = poly_sel->priv;
+
+  if (priv->widget)
+    return gimp_tool_polygon_is_closed (GIMP_TOOL_POLYGON (priv->widget));
+
+  return FALSE;
+}
+
+void
+gimp_polygon_select_tool_get_points (GimpPolygonSelectTool  *poly_sel,
+                                     const GimpVector2     **points,
+                                     gint                   *n_points)
+{
+  GimpPolygonSelectToolPrivate *priv;
+
+  g_return_if_fail (GIMP_IS_POLYGON_SELECT_TOOL (poly_sel));
+
+  priv = poly_sel->priv;
+
+  if (priv->widget)
+    {
+      gimp_tool_polygon_get_points (GIMP_TOOL_POLYGON (priv->widget),
+                                    points, n_points);
+    }
+  else
+    {
+      if (points)   *points   = NULL;
+      if (n_points) *n_points = 0;
+    }
+}
+
+
+/*  protected functions  */
+
+gboolean
+gimp_polygon_select_tool_is_grabbed (GimpPolygonSelectTool *poly_sel)
+{
+  GimpPolygonSelectToolPrivate *priv;
+
+  g_return_val_if_fail (GIMP_IS_POLYGON_SELECT_TOOL (poly_sel), FALSE);
+
+  priv = poly_sel->priv;
+
+  return priv->grab_widget != NULL;
+}
+
+void
+gimp_polygon_select_tool_halt (GimpPolygonSelectTool *poly_sel)
+{
+  GimpPolygonSelectToolPrivate *priv;
+
+  g_return_if_fail (GIMP_IS_POLYGON_SELECT_TOOL (poly_sel));
+
+  priv = poly_sel->priv;
+
+  gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (poly_sel), NULL);
+  g_clear_object (&priv->widget);
+}
diff --git a/app/tools/gimppolygonselecttool.h b/app/tools/gimppolygonselecttool.h
new file mode 100644
index 0000000000..13de063323
--- /dev/null
+++ b/app/tools/gimppolygonselecttool.h
@@ -0,0 +1,69 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is polygon software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Polygon 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_POLYGON_SELECT_TOOL_H__
+#define __GIMP_POLYGON_SELECT_TOOL_H__
+
+
+#include "gimpselectiontool.h"
+
+
+#define GIMP_TYPE_POLYGON_SELECT_TOOL            (gimp_polygon_select_tool_get_type ())
+#define GIMP_POLYGON_SELECT_TOOL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GIMP_TYPE_POLYGON_SELECT_TOOL, GimpPolygonSelectTool))
+#define GIMP_POLYGON_SELECT_TOOL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
GIMP_TYPE_POLYGON_SELECT_TOOL, GimpPolygonSelectToolClass))
+#define GIMP_IS_POLYGON_SELECT_TOOL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GIMP_TYPE_POLYGON_SELECT_TOOL))
+#define GIMP_IS_POLYGON_SELECT_TOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GIMP_TYPE_POLYGON_SELECT_TOOL))
+#define GIMP_POLYGON_SELECT_TOOL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GIMP_TYPE_POLYGON_SELECT_TOOL, GimpPolygonSelectToolClass))
+
+
+typedef struct _GimpPolygonSelectTool        GimpPolygonSelectTool;
+typedef struct _GimpPolygonSelectToolPrivate GimpPolygonSelectToolPrivate;
+typedef struct _GimpPolygonSelectToolClass   GimpPolygonSelectToolClass;
+
+struct _GimpPolygonSelectTool
+{
+  GimpSelectionTool             parent_instance;
+
+  GimpPolygonSelectToolPrivate *priv;
+};
+
+struct _GimpPolygonSelectToolClass
+{
+  GimpSelectionToolClass  parent_class;
+
+  /*  virtual functions  */
+  void (* change_complete) (GimpPolygonSelectTool *poly_sel,
+                            GimpDisplay           *display);
+  void (* confirm)         (GimpPolygonSelectTool *poly_sel,
+                            GimpDisplay           *display);
+};
+
+
+GType      gimp_polygon_select_tool_get_type   (void) G_GNUC_CONST;
+
+gboolean   gimp_polygon_select_tool_is_closed  (GimpPolygonSelectTool  *poly_sel);
+void       gimp_polygon_select_tool_get_points (GimpPolygonSelectTool  *poly_sel,
+                                                const GimpVector2     **points,
+                                                gint                   *n_points);
+
+/*  protected functions */
+gboolean   gimp_polygon_select_tool_is_grabbed (GimpPolygonSelectTool  *poly_sel);
+
+void       gimp_polygon_select_tool_halt       (GimpPolygonSelectTool  *poly_sel);
+
+
+#endif  /*  __GIMP_POLYGON_SELECT_TOOL_H__  */


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