[calligra/calligra/2.9] /: Third time! The Selection modifiers patch!!!

Boudewijn Rempt boud at valdyas.org
Sun Jun 21 13:51:01 UTC 2015


Git commit a7f5592308fa8e841f444106a15f37ffa66ae001 by Boudewijn Rempt.
Committed on 21/06/2015 at 13:50.
Pushed by rempt into branch 'calligra/2.9'.

Third time! The Selection modifiers patch!!!

This refactors polygonal, elliptical, and rectangular selection tools to
use a basic selection tool template which unifies previously duplicated
code. The template overrides the ability to execute alternate actions, but
none of those tools supported alternate actions previously and the ellipse
and rectangle were already overriding the modifier keys to begin with.

Shift: add to selection

Alt: subtract from selection

Shift+Alt: intersect current selection

Ctrl: replace selection

Certain key combinations allow users the ability to expose the modifier
keys to the base tool, i.e. to make proportional / translated / scaled
alterations using ctrl/alt/shift.

1) If the user clicks first and then presses modifier keys, those modifier
keys will only alter the selection method.

2) If the user presses modifier keys first and then clicks, the selection
method is locked in, and subsequent modifier keystrokes will change how
the selection is drawn.

3) If the underlying tool never takes modifier keys, modifier keys will
always alter the selection method.

Note: the bezier curve selection tool doesn't support the modifiers yet.

Patch by Michael Abrahams. Thanks!
CCMAIL: miabraha at gmail.com
CCMAIL: kimageshop at kde.org
CCMAIL: animtim at gmail.com, info at davidrevoy.com
REVIEW: 123833
BUG: 315255
BUG: 348234

NOTE: Changing the secondary invocation action is still broken, but that's
not the fault of this patch, that's another bug!

M  +2    -1    krita/image/kis_selection.h
M  +14   -0    krita/plugins/tools/defaulttools/kis_tool_path.cc
M  +4    -1    krita/plugins/tools/defaulttools/kis_tool_path.h
M  +25   -10   krita/plugins/tools/selectiontools/kis_tool_select_contiguous.cc
M  +9    -4    krita/plugins/tools/selectiontools/kis_tool_select_contiguous.h
M  +30   -22   krita/plugins/tools/selectiontools/kis_tool_select_elliptical.cc
M  +29   -13   krita/plugins/tools/selectiontools/kis_tool_select_elliptical.h
M  +32   -4    krita/plugins/tools/selectiontools/kis_tool_select_outline.cc
M  +10   -5    krita/plugins/tools/selectiontools/kis_tool_select_outline.h
M  +30   -4    krita/plugins/tools/selectiontools/kis_tool_select_path.cc
M  +18   -12   krita/plugins/tools/selectiontools/kis_tool_select_path.h
M  +30   -41   krita/plugins/tools/selectiontools/kis_tool_select_polygonal.cc
M  +21   -16   krita/plugins/tools/selectiontools/kis_tool_select_polygonal.h
M  +29   -44   krita/plugins/tools/selectiontools/kis_tool_select_rectangular.cc
M  +20   -16   krita/plugins/tools/selectiontools/kis_tool_select_rectangular.h
M  +23   -8    krita/plugins/tools/selectiontools/kis_tool_select_similar.cc
M  +18   -10   krita/plugins/tools/selectiontools/kis_tool_select_similar.h
M  +0    -1    krita/ui/CMakeLists.txt
M  +8    -3    krita/ui/canvas/kis_tool_proxy.h
M  +1    -0    krita/ui/input/kis_alternate_invocation_action.cpp
M  +0    -1    krita/ui/input/kis_input_manager.cpp
M  +1    -1    krita/ui/tool/kis_delegated_tool.h
M  +8    -0    krita/ui/tool/kis_tool.cc
M  +11   -1    krita/ui/tool/kis_tool.h
M  +4    -4    krita/ui/tool/kis_tool_paint.h
M  +12   -0    krita/ui/tool/kis_tool_polyline_base.cpp
M  +3    -0    krita/ui/tool/kis_tool_polyline_base.h
M  +34   -24   krita/ui/tool/kis_tool_rectangle_base.cpp
M  +8    -5    krita/ui/tool/kis_tool_rectangle_base.h
D  +0    -79   krita/ui/tool/kis_tool_select_base.cpp
M  +185  -18   krita/ui/tool/kis_tool_select_base.h
M  +19   -7    libs/basicflakes/tools/KoCreatePathTool.cpp
M  +9    -2    libs/basicflakes/tools/KoCreatePathTool.h
M  +7    -5    libs/basicflakes/tools/KoCreatePathTool_p.h

http://commits.kde.org/calligra/a7f5592308fa8e841f444106a15f37ffa66ae001

diff --git a/krita/image/kis_selection.h b/krita/image/kis_selection.h
index 6376f874..b2679d8 100644
--- a/krita/image/kis_selection.h
+++ b/krita/image/kis_selection.h
@@ -33,7 +33,8 @@ enum SelectionAction {
     SELECTION_REPLACE,
     SELECTION_ADD,
     SELECTION_SUBTRACT,
-    SELECTION_INTERSECT
+    SELECTION_INTERSECT,
+    SELECTION_DEFAULT
 };
 
 #include "kis_pixel_selection.h"
diff --git a/krita/plugins/tools/defaulttools/kis_tool_path.cc b/krita/plugins/tools/defaulttools/kis_tool_path.cc
index f05b4eb..31dadc1 100644
--- a/krita/plugins/tools/defaulttools/kis_tool_path.cc
+++ b/krita/plugins/tools/defaulttools/kis_tool_path.cc
@@ -50,6 +50,20 @@ void KisToolPath::mousePressEvent(KoPointerEvent *event)
     DelegatedPathTool::mousePressEvent(event);
 }
 
+void KisToolPath::beginAlternateAction(KoPointerEvent *event, AlternateAction action) {
+ Q_UNUSED(action)
+ mousePressEvent(event);
+}
+void KisToolPath::continueAlternateAction(KoPointerEvent *event, AlternateAction action){
+ Q_UNUSED(action)
+ mouseMoveEvent(event);
+}
+
+void KisToolPath::endAlternateAction(KoPointerEvent *event, AlternateAction action) {
+ Q_UNUSED(action)
+ mouseReleaseEvent(event);
+}
+
 QList<QPointer<QWidget> > KisToolPath::createOptionWidgets()
 {
     QList<QPointer<QWidget> > widgets = DelegatedPathTool::createOptionWidgets();
diff --git a/krita/plugins/tools/defaulttools/kis_tool_path.h b/krita/plugins/tools/defaulttools/kis_tool_path.h
index 468aca3..9e53a4e 100644
--- a/krita/plugins/tools/defaulttools/kis_tool_path.h
+++ b/krita/plugins/tools/defaulttools/kis_tool_path.h
@@ -61,6 +61,10 @@ public:
 
     virtual QList< QPointer<QWidget> > createOptionWidgets();
 
+    void beginAlternateAction(KoPointerEvent *event, AlternateAction action);
+    void continueAlternateAction(KoPointerEvent *event, AlternateAction action);
+    void endAlternateAction(KoPointerEvent *event, AlternateAction action);
+
 protected:
     void requestStrokeCancellation();
     void requestStrokeEnd();
@@ -95,4 +99,3 @@ public:
 
 
 #endif // KIS_TOOL_PATH_H_
-
diff --git a/krita/plugins/tools/selectiontools/kis_tool_select_contiguous.cc b/krita/plugins/tools/selectiontools/kis_tool_select_contiguous.cc
index 5bd4d2f..dfdeeab 100644
--- a/krita/plugins/tools/selectiontools/kis_tool_select_contiguous.cc
+++ b/krita/plugins/tools/selectiontools/kis_tool_select_contiguous.cc
@@ -5,6 +5,7 @@
  *  Copyright (c) 2002 Patrick Julien <freak at codepimps.org>
  *  Copyright (c) 2004 Boudewijn Rempt <boud at valdyas.org>
  *  Copyright (c) 2012 José Luis Vergara <pentalis at gmail.com>
+ *  Copyright (c) 2015 Michael Abrahams <miabraha at gmail.com>
  *
  *  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
@@ -51,13 +52,14 @@
 KisToolSelectContiguous::KisToolSelectContiguous(KoCanvasBase *canvas)
         : KisToolSelectBase(canvas,
                             KisCursor::load("tool_contiguous_selection_cursor.png", 6, 6),
-                            i18n("Contiguous Area Selection"))
+                            i18n("Contiguous Area Selection")),
+          m_fuzziness(20),
+          m_sizemod(0),
+          m_feather(0),
+          m_limitToCurrentLayer(false)
 {
-    setObjectName("tool_select_contiguous");
-    m_fuzziness = 20;
-    m_sizemod = 0;
-    m_feather = 0;
-    m_limitToCurrentLayer = false;
+    setObjectName("tool_select_contiguous");    
+    connect(&m_widgetHelper, SIGNAL(selectionActionChanged(int)), this, SLOT(setSelectionAction(int)));
 }
 
 KisToolSelectContiguous::~KisToolSelectContiguous()
@@ -72,13 +74,14 @@ void KisToolSelectContiguous::activate(ToolActivation toolActivation, const QSet
 
 void KisToolSelectContiguous::beginPrimaryAction(KoPointerEvent *event)
 {
+
+    KisToolSelectBase::beginPrimaryAction(event);
     KisPaintDeviceSP dev;
 
     if (!currentNode() ||
         !(dev = currentNode()->projection()) ||
         !currentNode()->visible() ||
         !selectionEditable()) {
-
         event->ignore();
         return;
     }
@@ -110,7 +113,6 @@ void KisToolSelectContiguous::beginPrimaryAction(KoPointerEvent *event)
     selection->pixelSelection()->invalidateOutlineCache();
     KisSelectionToolHelper helper(kisCanvas, kundo2_i18n("Select Contiguous Area"));
     helper.selectPixelSelection(selection->pixelSelection(), selectionAction());
-
     QApplication::restoreOverrideCursor();
 }
 
@@ -205,8 +207,6 @@ QWidget* KisToolSelectContiguous::createOptionWidget()
         sizemod->setValue( m_configGroup.readEntry("sizemod", 0)); //grow/shrink
         feather->setValue(m_configGroup.readEntry("feather", 0));
         limitToCurrentLayer->setChecked(m_configGroup.readEntry("limitToCurrentLayer", false));
-
-
     }
     return selectionWidget;
 }
@@ -219,4 +219,19 @@ void KisToolSelectContiguous::slotLimitToCurrentLayer(int state)
     m_configGroup.writeEntry("limitToCurrentLayer", state);
 }
 
+void KisToolSelectContiguous::setSelectionAction(int newSelectionAction)
+{
+    if(newSelectionAction >= SELECTION_REPLACE && newSelectionAction <= SELECTION_INTERSECT && m_selectionAction != newSelectionAction)
+    {
+        if(m_widgetHelper.optionWidget())
+        {
+            m_widgetHelper.slotSetAction(newSelectionAction);
+        }
+        m_selectionAction = (SelectionAction)newSelectionAction;
+        emit selectionActionChanged();
+    }
+}
+
+
+
 #include "kis_tool_select_contiguous.moc"
diff --git a/krita/plugins/tools/selectiontools/kis_tool_select_contiguous.h b/krita/plugins/tools/selectiontools/kis_tool_select_contiguous.h
index a470d82..048a16e 100644
--- a/krita/plugins/tools/selectiontools/kis_tool_select_contiguous.h
+++ b/krita/plugins/tools/selectiontools/kis_tool_select_contiguous.h
@@ -3,6 +3,7 @@
  *
  *  Copyright (c) 1999 Michael Koch <koch at kde.org>
  *  Copyright (c) 2002 Patrick Julien <freak at codepimps.org>
+ *  Copyright (c) 2015 Michael Abrahams <miabraha at gmail.com>
  *
  *  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
@@ -23,7 +24,7 @@
 #define __KIS_TOOL_SELECT_CONTIGUOUS_H__
 
 #include "KoToolFactoryBase.h"
-#include "krita/ui/tool/kis_tool_select_base.h"
+#include "kis_tool_select_base.h"
 #include <KoIcon.h>
 #include <kconfig.h>
 #include <kconfiggroup.h>
@@ -37,6 +38,8 @@ class KisToolSelectContiguous : public KisToolSelectBase
 {
 
     Q_OBJECT
+    Q_PROPERTY(int selectionAction READ selectionAction WRITE setSelectionAction NOTIFY selectionActionChanged)
+    Q_SIGNALS: void selectionActionChanged();
 
 public:
     KisToolSelectContiguous(KoCanvasBase *canvas);
@@ -57,6 +60,11 @@ public Q_SLOTS:
     virtual void slotSetSizemod(int);
     virtual void slotSetFeather(int);
     virtual void slotLimitToCurrentLayer(int);
+    //virtual bool antiAliasSelection();
+    void setSelectionAction(int newSelectionAction);
+
+protected:
+    using KisToolSelectBase::m_widgetHelper;
 
 private:
     int  m_fuzziness;
@@ -68,7 +76,6 @@ private:
 
 class KisToolSelectContiguousFactory : public KoToolFactoryBase
 {
-
 public:
     KisToolSelectContiguousFactory(const QStringList&)
             : KoToolFactoryBase("KisToolSelectContiguous") {
@@ -84,8 +91,6 @@ public:
     virtual KoToolBase * createTool(KoCanvasBase *canvas) {
         return new KisToolSelectContiguous(canvas);
     }
-
 };
 
 #endif //__KIS_TOOL_SELECT_CONTIGUOUS_H__
-
diff --git a/krita/plugins/tools/selectiontools/kis_tool_select_elliptical.cc b/krita/plugins/tools/selectiontools/kis_tool_select_elliptical.cc
index 999f1a0..e44ec43 100644
--- a/krita/plugins/tools/selectiontools/kis_tool_select_elliptical.cc
+++ b/krita/plugins/tools/selectiontools/kis_tool_select_elliptical.cc
@@ -3,6 +3,7 @@
  *
  *  Copyright (c) 2004 Boudewijn Rempt (boud at valdyas.org)
  *  Copyright (c) 2007 Sven Langkamp <sven.langkamp at gmail.com>
+ *  Copyright (c) 2015 Michael Abrahams <miabraha at gmail.com>
  *
  *  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
@@ -34,29 +35,15 @@
 #include "kis_selection_manager.h"
 #include "kis_system_locker.h"
 
-KisToolSelectElliptical::KisToolSelectElliptical(KoCanvasBase *canvas)
-    : KisToolEllipseBase(canvas, KisToolEllipseBase::SELECT, KisCursor::load("tool_elliptical_selection_cursor.png", 6, 6)),
-      m_widgetHelper(i18n("Elliptical Selection"))
-{
-}
-
-QWidget* KisToolSelectElliptical::createOptionWidget()
-{
-    KisCanvas2* canvas = dynamic_cast<KisCanvas2*>(this->canvas());
-    Q_ASSERT(canvas);
 
-    m_widgetHelper.createOptionWidget(canvas, this->toolId());
-    return m_widgetHelper.optionWidget();
-}
-
-void KisToolSelectElliptical::keyPressEvent(QKeyEvent *event)
+__KisToolSelectEllipticalLocal::__KisToolSelectEllipticalLocal(KoCanvasBase *canvas)
+    : KisToolEllipseBase(canvas, KisToolEllipseBase::SELECT,
+                         KisCursor::load("tool_elliptical_selection_cursor.png", 6, 6))
 {
-    if (!m_widgetHelper.processKeyPressEvent(event)) {
-        KisTool::keyPressEvent(event);
-    }
+    setObjectName("tool_select_elliptical");
 }
 
-void KisToolSelectElliptical::finishRect(const QRectF &rect)
+void __KisToolSelectEllipticalLocal::finishRect(const QRectF &rect)
 {
     KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
     Q_ASSERT(kisCanvas);
@@ -70,13 +57,13 @@ void KisToolSelectElliptical::finishRect(const QRectF &rect)
 
     KisSelectionToolHelper helper(kisCanvas, kundo2_i18n("Select Ellipse"));
 
-    if (m_widgetHelper.selectionMode() == PIXEL_SELECTION) {
+    if (selectionMode() == PIXEL_SELECTION) {
         KisPixelSelectionSP tmpSel = new KisPixelSelection();
 
         KisPainter painter(tmpSel);
         painter.setPaintColor(KoColor(Qt::black, tmpSel->colorSpace()));
         painter.setPaintOpPreset(currentPaintOpPreset(), currentNode(), currentImage());
-        painter.setAntiAliasPolygonFill(m_widgetHelper.optionWidget()->antiAliasSelection());
+        painter.setAntiAliasPolygonFill(antiAliasSelection());
         painter.setFillStyle(KisPainter::FillStyleForegroundColor);
         painter.setStrokeStyle(KisPainter::StrokeStyleNone);
 
@@ -86,7 +73,7 @@ void KisToolSelectElliptical::finishRect(const QRectF &rect)
         cache.addEllipse(rect);
         tmpSel->setOutlineCache(cache);
 
-        helper.selectPixelSelection(tmpSel, m_widgetHelper.selectionAction());
+        helper.selectPixelSelection(tmpSel, selectionAction());
     } else {
         QRectF ptRect = convertToPt(rect);
         KoShape* shape = KisShapeToolHelper::createEllipseShape(ptRect);
@@ -94,3 +81,24 @@ void KisToolSelectElliptical::finishRect(const QRectF &rect)
         helper.addSelectionShape(shape);
     }
 }
+
+
+KisToolSelectElliptical::KisToolSelectElliptical(KoCanvasBase *canvas):
+    KisToolSelectEllipticalTemplate(canvas, i18n("Elliptical Selection"))
+{
+    connect(&m_widgetHelper, SIGNAL(selectionActionChanged(int)), this, SLOT(setSelectionAction(int)));
+}
+
+void KisToolSelectElliptical::setSelectionAction(int newSelectionAction)
+{
+    if(newSelectionAction >= SELECTION_REPLACE && newSelectionAction <= SELECTION_INTERSECT && m_selectionAction != newSelectionAction)
+    {
+        if(m_widgetHelper.optionWidget())
+        {
+            m_widgetHelper.slotSetAction(newSelectionAction);
+        }
+        m_selectionAction = (SelectionAction)newSelectionAction;
+        emit selectionActionChanged();
+    }
+}
+
diff --git a/krita/plugins/tools/selectiontools/kis_tool_select_elliptical.h b/krita/plugins/tools/selectiontools/kis_tool_select_elliptical.h
index 7b2cd2f..86bbf9c 100644
--- a/krita/plugins/tools/selectiontools/kis_tool_select_elliptical.h
+++ b/krita/plugins/tools/selectiontools/kis_tool_select_elliptical.h
@@ -3,7 +3,8 @@
  *
  *  Copyright (c) 2000 John Califf <jcaliff at compuzone.net>
  *  Copyright (c) 2002 Patrick Julien <freak at codepimps.org>
- *  Copyright (c) 2004 Boudewijn Rempt <boud at valdyas.org>
+ *  Copyright (c) 2004 Boudewijn Rempt <boud at valdyas.org> * 
+ *  Copyright (c) 2015 Michael Abrahams <miabraha at gmail.com>
  *
  *  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
@@ -25,29 +26,48 @@
 
 #include "KoToolFactoryBase.h"
 #include "kis_tool_ellipse_base.h"
+#include <kis_tool_select_base.h>
 #include "kis_selection_tool_config_widget_helper.h"
 #include <KoIcon.h>
 #include <kshortcut.h>
 
-class KisToolSelectElliptical : public KisToolEllipseBase
+
+
+class __KisToolSelectEllipticalLocal : public KisToolEllipseBase
 {
     Q_OBJECT
 
 public:
-    KisToolSelectElliptical(KoCanvasBase *canvas);
-    QWidget* createOptionWidget();
+    __KisToolSelectEllipticalLocal(KoCanvasBase *canvas);
 
+protected:
+    virtual SelectionMode selectionMode() const = 0;
+    virtual SelectionAction selectionAction() const = 0;
+    virtual bool antiAliasSelection() const = 0;
 private:
-    void keyPressEvent(QKeyEvent *event);
     void finishRect(const QRectF &rect);
+};
+
+
+
+typedef SelectionActionHandler<__KisToolSelectEllipticalLocal> KisToolSelectEllipticalTemplate;
+
+class KisToolSelectElliptical : public KisToolSelectEllipticalTemplate
+{
+    Q_OBJECT
+    Q_PROPERTY(int selectionAction READ selectionAction WRITE setSelectionAction NOTIFY selectionActionChanged)
+public:
+    KisToolSelectElliptical(KoCanvasBase* canvas);
+
+    Q_SIGNALS: void selectionActionChanged();
+    public Q_SLOTS:
+    void setSelectionAction(int newSelectionAction);
+
 
-private:
-    KisSelectionToolConfigWidgetHelper m_widgetHelper;
 };
 
 class KisToolSelectEllipticalFactory : public KoToolFactoryBase
 {
-
 public:
     KisToolSelectEllipticalFactory(const QStringList&)
             : KoToolFactoryBase("KisToolSelectElliptical") {
@@ -62,14 +82,10 @@ public:
     virtual ~KisToolSelectEllipticalFactory() {}
 
     virtual KoToolBase * createTool(KoCanvasBase *canvas) {
-        return  new KisToolSelectElliptical(canvas);
+        return new KisToolSelectElliptical(canvas);
     }
 
 };
 
-
-
-
-
 #endif //__KIS_TOOL_SELECT_ELLIPTICAL_H__
 
diff --git a/krita/plugins/tools/selectiontools/kis_tool_select_outline.cc b/krita/plugins/tools/selectiontools/kis_tool_select_outline.cc
index 46cca47..a7aad1b 100644
--- a/krita/plugins/tools/selectiontools/kis_tool_select_outline.cc
+++ b/krita/plugins/tools/selectiontools/kis_tool_select_outline.cc
@@ -5,6 +5,7 @@
  *  Copyright (c) 2002 Patrick Julien <freak at codepimps.org>
  *  Copyright (c) 2004 Boudewijn Rempt <boud at valdyas.org>
  *  Copyright (c) 2007 Sven Langkamp <sven.langkamp at gmail.com>
+ *  Copyright (c) 2015 Michael Abrahams <miabraha at gmail.com>
  *
  *  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
@@ -51,13 +52,16 @@
 #include "kis_pixel_selection.h"
 #include "kis_selection_tool_helper.h"
 
+#define FEEDBACK_LINE_WIDTH 2
+
 
 KisToolSelectOutline::KisToolSelectOutline(KoCanvasBase * canvas)
         : KisToolSelectBase(canvas,
-                            KisCursor::load("tool_outline_selection_cursor.png", 5, 5),
-                            i18n("Outline Selection")),
+                  KisCursor::load("tool_outline_selection_cursor.png", 5, 5),
+                  i18n("Outline Selection")),
           m_paintPath(new QPainterPath())
 {
+    connect(&m_widgetHelper, SIGNAL(selectionActionChanged(int)), this, SLOT(setSelectionAction(int)));
 }
 
 KisToolSelectOutline::~KisToolSelectOutline()
@@ -67,6 +71,8 @@ KisToolSelectOutline::~KisToolSelectOutline()
 
 void KisToolSelectOutline::beginPrimaryAction(KoPointerEvent *event)
 {
+
+    KisToolSelectBase::beginPrimaryAction(event);
     if (!selectionEditable()) {
         event->ignore();
         return;
@@ -77,22 +83,27 @@ void KisToolSelectOutline::beginPrimaryAction(KoPointerEvent *event)
     m_points.clear();
     m_points.append(convertToPixelCoord(event));
     m_paintPath->moveTo(pixelToView(convertToPixelCoord(event)));
+
 }
 
 void KisToolSelectOutline::continuePrimaryAction(KoPointerEvent *event)
 {
     CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
+    KisToolSelectBase::continuePrimaryAction(event);
 
     QPointF point = convertToPixelCoord(event);
     m_paintPath->lineTo(pixelToView(point));
     m_points.append(point);
     updateFeedback();
+
+
 }
 
 void KisToolSelectOutline::endPrimaryAction(KoPointerEvent *event)
 {
     Q_UNUSED(event);
     CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
+    KisToolSelectBase::endPrimaryAction(event);
     setMode(KisTool::HOVER_MODE);
 
     KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
@@ -155,7 +166,7 @@ void KisToolSelectOutline::paint(QPainter& gc, const KoViewConverter &converter)
     }
 }
 
-#define FEEDBACK_LINE_WIDTH 2
+
 
 void KisToolSelectOutline::updateFeedback()
 {
@@ -175,8 +186,25 @@ void KisToolSelectOutline::deactivate()
     KIS_ASSERT_RECOVER_RETURN(kisCanvas);
     kisCanvas->updateCanvas();
 
-    KisToolSelectBase::deactivate();
+    KisTool::deactivate();
 }
 
+
+void KisToolSelectOutline::setSelectionAction(int newSelectionAction)
+{
+    if(newSelectionAction >= SELECTION_REPLACE && newSelectionAction <= SELECTION_INTERSECT && m_selectionAction != newSelectionAction)
+    {
+        if(m_widgetHelper.optionWidget())
+        {
+            m_widgetHelper.slotSetAction(newSelectionAction);
+        }
+        m_selectionAction = (SelectionAction)newSelectionAction;
+        emit selectionActionChanged();
+    }
+}
+
+
+
+
 #include "kis_tool_select_outline.moc"
 
diff --git a/krita/plugins/tools/selectiontools/kis_tool_select_outline.h b/krita/plugins/tools/selectiontools/kis_tool_select_outline.h
index 4756870..d7688de 100644
--- a/krita/plugins/tools/selectiontools/kis_tool_select_outline.h
+++ b/krita/plugins/tools/selectiontools/kis_tool_select_outline.h
@@ -4,6 +4,7 @@
  *  Copyright (c) 2000 John Califf <jcaliff at compuzone.net>
  *  Copyright (c) 2002 Patrick Julien <freak at codepimps.org>
  *  Copyright (c) 2004 Boudewijn Rempt <boud at valdyas.org>
+ *  Copyright (c) 2015 Michael Abrahams <miabraha at gmail.com>
  *
  *  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
@@ -33,19 +34,26 @@ class QPainterPath;
 class KisToolSelectOutline : public KisToolSelectBase
 {
 
+
     Q_OBJECT
+    Q_PROPERTY(int selectionAction READ selectionAction WRITE setSelectionAction NOTIFY selectionActionChanged)
+    Q_SIGNALS: void selectionActionChanged();
+
 public:
     KisToolSelectOutline(KoCanvasBase *canvas);
     virtual ~KisToolSelectOutline();
-
     void beginPrimaryAction(KoPointerEvent *event);
     void continuePrimaryAction(KoPointerEvent *event);
     void endPrimaryAction(KoPointerEvent *event);
-
     virtual void paint(QPainter& gc, const KoViewConverter &converter);
 
+
 public Q_SLOTS:
     virtual void deactivate();
+    void setSelectionAction(int newSelectionAction);
+
+protected:
+    using KisToolSelectBase::m_widgetHelper;
 
 private:
     void updateFeedback();
@@ -53,13 +61,10 @@ private:
 
     QPainterPath * m_paintPath;
     vQPointF m_points;
-
 };
 
-
 class KisToolSelectOutlineFactory : public KoToolFactoryBase
 {
-
 public:
     KisToolSelectOutlineFactory(const QStringList&)
             : KoToolFactoryBase("KisToolSelectOutline") {
diff --git a/krita/plugins/tools/selectiontools/kis_tool_select_path.cc b/krita/plugins/tools/selectiontools/kis_tool_select_path.cc
index 9f1a65c..b7697ec 100644
--- a/krita/plugins/tools/selectiontools/kis_tool_select_path.cc
+++ b/krita/plugins/tools/selectiontools/kis_tool_select_path.cc
@@ -32,20 +32,21 @@
 
 
 KisToolSelectPath::KisToolSelectPath(KoCanvasBase * canvas)
-    : DelegatedSelectPathTool(canvas,
+    : SelectionActionHandler<KisDelegatedSelectPathWrapper>(canvas,
                               KisCursor::load("tool_polygonal_selection_cursor.png", 6, 6),
-                              new __KisToolSelectPathLocalTool(canvas, this))
+			      i18n("Select path"),
+                              (KisTool*) (new __KisToolSelectPathLocalTool(canvas, this)))
 {
 }
 
 void KisToolSelectPath::requestStrokeEnd()
 {
-    localTool()->endPathWithoutLastPoint();
+     localTool()->endPathWithoutLastPoint();
 }
 
 void KisToolSelectPath::requestStrokeCancellation()
 {
-    localTool()->cancelPath();
+ localTool()->cancelPath();
 }
 
 void KisToolSelectPath::mousePressEvent(KoPointerEvent* event)
@@ -62,6 +63,30 @@ QList<QPointer<QWidget> > KisToolSelectPath::createOptionWidgets()
     return widgetsList;
 }
 
+void KisToolSelectPath::setAlternateSelectionAction(SelectionAction action)
+{
+    // We will turn off the ability to change the selection in the middle of drawing a path.
+    if (!m_localTool->listeningToModifiers()) {
+      SelectionActionHandler<KisDelegatedSelectPathWrapper>::setAlternateSelectionAction(action);
+    }
+}
+
+
+bool KisDelegatedSelectPathWrapper::listeningToModifiers() {
+  return m_localTool->listeningToModifiers();
+}
+
+void KisDelegatedSelectPathWrapper::beginPrimaryAction(KoPointerEvent *event) {
+ mousePressEvent(event);
+}
+
+void KisDelegatedSelectPathWrapper::continuePrimaryAction(KoPointerEvent *event){
+ mouseMoveEvent(event);
+}
+
+void KisDelegatedSelectPathWrapper::endPrimaryAction(KoPointerEvent *event) {
+ mouseReleaseEvent(event);
+}
 
  __KisToolSelectPathLocalTool::__KisToolSelectPathLocalTool(KoCanvasBase * canvas, KisToolSelectPath* parentTool)
      : KoCreatePathTool(canvas), m_selectionTool(parentTool)
@@ -119,4 +144,5 @@ void __KisToolSelectPathLocalTool::addPathShape(KoPathShape* pathShape)
     }
 }
 
+
 #include "kis_tool_select_path.moc"
diff --git a/krita/plugins/tools/selectiontools/kis_tool_select_path.h b/krita/plugins/tools/selectiontools/kis_tool_select_path.h
index a67b584..f140de1 100644
--- a/krita/plugins/tools/selectiontools/kis_tool_select_path.h
+++ b/krita/plugins/tools/selectiontools/kis_tool_select_path.h
@@ -1,5 +1,6 @@
 /*
  *  Copyright (c) 2007 Sven Langkamp <sven.langkamp at gmail.com>
+ *  Copyright (c) 2015 Michael Abrahams <miabraha at gmail.com>
  *
  *  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
@@ -45,22 +46,29 @@ private:
     KisToolSelectPath* const m_selectionTool;
 };
 
-struct __KisToolSelectBaseWrapper : public KisToolSelectBase {
-    __KisToolSelectBaseWrapper(KoCanvasBase *canvas,
-                               const QCursor &cursor)
-        : KisToolSelectBase(canvas, cursor, i18n("Path Selection"))
+typedef KisDelegatedTool<KisTool, __KisToolSelectPathLocalTool,
+                         DeselectShapesActivationPolicy> DelegatedSelectPathTool;
+
+struct KisDelegatedSelectPathWrapper : public DelegatedSelectPathTool {
+        KisDelegatedSelectPathWrapper(KoCanvasBase *canvas,
+              const QCursor &cursor,
+              KisTool* delegateTool)
+        : DelegatedSelectPathTool(canvas, cursor, (__KisToolSelectPathLocalTool*) delegateTool)
     {
     }
+          bool listeningToModifiers();
+
+          // If an event is explicitly forwarded only as an action (e.g. shift-click is captured by "change size")
+          // we will receive a primary action but no mousePressEvent.  Thus these events must be explicitly forwarded.
+          void beginPrimaryAction(KoPointerEvent *event);
+          void continuePrimaryAction(KoPointerEvent *event);
+          void endPrimaryAction(KoPointerEvent *event);
 };
 
-typedef KisDelegatedTool<__KisToolSelectBaseWrapper,
-                         __KisToolSelectPathLocalTool,
-                         DeselectShapesActivationPolicy> DelegatedSelectPathTool;
 
-class KisToolSelectPath : public DelegatedSelectPathTool
+class KisToolSelectPath : public SelectionActionHandler<KisDelegatedSelectPathWrapper>
 {
     Q_OBJECT
-
 public:
     KisToolSelectPath(KoCanvasBase * canvas);
     void mousePressEvent(KoPointerEvent* event);
@@ -68,14 +76,13 @@ public:
 protected:
     void requestStrokeCancellation();
     void requestStrokeEnd();
-
+    void setAlternateSelectionAction(SelectionAction action);
     friend class __KisToolSelectPathLocalTool;
     QList<QPointer<QWidget> > createOptionWidgets();
 };
 
 class KisToolSelectPathFactory : public KoToolFactoryBase
 {
-
 public:
     KisToolSelectPathFactory(const QStringList&)
             : KoToolFactoryBase("KisToolSelectPath") {
@@ -96,4 +103,3 @@ public:
 
 
 #endif // KIS_TOOL_SELECT_PATH_H_
-
diff --git a/krita/plugins/tools/selectiontools/kis_tool_select_polygonal.cc b/krita/plugins/tools/selectiontools/kis_tool_select_polygonal.cc
index 9acca50..791b6c6 100644
--- a/krita/plugins/tools/selectiontools/kis_tool_select_polygonal.cc
+++ b/krita/plugins/tools/selectiontools/kis_tool_select_polygonal.cc
@@ -5,6 +5,7 @@
  *  Copyright (c) 2002 Patrick Julien <freak at codepimps.org>
  *  Copyright (c) 2004 Boudewijn Rempt <boud at valdyas.org>
  *  Copyright (c) 2007 Sven Langkamp <sven.langkamp at gmail.com>
+ *  Copyright (c) 2015 Michael Abrahams <miabraha at gmail.com>
  *
  *  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
@@ -34,52 +35,19 @@
 #include "kis_shape_tool_helper.h"
 
 #include "kis_system_locker.h"
+#include "KisViewManager.h"
+#include "kis_selection_manager.h"
 
 
-KisToolSelectPolygonal::KisToolSelectPolygonal(KoCanvasBase *canvas)
+__KisToolSelectPolygonalLocal::__KisToolSelectPolygonalLocal(KoCanvasBase *canvas)
     : KisToolPolylineBase(canvas, KisToolPolylineBase::SELECT,
-                          KisCursor::load("tool_polygonal_selection_cursor.png", 6, 6)),
-      m_widgetHelper(i18n("Polygonal Selection"))
+                          KisCursor::load("tool_polygonal_selection_cursor.png", 6, 6))
 {
     setObjectName("tool_select_polygonal");
-    connect(&m_widgetHelper, SIGNAL(selectionActionChanged(int)), this, SLOT(setSelectionAction(int)));
-}
-
-SelectionAction KisToolSelectPolygonal::selectionAction() const
-{
-    return m_selectionAction;
-}
-
-void KisToolSelectPolygonal::setSelectionAction(int newSelectionAction)
-{
-    if(newSelectionAction >= SELECTION_REPLACE && newSelectionAction <= SELECTION_INTERSECT && m_selectionAction != newSelectionAction)
-    {
-        if(m_widgetHelper.optionWidget())
-        {
-            m_widgetHelper.slotSetAction(newSelectionAction);
-        }
-        m_selectionAction = (SelectionAction)newSelectionAction;
-        emit selectionActionChanged();
-    }
 }
 
-QWidget* KisToolSelectPolygonal::createOptionWidget()
-{
-    KisCanvas2* canvas = dynamic_cast<KisCanvas2*>(this->canvas());
-    Q_ASSERT(canvas);
-
-    m_widgetHelper.createOptionWidget(canvas, this->toolId());
-    return m_widgetHelper.optionWidget();
-}
 
-void KisToolSelectPolygonal::keyPressEvent(QKeyEvent *event)
-{
-    if (!m_widgetHelper.processKeyPressEvent(event)) {
-        KisToolPolylineBase::keyPressEvent(event);
-    }
-}
-
-void KisToolSelectPolygonal::finishPolyline(const QVector<QPointF> &points)
+void __KisToolSelectPolygonalLocal::finishPolyline(const QVector<QPointF> &points)
 {
     KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
     Q_ASSERT(kisCanvas);
@@ -88,13 +56,13 @@ void KisToolSelectPolygonal::finishPolyline(const QVector<QPointF> &points)
 
     KisSelectionToolHelper helper(kisCanvas, kundo2_i18n("Select Polygon"));
 
-    if (m_widgetHelper.selectionMode() == PIXEL_SELECTION) {
+    if (selectionMode() == PIXEL_SELECTION) {
         KisPixelSelectionSP tmpSel = new KisPixelSelection();
 
         KisPainter painter(tmpSel);
         painter.setPaintColor(KoColor(Qt::black, tmpSel->colorSpace()));
         painter.setPaintOpPreset(currentPaintOpPreset(), currentNode(), currentImage());
-        painter.setAntiAliasPolygonFill(m_widgetHelper.optionWidget()->antiAliasSelection());
+        painter.setAntiAliasPolygonFill(antiAliasSelection());
         painter.setFillStyle(KisPainter::FillStyleForegroundColor);
         painter.setStrokeStyle(KisPainter::StrokeStyleNone);
 
@@ -105,7 +73,7 @@ void KisToolSelectPolygonal::finishPolyline(const QVector<QPointF> &points)
         cache.closeSubpath();
         tmpSel->setOutlineCache(cache);
 
-        helper.selectPixelSelection(tmpSel, m_widgetHelper.selectionAction());
+        helper.selectPixelSelection(tmpSel, selectionAction());
     } else {
         KoPathShape* path = new KoPathShape();
         path->setShapeId(KoPathShapeId);
@@ -121,3 +89,24 @@ void KisToolSelectPolygonal::finishPolyline(const QVector<QPointF> &points)
         helper.addSelectionShape(path);
     }
 }
+
+
+KisToolSelectPolygonal::KisToolSelectPolygonal(KoCanvasBase *canvas):
+    SelectionActionHandler<__KisToolSelectPolygonalLocal>(canvas, i18n("Polygonal Selection"))
+{
+    connect(&m_widgetHelper, SIGNAL(selectionActionChanged(int)), this, SLOT(setSelectionAction(int)));
+}
+
+void KisToolSelectPolygonal::setSelectionAction(int newSelectionAction)
+{
+    if(newSelectionAction >= SELECTION_REPLACE && newSelectionAction <= SELECTION_INTERSECT && m_selectionAction != newSelectionAction)
+    {
+        if(m_widgetHelper.optionWidget())
+        {
+            m_widgetHelper.slotSetAction(newSelectionAction);
+        }
+        m_selectionAction = (SelectionAction)newSelectionAction;
+        emit selectionActionChanged();
+    }
+}
+
diff --git a/krita/plugins/tools/selectiontools/kis_tool_select_polygonal.h b/krita/plugins/tools/selectiontools/kis_tool_select_polygonal.h
index feee9cb..397ec63 100644
--- a/krita/plugins/tools/selectiontools/kis_tool_select_polygonal.h
+++ b/krita/plugins/tools/selectiontools/kis_tool_select_polygonal.h
@@ -4,6 +4,7 @@
  *  Copyright (c) 2000 John Califf <jcaliff at compuzone.net>
  *  Copyright (c) 2002 Patrick Julien <freak at codepimps.org>
  *  Copyright (c) 2004 Boudewijn Rempt <boud at valdyas.org>
+ *  Copyright (c) 2015 Michael Abrahams <miabraha at gmail.com>
  *
  *  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
@@ -25,37 +26,41 @@
 
 #include "KoToolFactoryBase.h"
 #include "kis_tool_polyline_base.h"
+#include <kis_tool_select_base.h>
 #include "kis_selection_tool_config_widget_helper.h"
 #include <KoIcon.h>
 
 
-class KisToolSelectPolygonal : public KisToolPolylineBase
+class __KisToolSelectPolygonalLocal : public KisToolPolylineBase
 {
     Q_OBJECT
-    Q_PROPERTY(int selectionAction READ selectionAction WRITE setSelectionAction NOTIFY selectionActionChanged);
 public:
-    KisToolSelectPolygonal(KoCanvasBase *canvas);
-    QWidget* createOptionWidget();
-    SelectionAction selectionAction() const;
-
-public Q_SLOTS:
-    void setSelectionAction(int newSelectionAction);
-
-Q_SIGNALS:
-    void selectionActionChanged();
-
+    __KisToolSelectPolygonalLocal(KoCanvasBase *canvas);
+protected:
+    virtual SelectionMode selectionMode() const = 0;
+    virtual SelectionAction selectionAction() const = 0;
+    virtual bool antiAliasSelection() const = 0;
 private:
-    void keyPressEvent(QKeyEvent *event);
     void finishPolyline(const QVector<QPointF> &points);
 private:
-    KisSelectionToolConfigWidgetHelper m_widgetHelper;
-    SelectionAction m_selectionAction;
 };
 
+class KisToolSelectPolygonal : public SelectionActionHandler<__KisToolSelectPolygonalLocal>
+{
+    Q_OBJECT
+    Q_PROPERTY(int selectionAction READ selectionAction WRITE setSelectionAction NOTIFY selectionActionChanged)
+public:
+    KisToolSelectPolygonal(KoCanvasBase* canvas);
+
+    Q_SIGNALS: void selectionActionChanged();
+    public Q_SLOTS:
+    void setSelectionAction(int newSelectionAction);
+};
+
+
 
 class KisToolSelectPolygonalFactory : public KoToolFactoryBase
 {
-
 public:
     KisToolSelectPolygonalFactory(const QStringList&)
             : KoToolFactoryBase("KisToolSelectPolygonal") {
diff --git a/krita/plugins/tools/selectiontools/kis_tool_select_rectangular.cc b/krita/plugins/tools/selectiontools/kis_tool_select_rectangular.cc
index 331c6a4..d81f80c 100644
--- a/krita/plugins/tools/selectiontools/kis_tool_select_rectangular.cc
+++ b/krita/plugins/tools/selectiontools/kis_tool_select_rectangular.cc
@@ -2,9 +2,10 @@
  *  kis_tool_select_rectangular.cc -- part of Krita
  *
  *  Copyright (c) 1999 Michael Koch <koch at kde.org>
- *                2001 John Califf <jcaliff at compuzone.net>
- *                2002 Patrick Julien <freak at codepimps.org>
+ *  Copyright (c) 2001 John Califf <jcaliff at compuzone.net>
+ *  Copyright (c) 2002 Patrick Julien <freak at codepimps.org>
  *  Copyright (c) 2007 Sven Langkamp <sven.langkamp at gmail.com>
+ *  Copyright (c) 2015 Michael Abrahams <miabraha at gmail.com>
  *
  *  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
@@ -36,50 +37,14 @@
 #include "kis_selection_manager.h"
 
 
-KisToolSelectRectangular::KisToolSelectRectangular(KoCanvasBase * canvas)
+__KisToolSelectRectangularLocal::__KisToolSelectRectangularLocal(KoCanvasBase * canvas)
     : KisToolRectangleBase(canvas, KisToolRectangleBase::SELECT,
-                           KisCursor::load("tool_rectangular_selection_cursor.png", 6, 6)),
-      m_widgetHelper(i18n("Rectangular Selection"))
+                           KisCursor::load("tool_rectangular_selection_cursor.png", 6, 6))
 {
-    connect(&m_widgetHelper, SIGNAL(selectionActionChanged(int)), this, SLOT(setSelectionAction(int)));
-}
-
-SelectionAction KisToolSelectRectangular::selectionAction() const
-{
-    return m_selectionAction;
-}
-
-void KisToolSelectRectangular::setSelectionAction(int newSelectionAction)
-{
-    if(newSelectionAction >= SELECTION_REPLACE && newSelectionAction <= SELECTION_INTERSECT && m_selectionAction != newSelectionAction)
-    {
-        if(m_widgetHelper.optionWidget())
-        {
-            m_widgetHelper.slotSetAction(newSelectionAction);
-        }
-        m_selectionAction = (SelectionAction)newSelectionAction;
-        emit selectionActionChanged();
-    }
+        setObjectName("tool_select_rectangular");
 }
 
-QWidget* KisToolSelectRectangular::createOptionWidget()
-{
-    KisCanvas2* canvas = dynamic_cast<KisCanvas2*>(this->canvas());
-    Q_ASSERT(canvas);
-
-    m_widgetHelper.createOptionWidget(canvas, this->toolId());
-    m_widgetHelper.optionWidget()->disableAntiAliasSelectionOption();
-    return m_widgetHelper.optionWidget();
-}
-
-void KisToolSelectRectangular::keyPressEvent(QKeyEvent *event)
-{
-    if (!m_widgetHelper.processKeyPressEvent(event)) {
-        KisTool::keyPressEvent(event);
-    }
-}
-
-void KisToolSelectRectangular::finishRect(const QRectF& rect)
+void __KisToolSelectRectangularLocal::finishRect(const QRectF& rect)
 {
     KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
     if (!kisCanvas)
@@ -97,7 +62,7 @@ void KisToolSelectRectangular::finishRect(const QRectF& rect)
         return;
     }
 
-    if (m_widgetHelper.selectionMode() == PIXEL_SELECTION) {
+    if (selectionMode() == PIXEL_SELECTION) {
         if (rc.isValid()) {
             KisPixelSelectionSP tmpSel = KisPixelSelectionSP(new KisPixelSelection());
             tmpSel->select(rc);
@@ -106,10 +71,30 @@ void KisToolSelectRectangular::finishRect(const QRectF& rect)
             cache.addRect(rc);
             tmpSel->setOutlineCache(cache);
 
-            helper.selectPixelSelection(tmpSel, m_widgetHelper.selectionAction());
+            helper.selectPixelSelection(tmpSel, selectionAction());
         }
     } else {
         QRectF documentRect = convertToPt(rc);
         helper.addSelectionShape(KisShapeToolHelper::createRectangleShape(documentRect));
     }
 }
+
+KisToolSelectRectangular::KisToolSelectRectangular(KoCanvasBase *canvas):
+    SelectionActionHandler<__KisToolSelectRectangularLocal>(canvas, i18n("Rectangular Selection"))
+{
+    connect(&m_widgetHelper, SIGNAL(selectionActionChanged(int)), this, SLOT(setSelectionAction(int)));
+}
+
+
+void KisToolSelectRectangular::setSelectionAction(int newSelectionAction)
+{
+    if(newSelectionAction >= SELECTION_REPLACE && newSelectionAction <= SELECTION_INTERSECT && m_selectionAction != newSelectionAction)
+    {
+        if(m_widgetHelper.optionWidget())
+        {
+            m_widgetHelper.slotSetAction(newSelectionAction);
+        }
+        m_selectionAction = (SelectionAction)newSelectionAction;
+        emit selectionActionChanged();
+    }
+}
diff --git a/krita/plugins/tools/selectiontools/kis_tool_select_rectangular.h b/krita/plugins/tools/selectiontools/kis_tool_select_rectangular.h
index 5e88766..2fe5b88 100644
--- a/krita/plugins/tools/selectiontools/kis_tool_select_rectangular.h
+++ b/krita/plugins/tools/selectiontools/kis_tool_select_rectangular.h
@@ -4,6 +4,7 @@
  *  Copyright (c) 1999 Michael Koch <koch at kde.org>
  *                2002 Patrick Julien <freak at codepimps.org>
  *
+ *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
@@ -24,36 +25,39 @@
 
 #include "KoToolFactoryBase.h"
 #include "kis_tool_rectangle_base.h"
+#include <kis_tool_select_base.h>
 #include "kis_selection_tool_config_widget_helper.h"
 #include <KoIcon.h>
 #include <kshortcut.h>
 
 
-class KisToolSelectRectangular : public KisToolRectangleBase
+class __KisToolSelectRectangularLocal : public KisToolRectangleBase
 {
     Q_OBJECT
-    Q_PROPERTY(int selectionAction READ selectionAction WRITE setSelectionAction NOTIFY selectionActionChanged);
-public:
-    KisToolSelectRectangular(KoCanvasBase * canvas);
-    QWidget* createOptionWidget();
-    SelectionAction selectionAction() const;
 
-public Q_SLOTS:
-    void setSelectionAction(int newSelectionAction);
+public:
+    __KisToolSelectRectangularLocal(KoCanvasBase * canvas);
 
-Q_SIGNALS:
-    void selectionActionChanged();
+protected:
+    virtual SelectionMode selectionMode() const = 0;
+    virtual SelectionAction selectionAction() const = 0;
 
 private:
-    void keyPressEvent(QKeyEvent *event);
     void finishRect(const QRectF& rect);
-
-private:
-    KisSelectionToolConfigWidgetHelper m_widgetHelper;
-    SelectionAction m_selectionAction;
 };
 
 
+class KisToolSelectRectangular : public SelectionActionHandler<__KisToolSelectRectangularLocal>
+{
+    Q_OBJECT
+    Q_PROPERTY(int selectionAction READ selectionAction WRITE setSelectionAction NOTIFY selectionActionChanged)
+public:
+    KisToolSelectRectangular(KoCanvasBase* canvas);
+    Q_SIGNALS: void selectionActionChanged();
+public Q_SLOTS:
+    void setSelectionAction(int newSelectionAction);
+};
+
 class KisToolSelectRectangularFactory : public KoToolFactoryBase
 {
 
@@ -71,7 +75,7 @@ public:
     virtual ~KisToolSelectRectangularFactory() {}
 
     virtual KoToolBase * createTool(KoCanvasBase *canvas) {
-        return  new KisToolSelectRectangular(canvas);
+        return new KisToolSelectRectangular(canvas);
     }
 };
 
diff --git a/krita/plugins/tools/selectiontools/kis_tool_select_similar.cc b/krita/plugins/tools/selectiontools/kis_tool_select_similar.cc
index b2c51d9..70dcf06 100644
--- a/krita/plugins/tools/selectiontools/kis_tool_select_similar.cc
+++ b/krita/plugins/tools/selectiontools/kis_tool_select_similar.cc
@@ -2,6 +2,7 @@
  *  Copyright (c) 1999 Matthias Elter <me at kde.org>
  *  Copyright (c) 2002 Patrick Julien <freak at codepimps.org>
  *  Copyright (c) 2005 Boudewijn Rempt <boud at valdyas.org>
+ *  Copyright (c) 2015 Michael Abrahams <miabraha at gmail.com>
  *
  *  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
@@ -42,7 +43,6 @@ void selectByColor(KisPaintDeviceSP dev, KisPixelSelectionSP selection, const qu
     if (rc.isEmpty()) {
         return;
     }
-
     // XXX: Multithread this!
     qint32 x, y, w, h;
     x = rc.x();
@@ -59,26 +59,26 @@ void selectByColor(KisPaintDeviceSP dev, KisPixelSelectionSP selection, const qu
         do {
             //if (dev->colorSpace()->hasAlpha())
             //    opacity = dev->colorSpace()->alpha(hiter->rawData());
-
             quint8 match = cs->difference(c, hiter->oldRawData());
-
             if (match <= fuzziness) {
                 *(selIter->rawData()) = MAX_SELECTED;
             }
-        } while (hiter->nextPixel() && selIter->nextPixel());
-
+        }
+        while (hiter->nextPixel() && selIter->nextPixel());
         hiter->nextRow();
         selIter->nextRow();
     }
 
 }
 
+
 KisToolSelectSimilar::KisToolSelectSimilar(KoCanvasBase * canvas)
     : KisToolSelectBase(canvas,
                         KisCursor::load("tool_similar_selection_cursor.png", 6, 6),
                         i18n("Similar Color Selection")),
-      m_fuzziness(20)
+     m_fuzziness(20)
 {
+    connect(&m_widgetHelper, SIGNAL(selectionActionChanged(int)), this, SLOT(setSelectionAction(int)));
 }
 
 void KisToolSelectSimilar::activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes)
@@ -89,6 +89,7 @@ void KisToolSelectSimilar::activate(ToolActivation toolActivation, const QSet<Ko
 
 void KisToolSelectSimilar::beginPrimaryAction(KoPointerEvent *event)
 {
+    KisToolSelectBase::beginPrimaryAction(event);
     KisPaintDeviceSP dev;
 
     if (!currentNode() ||
@@ -126,7 +127,8 @@ void KisToolSelectSimilar::beginPrimaryAction(KoPointerEvent *event)
     KisSelectionToolHelper helper(kisCanvas, kundo2_i18n("Select Similar Color"));
     helper.selectPixelSelection(tmpSel, selectionAction());
 
-    QApplication::restoreOverrideCursor();
+    QApplication::restoreOverrideCursor();                        
+
 }
 
 void KisToolSelectSimilar::slotSetFuzziness(int fuzziness)
@@ -159,6 +161,19 @@ QWidget* KisToolSelectSimilar::createOptionWidget()
 
     // load setting from config
     input->setValue(m_configGroup.readEntry("fuzziness", 20));
-
     return selectionWidget;
 }
+
+void KisToolSelectSimilar::setSelectionAction(int newSelectionAction)
+{
+    if(newSelectionAction >= SELECTION_REPLACE && newSelectionAction <= SELECTION_INTERSECT && m_selectionAction != newSelectionAction)
+    {
+      if(m_widgetHelper.optionWidget())
+      {
+          m_widgetHelper.slotSetAction(newSelectionAction);
+      }
+      m_selectionAction = (SelectionAction)newSelectionAction;
+      emit selectionActionChanged();
+    }
+}
+
diff --git a/krita/plugins/tools/selectiontools/kis_tool_select_similar.h b/krita/plugins/tools/selectiontools/kis_tool_select_similar.h
index f701986..0beada4 100644
--- a/krita/plugins/tools/selectiontools/kis_tool_select_similar.h
+++ b/krita/plugins/tools/selectiontools/kis_tool_select_similar.h
@@ -1,5 +1,6 @@
 /*
  *  Copyright (c) 2004 Boudewijn Rempt (boud at valdyas.org)
+ *  Copyright (c) 2015 Michael Abrahams <miabraha at gmail.com>
  *
  *  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
@@ -19,33 +20,43 @@
 #define KIS_TOOL_SELECT_SIMILAR_H_
 
 #include <KoToolFactoryBase.h>
-#include "kis_tool_select_base.h"
 #include <KoIcon.h>
 #include <kconfig.h>
+#include "kis_tool_select_base.h"
 #include <kconfiggroup.h>
 
-/**
+
+
+/*
  * Tool to select colors by pointing at a color on the image.
  */
-class KisToolSelectSimilar : public KisToolSelectBase
+class KisToolSelectSimilar: public KisToolSelectBase
 {
     Q_OBJECT
+    Q_PROPERTY(int selectionAction READ selectionAction WRITE setSelectionAction NOTIFY selectionActionChanged)
+    Q_SIGNALS: void selectionActionChanged();
 
 public:
     KisToolSelectSimilar(KoCanvasBase * canvas);
     void beginPrimaryAction(KoPointerEvent *event);
-    QWidget* createOptionWidget();
     void paint(QPainter&, const KoViewConverter &) {}
+    QWidget* createOptionWidget();
 
-public Q_SLOTS:
-    virtual void activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes);
-    virtual void slotSetFuzziness(int);
+
+    public Q_SLOTS:    
+    void activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes);
+    void setSelectionAction(int newSelectionAction);
+    void slotSetFuzziness(int);
+
+protected:
+    using KisToolSelectBase::m_widgetHelper;
 
 private:
     int m_fuzziness;
     KConfigGroup m_configGroup;
 };
 
+
 class KisToolSelectSimilarFactory : public KoToolFactoryBase
 {
 public:
@@ -57,13 +68,10 @@ public:
         setIconName(koIconNameCStr("tool_similar_selection"));
         setPriority(57);
     }
-
     virtual ~KisToolSelectSimilarFactory() {}
-
     virtual KoToolBase * createTool(KoCanvasBase *canvas) {
         return  new KisToolSelectSimilar(canvas);
     }
-
 };
 
 
diff --git a/krita/ui/CMakeLists.txt b/krita/ui/CMakeLists.txt
index a2a293e..4588a7e 100644
--- a/krita/ui/CMakeLists.txt
+++ b/krita/ui/CMakeLists.txt
@@ -177,7 +177,6 @@ set(kritaui_LIB_SRCS
     tool/kis_recording_adapter.cpp
     tool/kis_tool_paint.cc
     tool/kis_tool_shape.cc
-    tool/kis_tool_select_base.cpp
     tool/kis_tool_ellipse_base.cpp
     tool/kis_tool_rectangle_base.cpp
     tool/kis_tool_polyline_base.cpp
diff --git a/krita/ui/canvas/kis_tool_proxy.h b/krita/ui/canvas/kis_tool_proxy.h
index dacadd8..61503cd 100644
--- a/krita/ui/canvas/kis_tool_proxy.h
+++ b/krita/ui/canvas/kis_tool_proxy.h
@@ -27,9 +27,9 @@ class KisToolProxy : public KoToolProxy
 {
 public:
     enum ActionState {
-        BEGIN,
-        CONTINUE,
-        END
+        BEGIN,  /**< Beginning an action */
+        CONTINUE, /**< Continuing an action */
+        END /**< Ending an action */
     };
 
 public:
@@ -41,6 +41,11 @@ public:
      * Forwards the event to the active tool and returns true if the
      * event 'was not ignored'.  That is by default the event is
      * considered accepted, but the tool can explicitly ignore it.
+     * @param state beginning, continuing, or ending the action.
+     * @param action alternate tool action requested.
+     * @param event the event being sent to the tool by the AbstractInputAction.
+     * @param originalEvent the original event received by the AbstractInputAction.
+     * @param lastTabletEvent The event object for the last tablet event.
      */
     bool forwardEvent(ActionState state, KisTool::ToolAction action, QEvent *event, QEvent *originalEvent, QTabletEvent *lastTabletEvent);
     bool primaryActionSupportsHiResEvents() const;
diff --git a/krita/ui/input/kis_alternate_invocation_action.cpp b/krita/ui/input/kis_alternate_invocation_action.cpp
index 48723bf..09107ce 100644
--- a/krita/ui/input/kis_alternate_invocation_action.cpp
+++ b/krita/ui/input/kis_alternate_invocation_action.cpp
@@ -43,6 +43,7 @@ KisAlternateInvocationAction::KisAlternateInvocationAction()
     shortcuts.insert(i18n("Primary Mode"), PrimaryAlternateModeShortcut);
     shortcuts.insert(i18n("Secondary Mode"), SecondaryAlternateModeShortcut);
 
+
     shortcuts.insert(i18n("Pick Foreground Color from Current Layer"), PickColorFgLayerModeShortcut);
     shortcuts.insert(i18n("Pick Background Color from Current Layer"), PickColorBgLayerModeShortcut);
 
diff --git a/krita/ui/input/kis_input_manager.cpp b/krita/ui/input/kis_input_manager.cpp
index 08a056e..5e3f060 100644
--- a/krita/ui/input/kis_input_manager.cpp
+++ b/krita/ui/input/kis_input_manager.cpp
@@ -987,4 +987,3 @@ void KisInputManager::profileChanged()
         kWarning() << "No Input Profile Found: canvas interaction will be impossible";
     }
 }
-
diff --git a/krita/ui/tool/kis_delegated_tool.h b/krita/ui/tool/kis_delegated_tool.h
index 20c690a..8e7ef31 100644
--- a/krita/ui/tool/kis_delegated_tool.h
+++ b/krita/ui/tool/kis_delegated_tool.h
@@ -120,7 +120,7 @@ public:
         return list;
     }
 
-private:
+protected:
     QScopedPointer<DelegateTool> m_localTool;
 };
 
diff --git a/krita/ui/tool/kis_tool.cc b/krita/ui/tool/kis_tool.cc
index f336241..86b35f1 100644
--- a/krita/ui/tool/kis_tool.cc
+++ b/krita/ui/tool/kis_tool.cc
@@ -789,6 +789,14 @@ bool KisTool::selectionEditable()
     return editable;
 }
 
+void KisTool::listenToModifiers(bool listen)
+{
+}
+
+bool KisTool::listeningToModifiers()
+{
+    return false;
+}
 
 #include "kis_tool.moc"
 
diff --git a/krita/ui/tool/kis_tool.h b/krita/ui/tool/kis_tool.h
index b4fe908..d11f6a2 100644
--- a/krita/ui/tool/kis_tool.h
+++ b/krita/ui/tool/kis_tool.h
@@ -261,6 +261,17 @@ protected:
 
     virtual QWidget* createOptionWidget();
 
+    /**
+     * To determine whether this tool will change its behavior when
+     * modifier keys are pressed
+     */
+    virtual bool listeningToModifiers();
+    /**
+     * Request that this tool no longer listen to modifier keys
+     * (Responding to the request is optional)
+     */
+    virtual void listenToModifiers(bool listen);
+
 protected:
     KisImageWSP image() const;
     QCursor cursor() const;
@@ -355,4 +366,3 @@ private:
 
 
 #endif // KIS_TOOL_H_
-
diff --git a/krita/ui/tool/kis_tool_paint.h b/krita/ui/tool/kis_tool_paint.h
index 4c42ef9..36d6368 100644
--- a/krita/ui/tool/kis_tool_paint.h
+++ b/krita/ui/tool/kis_tool_paint.h
@@ -71,6 +71,10 @@ public:
     virtual ~KisToolPaint();
     virtual int flags() const;
 
+    virtual void mousePressEvent(KoPointerEvent *event);
+    virtual void mouseReleaseEvent(KoPointerEvent *event);
+    virtual void mouseMoveEvent(KoPointerEvent *event);
+
 protected:
 
     void setMode(ToolMode mode);
@@ -89,10 +93,6 @@ protected:
     virtual void continueAlternateAction(KoPointerEvent *event, AlternateAction action);
     virtual void endAlternateAction(KoPointerEvent *event, AlternateAction action);
 
-    virtual void mousePressEvent(KoPointerEvent *event);
-    virtual void mouseReleaseEvent(KoPointerEvent *event);
-    virtual void mouseMoveEvent(KoPointerEvent *event);
-
     virtual void requestUpdateOutline(const QPointF &outlineDocPoint, const KoPointerEvent *event);
 
     /** If the paint tool support outline like brushes, set to true.
diff --git a/krita/ui/tool/kis_tool_polyline_base.cpp b/krita/ui/tool/kis_tool_polyline_base.cpp
index 6071f76..0c2eef6 100644
--- a/krita/ui/tool/kis_tool_polyline_base.cpp
+++ b/krita/ui/tool/kis_tool_polyline_base.cpp
@@ -256,4 +256,16 @@ QRectF KisToolPolylineBase::dragBoundingRect()
     return rect;
 }
 
+
+void KisToolPolylineBase::listenToModifiers(bool listen)
+{
+    Q_UNUSED(listen)
+}
+
+bool KisToolPolylineBase::listeningToModifiers()
+{
+    //Never grab modifier keys
+    return false;
+}
+
 #include "kis_tool_polyline_base.moc"
diff --git a/krita/ui/tool/kis_tool_polyline_base.h b/krita/ui/tool/kis_tool_polyline_base.h
index f681fd8..ea6490c 100644
--- a/krita/ui/tool/kis_tool_polyline_base.h
+++ b/krita/ui/tool/kis_tool_polyline_base.h
@@ -45,6 +45,8 @@ public:
 
     void activate(ToolActivation activation, const QSet<KoShape*> &shapes);
     void deactivate();
+    virtual void listenToModifiers(bool listen);
+    virtual bool listeningToModifiers();
     void requestStrokeEnd();
     void requestStrokeCancellation();
 
@@ -65,6 +67,7 @@ private:
     QPointF m_dragStart;
     QPointF m_dragEnd;
     bool m_dragging;
+    bool m_listenToModifiers;
     vQPointF m_points;
     ToolType m_type;
     bool m_closeSnappingActivated;
diff --git a/krita/ui/tool/kis_tool_rectangle_base.cpp b/krita/ui/tool/kis_tool_rectangle_base.cpp
index 8e091d0..4846ff8 100644
--- a/krita/ui/tool/kis_tool_rectangle_base.cpp
+++ b/krita/ui/tool/kis_tool_rectangle_base.cpp
@@ -36,6 +36,7 @@ KisToolRectangleBase::KisToolRectangleBase(KoCanvasBase * canvas, KisToolRectang
     , m_isRatioForced(false)
     , m_isWidthForced(false)
     , m_isHeightForced(false)
+    , m_listenToModifiers(true)
     , m_forcedRatio(1.0)
     , m_forcedWidth(0)
     , m_forcedHeight(0)
@@ -46,22 +47,22 @@ KisToolRectangleBase::KisToolRectangleBase(KoCanvasBase * canvas, KisToolRectang
 QList<QPointer<QWidget> > KisToolRectangleBase::createOptionWidgets()
 {
   QList<QPointer<QWidget> > widgetsList = KoToolBase::createOptionWidgets();
-  
+
   widgetsList.append(new KisRectangleConstraintWidget(0, this));
-  
+
   return widgetsList;
 }
 
-void KisToolRectangleBase::constraintsChanged(bool forceRatio, bool forceWidth, bool forceHeight, float ratio, float width, float height) 
+void KisToolRectangleBase::constraintsChanged(bool forceRatio, bool forceWidth, bool forceHeight, float ratio, float width, float height)
 {
   m_isWidthForced = forceWidth;
   m_isHeightForced = forceHeight;
   m_isRatioForced = forceRatio;
-  
+
   m_forcedHeight = height;
   m_forcedWidth = width;
   m_forcedRatio = ratio;
-  
+
   // Avoid division by zero in size calculations
   if (ratio < 0.0001f) m_isRatioForced = false;
 }
@@ -81,6 +82,15 @@ void KisToolRectangleBase::deactivate()
     KisToolShape::deactivate();
 }
 
+void KisToolRectangleBase::listenToModifiers(bool listen)
+{
+    m_listenToModifiers = listen;
+}
+bool KisToolRectangleBase::listeningToModifiers()
+{
+    return m_listenToModifiers;
+}
+
 void KisToolRectangleBase::beginPrimaryAction(KoPointerEvent *event)
 {
     if ((m_type == PAINT && (!nodeEditable() || nodePaintAbility() == NONE)) ||
@@ -93,21 +103,21 @@ void KisToolRectangleBase::beginPrimaryAction(KoPointerEvent *event)
 
     QPointF pos = convertToPixelCoord(event);
     m_dragStart = m_dragCenter = pos;
-    
+
     QSizeF area = QSizeF(0,0);
-    
+
     applyConstraints(area, false);
-    
+
     m_dragEnd.setX(m_dragStart.x() + area.width());
     m_dragEnd.setY(m_dragStart.y() + area.height());
-    
+
     event->accept();
 }
 
 bool KisToolRectangleBase::isFixedSize() {
   if (m_isWidthForced && m_isHeightForced) return true;
   if (m_isRatioForced && (m_isWidthForced || m_isHeightForced)) return true;
-  
+
   return false;
 }
 
@@ -118,12 +128,12 @@ void KisToolRectangleBase::applyConstraints(QSizeF &area, bool overrideRatio) {
   if (m_isHeightForced) {
     area.setHeight(m_forcedHeight);
   }
-  
+
   if (m_isHeightForced && m_isWidthForced) return;
-  
+
   if (m_isRatioForced || overrideRatio) {
     float ratio = m_isRatioForced ? m_forcedRatio : 1.0f;
-    
+
     if (m_isWidthForced) {
       area.setHeight(area.width() / ratio);
     } else {
@@ -136,12 +146,12 @@ void KisToolRectangleBase::continuePrimaryAction(KoPointerEvent *event)
 {
     CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
 
-    bool constraintToggle = (event->modifiers() & Qt::ShiftModifier);
-    bool translateMode = (event->modifiers() & Qt::AltModifier);
-    bool expandFromCenter = (event->modifiers() & Qt::ControlModifier);
-    
+    bool constraintToggle = (event->modifiers() & Qt::ShiftModifier) && m_listenToModifiers;
+    bool translateMode = (event->modifiers() & Qt::AltModifier) && m_listenToModifiers;
+    bool expandFromCenter = (event->modifiers() & Qt::ControlModifier) && m_listenToModifiers;
+
     bool fixedSize = isFixedSize() && !constraintToggle;
-    
+
     QPointF pos = convertToPixelCoord(event);
 
     if (fixedSize) {
@@ -151,20 +161,20 @@ void KisToolRectangleBase::continuePrimaryAction(KoPointerEvent *event)
       m_dragStart += trans;
       m_dragEnd += trans;
     }
-    
+
     QPointF diag = pos - m_dragStart;
     QSizeF area = QSizeF(fabs(diag.x()), fabs(diag.y()));
-    
+
     bool overrideRatio = constraintToggle && !(m_isHeightForced || m_isWidthForced || m_isRatioForced);
     if (!constraintToggle || overrideRatio) {
       applyConstraints(area, overrideRatio);
     }
-    
+
     diag = QPointF(
       (diag.x() < 0) ? -area.width() : area.width(),
       (diag.y() < 0) ? -area.height() : area.height()
     );
-    
+
     // resize around center point?
     if (expandFromCenter && !fixedSize) {
       m_dragStart = m_dragCenter - diag / 2;
@@ -172,7 +182,7 @@ void KisToolRectangleBase::continuePrimaryAction(KoPointerEvent *event)
     } else {
       m_dragEnd = m_dragStart + diag;
     }
-    
+
     updateArea();
 
     m_dragCenter = QPointF((m_dragStart.x() + m_dragEnd.x()) / 2,
@@ -235,7 +245,7 @@ void KisToolRectangleBase::updateArea() {
     bound = bound.normalized();
 
     canvas()->updateCanvas(convertToPt(bound).adjusted(-100, -100, +200, +200));
-    
+
     emit rectangleChanged(bound);
 }
 
diff --git a/krita/ui/tool/kis_tool_rectangle_base.h b/krita/ui/tool/kis_tool_rectangle_base.h
index a0b470c..fbd8ea7 100644
--- a/krita/ui/tool/kis_tool_rectangle_base.h
+++ b/krita/ui/tool/kis_tool_rectangle_base.h
@@ -29,10 +29,10 @@ Q_OBJECT
 
 Q_SIGNALS:
     void rectangleChanged(const QRectF &newRect);
-    
+
 public Q_SLOTS:
     void constraintsChanged(bool forceRatio, bool forceWidth, bool forceHeight, float ratio, float width, float height);
-    
+
 public:
     enum ToolType {
         PAINT,
@@ -47,9 +47,11 @@ public:
 
     virtual void paint(QPainter& gc, const KoViewConverter &converter);
     virtual void deactivate();
+    void listenToModifiers(bool listen);
+    bool listeningToModifiers();
 
     QList<QPointer<QWidget> > createOptionWidgets();
-    
+
 protected:
     virtual void finishRect(const QRectF&)=0;
 
@@ -57,14 +59,15 @@ protected:
     QPointF m_dragStart;
     QPointF m_dragEnd;
     ToolType m_type;
-    
+
     bool m_isRatioForced;
     bool m_isWidthForced;
     bool m_isHeightForced;
+    bool m_listenToModifiers;
     float m_forcedRatio;
     float m_forcedWidth;
     float m_forcedHeight;
-    
+
     bool isFixedSize();
     void applyConstraints(QSizeF& area, bool overrideRatio);
 
diff --git a/krita/ui/tool/kis_tool_select_base.cpp b/krita/ui/tool/kis_tool_select_base.cpp
deleted file mode 100644
index 40779ad..0000000
--- a/krita/ui/tool/kis_tool_select_base.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/* This file is part of the KDE project
- * Copyright (C) 2009 Boudewijn Rempt <boud at valdyas.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB.  If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#include "kis_tool_select_base.h"
-
-#include "kis_cursor.h"
-#include "kis_canvas2.h"
-#include "kis_selection_options.h"
-
-
-KisToolSelectBase::KisToolSelectBase(KoCanvasBase *canvas,
-                                     const QCursor& cursor,
-                                     const QString &windowTitle)
-    : KisTool(canvas, cursor),
-      m_widgetHelper(windowTitle),
-      m_selectionAction(SELECTION_REPLACE)
-{
-    connect(&m_widgetHelper, SIGNAL(selectionActionChanged(int)), this, SLOT(setSelectionAction(int)));
-}
-
-SelectionMode KisToolSelectBase::selectionMode() const
-{
-    return m_widgetHelper.selectionMode();
-}
-
-SelectionAction KisToolSelectBase::selectionAction() const
-{
-    return m_selectionAction;
-}
-
-void KisToolSelectBase::setSelectionAction(int newSelectionAction)
-{
-    if(newSelectionAction >= SELECTION_REPLACE && newSelectionAction <= SELECTION_INTERSECT && m_selectionAction != newSelectionAction)
-    {
-        if(m_widgetHelper.optionWidget())
-        {
-            m_widgetHelper.slotSetAction(newSelectionAction);
-        }
-        m_selectionAction = (SelectionAction)newSelectionAction;
-        emit selectionActionChanged();
-    }
-}
-
-QWidget* KisToolSelectBase::createOptionWidget()
-{
-    KisCanvas2* canvas = dynamic_cast<KisCanvas2*>(this->canvas());
-    Q_ASSERT(canvas);
-
-    m_widgetHelper.createOptionWidget(canvas, this->toolId());
-    return m_widgetHelper.optionWidget();
-}
-
-KisSelectionOptions* KisToolSelectBase::selectionOptionWidget()
-{
-    return m_widgetHelper.optionWidget();
-}
-
-void KisToolSelectBase::keyPressEvent(QKeyEvent *event)
-{
-    if (!m_widgetHelper.processKeyPressEvent(event)) {
-        KisTool::keyPressEvent(event);
-    }
-}
diff --git a/krita/ui/tool/kis_tool_select_base.h b/krita/ui/tool/kis_tool_select_base.h
index 500d6dd..143459f 100644
--- a/krita/ui/tool/kis_tool_select_base.h
+++ b/krita/ui/tool/kis_tool_select_base.h
@@ -1,5 +1,6 @@
 /* This file is part of the KDE project
  * Copyright (C) 2009 Boudewijn Rempt <boud at valdyas.org>
+ * Copyright (C) 2015 Michael Abrahams <miabraha at gmail.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -20,39 +21,205 @@
 #ifndef KISTOOLSELECTBASE_H
 #define KISTOOLSELECTBASE_H
 
+#include "KoPointerEvent.h"
 #include "kis_tool.h"
+#include "kis_canvas2.h"
+#include "kis_selection.h"
+#include "kis_selection_options.h"
 #include "kis_selection_tool_config_widget_helper.h"
+#include "KisViewManager.h"
+#include "kis_selection_manager.h"
+#include <bitset>
 
-class KisSelectionOptions;
 
+#define QMOD_BINARY() QString(std::bitset<sizeof(int) * 8>(event->modifiers()).to_string().c_str())
 
-class KRITAUI_EXPORT KisToolSelectBase : public KisTool
+/** 
+ * This is a basic template to create selection tools from basic path based drawing tools. 
+ * The template overrides the ability to execute alternate actions correctly. 
+ * Modifier keys are overridden with the following behavior: 
+ * 
+ * Shift: add to selection
+ * Alt: subtract from selection
+ * Shift+Alt: intersect current selection
+ * Ctrl: replace selection
+ *
+ * Certain tools also use modifier keys to alter their behavior, e.g. forcing square proportions with the rectangle tool. 
+ * The template enables the following rules for forwarding keys: 
+ * 1) Any modifier keys held *when the tool is first activated* will determine the new selection method.  
+ * 2) If the underlying tool *does not take modifier keys*, pressing modifier keys in the middle of a stroke will change the selection method.  This applies to the lasso tool and polygon tool. 
+ * 3) If the underlying tool *takes modifier keys,* they will always be forwarded to the underlying tool, and it is not possible to change the selection method in the middle of a stroke.
+ * 
+ */ 
+
+
+static SelectionAction selectionModifierMap(Qt::KeyboardModifiers m) {
+    SelectionAction newAction = SELECTION_DEFAULT;
+    if (m & Qt::ControlModifier) {
+        newAction = SELECTION_REPLACE;
+    } else if ((m & Qt::ShiftModifier) && (m & Qt::AltModifier)) {
+        newAction = SELECTION_INTERSECT;
+    } else if (m & Qt::ShiftModifier) {
+        newAction = SELECTION_ADD;
+    } else if (m & Qt::AltModifier) {
+        newAction = SELECTION_SUBTRACT;
+    }
+    return newAction;
+}
+
+template <class BaseClass>
+    class SelectionActionHandler : public BaseClass
 {
-    Q_OBJECT
-    Q_PROPERTY(int selectionAction READ selectionAction WRITE setSelectionAction NOTIFY selectionActionChanged);
+
 public:
-    KisToolSelectBase(KoCanvasBase *canvas,
-                      const QCursor& cursor,
-                      const QString &windowTitle);
 
-    QWidget* createOptionWidget();
-    KisSelectionOptions* selectionOptionWidget();
+    SelectionActionHandler(KoCanvasBase* canvas, const QString toolName)
+        :BaseClass(canvas),
+         m_widgetHelper(toolName),
+         m_selectionAction(SELECTION_DEFAULT),
+         m_selectionActionAlternate(SELECTION_DEFAULT)
+    {
+    }
 
-    SelectionMode selectionMode() const;
-    SelectionAction selectionAction() const;
+    SelectionActionHandler(KoCanvasBase* canvas, const QCursor cursor, const QString toolName)
+        :BaseClass(canvas, cursor),
+         m_widgetHelper(toolName),
+         m_selectionAction(SELECTION_DEFAULT),
+         m_selectionActionAlternate(SELECTION_DEFAULT)
+    {
+    }
 
-public Q_SLOTS:
-    void setSelectionAction(int newSelectionAction);
+    SelectionActionHandler(KoCanvasBase* canvas, QCursor cursor, QString toolName, KisTool *delegateTool)
+        :BaseClass(canvas, cursor, delegateTool),
+         m_widgetHelper(toolName),
+         m_selectionAction(SELECTION_DEFAULT),
+         m_selectionActionAlternate(SELECTION_DEFAULT)
+    {
+    }
 
-Q_SIGNALS:
-    void selectionActionChanged();
+    QWidget* createOptionWidget()
+    {
+        KisCanvas2* canvas = dynamic_cast<KisCanvas2*>(this->canvas());
+        Q_ASSERT(canvas);
 
-protected:
-    virtual void keyPressEvent(QKeyEvent *event);
+        m_widgetHelper.createOptionWidget(canvas, this->toolId());
+        m_widgetHelper.optionWidget()->disableAntiAliasSelectionOption();
+        return m_widgetHelper.optionWidget();
+    }
 
-private:
+    void keyPressEvent(QKeyEvent *event)
+    {
+        if (!m_widgetHelper.processKeyPressEvent(event)) {
+            BaseClass::keyPressEvent(event);
+        }
+    }
+
+    SelectionMode selectionMode() const
+    {
+        return m_widgetHelper.selectionMode();
+    }
+
+    SelectionAction selectionAction() const
+    {
+        if (alternateSelectionAction() == SELECTION_DEFAULT) {
+            return m_widgetHelper.selectionAction();
+        }
+        return alternateSelectionAction();
+    }
+
+    bool antiAliasSelection() const
+    {
+        return m_widgetHelper.optionWidget()->antiAliasSelection();
+    }
+
+    SelectionAction alternateSelectionAction() const
+    {
+      return m_selectionActionAlternate;
+    }
+
+    KisSelectionOptions* selectionOptionWidget()
+    {
+        return m_widgetHelper.optionWidget();
+    }
+
+    virtual void setAlternateSelectionAction(SelectionAction action)
+    {
+        m_selectionActionAlternate = action;
+  kDebug() << "Changing to selection action" << m_selectionActionAlternate;
+    }
+
+    void activateAlternateAction(KisTool::AlternateAction action)
+    {
+        Q_UNUSED(action);
+        BaseClass::activatePrimaryAction();
+    }
+
+    void deactivateAlternateAction(KisTool::AlternateAction action)
+    {
+        Q_UNUSED(action);
+        BaseClass::deactivatePrimaryAction();
+    }
+
+    void beginAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) {
+        Q_UNUSED(action);
+        beginPrimaryAction(event);
+    }
+
+    void continueAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) {
+        Q_UNUSED(action);
+        continuePrimaryAction(event);
+    }
+
+    void endAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) {
+        Q_UNUSED(action);
+        endPrimaryAction(event);
+    }
+
+    virtual void beginPrimaryAction(KoPointerEvent *event)
+    {
+        keysAtStart = event->modifiers();
+
+        setAlternateSelectionAction(selectionModifierMap(keysAtStart));
+  if (alternateSelectionAction() != SELECTION_DEFAULT) {
+    BaseClass::listenToModifiers(false);
+  }
+        BaseClass::beginPrimaryAction(event);
+    }
+
+    virtual void continuePrimaryAction(KoPointerEvent *event)
+    {
+        //If modifier keys have changed, tell the base tool it can start capturing modifiers
+        if ((keysAtStart != event->modifiers()) && !BaseClass::listeningToModifiers()) {
+                BaseClass::listenToModifiers(true);
+        }
+
+        //Always defer to the base class if it signals it is capturing modifier keys
+        if (!BaseClass::listeningToModifiers()) {
+            setAlternateSelectionAction(selectionModifierMap(event->modifiers()));
+        }
+
+        BaseClass::continuePrimaryAction(event);
+    }
+
+    void endPrimaryAction(KoPointerEvent *event)
+    {
+        keysAtStart = Qt::NoModifier; //reset this with each action
+        BaseClass::endPrimaryAction(event);
+    }
+
+
+protected:
+    using BaseClass::canvas;
     KisSelectionToolConfigWidgetHelper m_widgetHelper;
     SelectionAction m_selectionAction;
+    SelectionAction m_selectionActionAlternate;
+
+private:
+    Qt::KeyboardModifiers keysAtStart;
 };
 
+
+typedef SelectionActionHandler<KisTool> KisToolSelectBase;
+
+
 #endif // KISTOOLSELECTBASE_H
diff --git a/libs/basicflakes/tools/KoCreatePathTool.cpp b/libs/basicflakes/tools/KoCreatePathTool.cpp
index 1ababf7..16519af 100644
--- a/libs/basicflakes/tools/KoCreatePathTool.cpp
+++ b/libs/basicflakes/tools/KoCreatePathTool.cpp
@@ -51,7 +51,7 @@ KoCreatePathTool::~KoCreatePathTool()
 void KoCreatePathTool::paint(QPainter &painter, const KoViewConverter &converter)
 {
     Q_D(KoCreatePathTool);
-    if (d->shape) {
+    if (pathStarted()) {
         KoShapeStroke *stroke(createStroke());
         if (stroke) {
             d->shape->setStroke(stroke);
@@ -78,11 +78,10 @@ void KoCreatePathTool::paint(QPainter &painter, const KoViewConverter &converter
             d->activePoint->paint(painter, d->handleRadius, paintFlags, onlyPaintActivePoints);
         }
 
-        // paint the first point
 
         // check if we have to color the first point
         if (d->mouseOverFirstPoint)
-            painter.setBrush(Qt::red);   // //TODO make configurable
+            painter.setBrush(Qt::red);     //TODO make configurable
         else
             painter.setBrush(Qt::white);   //TODO make configurable
 
@@ -128,16 +127,16 @@ void KoCreatePathTool::mousePressEvent(KoPointerEvent *event)
 
     const bool isOverFirstPoint = d->shape &&
         handleGrabRect(d->firstPoint->point()).contains(event->point);
-    const bool haveCloseModifier = event->modifiers() & Qt::ShiftModifier;
+    bool haveCloseModifier = (listeningToModifiers() && (event->modifiers() & Qt::ShiftModifier));
 
-    if (event->button() == Qt::RightButton || (event->button() == Qt::LeftButton && haveCloseModifier && !isOverFirstPoint)) {
+    if ((event->button() == Qt::RightButton) || ((event->button() == Qt::LeftButton) && haveCloseModifier && !isOverFirstPoint)) {
         endPathWithoutLastPoint();
         return;
     }
 
     d->finishAfterThisPoint = false;
 
-    if (d->shape) {
+    if (pathStarted()) {
         if (isOverFirstPoint) {
             d->activePoint->setPoint(d->firstPoint->point());
             canvas()->updateCanvas(d->shape->boundingRect());
@@ -204,6 +203,18 @@ void KoCreatePathTool::mousePressEvent(KoPointerEvent *event)
         d->angleSnapStrategy->setStartPoint(d->activePoint->point());
 }
 
+bool KoCreatePathTool::listeningToModifiers()
+{
+  Q_D(KoCreatePathTool);
+  return d->listeningToModifiers;
+}
+
+bool KoCreatePathTool::pathStarted()
+{
+  Q_D(KoCreatePathTool);
+  return ((bool) d->shape);
+}
+
 void KoCreatePathTool::mouseDoubleClickEvent(KoPointerEvent *event)
 {
     //remove handle
@@ -229,7 +240,7 @@ void KoCreatePathTool::mouseMoveEvent(KoPointerEvent *event)
         }
     }
 
-    if (! d->shape) {
+    if (!pathStarted()) {
         canvas()->updateCanvas(canvas()->snapGuide()->boundingRect());
         canvas()->snapGuide()->snap(event->point, event->modifiers());
         canvas()->updateCanvas(canvas()->snapGuide()->boundingRect());
@@ -268,6 +279,7 @@ void KoCreatePathTool::mouseReleaseEvent(KoPointerEvent *event)
     if (! d->shape || (event->buttons() & Qt::RightButton))
         return;
 
+    d->listeningToModifiers = true; // After the first press-and-release
     d->repaintActivePoint();
     d->pointIsDragged = false;
     KoPathPoint *lastActivePoint = d->activePoint;
diff --git a/libs/basicflakes/tools/KoCreatePathTool.h b/libs/basicflakes/tools/KoCreatePathTool.h
index ec03ebc..88fd0f8 100644
--- a/libs/basicflakes/tools/KoCreatePathTool.h
+++ b/libs/basicflakes/tools/KoCreatePathTool.h
@@ -62,6 +62,14 @@ public:
     /// reimplemented
     virtual void keyPressEvent(QKeyEvent *event);
 
+    /// For behavior as selection tool and with initial shift-key
+    virtual bool listeningToModifiers();
+
+    /**
+    * Returns true if path has been started
+    */
+    bool pathStarted();
+
 public Q_SLOTS:
     /// reimplemented
     virtual void activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes);
@@ -74,7 +82,7 @@ protected:
     /**
      * Add path shape to document.
      * This method can be overridden and change the behaviour of the tool. In that case the subclass takes ownership of pathShape.
-     * It gets only called, if there are two or more points in the path.
+     * It gets only called if there are two or more points in the path.
      */
     virtual void addPathShape(KoPathShape* pathShape);
 
@@ -99,4 +107,3 @@ private:
     Q_PRIVATE_SLOT(d_func(), void angleSnapChanged(int))
 };
 #endif
-
diff --git a/libs/basicflakes/tools/KoCreatePathTool_p.h b/libs/basicflakes/tools/KoCreatePathTool_p.h
index 4cf9e0c..807c0c8 100644
--- a/libs/basicflakes/tools/KoCreatePathTool_p.h
+++ b/libs/basicflakes/tools/KoCreatePathTool_p.h
@@ -198,6 +198,7 @@ public:
         pointIsDragged(false),
         finishAfterThisPoint(false),
         hoveredPoint(0),
+        listeningToModifiers(false),
         angleSnapStrategy(0),
         angleSnappingDelta(15),
         angleSnapStatus(false),
@@ -214,6 +215,7 @@ public:
     PathConnectionPoint existingStartPoint; ///< an existing path point we started a new path at
     PathConnectionPoint existingEndPoint;   ///< an existing path point we finished a new path at
     KoPathPoint *hoveredPoint; ///< an existing path end point the mouse is hovering on
+    bool listeningToModifiers; //  Fine tune when to begin processing modifiers at the beginning of a stroke.
 
     AngleSnapStrategy *angleSnapStrategy;
     int angleSnappingDelta;
@@ -237,7 +239,7 @@ public:
         const QPointF &controlPoint = activePoint->controlPoint2();
         rect = rect.united(QRectF(point, controlPoint).normalized());
 
-        // when paiting the fist point we want the
+        // when painting the first point we want the
         // first control point to be painted as well
         if (isFirstPoint) {
             const QPointF &controlPoint = activePoint->controlPoint1();
@@ -262,11 +264,11 @@ public:
         uint grabSensitivity = q->grabSensitivity();
         qreal maxDistance = q->canvas()->viewConverter()->viewToDocumentX(grabSensitivity);
 
-        foreach(KoShape *shape, shapes) {
-            KoPathShape * path = dynamic_cast<KoPathShape*>(shape);
+        foreach(KoShape *s, shapes) {
+            KoPathShape * path = dynamic_cast<KoPathShape*>(s);
             if (!path)
                 continue;
-            KoParameterShape *paramShape = dynamic_cast<KoParameterShape*>(shape);
+            KoParameterShape *paramShape = dynamic_cast<KoParameterShape*>(s);
             if (paramShape && paramShape->isParametricShape())
                 continue;
 
@@ -415,10 +417,10 @@ public:
 
         delete shape;
         shape=0;
-
         existingStartPoint = 0;
         existingEndPoint = 0;
         hoveredPoint = 0;
+        listeningToModifiers = false;
     }
 
     void angleDeltaChanged(int value)


More information about the kimageshop mailing list