[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [krita/kazakov/svg-loading] /: Implement basic strategies for editing the Gradient handles
From:       Dmitry Kazakov <null () kde ! org>
Date:       2017-02-06 10:23:07
Message-ID: E1cagRv-0000uv-Ue () code ! kde ! org
[Download RAW message or body]

Git commit daa32b7a8083f896b11a18313c2052c39be4c2a5 by Dmitry Kazakov.
Committed on 06/02/2017 at 08:59.
Pushed by dkazakov into branch 'kazakov/svg-loading'.

Implement basic strategies for editing the Gradient handles

Now we also have an abstract KoInteractionStrategyFactory which
can be added/removed from the Interaction tool and therefore activate/
deactivate some user interaction.

M  +1    -0    libs/flake/CMakeLists.txt
M  +1    -0    libs/flake/KoGradientBackground.cpp
A  +54   -0    libs/flake/tools/KoInteractionStrategyFactory.cpp     [License: GPL \
(v2+)] A  +58   -0    libs/flake/tools/KoInteractionStrategyFactory.h     [License: \
GPL (v2+)] M  +90   -3    libs/flake/tools/KoInteractionTool.cpp
M  +8    -0    libs/flake/tools/KoInteractionTool.h
M  +2    -0    libs/flake/tools/KoInteractionTool_p.h
M  +77   -0    libs/global/KisHandlePainterHelper.cpp
M  +23   -1    libs/global/KisHandlePainterHelper.h
M  +1    -0    libs/ui/CMakeLists.txt
A  +69   -0    libs/ui/canvas/KisSnapPointStrategy.cpp     [License: GPL (v2+)]
A  +50   -0    libs/ui/canvas/KisSnapPointStrategy.h     [License: GPL (v2+)]
M  +2    -0    plugins/tools/defaulttool/CMakeLists.txt
M  +95   -15   plugins/tools/defaulttool/defaulttool/DefaultTool.cpp
M  +4    -1    plugins/tools/defaulttool/defaulttool/DefaultTool.h
A  +186  -0    plugins/tools/defaulttool/defaulttool/KoShapeGradientHandles.cpp     \
[License: GPL (v2+)] A  +68   -0    \
plugins/tools/defaulttool/defaulttool/KoShapeGradientHandles.h     [License: GPL \
(v2+)] M  +36   -0    plugins/tools/defaulttool/defaulttool/SelectionDecorator.cpp
A  +106  -0    plugins/tools/defaulttool/defaulttool/ShapeGradientEditStrategy.cpp    \
[License: GPL (v2+)] A  +44   -0    \
plugins/tools/defaulttool/defaulttool/ShapeGradientEditStrategy.h     [License: GPL \
(v2+)] M  +0    -4    plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.cpp
M  +4    -4    plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.h
M  +0    -4    plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp
M  +0    -5    plugins/tools/defaulttool/defaulttool/ShapeRotateStrategy.cpp
M  +0    -4    plugins/tools/defaulttool/defaulttool/ShapeShearStrategy.cpp

https://commits.kde.org/krita/daa32b7a8083f896b11a18313c2052c39be4c2a5

diff --git a/libs/flake/CMakeLists.txt b/libs/flake/CMakeLists.txt
index f15c3f3c6a5..ef8d49d509b 100644
--- a/libs/flake/CMakeLists.txt
+++ b/libs/flake/CMakeLists.txt
@@ -179,6 +179,7 @@ set(kritaflake_SRCS
     tools/KoPanToolFactory.cpp
     tools/KoInteractionTool.cpp
     tools/KoInteractionStrategy.cpp
+    tools/KoInteractionStrategyFactory.cpp
     tools/KoCreateShapesTool.cpp
     tools/KoCreateShapesToolFactory.cpp
     tools/KoShapeRubberSelectStrategy.cpp
diff --git a/libs/flake/KoGradientBackground.cpp \
b/libs/flake/KoGradientBackground.cpp index 01f8aaf5edb..e6bbce34251 100644
--- a/libs/flake/KoGradientBackground.cpp
+++ b/libs/flake/KoGradientBackground.cpp
@@ -128,6 +128,7 @@ void KoGradientBackground::paint(QPainter &painter, const \
                KoViewConverter &/*con
         QTransform gradientToUser(boundingRect.width(), 0, 0, boundingRect.height(),
                                   boundingRect.x(), boundingRect.y());
 
+        // TODO: how about slicing the object?
         QGradient g = *d->gradient;
         g.setCoordinateMode(QGradient::LogicalMode);
 
diff --git a/libs/flake/tools/KoInteractionStrategyFactory.cpp \
b/libs/flake/tools/KoInteractionStrategyFactory.cpp new file mode 100644
index 00000000000..d01d1f1e7bc
--- /dev/null
+++ b/libs/flake/tools/KoInteractionStrategyFactory.cpp
@@ -0,0 +1,54 @@
+/*
+ *  Copyright (c) 2017 Dmitry Kazakov <dimula73@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
+ *  the Free Software Foundation; either version 2 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "KoInteractionStrategyFactory.h"
+
+#include <QString>
+
+struct KoInteractionStrategyFactory::Private
+{
+    int priority = 0;
+    QString id;
+};
+
+KoInteractionStrategyFactory::KoInteractionStrategyFactory(int priority, const \
QString &id) +    : m_d(new Private)
+{
+    m_d->priority = priority;
+    m_d->id = id;
+}
+
+KoInteractionStrategyFactory::~KoInteractionStrategyFactory()
+{
+}
+
+QString KoInteractionStrategyFactory::id() const
+{
+    return m_d->id;
+}
+
+int KoInteractionStrategyFactory::priority() const
+{
+    return m_d->priority;
+}
+
+bool KoInteractionStrategyFactory::compareLess(KoInteractionStrategyFactorySP f1, \
KoInteractionStrategyFactorySP f2) +{
+    return f1->priority() < f2->priority();
+}
+
diff --git a/libs/flake/tools/KoInteractionStrategyFactory.h \
b/libs/flake/tools/KoInteractionStrategyFactory.h new file mode 100644
index 00000000000..627e05daed8
--- /dev/null
+++ b/libs/flake/tools/KoInteractionStrategyFactory.h
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (c) 2017 Dmitry Kazakov <dimula73@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
+ *  the Free Software Foundation; either version 2 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KOINTERACTIONSTRATEGYFACTORY_H
+#define KOINTERACTIONSTRATEGYFACTORY_H
+
+#include <QScopedPointer>
+#include <QSharedPointer>
+#include "kritaflake_export.h"
+
+class QString;
+class QPainter;
+class KoInteractionStrategy;
+class KoPointerEvent;
+class KoViewConverter;
+
+class KoInteractionStrategyFactory;
+typedef QSharedPointer<KoInteractionStrategyFactory> KoInteractionStrategyFactorySP;
+
+class KRITAFLAKE_EXPORT KoInteractionStrategyFactory
+{
+public:
+    KoInteractionStrategyFactory(int priority, const QString &id);
+    virtual ~KoInteractionStrategyFactory();
+
+    QString id() const;
+    int priority() const;
+
+    virtual KoInteractionStrategy* createStrategy(KoPointerEvent *ev) = 0;
+    virtual bool hoverEvent(KoPointerEvent *ev) = 0;
+    virtual bool paintOnHover(QPainter &painter, const KoViewConverter &converter) = \
0; +    virtual bool tryUseCustomCursor() = 0;
+
+    static bool compareLess(KoInteractionStrategyFactorySP f1, \
KoInteractionStrategyFactorySP f2); +
+private:
+    struct Private;
+    QScopedPointer<Private> m_d;
+};
+
+
+
+#endif // KOINTERACTIONSTRATEGYFACTORY_H
diff --git a/libs/flake/tools/KoInteractionTool.cpp \
b/libs/flake/tools/KoInteractionTool.cpp index dd624b75f6f..f0a89594c45 100644
--- a/libs/flake/tools/KoInteractionTool.cpp
+++ b/libs/flake/tools/KoInteractionTool.cpp
@@ -25,6 +25,10 @@
 #include "KoCanvasBase.h"
 #include "KoPanTool.h"
 
+#include "kis_global.h"
+#include "kis_assert.h"
+
+
 KoInteractionTool::KoInteractionTool(KoCanvasBase *canvas)
     : KoToolBase(*(new KoInteractionToolPrivate(this, canvas)))
 {
@@ -37,8 +41,15 @@ KoInteractionTool::~KoInteractionTool()
 void KoInteractionTool::paint(QPainter &painter, const KoViewConverter &converter)
 {
     Q_D(KoInteractionTool);
-    if (d->currentStrategy)
+
+    if (d->currentStrategy) {
         d->currentStrategy->paint(painter, converter);
+    } else {
+        Q_FOREACH (KoInteractionStrategyFactorySP factory, d->interactionFactories) \
{ +            // skip the rest of rendering if the factory asks for it
+            if (factory->paintOnHover(painter, converter)) break;
+        }
+    }
 }
 
 void KoInteractionTool::mousePressEvent(KoPointerEvent *event)
@@ -48,7 +59,7 @@ void KoInteractionTool::mousePressEvent(KoPointerEvent *event)
         cancelCurrentStrategy();
         return;
     }
-    d->currentStrategy = createStrategy(event);
+    d->currentStrategy = createStrategyBase(event);
     if (d->currentStrategy == 0)
         event->ignore();
 }
@@ -57,10 +68,17 @@ void KoInteractionTool::mouseMoveEvent(KoPointerEvent *event)
 {
     Q_D(KoInteractionTool);
     d->lastPoint = event->point;
+
     if (d->currentStrategy)
         d->currentStrategy->handleMouseMove(d->lastPoint, event->modifiers());
-    else
+    else {
+        Q_FOREACH (KoInteractionStrategyFactorySP factory, d->interactionFactories) \
{ +            // skip the rest of rendering if the factory asks for it
+            if (factory->hoverEvent(event)) return;
+        }
+
         event->ignore();
+    }
 }
 
 void KoInteractionTool::mouseReleaseEvent(KoPointerEvent *event)
@@ -123,6 +141,75 @@ void KoInteractionTool::cancelCurrentStrategy()
     }
 }
 
+KoInteractionStrategy *KoInteractionTool::createStrategyBase(KoPointerEvent *event)
+{
+    Q_D(KoInteractionTool);
+
+    Q_FOREACH (KoInteractionStrategyFactorySP factory, d->interactionFactories) {
+        KoInteractionStrategy *strategy = factory->createStrategy(event);
+        if (strategy) {
+            return strategy;
+        }
+    }
+
+    return createStrategy(event);
+}
+
+void KoInteractionTool::addInteractionFactory(KoInteractionStrategyFactory *factory)
+{
+    Q_D(KoInteractionTool);
+
+    Q_FOREACH (auto f, d->interactionFactories) {
+        KIS_SAFE_ASSERT_RECOVER_RETURN(f->id() != factory->id());
+    }
+
+    d->interactionFactories.append(toQShared(factory));
+    qSort(d->interactionFactories.begin(),
+          d->interactionFactories.end(),
+          KoInteractionStrategyFactory::compareLess);
+}
+
+void KoInteractionTool::removeInteractionFactory(const QString &id)
+{
+    Q_D(KoInteractionTool);
+    QList<KoInteractionStrategyFactorySP>::iterator it =
+            d->interactionFactories.begin();
+
+    while (it != d->interactionFactories.end()) {
+        if ((*it)->id() == id) {
+            it = d->interactionFactories.erase(it);
+        } else {
+            ++it;
+        }
+    }
+}
+
+bool KoInteractionTool::hasInteractioFactory(const QString &id)
+{
+    Q_D(KoInteractionTool);
+
+    Q_FOREACH (auto f, d->interactionFactories) {
+        if (f->id() == id) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+bool KoInteractionTool::tryUseCustomCursor()
+{
+    Q_D(KoInteractionTool);
+
+    Q_FOREACH (auto f, d->interactionFactories) {
+        if (f->tryUseCustomCursor()) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 KoInteractionTool::KoInteractionTool(KoInteractionToolPrivate &dd)
     : KoToolBase(dd)
 {
diff --git a/libs/flake/tools/KoInteractionTool.h \
b/libs/flake/tools/KoInteractionTool.h index 4afc29985ca..a028a6bc4ac 100644
--- a/libs/flake/tools/KoInteractionTool.h
+++ b/libs/flake/tools/KoInteractionTool.h
@@ -26,6 +26,7 @@
 #include "kritaflake_export.h"
 
 class KoInteractionStrategy;
+class KoInteractionStrategyFactory;
 class KoInteractionToolPrivate;
 
 #define KoInteractionTool_ID "InteractionTool"
@@ -86,8 +87,15 @@ protected:
      * Reimplement this factory method to create your strategy to be used for mouse \
                interaction.
      * @returns a new strategy, or 0 when there is nothing to do.
      */
+    KoInteractionStrategy *createStrategyBase(KoPointerEvent *event);
     virtual KoInteractionStrategy *createStrategy(KoPointerEvent *event) = 0;
 
+    void addInteractionFactory(KoInteractionStrategyFactory *factory);
+    void removeInteractionFactory(const QString &id);
+    bool hasInteractioFactory(const QString &id);
+
+    bool tryUseCustomCursor();
+
 private:
     KoInteractionTool(const KoInteractionTool&);
     KoInteractionTool& operator=(const KoInteractionTool&);
diff --git a/libs/flake/tools/KoInteractionTool_p.h \
b/libs/flake/tools/KoInteractionTool_p.h index 069f24d6bcd..af55eef98d5 100644
--- a/libs/flake/tools/KoInteractionTool_p.h
+++ b/libs/flake/tools/KoInteractionTool_p.h
@@ -23,6 +23,7 @@
 
 #include "KoToolBase_p.h"
 #include "KoInteractionStrategy.h"
+#include "KoInteractionStrategyFactory.h"
 
 class KoInteractionToolPrivate : public KoToolBasePrivate
 {
@@ -39,6 +40,7 @@ public:
 
     QPointF lastPoint;
     KoInteractionStrategy *currentStrategy;
+    QList<QSharedPointer<KoInteractionStrategyFactory>> interactionFactories;
 };
 
 #endif
diff --git a/libs/global/KisHandlePainterHelper.cpp \
b/libs/global/KisHandlePainterHelper.cpp index 32d1891bb56..e434087ac0f 100644
--- a/libs/global/KisHandlePainterHelper.cpp
+++ b/libs/global/KisHandlePainterHelper.cpp
@@ -19,6 +19,7 @@
 #include "KisHandlePainterHelper.h"
 
 #include <QPainter>
+#include "kis_algebra_2d.h"
 
 
 KisHandlePainterHelper::KisHandlePainterHelper(QPainter *_painter, qreal \
handleRadius) @@ -50,6 +51,82 @@ void KisHandlePainterHelper::drawHandleRect(const \
                QPointF &center) {
     m_painter->drawPolygon(m_handlePolygon.translated(m_painterTransform.map(center)));
  }
 
+void KisHandlePainterHelper::drawGradientHandle(const QPointF &center, qreal radius) \
{ +    QPolygonF handlePolygon;
+
+    handlePolygon << QPointF(-radius, 0);
+    handlePolygon << QPointF(0, radius);
+    handlePolygon << QPointF(radius, 0);
+    handlePolygon << QPointF(0, -radius);
+
+    handlePolygon = m_handleTransform.map(handlePolygon);
+    m_painter->drawPolygon(handlePolygon.translated(m_painterTransform.map(center)));
 +}
+
+void KisHandlePainterHelper::drawGradientCrossHandle(const QPointF &center, qreal \
radius) { +
+    { // Draw a cross
+        QPainterPath p;
+        p.moveTo(-radius, -radius);
+        p.lineTo(radius, radius);
+        p.moveTo(radius, -radius);
+        p.lineTo(-radius, radius);
+
+        p = m_handleTransform.map(p);
+        m_painter->drawPath(p.translated(m_painterTransform.map(center)));
+    }
+
+    { // Draw a square
+        const qreal halfRadius = 0.5 * radius;
+
+        QPolygonF handlePolygon;
+        handlePolygon << QPointF(-halfRadius, 0);
+        handlePolygon << QPointF(0, halfRadius);
+        handlePolygon << QPointF(halfRadius, 0);
+        handlePolygon << QPointF(0, -halfRadius);
+
+        handlePolygon = m_handleTransform.map(handlePolygon);
+        m_painter->drawPolygon(handlePolygon.translated(m_painterTransform.map(center)));
 +    }
+}
+
+void KisHandlePainterHelper::drawArrow(const QPointF &pos, const QPointF &from, \
qreal radius) +{
+    QPainterPath p;
+
+    QLineF line(pos, from);
+    line.setLength(radius);
+
+    QPointF norm = KisAlgebra2D::leftUnitNormal(pos - from);
+    norm *= 0.34 * radius;
+
+    p.moveTo(line.p2() + norm);
+    p.lineTo(line.p1());
+    p.lineTo(line.p2() - norm);
+
+    p.translate(-pos);
+
+    m_painter->drawPath(m_handleTransform.map(p).translated(m_painterTransform.map(pos)));
 +}
+
+void KisHandlePainterHelper::drawGradientArrow(const QPointF &start, const QPointF \
&end, qreal radius) +{
+    QPainterPath p;
+    p.moveTo(start);
+    p.lineTo(end);
+    m_painter->drawPath(m_painterTransform.map(p));
+
+    const qreal length = kisDistance(start, end);
+    const QPointF diff = end - start;
+
+    if (length > 5 * radius) {
+        drawArrow(start + 0.33 * diff, start, radius);
+        drawArrow(start + 0.66 * diff, start, radius);
+    } else if (length > 3 * radius) {
+        drawArrow(start + 0.5 * diff, start, radius);
+    }
+}
+
 void KisHandlePainterHelper::drawRubberLine(const QPolygonF &poly) {
     m_painter->drawPolygon(m_painterTransform.map(poly));
 }
diff --git a/libs/global/KisHandlePainterHelper.h \
b/libs/global/KisHandlePainterHelper.h index 72d940323d0..84e01be2530 100644
--- a/libs/global/KisHandlePainterHelper.h
+++ b/libs/global/KisHandlePainterHelper.h
@@ -62,17 +62,39 @@ public:
     void drawHandleRect(const QPointF &center);
 
     /**
+     * Draw a rotated handle representing the gradient handle
+     */
+    void drawGradientHandle(const QPointF &center, qreal radius);
+
+    /**
+     * Draw a special handle representing the center of the gradient
+     */
+    void drawGradientCrossHandle(const QPointF &center, qreal radius);
+
+    /**
+     * Draw an arrow representing gradient position
+     */
+    void drawGradientArrow(const QPointF &start, const QPointF &end, qreal radius);
+
+    /**
      * Draw a line showing the bounding box of the selection
      */
     void drawRubberLine(const QPolygonF &poly);
 
 private:
+
+    /**
+     * Draw a single arrow with the tip at position \p pos, directed from \p from,
+     * of size \p radius.
+     */
+    void drawArrow(const QPointF &pos, const QPointF &from, qreal radius);
+
+private:
     QPainter *m_painter;
     QTransform m_painterTransform;
     KisAlgebra2D::DecomposedMatix m_decomposedMatrix;
     QTransform m_handleTransform;
     QPolygonF m_handlePolygon;
-
 };
 
 #endif // KISHANDLEPAINTERHELPER_H
diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt
index 659ed48a7fe..f1afd3b65d0 100644
--- a/libs/ui/CMakeLists.txt
+++ b/libs/ui/CMakeLists.txt
@@ -43,6 +43,7 @@ set(kritaui_LIB_SRCS
     canvas/kis_guides_config.cpp
     canvas/kis_snap_config.cpp
     canvas/kis_snap_line_strategy.cpp
+    canvas/KisSnapPointStrategy.cpp
     dialogs/kis_about_application.cpp
     dialogs/kis_dlg_adj_layer_props.cc
     dialogs/kis_dlg_adjustment_layer.cc
diff --git a/libs/ui/canvas/KisSnapPointStrategy.cpp \
b/libs/ui/canvas/KisSnapPointStrategy.cpp new file mode 100644
index 00000000000..712ebbedc12
--- /dev/null
+++ b/libs/ui/canvas/KisSnapPointStrategy.cpp
@@ -0,0 +1,69 @@
+/*
+ *  Copyright (c) 2017 Dmitry Kazakov <dimula73@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
+ *  the Free Software Foundation; either version 2 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "KisSnapPointStrategy.h"
+
+#include <QPainterPath>
+#include "kis_global.h"
+
+struct KisSnapPointStrategy::Private
+{
+    QList<QPointF> points;
+};
+
+KisSnapPointStrategy::KisSnapPointStrategy(KoSnapGuide::Strategy type)
+    : KoSnapStrategy(type),
+      m_d(new Private)
+{
+}
+
+KisSnapPointStrategy::~KisSnapPointStrategy()
+{
+}
+
+bool KisSnapPointStrategy::snap(const QPointF &mousePosition, KoSnapProxy *proxy, \
qreal maxSnapDistance) +{
+    Q_UNUSED(proxy);
+
+    QPointF snappedPoint = mousePosition;
+    qreal minDistance = std::numeric_limits<qreal>::max();
+
+    Q_FOREACH (const QPointF &pt, m_d->points) {
+        const qreal dist = kisDistance(mousePosition, pt);
+
+        if (dist < maxSnapDistance && dist < minDistance) {
+            minDistance = dist;
+            snappedPoint = pt;
+        }
+    }
+
+    setSnappedPosition(snappedPoint);
+    return minDistance < std::numeric_limits<qreal>::max();
+}
+
+QPainterPath KisSnapPointStrategy::decoration(const KoViewConverter &converter) \
const +{
+    Q_UNUSED(converter);
+    return QPainterPath();
+}
+
+void KisSnapPointStrategy::addPoint(const QPointF &pt)
+{
+    m_d->points << pt;
+}
+
diff --git a/libs/ui/canvas/KisSnapPointStrategy.h \
b/libs/ui/canvas/KisSnapPointStrategy.h new file mode 100644
index 00000000000..506f1da0473
--- /dev/null
+++ b/libs/ui/canvas/KisSnapPointStrategy.h
@@ -0,0 +1,50 @@
+/*
+ *  Copyright (c) 2017 Dmitry Kazakov <dimula73@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
+ *  the Free Software Foundation; either version 2 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KISSNAPPOINTSTRATEGY_H
+#define KISSNAPPOINTSTRATEGY_H
+
+#include <QScopedPointer>
+
+#include "KoSnapStrategy.h"
+#include "kritaui_export.h"
+
+/**
+ * The KisSnapPointStrategy class is a custom strategy that allows snapping to
+ * arbitrary points on canvas, not linked to any real objects. It can be used,
+ * for example, for snapping to the *previous position* of the handle, while it
+ * is dragging by the user.
+ */
+
+class KRITAUI_EXPORT KisSnapPointStrategy : public KoSnapStrategy
+{
+public:
+    KisSnapPointStrategy(KoSnapGuide::Strategy type = KoSnapGuide::CustomSnapping);
+    ~KisSnapPointStrategy();
+
+    bool snap(const QPointF &mousePosition, KoSnapProxy * proxy, qreal \
maxSnapDistance) override; +    QPainterPath decoration(const KoViewConverter \
&converter) const override; +
+     void addPoint(const QPointF &pt);
+
+private:
+    struct Private;
+    const QScopedPointer<Private> m_d;
+};
+
+#endif // KISSNAPPOINTSTRATEGY_H
diff --git a/plugins/tools/defaulttool/CMakeLists.txt \
b/plugins/tools/defaulttool/CMakeLists.txt index afca8e6dbe5..c8e9cbba47b 100644
--- a/plugins/tools/defaulttool/CMakeLists.txt
+++ b/plugins/tools/defaulttool/CMakeLists.txt
@@ -10,8 +10,10 @@ set ( defaulttools_SRCS
     defaulttool/ShapeResizeStrategy.cpp
     defaulttool/ShapeRotateStrategy.cpp
     defaulttool/ShapeShearStrategy.cpp
+    defaulttool/ShapeGradientEditStrategy.cpp
     defaulttool/SelectionDecorator.cpp
     defaulttool/KoFillConfigWidget.cpp
+    defaulttool/KoShapeGradientHandles.cpp
 
     connectionTool/ConnectionTool.cpp
     connectionTool/ConnectionToolFactory.cpp
diff --git a/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp \
b/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp index \
                69751cc65dd..246113bd266 100644
--- a/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp
+++ b/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp
@@ -52,6 +52,7 @@
 #include <KoSnapGuide.h>
 #include <KoStrokeConfigWidget.h>
 #include "kis_action_registry.h"
+#include <KoInteractionStrategyFactory.h>
 
 #include <KoIcon.h>
 
@@ -116,10 +117,6 @@ public:
     void finishInteraction(Qt::KeyboardModifiers /*modifiers*/) override {}
 
     void paint(QPainter &painter, const KoViewConverter &converter) {
-        SelectionDecorator decorator(tool()->canvas()->resourceManager());
-        decorator.setSelection(tool()->canvas()->shapeManager()->selection());
-        decorator.setHandleRadius(handleRadius());
-        decorator.paint(painter, converter);
     }
 };
 
@@ -132,15 +129,94 @@ public:
     }
 
     void paint(QPainter &painter, const KoViewConverter &converter) {
-        SelectionDecorator decorator(tool()->canvas()->resourceManager());
-        decorator.setSelection(tool()->canvas()->shapeManager()->selection());
-        decorator.setHandleRadius(handleRadius());
-        decorator.paint(painter, converter);
-
         KoShapeRubberSelectStrategy::paint(painter, converter);
     }
 };
+#include <KoGradientBackground.h>
+#include "KoShapeGradientHandles.h"
+#include "ShapeGradientEditStrategy.h"
+
+class DefaultTool::MoveGradientHandleInteractionFactory : public \
KoInteractionStrategyFactory +{
+public:
+    MoveGradientHandleInteractionFactory(DefaultTool *_q)
+        : KoInteractionStrategyFactory(0, "move_gradient_handle"),
+          q(_q)
+    {
+    }
+
+    KoInteractionStrategy* createStrategy(KoPointerEvent *ev) override
+    {
+        m_currentHandle = handleAt(ev->point);
+
+        if (m_currentHandle.type != KoShapeGradientHandles::Handle::None) {
+            KoShape *shape = onlyEditableShape();
+            KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shape, 0);
+
+            return new ShapeGradientEditStrategy(q, shape, m_currentHandle.type, \
ev->point); +        }
+
+        return 0;
+    }
+
+    bool hoverEvent(KoPointerEvent *ev) override
+    {
+        m_currentHandle = handleAt(ev->point);
+        return false;
+    }
+
+    bool paintOnHover(QPainter &painter, const KoViewConverter &converter) override
+    {
+        return false;
+    }
+
+    bool tryUseCustomCursor() {
+        if (m_currentHandle.type != KoShapeGradientHandles::Handle::None) {
+            q->useCursor(Qt::OpenHandCursor);
+        }
+
+        return m_currentHandle.type != KoShapeGradientHandles::Handle::None;
+    }
+
+private:
+
+    KoShape* onlyEditableShape() const {
+        KoSelection *selection = q->koSelection();
+        QList<KoShape*> shapes = selection->selectedEditableShapes();
+
+        KoShape *shape = 0;
+        if (shapes.size() == 1) {
+            shape = shapes.first();
+        }
+
+        return shape;
+    }
+
+    KoShapeGradientHandles::Handle handleAt(const QPointF &pos) {
+        KoShapeGradientHandles::Handle result;
 
+        KoShape *shape = onlyEditableShape();
+        if (shape) {
+            qreal minDistanceSq = std::numeric_limits<qreal>::max();
+
+            KoShapeGradientHandles sh(shape);
+            Q_FOREACH (const KoShapeGradientHandles::Handle &handle, sh.handles()) {
+                const qreal distanceSq = kisSquareDistance(handle.pos, pos);
+
+                if (distanceSq < HANDLE_DISTANCE_SQ && distanceSq < minDistanceSq) {
+                    result = handle;
+                    minDistanceSq = distanceSq;
+                }
+            }
+        }
+
+        return result;
+    }
+
+private:
+    DefaultTool *q;
+    KoShapeGradientHandles::Handle m_currentHandle;
+};
 
 class SelectionHandler : public KoToolSelection
 {
@@ -215,6 +291,8 @@ DefaultTool::DefaultTool(KoCanvasBase *canvas)
 
     KoShapeManager *manager = canvas->shapeManager();
     connect(manager, SIGNAL(selectionChanged()), this, SLOT(updateActions()));
+
+    addInteractionFactory(new MoveGradientHandleInteractionFactory(this));
 }
 
 DefaultTool::~DefaultTool()
