[krita] /: FEATURE: actions for rotate/scale/mirror/shear selection

Dmitry Kazakov null at kde.org
Sun Sep 9 14:52:43 BST 2018


Git commit 3014d62c9817690ed21384450cb771c75bf60ce4 by Dmitry Kazakov.
Committed on 09/09/2018 at 13:52.
Pushed by dkazakov into branch 'master'.

FEATURE: actions for rotate/scale/mirror/shear selection

Now Rotate/Scale/Mirror/Shear Layer actions also support
handling a selection. If there is a selection active, then
the action will transform selected content only.

Now there are also 8 new actions, that will rotate/scale/
mirror/shear *all* layers without resizing the image. These
actions can also handle selections.

Animation Note:

* when no selection present, the actions transform *all*
  the frames of the layers in question

* when there is a selection active, only current frame
  is transformed

BUG:365595
Ref T3920
CC:kimageshop at kde.org

M  +96   -0    krita/krita.action
M  +15   -0    krita/krita4.xmlgui
M  +2    -0    libs/image/CMakeLists.txt
R  +0    -0    libs/image/commands_new/kis_transaction_based_command.cpp [from: libs/ui/kis_transaction_based_command.cpp - 100% similarity]
R  +2    -2    libs/image/commands_new/kis_transaction_based_command.h [from: libs/ui/kis_transaction_based_command.h - 092% similarity]
M  +79   -27   libs/image/kis_image.cc
M  +6    -7    libs/image/kis_image.h
M  +12   -0    libs/image/kis_processing_applicator.cpp
M  +5    -0    libs/image/kis_processing_visitor.cpp
M  +8    -0    libs/image/kis_processing_visitor.h
A  +91   -0    libs/image/processing/KisSelectionBasedProcessingHelper.cpp     [License: UNKNOWN]  *
A  +37   -0    libs/image/processing/KisSelectionBasedProcessingHelper.h     [License: UNKNOWN]  *
M  +26   -9    libs/image/processing/kis_mirror_processing_visitor.cpp
M  +10   -0    libs/image/processing/kis_mirror_processing_visitor.h
M  +37   -5    libs/image/processing/kis_transform_processing_visitor.cpp
M  +8    -1    libs/image/processing/kis_transform_processing_visitor.h
M  +11   -4    libs/libkis/Node.cpp
M  +1    -1    libs/libkis/Node.h
M  +0    -1    libs/ui/CMakeLists.txt
M  +1    -1    libs/ui/actions/kis_selection_action_factories.cpp
M  +42   -56   libs/ui/kis_node_manager.cpp
M  +7    -12   libs/ui/kis_node_manager.h
M  +1    -1    libs/ui/operations/kis_filter_selection_operation.cpp
M  +0    -104  libs/ui/tests/kis_node_manager_test.cpp
M  +0    -6    libs/ui/tests/kis_node_manager_test.h
M  +1    -1    libs/ui/tool/kis_selection_tool_helper.cpp
M  +1    -1    plugins/dockers/gamutmask/gamutmask_dock.cpp
M  +46   -14   plugins/extensions/imagesize/imagesize.cc
M  +6    -0    plugins/extensions/imagesize/imagesize.h
M  +1    -1    plugins/extensions/pykrita/sip/krita/Node.sip
M  +91   -10   plugins/extensions/rotateimage/rotateimage.cc
M  +12   -0    plugins/extensions/rotateimage/rotateimage.h
M  +25   -4    plugins/extensions/shearimage/shearimage.cc
M  +6    -0    plugins/extensions/shearimage/shearimage.h
M  +1    -1    plugins/tools/tool_smart_patch/kis_tool_smart_patch.cpp

The files marked with a * at the end have a non valid license. Please read: https://community.kde.org/Policies/Licensing_Policy and use the headers which are listed at that page.


https://commits.kde.org/krita/3014d62c9817690ed21384450cb771c75bf60ce4

diff --git a/krita/krita.action b/krita/krita.action
index 2a524c374f3..d247f8d0891 100644
--- a/krita/krita.action
+++ b/krita/krita.action
@@ -3303,6 +3303,102 @@
       <isCheckable>false</isCheckable>
       <statusTip></statusTip>
     </Action>
+    <Action name="mirrorAllNodesX">
+      <icon>symmetry-horizontal</icon>
+      <text>Mirror All Layers Hori&zontally</text>
+      <whatsThis></whatsThis>
+      <toolTip>Mirror All Layers Horizontally</toolTip>
+      <iconText>Mirror All Layers Horizontally</iconText>
+      <activationFlags>1000</activationFlags>
+      <activationConditions>1</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
+    <Action name="mirrorAllNodesY">
+      <icon>symmetry-vertical</icon>
+      <text>Mirror All Layers &Vertically</text>
+      <whatsThis></whatsThis>
+      <toolTip>Mirror All Layers Vertically</toolTip>
+      <iconText>Mirror All Layers Vertically</iconText>
+      <activationFlags>1000</activationFlags>
+      <activationConditions>1</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
+    <Action name="rotateAllLayers">
+      <icon></icon>
+      <text>&Rotate All Layers...</text>
+      <whatsThis></whatsThis>
+      <toolTip>Rotate All Layers</toolTip>
+      <iconText>Rotate All Layers</iconText>
+      <activationFlags>1000</activationFlags>
+      <activationConditions>1</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
+    <Action name="rotateAllLayersCW90">
+      <icon>object-rotate-right</icon>
+      <text>Rotate All &Layers 90° to the Right</text>
+      <whatsThis></whatsThis>
+      <toolTip>Rotate All Layers 90° to the Right</toolTip>
+      <iconText>Rotate All Layers 90° to the Right</iconText>
+      <activationFlags>1000</activationFlags>
+      <activationConditions>1</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
+    <Action name="rotateAllLayersCCW90">
+      <icon>object-rotate-left</icon>
+      <text>Rotate All Layers &90° to the Left</text>
+      <whatsThis></whatsThis>
+      <toolTip>Rotate All Layers 90° to the Left</toolTip>
+      <iconText>Rotate All Layers 90° to the Left</iconText>
+      <activationFlags>1000</activationFlags>
+      <activationConditions>1</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
+    <Action name="rotateAllLayers180">
+      <icon></icon>
+      <text>Rotate All Layers &180°</text>
+      <whatsThis></whatsThis>
+      <toolTip>Rotate All Layers 180°</toolTip>
+      <iconText>Rotate All Layers 180°</iconText>
+      <activationFlags>1000</activationFlags>
+      <activationConditions>1</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
+    <Action name="scaleAllLayers">
+      <icon></icon>
+      <text>Scale All &Layers to new Size...</text>
+      <whatsThis></whatsThis>
+      <toolTip>Scale All Layers to new Size</toolTip>
+      <iconText>Scale All Layers to new Size</iconText>
+      <activationFlags>100000</activationFlags>
+      <activationConditions>1</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
+    <Action name="shearAllLayers">
+      <icon></icon>
+      <text>&Shear All Layers...</text>
+      <whatsThis></whatsThis>
+      <toolTip>Shear All Layers</toolTip>
+      <iconText>Shear All Layers</iconText>
+      <activationFlags>1000</activationFlags>
+      <activationConditions>1</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
     <Action name="offsetlayer">
       <icon></icon>
       <text>&Offset Layer...</text>
diff --git a/krita/krita4.xmlgui b/krita/krita4.xmlgui
index d050f120370..5e6da25a83f 100644
--- a/krita/krita4.xmlgui
+++ b/krita/krita4.xmlgui
@@ -236,6 +236,21 @@ xsi:schemaLocation="http://www.kde.org/standards/kxmlgui/1.0  http://www.kde.org
          <Action name="shearlayer"/>
          <Action name="offsetlayer"/>
       </Menu>
+      <Menu name="LayerTransformAll">
+         <text>Transform &All Layers</text>
+         <Action name="mirrorAllNodesX"/>
+         <Action name="mirrorAllNodesY"/>
+         <Action name="scaleAllLayers"/>
+         <Menu name="Rotate">
+             <text>&Rotate</text>
+             <Action name="rotateAllLayers"/>
+             <Separator/>
+             <Action name="rotateAllLayersCW90"/>
+             <Action name="rotateAllLayersCCW90"/>
+             <Action name="rotateAllLayers180"/>
+         </Menu>
+         <Action name="shearAllLayers"/>
+      </Menu>
       <Menu name="LayerSplitAlpha">
          <text>S&plit</text>
          <Menu name="LayerSplitAlpha">
diff --git a/libs/image/CMakeLists.txt b/libs/image/CMakeLists.txt
index 06d757f161a..9696acc0ce7 100644
--- a/libs/image/CMakeLists.txt
+++ b/libs/image/CMakeLists.txt
@@ -110,12 +110,14 @@ set(kritaimage_LIB_SRCS
    commands_new/kis_switch_current_time_command.cpp
    commands_new/kis_change_projection_color_command.cpp
    commands_new/kis_activate_selection_mask_command.cpp
+   commands_new/kis_transaction_based_command.cpp
    processing/kis_do_nothing_processing_visitor.cpp
    processing/kis_simple_processing_visitor.cpp
    processing/kis_crop_processing_visitor.cpp
    processing/kis_crop_selections_processing_visitor.cpp
    processing/kis_transform_processing_visitor.cpp
    processing/kis_mirror_processing_visitor.cpp
+   processing/KisSelectionBasedProcessingHelper.cpp
    filter/kis_filter.cc
    filter/kis_filter_category_ids.cpp
    filter/kis_filter_configuration.cc
diff --git a/libs/ui/kis_transaction_based_command.cpp b/libs/image/commands_new/kis_transaction_based_command.cpp
similarity index 100%
rename from libs/ui/kis_transaction_based_command.cpp
rename to libs/image/commands_new/kis_transaction_based_command.cpp
diff --git a/libs/ui/kis_transaction_based_command.h b/libs/image/commands_new/kis_transaction_based_command.h
similarity index 92%
rename from libs/ui/kis_transaction_based_command.h
rename to libs/image/commands_new/kis_transaction_based_command.h
index a47b1d40e0a..8c381bb4763 100644
--- a/libs/ui/kis_transaction_based_command.h
+++ b/libs/image/commands_new/kis_transaction_based_command.h
@@ -19,10 +19,10 @@
 #ifndef KIS_TRANSACTION_BASED_COMMAND_H
 #define KIS_TRANSACTION_BASED_COMMAND_H
 
-#include <kritaui_export.h>
+#include <kritaimage_export.h>
 #include <kundo2command.h>
 
-class KRITAUI_EXPORT KisTransactionBasedCommand : public KUndo2Command
+class KRITAIMAGE_EXPORT KisTransactionBasedCommand : public KUndo2Command
 {
 public:
     KisTransactionBasedCommand(const KUndo2MagicString &text = KUndo2MagicString(), KUndo2Command *parent = 0);
diff --git a/libs/image/kis_image.cc b/libs/image/kis_image.cc
index fbea2059cde..37b47fc1a4b 100644
--- a/libs/image/kis_image.cc
+++ b/libs/image/kis_image.cc
@@ -741,33 +741,64 @@ void KisImage::scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterSt
     applicator.end();
 }
 
-void KisImage::scaleNode(KisNodeSP node, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy)
+void KisImage::scaleNode(KisNodeSP node, const QPointF &center, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selection)
 {
     KUndo2MagicString actionName(kundo2_i18n("Scale Layer"));
     KisImageSignalVector emitSignals;
     emitSignals << ModifiedSignal;
 
+    QPointF offset;
+    {
+        KisTransformWorker worker(0,
+                                  scaleX, scaleY,
+                                  0, 0, 0, 0,
+                                  0.0,
+                                  0, 0, 0, 0);
+        QTransform transform = worker.transform();
+
+        offset = center - transform.map(center);
+    }
+
     KisProcessingApplicator applicator(this, node,
                                        KisProcessingApplicator::RECURSIVE,
                                        emitSignals, actionName);
 
-    KisProcessingVisitorSP visitor =
+    KisTransformProcessingVisitor *visitor =
         new KisTransformProcessingVisitor(scaleX, scaleY,
                                           0, 0,
                                           QPointF(),
                                           0,
-                                          0, 0,
+                                          offset.x(), offset.y(),
                                           filterStrategy);
 
-    applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
+    visitor->setSelection(selection);
+
+    if (selection) {
+        applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
+    } else {
+        applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
+    }
+
     applicator.end();
 }
 
 void KisImage::rotateImpl(const KUndo2MagicString &actionName,
                           KisNodeSP rootNode,
+                          double radians,
                           bool resizeImage,
-                          double radians)
+                          KisSelectionSP selection)
 {
+    // we can either transform (and resize) the whole image or
+    // transform a selection, we cannot do both at the same time
+    KIS_SAFE_ASSERT_RECOVER(!(bool(selection) && resizeImage)) {
+        selection = 0;
+    }
+
+    const QRect baseBounds =
+        resizeImage ? bounds() :
+        selection ? selection->selectedExactRect() :
+        rootNode->exactBounds();
+
     QPointF offset;
     QSize newSize;
 
@@ -780,12 +811,12 @@ void KisImage::rotateImpl(const KUndo2MagicString &actionName,
         QTransform transform = worker.transform();
 
         if (resizeImage) {
-            QRect newRect = transform.mapRect(bounds());
+            QRect newRect = transform.mapRect(baseBounds);
             newSize = newRect.size();
             offset = -newRect.topLeft();
         }
         else {
-            QPointF origin = QRectF(rootNode->exactBounds()).center();
+            QPointF origin = QRectF(baseBounds).center();
 
             newSize = size();
             offset = -(transform.map(origin) - origin);
@@ -793,11 +824,12 @@ void KisImage::rotateImpl(const KUndo2MagicString &actionName,
     }
 
     bool sizeChanged = resizeImage &&
-        (newSize.width() != width() || newSize.height() != height());
+        (newSize.width() != baseBounds.width() ||
+         newSize.height() != baseBounds.height());
 
     // These signals will be emitted after processing is done
     KisImageSignalVector emitSignals;
-    if (sizeChanged) emitSignals << ComplexSizeChangedSignal(bounds(), newSize);
+    if (sizeChanged) emitSignals << ComplexSizeChangedSignal(baseBounds, newSize);
     emitSignals << ModifiedSignal;
 
     // These flags determine whether updates are transferred to the UI during processing
@@ -813,14 +845,21 @@ void KisImage::rotateImpl(const KUndo2MagicString &actionName,
 
     KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value("Bicubic");
 
-    KisProcessingVisitorSP visitor =
+    KisTransformProcessingVisitor *visitor =
             new KisTransformProcessingVisitor(1.0, 1.0, 0.0, 0.0,
                                               QPointF(),
                                               radians,
                                               offset.x(), offset.y(),
                                               filter);
+    if (selection) {
+        visitor->setSelection(selection);
+    }
 
-    applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
+    if (selection) {
+        applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
+    } else {
+        applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
+    }
 
     if (sizeChanged) {
         applicator.applyCommand(new KisImageResizeCommand(this, newSize));
@@ -831,15 +870,15 @@ void KisImage::rotateImpl(const KUndo2MagicString &actionName,
 
 void KisImage::rotateImage(double radians)
 {
-    rotateImpl(kundo2_i18n("Rotate Image"), root(), true, radians);
+    rotateImpl(kundo2_i18n("Rotate Image"), root(), radians, true, 0);
 }
 
-void KisImage::rotateNode(KisNodeSP node, double radians)
+void KisImage::rotateNode(KisNodeSP node, double radians, KisSelectionSP selection)
 {
     if (node->inherits("KisMask")) {
-        rotateImpl(kundo2_i18n("Rotate Mask"), node, false, radians);
+        rotateImpl(kundo2_i18n("Rotate Mask"), node, radians, false, selection);
     } else {
-        rotateImpl(kundo2_i18n("Rotate Layer"), node, false, radians);
+        rotateImpl(kundo2_i18n("Rotate Layer"), node, radians, false, selection);
     }
 }
 
@@ -847,8 +886,15 @@ void KisImage::shearImpl(const KUndo2MagicString &actionName,
                          KisNodeSP rootNode,
                          bool resizeImage,
                          double angleX, double angleY,
-                         const QPointF &origin)
+                         KisSelectionSP selection)
 {
+    const QRect baseBounds =
+        resizeImage ? bounds() :
+        selection ? selection->selectedExactRect() :
+        rootNode->exactBounds();
+
+    const QPointF origin = QRectF(baseBounds).center();
+
     //angleX, angleY are in degrees
     const qreal pi = 3.1415926535897932385;
     const qreal deg2rad = pi / 180.0;
@@ -866,15 +912,15 @@ void KisImage::shearImpl(const KUndo2MagicString &actionName,
                                   0,
                                   0, 0, 0, 0);
 
-        QRect newRect = worker.transform().mapRect(bounds());
+        QRect newRect = worker.transform().mapRect(baseBounds);
         newSize = newRect.size();
         if (resizeImage) offset = -newRect.topLeft();
     }
 
-    if (newSize == size()) return;
+    if (newSize == baseBounds.size()) return;
 
     KisImageSignalVector emitSignals;
-    if (resizeImage) emitSignals << ComplexSizeChangedSignal(bounds(), newSize);
+    if (resizeImage) emitSignals << ComplexSizeChangedSignal(baseBounds, newSize);
     emitSignals << ModifiedSignal;
 
     KisProcessingApplicator::ProcessingFlags signalFlags =
@@ -887,14 +933,22 @@ void KisImage::shearImpl(const KUndo2MagicString &actionName,
 
     KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value("Bilinear");
 
-    KisProcessingVisitorSP visitor =
+    KisTransformProcessingVisitor *visitor =
             new KisTransformProcessingVisitor(1.0, 1.0,
                                               tanX, tanY, origin,
                                               0,
                                               offset.x(), offset.y(),
                                               filter);
 
-    applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
+    if (selection) {
+        visitor->setSelection(selection);
+    }
+
+    if (selection) {
+        applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
+    } else {
+        applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
+    }
 
     if (resizeImage) {
         applicator.applyCommand(new KisImageResizeCommand(this, newSize));
@@ -903,23 +957,21 @@ void KisImage::shearImpl(const KUndo2MagicString &actionName,
     applicator.end();
 }
 
-void KisImage::shearNode(KisNodeSP node, double angleX, double angleY)
+void KisImage::shearNode(KisNodeSP node, double angleX, double angleY, KisSelectionSP selection)
 {
-    QPointF shearOrigin = QRectF(bounds()).center();
-
     if (node->inherits("KisMask")) {
         shearImpl(kundo2_i18n("Shear Mask"), node, false,
-                  angleX, angleY, shearOrigin);
+                  angleX, angleY, selection);
     } else {
         shearImpl(kundo2_i18n("Shear Layer"), node, false,
-                  angleX, angleY, shearOrigin);
+                  angleX, angleY, selection);
     }
 }
 
 void KisImage::shear(double angleX, double angleY)
 {
     shearImpl(kundo2_i18n("Shear Image"), m_d->rootLayer, true,
-              angleX, angleY, QPointF());
+              angleX, angleY, 0);
 }
 
 void KisImage::convertImageColorSpace(const KoColorSpace *dstColorSpace,
diff --git a/libs/image/kis_image.h b/libs/image/kis_image.h
index ba687623131..148ebd2da89 100644
--- a/libs/image/kis_image.h
+++ b/libs/image/kis_image.h
@@ -285,7 +285,7 @@ public:
      * a background, so you cannot expect the image having new size
      * right after ths call.
      */
-    void scaleNode(KisNodeSP node, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy);
+    void scaleNode(KisNodeSP node, const QPointF &center, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selection);
 
     /**
      * @brief start asynchronous operation on rotating the image
@@ -312,7 +312,7 @@ public:
      * a background, so you cannot expect the operation being completed
      * right after the call
      */
-    void rotateNode(KisNodeSP node, double radians);
+    void rotateNode(KisNodeSP node, double radians, KisSelectionSP selection);
 
     /**
      * @brief start asynchronous operation on shearing the image
@@ -339,7 +339,7 @@ public:
      * a background, so you cannot expect the operation being completed
      * right after the call
      */
-    void shearNode(KisNodeSP node, double angleX, double angleY);
+    void shearNode(KisNodeSP node, double angleX, double angleY, KisSelectionSP selection);
 
     /**
      * Convert the image and all its layers to the dstColorSpace
@@ -1066,11 +1066,10 @@ private:
     void emitSizeChanged();
 
     void resizeImageImpl(const QRect& newRect, bool cropLayers);
-    void rotateImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode,
-                    bool resizeImage, double radians);
+    void rotateImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode, double radians,
+                    bool resizeImage, KisSelectionSP selection);
     void shearImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode,
-                   bool resizeImage, double angleX, double angleY,
-                   const QPointF &origin);
+                   bool resizeImage, double angleX, double angleY, KisSelectionSP selection);
 
     void safeRemoveTwoNodes(KisNodeSP node1, KisNodeSP node2);
 
diff --git a/libs/image/kis_processing_applicator.cpp b/libs/image/kis_processing_applicator.cpp
index f8d101b1980..08f493c3ed8 100644
--- a/libs/image/kis_processing_applicator.cpp
+++ b/libs/image/kis_processing_applicator.cpp
@@ -207,6 +207,12 @@ void KisProcessingApplicator::applyVisitor(KisProcessingVisitorSP visitor,
                                            KisStrokeJobData::Sequentiality sequentiality,
                                            KisStrokeJobData::Exclusivity exclusivity)
 {
+    KUndo2Command *initCommand = visitor->createInitCommand();
+    if (initCommand) {
+        applyCommand(initCommand,
+                     KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL);
+    }
+
     if(!m_flags.testFlag(RECURSIVE)) {
         applyCommand(new KisProcessingCommand(visitor, m_node),
                      sequentiality, exclusivity);
@@ -220,6 +226,12 @@ void KisProcessingApplicator::applyVisitorAllFrames(KisProcessingVisitorSP visit
                                                     KisStrokeJobData::Sequentiality sequentiality,
                                                     KisStrokeJobData::Exclusivity exclusivity)
 {
+    KUndo2Command *initCommand = visitor->createInitCommand();
+    if (initCommand) {
+        applyCommand(initCommand,
+                     KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL);
+    }
+
     KisLayerUtils::FrameJobs jobs;
 
     // TODO: implement a nonrecursive case when !m_flags.testFlag(RECURSIVE)
diff --git a/libs/image/kis_processing_visitor.cpp b/libs/image/kis_processing_visitor.cpp
index accd4ae9932..7aa9938ab86 100644
--- a/libs/image/kis_processing_visitor.cpp
+++ b/libs/image/kis_processing_visitor.cpp
@@ -56,3 +56,8 @@ KoUpdater* KisProcessingVisitor::ProgressHelper::updater() const
 KisProcessingVisitor::~KisProcessingVisitor()
 {
 }
+
+KUndo2Command *KisProcessingVisitor::createInitCommand()
+{
+    return 0;
+}
diff --git a/libs/image/kis_processing_visitor.h b/libs/image/kis_processing_visitor.h
index 6d3fe843698..4c5114897b5 100644
--- a/libs/image/kis_processing_visitor.h
+++ b/libs/image/kis_processing_visitor.h
@@ -39,6 +39,7 @@ class KisTransparencyMask;
 class KisSelectionMask;
 class KisGeneratorLayer;
 class KisColorizeMask;
+class KUndo2Command;
 
 /**
  * A visitor that processes a single layer; it does not recurse into the
@@ -63,6 +64,13 @@ public:
     virtual void visit(KisColorizeMask *mask, KisUndoAdapter *undoAdapter) = 0;
     virtual void visit(KisSelectionMask *mask, KisUndoAdapter *undoAdapter) = 0;
 
+    /**
+     * Create a command that initializes the processing visitor before running
+     * on all the layers. The command is executed sequentially, non-exclusively
+     * on the image by applicator.
+     */
+    virtual KUndo2Command* createInitCommand();
+
 public:
     class KRITAIMAGE_EXPORT ProgressHelper {
     public:
diff --git a/libs/image/processing/KisSelectionBasedProcessingHelper.cpp b/libs/image/processing/KisSelectionBasedProcessingHelper.cpp
new file mode 100644
index 00000000000..17fcb77bab7
--- /dev/null
+++ b/libs/image/processing/KisSelectionBasedProcessingHelper.cpp
@@ -0,0 +1,91 @@
+#include "KisSelectionBasedProcessingHelper.h"
+
+#include "kis_paint_device.h"
+#include "kis_painter.h"
+#include "kis_selection.h"
+#include "kis_transaction_based_command.h"
+#include "kis_transaction.h"
+#include "kis_undo_adapter.h"
+
+KisSelectionBasedProcessingHelper::KisSelectionBasedProcessingHelper(KisSelectionSP selection, Functor func)
+    : m_selection(selection),
+      m_func(func)
+{
+}
+
+void KisSelectionBasedProcessingHelper::setSelection(KisSelectionSP selection)
+{
+    m_selection = selection;
+}
+
+KUndo2Command *KisSelectionBasedProcessingHelper::createInitCommand(Functor func)
+{
+    if (!m_selection) return 0;
+
+    struct ProcessSelectionCommand : KisTransactionBasedCommand {
+        ProcessSelectionCommand(KisSelectionSP selection,
+                                KisSelectionSP cutSelection,
+                                std::function<void(KisPaintDeviceSP)> func)
+            : m_selection(selection),
+              m_cutSelection(cutSelection),
+              m_func(func)
+        {
+        }
+
+        KUndo2Command* paint() {
+            m_cutSelection->pixelSelection()->makeCloneFromRough(m_selection->pixelSelection(), m_selection->selectedRect());
+
+            KisTransaction t(m_selection->pixelSelection());
+            m_func(m_selection->pixelSelection());
+
+            return t.endAndTake();
+        }
+
+        KisSelectionSP m_selection;
+        KisSelectionSP m_cutSelection;
+        Functor m_func;
+    };
+
+    m_cutSelection = new KisSelection();
+    return new ProcessSelectionCommand(m_selection, m_cutSelection, func);
+}
+
+KUndo2Command *KisSelectionBasedProcessingHelper::createInitCommand()
+{
+    return createInitCommand(m_func);
+}
+
+void KisSelectionBasedProcessingHelper::transformPaintDevice(KisPaintDeviceSP device, KisUndoAdapter *undoAdapter)
+{
+    transformPaintDevice(device, undoAdapter, m_func);
+}
+
+void KisSelectionBasedProcessingHelper::transformPaintDevice(KisPaintDeviceSP device, KisUndoAdapter *undoAdapter, Functor func)
+{
+    KIS_SAFE_ASSERT_RECOVER_RETURN(!!m_selection == !!m_cutSelection);
+
+    if (m_selection && m_cutSelection) {
+        // we have already processed the selection in the init command so try to skip it
+        if (device != static_cast<KisPaintDevice*>(m_selection->pixelSelection().data())) {
+            KisTransaction transaction(device);
+
+            const QRect cutBounds = m_cutSelection->selectedExactRect();
+            const QRect pasteBounds = m_selection->selectedExactRect();
+
+
+            KisPaintDeviceSP tempDev = new KisPaintDevice(device->colorSpace());
+            tempDev->makeCloneFromRough(device, cutBounds);
+
+            func(tempDev);
+
+            device->clearSelection(m_cutSelection);
+            KisPainter::copyAreaOptimized(pasteBounds.topLeft(), tempDev, device, pasteBounds, m_selection);
+            transaction.commit(undoAdapter);
+
+        }
+    } else {
+        KisTransaction transaction(device);
+        func(device);
+        transaction.commit(undoAdapter);
+    }
+}
diff --git a/libs/image/processing/KisSelectionBasedProcessingHelper.h b/libs/image/processing/KisSelectionBasedProcessingHelper.h
new file mode 100644
index 00000000000..20bfa0b8455
--- /dev/null
+++ b/libs/image/processing/KisSelectionBasedProcessingHelper.h
@@ -0,0 +1,37 @@
+#ifndef KISSELECTIONBASEDPROCESSINGHELPER_H
+#define KISSELECTIONBASEDPROCESSINGHELPER_H
+
+#include "kritaimage_export.h"
+#include "kis_types.h"
+
+#include <functional>
+#include <QRect>
+
+class KisUndoAdapter;
+
+
+class KisSelectionBasedProcessingHelper
+{
+public:
+    using Functor = std::function<void(KisPaintDeviceSP)>;
+public:
+    KisSelectionBasedProcessingHelper(KisSelectionSP selection, Functor func);
+
+    void setSelection(KisSelectionSP selection);
+
+    KUndo2Command *createInitCommand();
+    KUndo2Command *createInitCommand(Functor func);
+
+
+    void transformPaintDevice(KisPaintDeviceSP device, KisUndoAdapter *undoAdapter);
+
+    void transformPaintDevice(KisPaintDeviceSP device, KisUndoAdapter *undoAdapter, Functor func);
+
+
+private:
+    KisSelectionSP m_selection;
+    KisSelectionSP m_cutSelection;
+    Functor m_func;
+};
+
+#endif // KISSELECTIONBASEDPROCESSINGHELPER_H
diff --git a/libs/image/processing/kis_mirror_processing_visitor.cpp b/libs/image/processing/kis_mirror_processing_visitor.cpp
index 2dc840fba80..814cc2fe240 100644
--- a/libs/image/processing/kis_mirror_processing_visitor.cpp
+++ b/libs/image/processing/kis_mirror_processing_visitor.cpp
@@ -22,33 +22,50 @@
 #include "kis_transaction.h"
 #include "kis_node.h"
 #include "kis_image.h"
+#include "kis_painter.h"
 
 #include "kis_transform_worker.h"
 #include "lazybrush/kis_colorize_mask.h"
 #include "processing/kis_transform_processing_visitor.h"
 
+#include "commands_new/kis_transaction_based_command.h"
+#include <functional>
+
 
 KisMirrorProcessingVisitor::KisMirrorProcessingVisitor(const QRect &bounds, Qt::Orientation orientation)
-    : m_bounds(bounds), m_orientation(orientation)
+    : m_bounds(bounds),
+      m_orientation(orientation),
+      m_selectionHelper(0, std::bind(&KisMirrorProcessingVisitor::mirrorDevice, this, std::placeholders::_1))
 {
+    m_axis = m_orientation == Qt::Horizontal ?
+        m_bounds.x() + 0.5 * m_bounds.width() :
+        m_bounds.y() + 0.5 * m_bounds.height();
 }
 
-void KisMirrorProcessingVisitor::transformPaintDevice(KisPaintDeviceSP device, KisUndoAdapter *undoAdapter)
+KisMirrorProcessingVisitor::KisMirrorProcessingVisitor(KisSelectionSP selection, Qt::Orientation orientation)
+    : KisMirrorProcessingVisitor(selection->selectedExactRect(), orientation)
 {
-    KisTransaction transaction(device);
+    m_selectionHelper.setSelection(selection);
+}
 
-    qreal axis = m_orientation == Qt::Horizontal ?
-        m_bounds.x() + 0.5 * m_bounds.width() :
-        m_bounds.y() + 0.5 * m_bounds.height();
+KUndo2Command *KisMirrorProcessingVisitor::createInitCommand()
+{
+    return m_selectionHelper.createInitCommand();
+}
 
-    KisTransformWorker::mirror(device, axis, m_orientation);
-    transaction.commit(undoAdapter);
+void KisMirrorProcessingVisitor::mirrorDevice(KisPaintDeviceSP device)
+{
+    KisTransformWorker::mirror(device, m_axis, m_orientation);
+}
+
+void KisMirrorProcessingVisitor::transformPaintDevice(KisPaintDeviceSP device, KisUndoAdapter *undoAdapter)
+{
+    m_selectionHelper.transformPaintDevice(device, undoAdapter);
 }
 
 void KisMirrorProcessingVisitor::visitNodeWithPaintDevice(KisNode *node, KisUndoAdapter *undoAdapter)
 {
     transformPaintDevice(node->paintDevice(), undoAdapter);
-
 }
 
 void KisMirrorProcessingVisitor::visitExternalLayer(KisExternalLayer *layer, KisUndoAdapter *undoAdapter)
diff --git a/libs/image/processing/kis_mirror_processing_visitor.h b/libs/image/processing/kis_mirror_processing_visitor.h
index e8f294070c0..e09f64c10e2 100644
--- a/libs/image/processing/kis_mirror_processing_visitor.h
+++ b/libs/image/processing/kis_mirror_processing_visitor.h
@@ -23,11 +23,14 @@
 #include <QRect>
 #include "kis_types.h"
 
+#include "KisSelectionBasedProcessingHelper.h"
+
 
 class KRITAIMAGE_EXPORT KisMirrorProcessingVisitor : public KisSimpleProcessingVisitor
 {
 public:
     KisMirrorProcessingVisitor(const QRect &bounds, Qt::Orientation orientation);
+    KisMirrorProcessingVisitor(KisSelectionSP selection, Qt::Orientation orientation);
 
 private:
     void visitNodeWithPaintDevice(KisNode *node, KisUndoAdapter *undoAdapter) override;
@@ -35,11 +38,18 @@ private:
 
     void visitColorizeMask(KisColorizeMask *node, KisUndoAdapter *undoAdapter) override;
 
+    KUndo2Command* createInitCommand() override;
+
+    void mirrorDevice(KisPaintDeviceSP device);
+
 private:
     void transformPaintDevice(KisPaintDeviceSP device, KisUndoAdapter *undoAdapter);
 
     QRect m_bounds;
     Qt::Orientation m_orientation;
+    qreal m_axis = 0.0;
+
+    KisSelectionBasedProcessingHelper m_selectionHelper;
 };
 
 #endif /* __KIS_MIRROR_PROCESSING_VISITOR_H */
diff --git a/libs/image/processing/kis_transform_processing_visitor.cpp b/libs/image/processing/kis_transform_processing_visitor.cpp
index d62099c536f..0e6f61a9cdb 100644
--- a/libs/image/processing/kis_transform_processing_visitor.cpp
+++ b/libs/image/processing/kis_transform_processing_visitor.cpp
@@ -62,9 +62,24 @@ KisTransformProcessingVisitor(qreal  xscale, qreal  yscale,
     , m_filter(filter)
     , m_angle(angle)
     , m_shapesCorrection(shapesCorrection)
+    , m_selectionHelper(0, KisSelectionBasedProcessingHelper::Functor())
 {
 }
 
+void KisTransformProcessingVisitor::setSelection(KisSelectionSP selection)
+{
+    m_selectionHelper.setSelection(selection);
+}
+
+KUndo2Command *KisTransformProcessingVisitor::createInitCommand()
+{
+    return m_selectionHelper.createInitCommand(
+        std::bind(&KisTransformProcessingVisitor::transformOneDevice,
+                  this,
+                  std::placeholders::_1,
+                  (KoUpdater*)0));
+}
+
 void KisTransformProcessingVisitor::visit(KisNode *node, KisUndoAdapter *undoAdapter)
 {
     Q_UNUSED(node);
@@ -186,6 +201,16 @@ void KisTransformProcessingVisitor::transformClones(KisLayer *layer, KisUndoAdap
 
 void KisTransformProcessingVisitor::transformPaintDevice(KisPaintDeviceSP device, KisUndoAdapter *adapter, const ProgressHelper &helper)
 {
+    m_selectionHelper.transformPaintDevice(device,
+                                           adapter,
+                                           std::bind(&KisTransformProcessingVisitor::transformOneDevice,
+                                                     this,
+                                                     std::placeholders::_1,
+                                                     helper.updater()));
+
+
+    return;
+
     KisTransaction transaction(kundo2_i18n("Transform Layer"), device);
 
     KisTransformWorker tw(device, m_sx, m_sy, m_shearx, m_sheary,
@@ -196,12 +221,18 @@ void KisTransformProcessingVisitor::transformPaintDevice(KisPaintDeviceSP device
     transaction.commit(adapter);
 }
 
-void KisTransformProcessingVisitor::transformSelection(KisSelectionSP selection, KisUndoAdapter *adapter, const ProgressHelper &helper)
+void KisTransformProcessingVisitor::transformOneDevice(KisPaintDeviceSP device,
+                                                       KoUpdater *updater)
 {
-    if(selection->hasPixelSelection()) {
-        transformPaintDevice(selection->pixelSelection(), adapter, helper);
-    }
+    KisTransformWorker tw(device, m_sx, m_sy, m_shearx, m_sheary,
+                          m_shearOrigin.x(), m_shearOrigin.y(),
+                          m_angle, m_tx, m_ty, updater,
+                          m_filter);
+    tw.run();
+}
 
+void KisTransformProcessingVisitor::transformSelection(KisSelectionSP selection, KisUndoAdapter *adapter, const ProgressHelper &helper)
+{
     if (selection->hasShapeSelection()) {
         KisTransformWorker tw(selection->projection(), m_sx, m_sy, m_shearx, m_sheary,
                               m_shearOrigin.x(), m_shearOrigin.y(),
@@ -213,8 +244,9 @@ void KisTransformProcessingVisitor::transformSelection(KisSelectionSP selection,
         if (command) {
             adapter->addCommand(command);
         }
+    } else {
+        transformPaintDevice(selection->pixelSelection(), adapter, helper);
     }
 
     selection->updateProjection();
 }
-
diff --git a/libs/image/processing/kis_transform_processing_visitor.h b/libs/image/processing/kis_transform_processing_visitor.h
index e72fa64bbdc..d8b8a66e6a3 100644
--- a/libs/image/processing/kis_transform_processing_visitor.h
+++ b/libs/image/processing/kis_transform_processing_visitor.h
@@ -20,7 +20,7 @@
 #define __KIS_TRANSFORM_PROCESSING_VISITOR_H
 
 #include "kis_processing_visitor.h"
-
+#include "KisSelectionBasedProcessingHelper.h"
 
 #include <kis_types.h>
 
@@ -39,6 +39,10 @@ public:
                                   KisFilterStrategy *filter,
                                   const QTransform &shapesCorrection = QTransform());
 
+    void setSelection(KisSelectionSP selection);
+    KUndo2Command *createInitCommand();
+
+
     void visit(KisNode *node, KisUndoAdapter *undoAdapter) override;
     void visit(KisPaintLayer *layer, KisUndoAdapter *undoAdapter) override;
     void visit(KisGroupLayer *layer, KisUndoAdapter *undoAdapter) override;
@@ -57,6 +61,8 @@ private:
     void transformPaintDevice(KisPaintDeviceSP device, KisUndoAdapter *adapter, const ProgressHelper &helper);
     void transformSelection(KisSelectionSP selection, KisUndoAdapter *adapter, const ProgressHelper &helper);
 
+    void transformOneDevice(KisPaintDeviceSP device, KoUpdater *updater);
+
 private:
     qreal m_sx, m_sy;
     qint32 m_tx, m_ty;
@@ -65,6 +71,7 @@ private:
     KisFilterStrategy *m_filter;
     qreal m_angle;
     QTransform m_shapesCorrection;
+    KisSelectionBasedProcessingHelper m_selectionHelper;
 };
 
 #endif /* __KIS_TRANSFORM_PROCESSING_VISITOR_H */
diff --git a/libs/libkis/Node.cpp b/libs/libkis/Node.cpp
index 3341db586bb..521dc6e5082 100644
--- a/libs/libkis/Node.cpp
+++ b/libs/libkis/Node.cpp
@@ -50,6 +50,7 @@
 
 #include <kis_raster_keyframe_channel.h>
 #include <kis_keyframe.h>
+#include "kis_selection.h"
 
 #include "Krita.h"
 #include "Node.h"
@@ -546,7 +547,7 @@ Node* Node::mergeDown()
     return new Node(d->image, d->node->prevSibling());
 }
 
-void Node::scaleNode(int width, int height, QString strategy)
+void Node::scaleNode(const QPointF &origin, int width, int height, QString strategy)
 {
     if (!d->node) return;
     if (!qobject_cast<KisLayer*>(d->node.data())) return;
@@ -555,7 +556,13 @@ void Node::scaleNode(int width, int height, QString strategy)
     KisFilterStrategy *actualStrategy = KisFilterStrategyRegistry::instance()->get(strategy);
     if (!actualStrategy) actualStrategy = KisFilterStrategyRegistry::instance()->get("Bicubic");
 
-    d->image->scaleNode(d->node, width, height, actualStrategy);
+    const QRect bounds(d->node->exactBounds());
+
+    d->image->scaleNode(d->node,
+                        origin,
+                        qreal(width) / bounds.width(),
+                        qreal(height) / bounds.height(),
+                        actualStrategy, 0);
 }
 
 void Node::rotateNode(double radians)
@@ -564,7 +571,7 @@ void Node::rotateNode(double radians)
     if (!qobject_cast<KisLayer*>(d->node.data())) return;
     if (!d->node->parent()) return;
 
-    d->image->rotateNode(d->node, radians);
+    d->image->rotateNode(d->node, radians, 0);
 }
 
 void Node::cropNode(int x, int y, int w, int h)
@@ -583,7 +590,7 @@ void Node::shearNode(double angleX, double angleY)
     if (!qobject_cast<KisLayer*>(d->node.data())) return;
     if (!d->node->parent()) return;
 
-    d->image->shearNode(d->node, angleX, angleY);
+    d->image->shearNode(d->node, angleX, angleY, 0);
 }
 
 QImage Node::thumbnail(int w, int h)
diff --git a/libs/libkis/Node.h b/libs/libkis/Node.h
index 790ee974c6e..11019b73d49 100644
--- a/libs/libkis/Node.h
+++ b/libs/libkis/Node.h
@@ -489,7 +489,7 @@ public Q_SLOTS:
      * <li>Mitchell</li>
      * </ul>
      */
-    void scaleNode(int width, int height, QString strategy);
+    void scaleNode(const QPointF &origin, int width, int height, QString strategy);
 
     /**
      * @brief rotateNode rotate this layer by the given radians.
diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt
index bb15a54a192..74973e3ba86 100644
--- a/libs/ui/CMakeLists.txt
+++ b/libs/ui/CMakeLists.txt
@@ -294,7 +294,6 @@ set(kritaui_LIB_SRCS
     actions/KisPasteActionFactory.cpp
     input/kis_touch_shortcut.cpp
     kis_document_undo_store.cpp
-    kis_transaction_based_command.cpp
     kis_gui_context_command.cpp
     kis_gui_context_command_p.cpp
     input/kis_tablet_debugger.cpp
diff --git a/libs/ui/actions/kis_selection_action_factories.cpp b/libs/ui/actions/kis_selection_action_factories.cpp
index 5a0b4bb13b3..5952680035d 100644
--- a/libs/ui/actions/kis_selection_action_factories.cpp
+++ b/libs/ui/actions/kis_selection_action_factories.cpp
@@ -54,7 +54,7 @@
 #include "kis_canvas2.h"
 #include "kis_canvas_controller.h"
 #include "kis_selection_manager.h"
-#include "kis_transaction_based_command.h"
+#include "commands_new/kis_transaction_based_command.h"
 #include "kis_selection_filters.h"
 #include "kis_shape_selection.h"
 #include "kis_shape_layer.h"
diff --git a/libs/ui/kis_node_manager.cpp b/libs/ui/kis_node_manager.cpp
index bf1eab182d1..45acb4cf796 100644
--- a/libs/ui/kis_node_manager.cpp
+++ b/libs/ui/kis_node_manager.cpp
@@ -251,12 +251,20 @@ void KisNodeManager::setup(KActionCollection * actionCollection, KisActionManage
     m_d->layerManager.setup(actionManager);
     m_d->maskManager.setup(actionCollection, actionManager);
 
-    KisAction * action  = actionManager->createAction("mirrorNodeX");
+    KisAction * action = 0;
+
+    action = actionManager->createAction("mirrorNodeX");
     connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeX()));
 
     action  = actionManager->createAction("mirrorNodeY");
     connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeY()));
 
+    action = actionManager->createAction("mirrorAllNodesX");
+    connect(action, SIGNAL(triggered()), this, SLOT(mirrorAllNodesX()));
+
+    action  = actionManager->createAction("mirrorAllNodesY");
+    connect(action, SIGNAL(triggered()), this, SLOT(mirrorAllNodesY()));
+
     action = actionManager->createAction("activateNextLayer");
     connect(action, SIGNAL(triggered()), this, SLOT(activateNextNode()));
 
@@ -904,7 +912,7 @@ void KisNodeManager::mirrorNodeX()
     } else if (node->inherits("KisMask")) {
         commandName = kundo2_i18n("Mirror Mask X");
     }
-    mirrorNode(node, commandName, Qt::Horizontal);
+    mirrorNode(node, commandName, Qt::Horizontal, m_d->view->selection());
 }
 
 void KisNodeManager::mirrorNodeY()
@@ -917,7 +925,21 @@ void KisNodeManager::mirrorNodeY()
     } else if (node->inherits("KisMask")) {
         commandName = kundo2_i18n("Mirror Mask Y");
     }
-    mirrorNode(node, commandName, Qt::Vertical);
+    mirrorNode(node, commandName, Qt::Vertical, m_d->view->selection());
+}
+
+void KisNodeManager::mirrorAllNodesX()
+{
+    KisNodeSP node = m_d->view->image()->root();
+    mirrorNode(node, kundo2_i18n("Mirror All Layers X"),
+               Qt::Vertical, m_d->view->selection());
+}
+
+void KisNodeManager::mirrorAllNodesY()
+{
+    KisNodeSP node = m_d->view->image()->root();
+    mirrorNode(node, kundo2_i18n("Mirror All Layers Y"),
+               Qt::Vertical, m_d->view->selection());
 }
 
 void KisNodeManager::activateNextNode()
@@ -979,56 +1001,10 @@ void KisNodeManager::switchToPreviouslyActiveNode()
     }
 }
 
-void KisNodeManager::rotate(double radians)
-{
-    if(!m_d->view->image()) return;
-
-    KisNodeSP node = activeNode();
-    if (!node) return;
-
-    if (!m_d->view->blockUntilOperationsFinished(m_d->view->image())) return;
-
-    m_d->view->image()->rotateNode(node, radians);
-}
-
-void KisNodeManager::rotate180()
-{
-    rotate(M_PI);
-}
-
-void KisNodeManager::rotateLeft90()
-{
-   rotate(-M_PI / 2);
-}
-
-void KisNodeManager::rotateRight90()
-{
-    rotate(M_PI / 2);
-}
-
-void KisNodeManager::shear(double angleX, double angleY)
-{
-    if (!m_d->view->image()) return;
-
-    KisNodeSP node = activeNode();
-    if (!node) return;
-
-    if(!m_d->view->blockUntilOperationsFinished(m_d->view->image())) return;
-
-    m_d->view->image()->shearNode(node, angleX, angleY);
-}
-
-void KisNodeManager::scale(double sx, double sy, KisFilterStrategy *filterStrategy)
-{
-    KisNodeSP node = activeNode();
-    KIS_ASSERT_RECOVER_RETURN(node);
-
-    m_d->view->image()->scaleNode(node, sx, sy, filterStrategy);
-
-    nodesUpdated();
-}
-
-void KisNodeManager::mirrorNode(KisNodeSP node, const KUndo2MagicString& actionName, Qt::Orientation orientation)
+void KisNodeManager::mirrorNode(KisNodeSP node,
+                                const KUndo2MagicString& actionName,
+                                Qt::Orientation orientation,
+                                KisSelectionSP selection)
 {
     KisImageSignalVector emitSignals;
     emitSignals << ModifiedSignal;
@@ -1037,10 +1013,20 @@ void KisNodeManager::mirrorNode(KisNodeSP node, const KUndo2MagicString& actionN
                                        KisProcessingApplicator::RECURSIVE,
                                        emitSignals, actionName);
 
-    KisProcessingVisitorSP visitor =
-        new KisMirrorProcessingVisitor(m_d->view->image()->bounds(), orientation);
+    KisProcessingVisitorSP visitor;
+
+    if (selection) {
+        visitor = new KisMirrorProcessingVisitor(selection, orientation);
+    } else {
+        visitor = new KisMirrorProcessingVisitor(m_d->view->image()->bounds(), orientation);
+    }
+
+    if (!selection) {
+        applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
+    } else {
+        applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
+    }
 
-    applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
     applicator.end();
 
     nodesUpdated();
diff --git a/libs/ui/kis_node_manager.h b/libs/ui/kis_node_manager.h
index dabe293cb2b..29c80823d02 100644
--- a/libs/ui/kis_node_manager.h
+++ b/libs/ui/kis_node_manager.h
@@ -180,7 +180,13 @@ public Q_SLOTS:
     void removeNode();
     void mirrorNodeX();
     void mirrorNodeY();
-    void mirrorNode(KisNodeSP node, const KUndo2MagicString& commandName, Qt::Orientation orientation);
+    void mirrorAllNodesX();
+    void mirrorAllNodesY();
+
+
+    void mirrorNode(KisNodeSP node, const KUndo2MagicString& commandName, Qt::Orientation orientation, KisSelectionSP selection);
+
+
     void activateNextNode();
     void activatePreviousNode();
     void switchToPreviouslyActiveNode();
@@ -195,11 +201,6 @@ public Q_SLOTS:
      */
     void lowerNode();
 
-    void rotate(double radians);
-    void rotate180();
-    void rotateLeft90();
-    void rotateRight90();
-
     void saveNodeAsImage();
     void saveVectorLayerAsImage();
 
@@ -235,12 +236,6 @@ public Q_SLOTS:
     void selectUnlockedNodes();
 
 public:
-
-
-    void shear(double angleX, double angleY);
-
-    void scale(double sx, double sy, KisFilterStrategy *filterStrategy);
-
     void removeSingleNode(KisNodeSP node);
     KisLayerSP createPaintLayer();
 
diff --git a/libs/ui/operations/kis_filter_selection_operation.cpp b/libs/ui/operations/kis_filter_selection_operation.cpp
index d0e3226bb9c..ed02df14574 100644
--- a/libs/ui/operations/kis_filter_selection_operation.cpp
+++ b/libs/ui/operations/kis_filter_selection_operation.cpp
@@ -19,7 +19,7 @@
 
 
 #include "kis_filter_selection_operation.h"
-#include <kis_transaction_based_command.h>
+#include <commands_new/kis_transaction_based_command.h>
 #include <KisViewManager.h>
 #include <kis_stroke_job_strategy.h>
 #include <kis_selection_filters.h>
diff --git a/libs/ui/tests/kis_node_manager_test.cpp b/libs/ui/tests/kis_node_manager_test.cpp
index c8c293d0836..9486dfbf0ea 100644
--- a/libs/ui/tests/kis_node_manager_test.cpp
+++ b/libs/ui/tests/kis_node_manager_test.cpp
@@ -68,80 +68,6 @@ public:
     KisNodeManager *nodeManager;
 };
 
-void testRotateNode(bool useShapeLayer, const QString &name)
-{
-    NodeManagerTester t;
-    if(useShapeLayer) {
-        t.activateShapeLayer();
-    }
-
-    t.nodeManager->rotate(M_PI / 6.0);
-    QTest::qWait(1000);
-    t.image->waitForDone();
-    QVERIFY(t.checkLayersFuzzy(name));
-
-    t.checkUndoWait();
-    t.startConcurrentTask();
-
-    t.nodeManager->rotate(M_PI / 6.0);
-    QTest::qWait(1000);
-    t.image->waitForDone();
-
-    if (!useShapeLayer) {
-        QEXPECT_FAIL("", "The user may run Rotate Layer concurrently. It will cause wrong image/selection size fetched for the crop. There is some barrier needed. At least it doesn't crash.", Continue);
-    }
-    QVERIFY(t.checkLayersFuzzy(name));
-}
-
-void testShearNode(bool useShapeLayer, const QString &name)
-{
-    NodeManagerTester t;
-    if(useShapeLayer) {
-        t.activateShapeLayer();
-    }
-
-    t.nodeManager->shear(30, 0);
-    QTest::qWait(1000);
-    t.image->waitForDone();
-    QVERIFY(t.checkLayersFuzzy(name));
-
-    t.checkUndoWait();
-    t.startConcurrentTask();
-
-    t.nodeManager->shear(30, 0);
-    QTest::qWait(1000);
-    t.image->waitForDone();
-
-    QEXPECT_FAIL("", "The user may run Shear Layer concurrently. It will cause wrong image/selection size fetched for the crop. There is some barrier needed. At least it doesn't crash.", Continue);
-    QVERIFY(t.checkLayersFuzzy(name));
-}
-
-void testScaleNode(bool useShapeLayer, const QString &name)
-{
-    KisFilterStrategy *strategy = new KisBicubicFilterStrategy();
-
-    NodeManagerTester t;
-    if(useShapeLayer) {
-        t.activateShapeLayer();
-    }
-
-    t.nodeManager->scale(0.5, 0.5, strategy);
-    QTest::qWait(1000);
-    t.image->waitForDone();
-    QVERIFY(t.checkLayersFuzzy(name));
-
-    t.checkUndoWait();
-    t.startConcurrentTask();
-
-    t.nodeManager->scale(0.5, 0.5, strategy);
-    QTest::qWait(1000);
-    t.image->waitForDone();
-
-    QVERIFY(t.checkLayersFuzzy(name));
-
-    delete strategy;
-}
-
 void testMirrorNode(bool useShapeLayer, const QString &name, bool mirrorX)
 {
     NodeManagerTester t;
@@ -173,21 +99,6 @@ void testMirrorNode(bool useShapeLayer, const QString &name, bool mirrorX)
     QVERIFY(t.checkLayersFuzzy(name));
 }
 
-void KisNodeManagerTest::testRotatePaintNode()
-{
-    testRotateNode(false, "paint_rotated_30");
-}
-
-void KisNodeManagerTest::testShearPaintNode()
-{
-    testShearNode(false, "paint_shear_30");
-}
-
-void KisNodeManagerTest::testScalePaintNode()
-{
-    testScaleNode(false, "paint_scale_0.5");
-}
-
 void KisNodeManagerTest::testMirrorXPaintNode()
 {
     testMirrorNode(false, "paint_mirrorX", true);
@@ -198,21 +109,6 @@ void KisNodeManagerTest::testMirrorYPaintNode()
     testMirrorNode(false, "paint_mirrorY", false);
 }
 
-void KisNodeManagerTest::testRotateShapeNode()
-{
-    testRotateNode(true, "shape_rotated_30");
-}
-
-void KisNodeManagerTest::testShearShapeNode()
-{
-    testShearNode(true, "shape_shear_30");
-}
-
-void KisNodeManagerTest::testScaleShapeNode()
-{
-    testScaleNode(true, "shape_scale_0.5");
-}
-
 void KisNodeManagerTest::testMirrorShapeNode()
 {
     testMirrorNode(true, "shape_mirrorX", true);
diff --git a/libs/ui/tests/kis_node_manager_test.h b/libs/ui/tests/kis_node_manager_test.h
index 0401b95a1b4..458d512213a 100644
--- a/libs/ui/tests/kis_node_manager_test.h
+++ b/libs/ui/tests/kis_node_manager_test.h
@@ -25,15 +25,9 @@ class KisNodeManagerTest : public QObject
 {
     Q_OBJECT
 private Q_SLOTS:
-    void testRotatePaintNode();
-    void testShearPaintNode();
-    void testScalePaintNode();
     void testMirrorXPaintNode();
     void testMirrorYPaintNode();
 
-    void testRotateShapeNode();
-    void testShearShapeNode();
-    void testScaleShapeNode();
     void testMirrorShapeNode();
 
     void testConvertCloneToPaintLayer();
diff --git a/libs/ui/tool/kis_selection_tool_helper.cpp b/libs/ui/tool/kis_selection_tool_helper.cpp
index 198fae9ac7b..fa3684dd249 100644
--- a/libs/ui/tool/kis_selection_tool_helper.cpp
+++ b/libs/ui/tool/kis_selection_tool_helper.cpp
@@ -36,7 +36,7 @@
 
 #include <kis_icon.h>
 #include "kis_processing_applicator.h"
-#include "kis_transaction_based_command.h"
+#include "commands_new/kis_transaction_based_command.h"
 #include "kis_gui_context_command.h"
 #include "kis_command_utils.h"
 #include "commands/kis_deselect_global_selection_command.h"
diff --git a/plugins/dockers/gamutmask/gamutmask_dock.cpp b/plugins/dockers/gamutmask/gamutmask_dock.cpp
index 781b0244f97..5343eefe7ad 100644
--- a/plugins/dockers/gamutmask/gamutmask_dock.cpp
+++ b/plugins/dockers/gamutmask/gamutmask_dock.cpp
@@ -352,7 +352,7 @@ KoGamutMask *GamutMaskDock::createMaskResource(KoGamutMask* sourceMask, QString
         newMask = new KoGamutMask();
 
         QString defaultPreviewPath = KoResourcePaths::findResource("ko_gamutmasks", "empty_mask_preview.png");
-        KIS_SAFE_ASSERT_RECOVER(!(defaultPreviewPath.isEmpty() || defaultPreviewPath.isNull() || !QFile::exists(defaultPreviewPath)));
+        KIS_SAFE_ASSERT_RECOVER_NOOP(!(defaultPreviewPath.isEmpty() || defaultPreviewPath.isNull() || !QFile::exists(defaultPreviewPath)));
 
         newMask->setImage(QImage(defaultPreviewPath, "PNG"));
     }
diff --git a/plugins/extensions/imagesize/imagesize.cc b/plugins/extensions/imagesize/imagesize.cc
index 7adb79aaa0e..236afc02f17 100644
--- a/plugins/extensions/imagesize/imagesize.cc
+++ b/plugins/extensions/imagesize/imagesize.cc
@@ -57,6 +57,9 @@ ImageSize::ImageSize(QObject *parent, const QVariantList &)
     action = createAction("layersize");
     connect(action, SIGNAL(triggered()), this, SLOT(slotLayerSize()));
 
+    action = createAction("scaleAllLayers");
+    connect(action, SIGNAL(triggered()), this, SLOT(slotScaleAllLayers()));
+
     action  = createAction("selectionscale");
     connect(action, SIGNAL(triggered()), this, SLOT(slotSelectionScale()));
 }
@@ -70,6 +73,8 @@ void ImageSize::slotImageSize()
     KisImageSP image = viewManager()->image().toStrongRef();
     if (!image) return;
 
+    if(!viewManager()->blockUntilOperationsFinished(image)) return;
+
     DlgImageSize * dlgImageSize = new DlgImageSize(viewManager()->mainWindow(), image->width(), image->height(), image->yRes());
     Q_CHECK_PTR(dlgImageSize);
     dlgImageSize->setObjectName("ImageSize");
@@ -88,9 +93,10 @@ void ImageSize::slotImageSize()
 void ImageSize::slotCanvasSize()
 {
     KisImageWSP image = viewManager()->image();
-
     if (!image) return;
 
+    if(!viewManager()->blockUntilOperationsFinished(image)) return;
+
     DlgCanvasSize * dlgCanvasSize = new DlgCanvasSize(viewManager()->mainWindow(), image->width(), image->height(), image->yRes());
     Q_CHECK_PTR(dlgCanvasSize);
 
@@ -105,17 +111,25 @@ void ImageSize::slotCanvasSize()
     delete dlgCanvasSize;
 }
 
-void ImageSize::slotLayerSize()
+void ImageSize::scaleLayerImpl(KisNodeSP rootNode)
 {
     KisImageWSP image = viewManager()->image();
-
     if (!image) return;
 
-    KisPaintDeviceSP dev = viewManager()->activeLayer()->projection();
-    Q_ASSERT(dev);
-    QRect rc = dev->exactBounds();
+    if(!viewManager()->blockUntilOperationsFinished(image)) return;
 
-    DlgLayerSize * dlgLayerSize = new DlgLayerSize(viewManager()->mainWindow(), "LayerSize", rc.width(), rc.height(), image->yRes());
+    QRect bounds;
+    KisSelectionSP selection = viewManager()->selection();
+
+    if (selection) {
+        bounds = selection->selectedExactRect();
+    } else {
+        KisPaintDeviceSP dev = rootNode->projection();
+        KIS_SAFE_ASSERT_RECOVER_RETURN(dev);
+        bounds = dev->exactBounds();
+    }
+
+    DlgLayerSize * dlgLayerSize = new DlgLayerSize(viewManager()->mainWindow(), "LayerSize", bounds.width(), bounds.height(), image->yRes());
     Q_CHECK_PTR(dlgLayerSize);
     dlgLayerSize->setCaption(i18n("Resize Layer"));
 
@@ -123,19 +137,36 @@ void ImageSize::slotLayerSize()
         qint32 w = dlgLayerSize->width();
         qint32 h = dlgLayerSize->height();
 
-        viewManager()->nodeManager()->scale((double)w / ((double)(rc.width())),
-                                     (double)h / ((double)(rc.height())),
-                                     dlgLayerSize->filterType());
+        viewManager()->image()->scaleNode(rootNode,
+                                          QRectF(bounds).center(),
+                                          qreal(w) / bounds.width(),
+                                          qreal(h) / bounds.height(),
+                                          dlgLayerSize->filterType(),
+                                          selection);
     }
     delete dlgLayerSize;
 }
 
+void ImageSize::slotLayerSize()
+{
+    scaleLayerImpl(viewManager()->activeNode());
+}
+
+void ImageSize::slotScaleAllLayers()
+{
+    KisImageWSP image = viewManager()->image();
+    if (!image) return;
+
+    scaleLayerImpl(image->root());
+}
+
 void ImageSize::slotSelectionScale()
 {
     KisImageSP image = viewManager()->image();
-    if (!image) {
-        return;
-    }
+    if (!image) return;
+
+    if(!viewManager()->blockUntilOperationsFinished(image)) return;
+
     KisLayerSP layer = viewManager()->activeLayer();
 
     KIS_ASSERT_RECOVER_RETURN(image && layer);
@@ -156,9 +187,10 @@ void ImageSize::slotSelectionScale()
         qint32 h = dlgSize->height();
 
         image->scaleNode(selectionMask,
+                         QRectF(rc).center(),
                          qreal(w) / rc.width(),
                          qreal(h) / rc.height(),
-                         dlgSize->filterType());
+                         dlgSize->filterType(), 0);
     }
     delete dlgSize;
 }
diff --git a/plugins/extensions/imagesize/imagesize.h b/plugins/extensions/imagesize/imagesize.h
index 892fc333eeb..bcf1093031d 100644
--- a/plugins/extensions/imagesize/imagesize.h
+++ b/plugins/extensions/imagesize/imagesize.h
@@ -23,6 +23,7 @@
 #include <QVariant>
 
 #include <KisActionPlugin.h>
+#include "kis_types.h"
 
 class ImageSize : public KisActionPlugin
 {
@@ -31,12 +32,17 @@ public:
     ImageSize(QObject *parent, const QVariantList &);
     ~ImageSize() override;
 
+private:
+    void scaleLayerImpl(KisNodeSP rootNode);
+
 private Q_SLOTS:
 
     void slotImageSize();
     void slotCanvasSize();
     void slotLayerSize();
     void slotSelectionScale();
+
+    void slotScaleAllLayers();
 };
 
 #endif // IMAGESIZE_H
diff --git a/plugins/extensions/pykrita/sip/krita/Node.sip b/plugins/extensions/pykrita/sip/krita/Node.sip
index fdd535229e5..b52cb24d5c9 100644
--- a/plugins/extensions/pykrita/sip/krita/Node.sip
+++ b/plugins/extensions/pykrita/sip/krita/Node.sip
@@ -55,7 +55,7 @@ public Q_SLOTS:
     Node *duplicate() /Factory/;
     void save(const QString &filename, double xRes, double yRes);
     Node *mergeDown() /Factory/;
-    void scaleNode(int width, int height, QString strategy);
+    void scaleNode(const QPointF &origin, int width, int height, QString strategy);
     void rotateNode(double radians);
     void cropNode(int x, int y, int w, int h);
     void shearNode(double angleX, double angleY);
diff --git a/plugins/extensions/rotateimage/rotateimage.cc b/plugins/extensions/rotateimage/rotateimage.cc
index 97dd9bdeca7..795060f92fd 100644
--- a/plugins/extensions/rotateimage/rotateimage.cc
+++ b/plugins/extensions/rotateimage/rotateimage.cc
@@ -35,6 +35,7 @@
 #include <kis_canvas_resource_provider.h>
 #include <kis_group_layer.h>
 #include <kis_action.h>
+#include <kis_selection.h>
 
 #include "dlg_rotateimage.h"
 
@@ -66,13 +67,25 @@ RotateImage::RotateImage(QObject *parent, const QVariantList &)
     connect(action, SIGNAL(triggered()), this, SLOT(slotRotateLayer()));
 
     action  = createAction("rotateLayer180");
-    connect(action, SIGNAL(triggered()), viewManager()->nodeManager(), SLOT(rotate180()));
+    connect(action, SIGNAL(triggered()), this, SLOT(slotRotateLayer180()));
 
     action  = createAction("rotateLayerCW90");
-    connect(action, SIGNAL(triggered()), viewManager()->nodeManager(), SLOT(rotateRight90()));
+    connect(action, SIGNAL(triggered()), this, SLOT(slotRotateLayerCW90()));
 
     action  = createAction("rotateLayerCCW90");
-    connect(action, SIGNAL(triggered()), viewManager()->nodeManager(), SLOT(rotateLeft90()));
+    connect(action, SIGNAL(triggered()), this, SLOT(slotRotateLayerCCW90()));
+
+    action  = createAction("rotateAllLayers");
+    connect(action, SIGNAL(triggered()), this, SLOT(slotRotateAllLayers()));
+
+    action  = createAction("rotateAllLayersCW90");
+    connect(action, SIGNAL(triggered()), this, SLOT(slotRotateAllLayersCW90()));
+
+    action  = createAction("rotateAllLayersCCW90");
+    connect(action, SIGNAL(triggered()), this, SLOT(slotRotateAllLayersCCW90()));
+
+    action  = createAction("rotateAllLayers180");
+    connect(action, SIGNAL(triggered()), this, SLOT(slotRotateAllLayers180()));
 }
 
 RotateImage::~RotateImage()
@@ -82,9 +95,10 @@ RotateImage::~RotateImage()
 void RotateImage::slotRotateImage()
 {
     KisImageWSP image = viewManager()->image();
-
     if (!image) return;
 
+    if (!viewManager()->blockUntilOperationsFinished(image)) return;
+
     DlgRotateImage * dlgRotateImage = new DlgRotateImage(viewManager()->mainWindow(), "RotateImage");
     Q_CHECK_PTR(dlgRotateImage);
 
@@ -116,22 +130,28 @@ void RotateImage::slotMirrorImageVertical()
 {
     KisImageWSP image = viewManager()->image();
     if (!image) return;
-    viewManager()->nodeManager()->mirrorNode(image->rootLayer(), kundo2_i18n("Mirror Image Vertically"), Qt::Vertical);
+    viewManager()->nodeManager()->mirrorNode(image->rootLayer(),
+                                             kundo2_i18n("Mirror Image Vertically"),
+                                             Qt::Vertical, 0);
 }
 
 void RotateImage::slotMirrorImageHorizontal()
 {
     KisImageWSP image = viewManager()->image();
     if (!image) return;
-    viewManager()->nodeManager()->mirrorNode(image->rootLayer(), kundo2_i18n("Mirror Image Horizontally"), Qt::Horizontal);
+    viewManager()->nodeManager()->mirrorNode(image->rootLayer(),
+                                             kundo2_i18n("Mirror Image Horizontally"),
+                                             Qt::Horizontal, 0);
 }
 
-void RotateImage::slotRotateLayer()
+
+void RotateImage::rotateLayerCustomImpl(KisNodeSP rootNode)
 {
     KisImageWSP image = viewManager()->image();
-
     if (!image) return;
 
+    if (!viewManager()->blockUntilOperationsFinished(image)) return;
+
     DlgRotateImage * dlgRotateImage = new DlgRotateImage(viewManager()->mainWindow(), "RotateLayer");
     Q_CHECK_PTR(dlgRotateImage);
 
@@ -139,10 +159,71 @@ void RotateImage::slotRotateLayer()
 
     if (dlgRotateImage->exec() == QDialog::Accepted) {
         double angle = dlgRotateImage->angle() * M_PI / 180;
-        viewManager()->nodeManager()->rotate(angle);
-
+        image->rotateNode(rootNode, angle, viewManager()->selection());
     }
     delete dlgRotateImage;
 }
 
+void RotateImage::rotateLayerImpl(KisNodeSP rootNode, qreal radians)
+{
+    KisImageWSP image = viewManager()->image();
+    if (!image) return;
+
+    if (!viewManager()->blockUntilOperationsFinished(image)) return;
+
+    image->rotateNode(rootNode, radians, viewManager()->selection());
+}
+
+void RotateImage::slotRotateLayer()
+{
+    rotateLayerCustomImpl(viewManager()->activeLayer());
+}
+
+void RotateImage::slotRotateAllLayers()
+{
+    KisImageWSP image = viewManager()->image();
+    if (!image) return;
+
+    rotateLayerCustomImpl(image->root());
+}
+
+void RotateImage::slotRotateLayerCW90()
+{
+    rotateLayerImpl(viewManager()->activeLayer(), M_PI / 2);
+}
+
+void RotateImage::slotRotateLayerCCW90()
+{
+    rotateLayerImpl(viewManager()->activeLayer(), -M_PI / 2);
+}
+
+void RotateImage::slotRotateLayer180()
+{
+    rotateLayerImpl(viewManager()->activeLayer(), M_PI);
+}
+
+void RotateImage::slotRotateAllLayersCW90()
+{
+    KisImageWSP image = viewManager()->image();
+    if (!image) return;
+
+    rotateLayerImpl(image->root(), M_PI / 2);
+}
+
+void RotateImage::slotRotateAllLayersCCW90()
+{
+    KisImageWSP image = viewManager()->image();
+    if (!image) return;
+
+    rotateLayerImpl(image->root(), -M_PI / 2);
+}
+
+void RotateImage::slotRotateAllLayers180()
+{
+    KisImageWSP image = viewManager()->image();
+    if (!image) return;
+
+    rotateLayerImpl(image->root(), M_PI);
+}
+
 #include "rotateimage.moc"
diff --git a/plugins/extensions/rotateimage/rotateimage.h b/plugins/extensions/rotateimage/rotateimage.h
index b0e88dcb23d..22e4d5e8019 100644
--- a/plugins/extensions/rotateimage/rotateimage.h
+++ b/plugins/extensions/rotateimage/rotateimage.h
@@ -23,6 +23,7 @@
 #include <QVariant>
 
 #include <KisActionPlugin.h>
+#include "kis_types.h"
 
 class RotateImage : public KisActionPlugin
 {
@@ -31,6 +32,10 @@ public:
     RotateImage(QObject *parent, const QVariantList &);
     ~RotateImage() override;
 
+private:
+    void rotateLayerCustomImpl(KisNodeSP rootNode);
+    void rotateLayerImpl(KisNodeSP rootNode, qreal radians);
+
 private Q_SLOTS:
 
     void slotRotateImage();
@@ -40,6 +45,13 @@ private Q_SLOTS:
     void slotMirrorImageVertical();
     void slotMirrorImageHorizontal();
     void slotRotateLayer();
+    void slotRotateLayerCW90();
+    void slotRotateLayerCCW90();
+    void slotRotateLayer180();
+    void slotRotateAllLayers();
+    void slotRotateAllLayersCW90();
+    void slotRotateAllLayersCCW90();
+    void slotRotateAllLayers180();
 };
 
 #endif // ROTATEIMAGE_H
diff --git a/plugins/extensions/shearimage/shearimage.cc b/plugins/extensions/shearimage/shearimage.cc
index ff009807d27..a89bca0d11e 100644
--- a/plugins/extensions/shearimage/shearimage.cc
+++ b/plugins/extensions/shearimage/shearimage.cc
@@ -28,6 +28,7 @@
 #include <kis_node_manager.h>
 #include <kis_image_manager.h>
 #include <kis_action.h>
+#include "kis_selection.h"
 
 #include "dlg_shearimage.h"
 
@@ -41,6 +42,9 @@ ShearImage::ShearImage(QObject *parent, const QVariantList &)
 
     action = createAction("shearlayer");
     connect(action,  SIGNAL(triggered()), this, SLOT(slotShearLayer()));
+
+    action = createAction("shearAllLayers");
+    connect(action,  SIGNAL(triggered()), this, SLOT(slotShearAllLayers()));
 }
 
 ShearImage::~ShearImage()
@@ -50,9 +54,10 @@ ShearImage::~ShearImage()
 void ShearImage::slotShearImage()
 {
     KisImageWSP image = viewManager()->image();
-
     if (!image) return;
 
+    if (!viewManager()->blockUntilOperationsFinished(image)) return;
+
     DlgShearImage * dlgShearImage = new DlgShearImage(viewManager()->mainWindow(), "ShearImage");
     Q_CHECK_PTR(dlgShearImage);
 
@@ -66,12 +71,13 @@ void ShearImage::slotShearImage()
     delete dlgShearImage;
 }
 
-void ShearImage::slotShearLayer()
+void ShearImage::shearLayerImpl(KisNodeSP rootNode)
 {
     KisImageWSP image = viewManager()->image();
-
     if (!image) return;
 
+    if (!viewManager()->blockUntilOperationsFinished(image)) return;
+
     DlgShearImage * dlgShearImage = new DlgShearImage(viewManager()->mainWindow(), "ShearLayer");
     Q_CHECK_PTR(dlgShearImage);
 
@@ -80,10 +86,25 @@ void ShearImage::slotShearLayer()
     if (dlgShearImage->exec() == QDialog::Accepted) {
         qint32 angleX = dlgShearImage->angleX();
         qint32 angleY = dlgShearImage->angleY();
-        viewManager()->nodeManager()->shear(angleX, angleY);
 
+        image->shearNode(rootNode,
+                         angleX, angleY,
+                         viewManager()->selection());
     }
     delete dlgShearImage;
 }
 
+void ShearImage::slotShearLayer()
+{
+    shearLayerImpl(viewManager()->activeNode());
+}
+
+void ShearImage::slotShearAllLayers()
+{
+    KisImageWSP image = viewManager()->image();
+    if (!image) return;
+
+    shearLayerImpl(image->root());
+}
+
 #include "shearimage.moc"
diff --git a/plugins/extensions/shearimage/shearimage.h b/plugins/extensions/shearimage/shearimage.h
index 49487245135..452958849cc 100644
--- a/plugins/extensions/shearimage/shearimage.h
+++ b/plugins/extensions/shearimage/shearimage.h
@@ -23,6 +23,8 @@
 #include <QVariant>
 
 #include <KisActionPlugin.h>
+#include "kis_types.h"
+
 
 class ShearImage : public KisActionPlugin
 {
@@ -31,10 +33,14 @@ public:
     ShearImage(QObject *parent, const QVariantList &);
     ~ShearImage() override;
 
+private:
+    void shearLayerImpl(KisNodeSP rootNode);
+
 private Q_SLOTS:
 
     void slotShearImage();
     void slotShearLayer();
+    void slotShearAllLayers();
 };
 
 #endif // SHEARIMAGE_H
diff --git a/plugins/tools/tool_smart_patch/kis_tool_smart_patch.cpp b/plugins/tools/tool_smart_patch/kis_tool_smart_patch.cpp
index 656ba388cab..b969bcd7eab 100644
--- a/plugins/tools/tool_smart_patch/kis_tool_smart_patch.cpp
+++ b/plugins/tools/tool_smart_patch/kis_tool_smart_patch.cpp
@@ -31,7 +31,7 @@
 
 #include "kundo2magicstring.h"
 #include "kundo2stack.h"
-#include "kis_transaction_based_command.h"
+#include "commands_new/kis_transaction_based_command.h"
 #include "kis_transaction.h"
 
 #include "kis_processing_applicator.h"


More information about the kimageshop mailing list