@@ -402,6 +480,8 @@ qreal DefaultTool::rotationOfHandle(KoFlake::SelectionHandle \
handle, bool useEdg  
 void DefaultTool::updateCursor()
 {
+    if (tryUseCustomCursor()) return;
+
     QCursor cursor = Qt::ArrowCursor;
 
     QString statusText;
@@ -515,13 +595,13 @@ void DefaultTool::updateCursor()
 
 void DefaultTool::paint(QPainter &painter, const KoViewConverter &converter)
 {
+    SelectionDecorator decorator(canvas()->resourceManager());
+    decorator.setSelection(koSelection());
+    decorator.setHandleRadius(handleRadius());
+    decorator.paint(painter, converter);
+
     KoInteractionTool::paint(painter, converter);
-    if (currentStrategy() == 0 && koSelection()->count() > 0) {
-        SelectionDecorator decorator(canvas()->resourceManager());
-        decorator.setSelection(koSelection());
-        decorator.setHandleRadius(handleRadius());
-        decorator.paint(painter, converter);
-    }
+
     painter.save();
     KoShape::applyConversion(painter, converter);
     canvas()->snapGuide()->paint(painter, converter);
diff --git a/plugins/tools/defaulttool/defaulttool/DefaultTool.h \
b/plugins/tools/defaulttool/defaulttool/DefaultTool.h index 441d838323e..e0c8a46078d \
                100644
--- a/plugins/tools/defaulttool/defaulttool/DefaultTool.h
+++ b/plugins/tools/defaulttool/defaulttool/DefaultTool.h
@@ -124,7 +124,10 @@ public: // Events
 protected:
     QList<QPointer<QWidget> > createOptionWidgets();
 
-    virtual KoInteractionStrategy *createStrategy(KoPointerEvent *event);
+    KoInteractionStrategy *createStrategy(KoPointerEvent *event) override;
+
+private:
+    class MoveGradientHandleInteractionFactory;
 
 private:
     void setupActions();
diff --git a/plugins/tools/defaulttool/defaulttool/KoShapeGradientHandles.cpp \
b/plugins/tools/defaulttool/defaulttool/KoShapeGradientHandles.cpp new file mode \
100644 index 00000000000..dec9f2eba51
--- /dev/null
+++ b/plugins/tools/defaulttool/defaulttool/KoShapeGradientHandles.cpp
@@ -0,0 +1,186 @@
+/*
+ *  Copyright (c) 2017 Dmitry Kazakov <dimula73@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
+ *  the Free Software Foundation; either version 2 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "KoShapeGradientHandles.h"
+
+#include <QGradient>
+#include <KoShape.h>
+#include <KoGradientBackground.h>
+#include <KoShapeBackgroundCommand.h>
+#include <kis_assert.h>
+
+KoShapeGradientHandles::KoShapeGradientHandles(KoShape *shape)
+    : m_shape(shape)
+{
+}
+
+QVector<KoShapeGradientHandles::Handle> KoShapeGradientHandles::handles(const \
KoViewConverter *converter) const { +    QVector<Handle> result;
+
+    const QGradient *g = gradient();
+    if (!g) return result;
+
+    switch (g->type()) {
+    case QGradient::LinearGradient: {
+        const QLinearGradient *lgradient = static_cast<const QLinearGradient*>(g);
+        result << Handle(Handle::LinearStart, lgradient->start());
+        result << Handle(Handle::LinearEnd, lgradient->finalStop());
+        break;
+    }
+    case QGradient::RadialGradient: {
+        const QRadialGradient *rgradient = static_cast<const QRadialGradient*>(g);
+
+        result << Handle(Handle::RadialCenter, rgradient->center());
+
+        if (rgradient->center() != rgradient->focalPoint()) {
+            result << Handle(Handle::RadialFocalPoint, rgradient->focalPoint());
+        }
+
+        result << Handle(Handle::RadialRadius,
+                         rgradient->center() + QPointF(rgradient->centerRadius(), \
0)); +        break;
+    }
+    case QGradient::ConicalGradient:
+        // not supported
+        break;
+    case QGradient::NoGradient:
+        // not supported
+        break;
+    }
+
+    if (g->coordinateMode() == QGradient::ObjectBoundingMode) {
+        const QRectF boundingRect = m_shape->outlineRect();
+        const QTransform gradientToUser(boundingRect.width(), 0, 0, \
boundingRect.height(), +                                        boundingRect.x(), \
boundingRect.y()); +        const QTransform t = gradientToUser * \
m_shape->absoluteTransformation(converter); +
+        QVector<Handle>::iterator it = result.begin();
+
+
+
+        for (; it != result.end(); ++it) {
+            it->pos = t.map(it->pos);
+        }
+    }
+
+    return result;
+}
+
+QGradient::Type KoShapeGradientHandles::type() const
+{
+    const QGradient *g = gradient();
+    return g ? g->type() : QGradient::NoGradient;
+}
+
+KUndo2Command *KoShapeGradientHandles::moveGradientHandle(KoShapeGradientHandles::Handle::Type \
handleType, const QPointF &absoluteOffset) +{
+    KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(handleType != Handle::None, 0);
+
+    QSharedPointer<KoShapeBackground> bg = m_shape->background();
+    KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(bg, 0);
+
+    QSharedPointer<KoGradientBackground> gradientBg =
+            qSharedPointerDynamicCast<KoGradientBackground>(bg);
+    KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(gradientBg, 0);
+
+    QGradient *newGradient = 0;
+
+    switch (gradientBg->gradient()->type()) {
+    case QGradient::LinearGradient: {
+        KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(handleType == Handle::LinearStart ||
+                                             handleType == Handle::LinearEnd, 0);
+
+        newGradient = KoFlake::cloneGradient(gradientBg->gradient());
+        QLinearGradient *lgradient = static_cast<QLinearGradient*>(newGradient);
+
+        if (handleType == Handle::LinearStart) {
+            lgradient->setStart(getNewHandlePos(lgradient->start(), absoluteOffset, \
newGradient->coordinateMode())); +        } else if (handleType == Handle::LinearEnd) \
{ +            lgradient->setFinalStop(getNewHandlePos(lgradient->finalStop(), \
absoluteOffset, newGradient->coordinateMode())); +
+        }
+        break;
+    }
+    case QGradient::RadialGradient: {
+        newGradient = KoFlake::cloneGradient(gradientBg->gradient());
+        QRadialGradient *rgradient = static_cast<QRadialGradient*>(newGradient);
+
+        if (handleType == Handle::RadialCenter) {
+            rgradient->setCenter(getNewHandlePos(rgradient->center(), \
absoluteOffset, newGradient->coordinateMode())); +        } else if (handleType == \
Handle::RadialFocalPoint) { +            \
rgradient->setFocalPoint(getNewHandlePos(rgradient->focalPoint(), absoluteOffset, \
newGradient->coordinateMode())); +        } else if (handleType == \
Handle::RadialRadius) { +            QPointF radiusPos = rgradient->center() + \
QPointF(QPointF(rgradient->radius(), 0)); +            radiusPos = \
getNewHandlePos(radiusPos, absoluteOffset, newGradient->coordinateMode()); +          \
rgradient->setRadius(radiusPos.x() - rgradient->center().x()); +        }
+        break;
+    }
+    case QGradient::ConicalGradient:
+        // not supported
+        break;
+    case QGradient::NoGradient:
+        // not supported
+        break;
+    }
+
+    QSharedPointer<KoGradientBackground> newFill(
+        new KoGradientBackground(newGradient, gradientBg->transform()));
+
+    return new KoShapeBackgroundCommand(m_shape, newFill);
+}
+
+KoShapeGradientHandles::Handle \
KoShapeGradientHandles::getHandle(KoShapeGradientHandles::Handle::Type handleType) +{
+    Handle result;
+
+    Q_FOREACH (const Handle &h, handles()) {
+        if (h.type == handleType) {
+            result = h;
+            break;
+        }
+    }
+
+    return result;
+}
+
+const QGradient *KoShapeGradientHandles::gradient() const {
+    QSharedPointer<KoShapeBackground> bg = m_shape->background();
+    if (!bg) return 0;
+
+    QSharedPointer<KoGradientBackground> gradientBg =
+            qSharedPointerDynamicCast<KoGradientBackground>(bg);
+    if (!gradientBg) return 0;
+
+    return gradientBg->gradient();
+}
+
+QPointF KoShapeGradientHandles::getNewHandlePos(const QPointF &oldPos, const QPointF \
&absoluteOffset, QGradient::CoordinateMode mode) +{
+    const QTransform offset = QTransform::fromTranslate(absoluteOffset.x(), \
absoluteOffset.y()); +    QTransform localToAbsolute = \
m_shape->absoluteTransformation(0); +
+    if (mode == QGradient::ObjectBoundingMode) {
+        const QRectF boundingRect = m_shape->outlineRect();
+        const QTransform gradientToUser(boundingRect.width(), 0, 0, \
boundingRect.height(), +                                        boundingRect.x(), \
boundingRect.y()); +        localToAbsolute = gradientToUser * localToAbsolute;
+    }
+
+    return (localToAbsolute * offset * localToAbsolute.inverted()).map(oldPos);
+}
diff --git a/plugins/tools/defaulttool/defaulttool/KoShapeGradientHandles.h \
b/plugins/tools/defaulttool/defaulttool/KoShapeGradientHandles.h new file mode 100644
index 00000000000..4c248552c30
--- /dev/null
+++ b/plugins/tools/defaulttool/defaulttool/KoShapeGradientHandles.h
@@ -0,0 +1,68 @@
+/*
+ *  Copyright (c) 2017 Dmitry Kazakov <dimula73@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
+ *  the Free Software Foundation; either version 2 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KOSHAPEGRADIENTHANDLES_H
+#define KOSHAPEGRADIENTHANDLES_H
+
+#include <QPointF>
+
+#include <QGradient>
+
+class KoShape;
+class KoViewConverter;
+class KUndo2Command;
+
+class KoShapeGradientHandles
+{
+public:
+    struct Handle {
+        enum Type {
+            None,
+            LinearStart,
+            LinearEnd,
+            RadialCenter,
+            RadialRadius,
+            RadialFocalPoint
+        };
+
+        Handle() : type(None) {}
+        Handle(Type t, const QPointF &p) : type(t), pos(p) {}
+
+        Type type;
+        QPointF pos;
+    };
+
+public:
+    KoShapeGradientHandles(KoShape *shape);
+    QVector<Handle> handles(const KoViewConverter *converter = 0) const;
+    QGradient::Type type() const;
+
+    KUndo2Command* moveGradientHandle(Handle::Type handleType, const QPointF \
&absoluteOffset); +    Handle getHandle(Handle::Type handleType);
+
+
+
+private:
+    const QGradient* gradient() const;
+    QPointF getNewHandlePos(const QPointF &oldPos, const QPointF &absoluteOffset, \
QGradient::CoordinateMode mode); +
+private:
+    KoShape *m_shape;
+};
+
+#endif // KOSHAPEGRADIENTHANDLES_H
diff --git a/plugins/tools/defaulttool/defaulttool/SelectionDecorator.cpp \
b/plugins/tools/defaulttool/defaulttool/SelectionDecorator.cpp index \
                1cd721933b2..ebe4e53c6df 100644
--- a/plugins/tools/defaulttool/defaulttool/SelectionDecorator.cpp
+++ b/plugins/tools/defaulttool/defaulttool/SelectionDecorator.cpp
@@ -30,6 +30,7 @@
 #include <KisHandlePainterHelper.h>
 #include <KoCanvasResourceManager.h>
 #include <KisQPainterStateSaver.h>
+#include "KoShapeGradientHandles.h"
 
 #define HANDLE_DISTANCE 10
 
@@ -124,5 +125,40 @@ void SelectionDecorator::paint(QPainter &painter, const \
KoViewConverter &convert  helper.drawHandleRect(hotPos);
         }
     }
+
+    if (editable && selectedShapes.size() == 1) {
+        KoShape *shape = selectedShapes.first();
+
+        KoShapeGradientHandles gradientHandles(shape);
+        QVector<KoShapeGradientHandles::Handle> handles = gradientHandles.handles();
+
+        KisHandlePainterHelper helper(&painter);
+        const QTransform t = shape->absoluteTransformation(0).inverted();
+
+        painter.setPen(pen);
+        painter.setBrush(Qt::white);
+
+        if (gradientHandles.type() == QGradient::LinearGradient) {
+            KIS_SAFE_ASSERT_RECOVER_NOOP(handles.size() == 2);
+
+            if (handles.size() == 2) {
+                helper.drawGradientArrow(t.map(handles[0].pos), \
t.map(handles[1].pos), 1.5 * m_handleRadius); +            }
+        }
+
+        pen.setColor(QColor(255, 197, 39));
+        painter.setPen(pen);
+        painter.setBrush(Qt::white);
+
+        Q_FOREACH (const KoShapeGradientHandles::Handle &h, handles) {
+            if (h.type == KoShapeGradientHandles::Handle::RadialCenter) {
+                helper.drawGradientCrossHandle(t.map(h.pos), 1.2 * m_handleRadius);
+            } else {
+                helper.drawGradientHandle(t.map(h.pos), 1.2 * m_handleRadius);
+            }
+        }
+
+
+    }
 }
 
diff --git a/plugins/tools/defaulttool/defaulttool/ShapeGradientEditStrategy.cpp \
b/plugins/tools/defaulttool/defaulttool/ShapeGradientEditStrategy.cpp new file mode \
100644 index 00000000000..9c7e22219f6
--- /dev/null
+++ b/plugins/tools/defaulttool/defaulttool/ShapeGradientEditStrategy.cpp
@@ -0,0 +1,106 @@
+/*
+ *  Copyright (c) 2017 Dmitry Kazakov <dimula73@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
+ *  the Free Software Foundation; either version 2 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "ShapeGradientEditStrategy.h"
+
+#include <KoToolBase.h>
+#include <KoCanvasBase.h>
+#include <KoCanvasResourceManager.h>
+#include <KoShapeManager.h>
+#include <KoShape.h>
+#include "kis_assert.h"
+#include "SelectionDecorator.h"
+#include <kundo2command.h>
+#include <KoSnapGuide.h>
+#include <KisSnapPointStrategy.h>
+
+#include "kis_debug.h"
+
+
+struct ShapeGradientEditStrategy::Private
+{
+    Private(const QPointF &_start, KoShape *shape)
+        : start(_start),
+          gradientHandles(shape)
+    {
+    }
+
+    QPointF start;
+    QPointF initialOffset;
+    KoShapeGradientHandles gradientHandles;
+    KoShapeGradientHandles::Handle::Type handleType;
+    QScopedPointer<KUndo2Command> intermediateCommand;
+};
+
+
+ShapeGradientEditStrategy::ShapeGradientEditStrategy(KoToolBase *tool,
+                                                     KoShape *shape,
+                                                     \
KoShapeGradientHandles::Handle::Type startHandleType, +                               \
const QPointF &clicked) +    : KoInteractionStrategy(tool)
+    , m_d(new Private(clicked, shape))
+{
+    KIS_SAFE_ASSERT_RECOVER_RETURN(shape);
+
+    m_d->handleType = startHandleType;
+
+    KoShapeGradientHandles::Handle handle = \
m_d->gradientHandles.getHandle(m_d->handleType); +    m_d->initialOffset = handle.pos \
- clicked; +
+    KisSnapPointStrategy *strategy = new KisSnapPointStrategy();
+    Q_FOREACH (const KoShapeGradientHandles::Handle &h, \
m_d->gradientHandles.handles()) { +        strategy->addPoint(h.pos);
+    }
+    tool->canvas()->snapGuide()->addCustomSnapStrategy(strategy);
+}
+
+ShapeGradientEditStrategy::~ShapeGradientEditStrategy()
+{
+}
+
+void ShapeGradientEditStrategy::handleMouseMove(const QPointF &mouseLocation, \
Qt::KeyboardModifiers modifiers) +{
+    if (m_d->intermediateCommand) {
+        m_d->intermediateCommand->undo();
+        m_d->intermediateCommand.reset();
+    }
+
+    const QPointF snappedPosition = \
tool()->canvas()->snapGuide()->snap(mouseLocation, m_d->initialOffset, modifiers); +  \
const QPointF diff = snappedPosition- m_d->start; +    \
m_d->intermediateCommand.reset(m_d->gradientHandles.moveGradientHandle(m_d->handleType, \
diff)); +    m_d->intermediateCommand->redo();
+
+    tool()->canvas()->updateCanvas(tool()->canvas()->snapGuide()->boundingRect());
+}
+
+KUndo2Command *ShapeGradientEditStrategy::createCommand()
+{
+    return m_d->intermediateCommand.take();
+}
+
+void ShapeGradientEditStrategy::finishInteraction(Qt::KeyboardModifiers modifiers)
+{
+    const QRectF dirtyRect = tool()->canvas()->snapGuide()->boundingRect();
+    tool()->canvas()->snapGuide()->reset();
+    tool()->canvas()->updateCanvas(dirtyRect);
+}
+
+void ShapeGradientEditStrategy::paint(QPainter &painter, const KoViewConverter \
&converter) +{
+}
+
diff --git a/plugins/tools/defaulttool/defaulttool/ShapeGradientEditStrategy.h \
b/plugins/tools/defaulttool/defaulttool/ShapeGradientEditStrategy.h new file mode \
100644 index 00000000000..6a390964613
--- /dev/null
+++ b/plugins/tools/defaulttool/defaulttool/ShapeGradientEditStrategy.h
@@ -0,0 +1,44 @@
+/*
+ *  Copyright (c) 2017 Dmitry Kazakov <dimula73@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
+ *  the Free Software Foundation; either version 2 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef SHAPEGRADIENTEDITSTRATEGY_H
+#define SHAPEGRADIENTEDITSTRATEGY_H
+
+#include <QScopedPointer>
+#include <KoInteractionStrategy.h>
+#include "KoShapeGradientHandles.h"
+
+class ShapeGradientEditStrategy : public KoInteractionStrategy
+{
+public:
+    ShapeGradientEditStrategy(KoToolBase *tool, KoShape *shape,
+                              KoShapeGradientHandles::Handle::Type startHandleType,
+                              const QPointF &clicked);
+    ~ShapeGradientEditStrategy();
+
+    void handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers \
modifiers) override; +    KUndo2Command *createCommand() override;
+    void finishInteraction(Qt::KeyboardModifiers modifiers) override;
+    void paint(QPainter &painter, const KoViewConverter &converter) override;
+
+private:
+    struct Private;
+    QScopedPointer<Private> m_d;
+};
+
+#endif // SHAPEGRADIENTEDITSTRATEGY_H
diff --git a/plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.cpp \
b/plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.cpp index \
                7b901b58213..fb5609d9f85 100644
--- a/plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.cpp
+++ b/plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.cpp
@@ -122,8 +122,4 @@ void ShapeMoveStrategy::finishInteraction(Qt::KeyboardModifiers \
modifiers)  
 void ShapeMoveStrategy::paint(QPainter &painter, const KoViewConverter &converter)
 {
-    SelectionDecorator decorator(tool()->canvas()->resourceManager());
-    decorator.setSelection(tool()->canvas()->shapeManager()->selection());
-    decorator.setHandleRadius(handleRadius());
-    decorator.paint(painter, converter);
 }
diff --git a/plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.h \
b/plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.h index \
                e7ed5bfd14c..b509d13f97c 100644
--- a/plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.h
+++ b/plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.h
@@ -46,10 +46,10 @@ public:
     ShapeMoveStrategy(KoToolBase *tool, const QPointF &clicked);
     virtual ~ShapeMoveStrategy() {}
 
-    void handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers \
                modifiers);
-    KUndo2Command *createCommand();
-    void finishInteraction(Qt::KeyboardModifiers modifiers);
-    virtual void paint(QPainter &painter, const KoViewConverter &converter);
+    void handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers \
modifiers) override; +    KUndo2Command *createCommand() override;
+    void finishInteraction(Qt::KeyboardModifiers modifiers) override;
+    virtual void paint(QPainter &painter, const KoViewConverter &converter) \
override;  private:
     void moveSelection(const QPointF &diff);
     QList<QPointF> m_previousPositions;
diff --git a/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp \
b/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp index \
                d914cee77f8..5354935d875 100644
--- a/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp
+++ b/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp
@@ -237,8 +237,4 @@ void ShapeResizeStrategy::finishInteraction(Qt::KeyboardModifiers \
modifiers)  
 void ShapeResizeStrategy::paint(QPainter &painter, const KoViewConverter &converter)
 {
-    SelectionDecorator decorator(tool()->canvas()->resourceManager());
-    decorator.setSelection(tool()->canvas()->shapeManager()->selection());
-    decorator.setHandleRadius(handleRadius());
-    decorator.paint(painter, converter);
 }
diff --git a/plugins/tools/defaulttool/defaulttool/ShapeRotateStrategy.cpp \
b/plugins/tools/defaulttool/defaulttool/ShapeRotateStrategy.cpp index \
                05ad433fa00..1dfa225bb3b 100644
--- a/plugins/tools/defaulttool/defaulttool/ShapeRotateStrategy.cpp
+++ b/plugins/tools/defaulttool/defaulttool/ShapeRotateStrategy.cpp
@@ -89,11 +89,6 @@ void ShapeRotateStrategy::rotateBy(qreal angle)
 
 void ShapeRotateStrategy::paint(QPainter &painter, const KoViewConverter &converter)
 {
-    SelectionDecorator decorator(tool()->canvas()->resourceManager());
-    decorator.setSelection(tool()->canvas()->shapeManager()->selection());
-    decorator.setHandleRadius(handleRadius());
-    decorator.paint(painter, converter);
-
     // paint the rotation center
     painter.setPen(QPen(Qt::red));
     painter.setBrush(QBrush(Qt::red));
diff --git a/plugins/tools/defaulttool/defaulttool/ShapeShearStrategy.cpp \
b/plugins/tools/defaulttool/defaulttool/ShapeShearStrategy.cpp index \
                74412c2473d..29fd6a45113 100644
--- a/plugins/tools/defaulttool/defaulttool/ShapeShearStrategy.cpp
+++ b/plugins/tools/defaulttool/defaulttool/ShapeShearStrategy.cpp
@@ -155,10 +155,6 @@ void ShapeShearStrategy::handleMouseMove(const QPointF &point, \
Qt::KeyboardModif  
 void ShapeShearStrategy::paint(QPainter &painter, const KoViewConverter &converter)
 {
-    SelectionDecorator decorator(tool()->canvas()->resourceManager());
-    decorator.setSelection(tool()->canvas()->shapeManager()->selection());
-    decorator.setHandleRadius(handleRadius());
-    decorator.paint(painter, converter);
 }
 
 KUndo2Command *ShapeShearStrategy::createCommand()


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic