[krita/rempt/T1004-recreate-the-text-tool] /: Implement boolean operations on shapes
Dmitry Kazakov
null at kde.org
Thu Jun 22 12:11:18 UTC 2017
Git commit eddf51d3b6bdf711e13575262887bc8d86860060 by Dmitry Kazakov.
Committed on 22/06/2017 at 12:07.
Pushed by dkazakov into branch 'rempt/T1004-recreate-the-text-tool'.
Implement boolean operations on shapes
Now one can Unite, Intersect and Subtract shapes
Ref T1005
CC:kimageshop at kde.org
M +30 -0 krita/data/actions/InteractionTool.action
M +1 -1 libs/basicflakes/tools/KoCreatePathTool.cpp
M +1 -1 libs/basicflakes/tools/KoPencilTool.cpp
M +1 -0 libs/flake/CMakeLists.txt
M +1 -1 libs/flake/KoCanvasControllerWidgetViewport_p.cpp
M +10 -10 libs/flake/KoShapeController.cpp
M +4 -3 libs/flake/KoShapeController.h
M +1 -1 libs/flake/KoToolProxy.cpp
A +49 -0 libs/flake/commands/KoKeepShapesSelectedCommand.cpp [License: GPL (v2+)]
A +46 -0 libs/flake/commands/KoKeepShapesSelectedCommand.h [License: GPL (v2+)]
M +14 -15 libs/flake/commands/KoShapeCreateCommand.cpp
M +3 -0 libs/flake/commands/KoShapeCreateCommand.h
M +1 -1 libs/flake/tests/TestShapePainting.cpp
M +1 -1 libs/flake/tools/KoCreateShapeStrategy.cpp
M +1 -1 libs/ui/actions/KisPasteActionFactory.cpp
M +1 -1 libs/ui/actions/kis_selection_action_factories.cpp
M +1 -1 libs/ui/tool/kis_selection_tool_helper.cpp
M +1 -1 libs/ui/tool/kis_tool_shape.cc
M +1 -2 plugins/flake/artistictextshape/ArtisticTextTool.cpp
M +1 -1 plugins/tools/basictools/kis_tool_line.cc
M +1 -1 plugins/tools/defaulttool/connectionTool/ConnectionTool.cpp
M +120 -3 plugins/tools/defaulttool/defaulttool/DefaultTool.cpp
M +1 -0 plugins/tools/defaulttool/defaulttool/DefaultTool.h
M +1 -1 plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.cpp
https://commits.kde.org/krita/eddf51d3b6bdf711e13575262887bc8d86860060
diff --git a/krita/data/actions/InteractionTool.action b/krita/data/actions/InteractionTool.action
index 575906569bf..3852938cfd8 100644
--- a/krita/data/actions/InteractionTool.action
+++ b/krita/data/actions/InteractionTool.action
@@ -257,6 +257,36 @@
<isCheckable>false</isCheckable>
</Action>
+ <Action name="object_unite">
+ <text>Unite</text>
+ <toolTip>Create boolean onion of multiple objects</toolTip>
+ <shortcut></shortcut>
+ <icon></icon>
+ <whatsThis></whatsThis>
+ <statusTip></statusTip>
+ <isCheckable>false</isCheckable>
+ </Action>
+
+ <Action name="object_intersect">
+ <text>Intersect</text>
+ <toolTip>Create boolean intersection of multiple objects</toolTip>
+ <shortcut></shortcut>
+ <icon></icon>
+ <whatsThis></whatsThis>
+ <statusTip></statusTip>
+ <isCheckable>false</isCheckable>
+ </Action>
+
+ <Action name="object_subtract">
+ <text>Subtract</text>
+ <toolTip>Subtract multiple objects from the first selected one</toolTip>
+ <shortcut></shortcut>
+ <icon></icon>
+ <whatsThis></whatsThis>
+ <statusTip></statusTip>
+ <isCheckable>false</isCheckable>
+ </Action>
+
</Actions>
</ActionCollection>
diff --git a/libs/basicflakes/tools/KoCreatePathTool.cpp b/libs/basicflakes/tools/KoCreatePathTool.cpp
index fa8027221f9..652e3bf8ebf 100644
--- a/libs/basicflakes/tools/KoCreatePathTool.cpp
+++ b/libs/basicflakes/tools/KoCreatePathTool.cpp
@@ -445,7 +445,7 @@ void KoCreatePathTool::addPathShape(KoPathShape *pathShape)
}
}
- KUndo2Command *cmd = canvas()->shapeController()->addShape(pathShape);
+ KUndo2Command *cmd = canvas()->shapeController()->addShape(pathShape, 0);
if (cmd) {
KoSelection *selection = canvas()->shapeManager()->selection();
selection->deselectAll();
diff --git a/libs/basicflakes/tools/KoPencilTool.cpp b/libs/basicflakes/tools/KoPencilTool.cpp
index e837a38457c..ac331d38391 100644
--- a/libs/basicflakes/tools/KoPencilTool.cpp
+++ b/libs/basicflakes/tools/KoPencilTool.cpp
@@ -400,7 +400,7 @@ void KoPencilTool::addPathShape(KoPathShape* path, bool closePath)
}
}
- KUndo2Command * cmd = canvas()->shapeController()->addShape(path);
+ KUndo2Command * cmd = canvas()->shapeController()->addShape(path, 0);
if (cmd) {
KoSelection *selection = canvas()->shapeManager()->selection();
selection->deselectAll();
diff --git a/libs/flake/CMakeLists.txt b/libs/flake/CMakeLists.txt
index c0847bdaadf..5bfb182d849 100644
--- a/libs/flake/CMakeLists.txt
+++ b/libs/flake/CMakeLists.txt
@@ -162,6 +162,7 @@ set(kritaflake_SRCS
commands/KoShapeConnectionChangeCommand.cpp
commands/KoMultiPathPointMergeCommand.cpp
commands/KoMultiPathPointJoinCommand.cpp
+ commands/KoKeepShapesSelectedCommand.cpp
tools/KoCreateShapeStrategy.cpp
tools/KoPathToolFactory.cpp
tools/KoPathTool.cpp
diff --git a/libs/flake/KoCanvasControllerWidgetViewport_p.cpp b/libs/flake/KoCanvasControllerWidgetViewport_p.cpp
index 5ab19095b75..aeb624a9875 100644
--- a/libs/flake/KoCanvasControllerWidgetViewport_p.cpp
+++ b/libs/flake/KoCanvasControllerWidgetViewport_p.cpp
@@ -213,7 +213,7 @@ void Viewport::handleDropEvent(QDropEvent *event)
m_draggedShape->setAbsolutePosition(newPos);
- KUndo2Command * cmd = m_parent->canvas()->shapeController()->addShape(m_draggedShape);
+ KUndo2Command * cmd = m_parent->canvas()->shapeController()->addShape(m_draggedShape, 0);
if (cmd) {
m_parent->canvas()->addCommand(cmd);
diff --git a/libs/flake/KoShapeController.cpp b/libs/flake/KoShapeController.cpp
index 0c67c96b159..8fd031c123a 100644
--- a/libs/flake/KoShapeController.cpp
+++ b/libs/flake/KoShapeController.cpp
@@ -54,7 +54,7 @@ public:
KoCanvasBase *canvas;
KoShapeBasedDocumentBase *shapeBasedDocument;
- KUndo2Command* addShape(KoShape *shape, bool showDialog, KUndo2Command *parent) {
+ KUndo2Command* addShape(KoShape *shape, bool showDialog, KoShapeContainer *parentShape, KUndo2Command *parent) {
if (canvas) {
if (showDialog && !shape->shapeId().isEmpty()) {
@@ -98,12 +98,12 @@ public:
}
}
- return addShapesDirect({shape}, parent);
+ return addShapesDirect({shape}, parentShape, parent);
}
- KUndo2Command* addShapesDirect(const QList<KoShape*> shapes, KUndo2Command *parent)
+ KUndo2Command* addShapesDirect(const QList<KoShape*> shapes, KoShapeContainer *parentShape, KUndo2Command *parent)
{
- return new KoShapeCreateCommand(shapeBasedDocument, shapes, parent);
+ return new KoShapeCreateCommand(shapeBasedDocument, shapes, parentShape, parent);
}
void handleAttachedConnections(KoShape *shape, KUndo2Command *parentCmd) {
@@ -143,19 +143,19 @@ void KoShapeController::reset()
d->shapeBasedDocument = 0;
}
-KUndo2Command* KoShapeController::addShape(KoShape *shape, KUndo2Command *parent)
+KUndo2Command* KoShapeController::addShape(KoShape *shape, KoShapeContainer *parentShape, KUndo2Command *parent)
{
- return d->addShape(shape, true, parent);
+ return d->addShape(shape, true, parentShape, parent);
}
-KUndo2Command* KoShapeController::addShapeDirect(KoShape *shape, KUndo2Command *parent)
+KUndo2Command* KoShapeController::addShapeDirect(KoShape *shape, KoShapeContainer *parentShape, KUndo2Command *parent)
{
- return d->addShapesDirect({shape}, parent);
+ return d->addShapesDirect({shape}, parentShape, parent);
}
-KUndo2Command *KoShapeController::addShapesDirect(const QList<KoShape *> shapes, KUndo2Command *parent)
+KUndo2Command *KoShapeController::addShapesDirect(const QList<KoShape *> shapes, KoShapeContainer *parentShape, KUndo2Command *parent)
{
- return d->addShapesDirect(shapes, parent);
+ return d->addShapesDirect(shapes, parentShape, parent);
}
KUndo2Command* KoShapeController::removeShape(KoShape *shape, KUndo2Command *parent)
diff --git a/libs/flake/KoShapeController.h b/libs/flake/KoShapeController.h
index 26171e50af8..b1d2912db2f 100644
--- a/libs/flake/KoShapeController.h
+++ b/libs/flake/KoShapeController.h
@@ -30,6 +30,7 @@
class KoCanvasBase;
class KoShape;
+class KoShapeContainer;
class KoShapeBasedDocumentBase;
class KUndo2Command;
class KoDocumentResourceManager;
@@ -73,7 +74,7 @@ public:
* @return command which will insert the shape into the document or 0 if the
* insertion was cancelled. The command is not yet executed.
*/
- KUndo2Command* addShape(KoShape *shape, KUndo2Command *parent = 0);
+ KUndo2Command* addShape(KoShape *shape, KoShapeContainer *parentShape, KUndo2Command *parent = 0);
/**
* @brief Add a shape to the document, skipping any dialogs or other user interaction.
@@ -83,7 +84,7 @@ public:
*
* @return command which will insert the shape into the document. The command is not yet executed.
*/
- KUndo2Command* addShapeDirect(KoShape *shape, KUndo2Command *parent = 0);
+ KUndo2Command* addShapeDirect(KoShape *shape, KoShapeContainer *parentShape, KUndo2Command *parent = 0);
/**
* @brief Add shapes to the document, skipping any dialogs or other user interaction.
@@ -93,7 +94,7 @@ public:
*
* @return command which will insert the shapes into the document. The command is not yet executed.
*/
- KUndo2Command* addShapesDirect(const QList<KoShape*> shape, KUndo2Command *parent = 0);
+ KUndo2Command* addShapesDirect(const QList<KoShape*> shape, KoShapeContainer *parentShape, KUndo2Command *parent = 0);
/**
* @brief Remove a shape from the document.
diff --git a/libs/flake/KoToolProxy.cpp b/libs/flake/KoToolProxy.cpp
index fcdb2f3b821..13678d100c2 100644
--- a/libs/flake/KoToolProxy.cpp
+++ b/libs/flake/KoToolProxy.cpp
@@ -516,7 +516,7 @@ bool KoToolProxy::paste()
if (!pastedShapes.isEmpty()) {
// add shape to the document
- canvas->shapeController()->addShapesDirect(pastedShapes, cmd);
+ canvas->shapeController()->addShapesDirect(pastedShapes, 0, cmd);
canvas->addCommand(cmd);
}
}
diff --git a/libs/flake/commands/KoKeepShapesSelectedCommand.cpp b/libs/flake/commands/KoKeepShapesSelectedCommand.cpp
new file mode 100644
index 00000000000..f1fcc2b82dc
--- /dev/null
+++ b/libs/flake/commands/KoKeepShapesSelectedCommand.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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 "KoKeepShapesSelectedCommand.h"
+
+#include <KoShape.h>
+#include <KoSelection.h>
+
+
+KoKeepShapesSelectedCommand::KoKeepShapesSelectedCommand(const QList<KoShape*> &selectedBefore,
+ const QList<KoShape*> &selectedAfter,
+ KoSelection *selection,
+ bool isFinalizing,
+ KUndo2Command *parent)
+ : KisCommandUtils::FlipFlopCommand(isFinalizing, parent),
+ m_selectedBefore(selectedBefore),
+ m_selectedAfter(selectedAfter),
+ m_selection(selection)
+{
+
+}
+
+void KoKeepShapesSelectedCommand::end()
+{
+ m_selection->deselectAll();
+
+ const QList<KoShape*> newSelectedShapes =
+ isFinalizing() ? m_selectedAfter : m_selectedBefore;
+
+ Q_FOREACH (KoShape *shape, newSelectedShapes) {
+ m_selection->select(shape);
+ }
+}
+
diff --git a/libs/flake/commands/KoKeepShapesSelectedCommand.h b/libs/flake/commands/KoKeepShapesSelectedCommand.h
new file mode 100644
index 00000000000..195ad91a6f8
--- /dev/null
+++ b/libs/flake/commands/KoKeepShapesSelectedCommand.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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 KOKEEPSHAPESSELECTEDCOMMAND_H
+#define KOKEEPSHAPESSELECTEDCOMMAND_H
+
+#include "kis_command_utils.h"
+#include <kritaflake_export.h>
+
+class KoSelection;
+class KoShape;
+
+class KRITAFLAKE_EXPORT KoKeepShapesSelectedCommand : public KisCommandUtils::FlipFlopCommand
+{
+public:
+ KoKeepShapesSelectedCommand(const QList<KoShape*> &selectedBefore,
+ const QList<KoShape*> &selectedAfter,
+ KoSelection *selection,
+ bool isFinalizing,
+ KUndo2Command *parent);
+
+protected:
+ void end();
+
+private:
+ QList<KoShape*> m_selectedBefore;
+ QList<KoShape*> m_selectedAfter;
+ KoSelection *m_selection;
+};
+
+#endif // KOKEEPSHAPESSELECTEDCOMMAND_H
diff --git a/libs/flake/commands/KoShapeCreateCommand.cpp b/libs/flake/commands/KoShapeCreateCommand.cpp
index a2c443c217d..f3312b3eb26 100644
--- a/libs/flake/commands/KoShapeCreateCommand.cpp
+++ b/libs/flake/commands/KoShapeCreateCommand.cpp
@@ -35,14 +35,12 @@
class Q_DECL_HIDDEN KoShapeCreateCommand::Private
{
public:
- Private(KoShapeBasedDocumentBase *_document, const QList<KoShape*> &_shapes)
+ Private(KoShapeBasedDocumentBase *_document, const QList<KoShape*> &_shapes, KoShapeContainer *_parentShape)
: shapesDocument(_document),
shapes(_shapes),
+ explicitParentShape(_parentShape),
deleteShapes(true)
{
- Q_FOREACH(KoShape *shape, shapes) {
- originalShapeParents << shape->parent();
- }
}
~Private() {
@@ -53,7 +51,7 @@ public:
KoShapeBasedDocumentBase *shapesDocument;
QList<KoShape*> shapes;
- QList<KoShapeContainer*> originalShapeParents;
+ KoShapeContainer *explicitParentShape;
bool deleteShapes;
std::vector<std::unique_ptr<KUndo2Command>> reorderingCommands;
@@ -61,14 +59,14 @@ public:
QScopedPointer<KUndo2Command> reorderingCommand;
};
-KoShapeCreateCommand::KoShapeCreateCommand(KoShapeBasedDocumentBase *controller, KoShape *shape, KUndo2Command *parent)
- : KoShapeCreateCommand(controller, QList<KoShape *>() << shape, parent)
+KoShapeCreateCommand::KoShapeCreateCommand(KoShapeBasedDocumentBase *controller, KoShape *shape, KoShapeContainer *parentShape, KUndo2Command *parent)
+ : KoShapeCreateCommand(controller, QList<KoShape *>() << shape, parentShape, parent)
{
}
-KoShapeCreateCommand::KoShapeCreateCommand(KoShapeBasedDocumentBase *controller, const QList<KoShape *> shapes, KUndo2Command *parent)
- : KUndo2Command(kundo2_i18np("Create shape", "Create shapes", shapes.size()), parent),
- d(new Private(controller, shapes))
+KoShapeCreateCommand::KoShapeCreateCommand(KoShapeBasedDocumentBase *controller, const QList<KoShape *> shapes, KoShapeContainer *parentShape, KUndo2Command *parent)
+ : KUndo2Command(kundo2_i18np("Create shape", "Create shapes", shapes.size()), parent),
+ d(new Private(controller, shapes, parentShape))
{
}
@@ -86,6 +84,10 @@ void KoShapeCreateCommand::redo()
d->reorderingCommands.clear();
Q_FOREACH(KoShape *shape, d->shapes) {
+ if (d->explicitParentShape) {
+ shape->setParent(d->explicitParentShape);
+ }
+
d->shapesDocument->addShape(shape);
KoShapeContainer *shapeParent = shape->parent();
@@ -116,11 +118,8 @@ void KoShapeCreateCommand::undo()
d->reorderingCommands.pop_back();
}
- KIS_SAFE_ASSERT_RECOVER_RETURN(d->shapes.size() == d->originalShapeParents.size());
-
- for (int i = 0; i < d->shapes.size(); i++) {
- d->shapesDocument->removeShape(d->shapes[i]);
- d->shapes[i]->setParent(d->originalShapeParents[i]);
+ Q_FOREACH(KoShape *shape, d->shapes) {
+ d->shapesDocument->removeShape(shape);
}
d->deleteShapes = true;
diff --git a/libs/flake/commands/KoShapeCreateCommand.h b/libs/flake/commands/KoShapeCreateCommand.h
index 82a7e6f8d81..140f33a4634 100644
--- a/libs/flake/commands/KoShapeCreateCommand.h
+++ b/libs/flake/commands/KoShapeCreateCommand.h
@@ -24,6 +24,7 @@
#include <kundo2command.h>
class KoShape;
+class KoShapeContainer;
class KoShapeBasedDocumentBase;
/// The undo / redo command for creating shapes
@@ -37,6 +38,7 @@ public:
* @param parent the parent command used for macro commands
*/
KoShapeCreateCommand(KoShapeBasedDocumentBase *controller, KoShape *shape,
+ KoShapeContainer *parentShape = 0,
KUndo2Command *parent = 0);
/**
@@ -46,6 +48,7 @@ public:
* @param parent the parent command used for macro commands
*/
KoShapeCreateCommand(KoShapeBasedDocumentBase *controller, const QList<KoShape*> shape,
+ KoShapeContainer *parentShape = 0,
KUndo2Command *parent = 0);
~KoShapeCreateCommand() override;
diff --git a/libs/flake/tests/TestShapePainting.cpp b/libs/flake/tests/TestShapePainting.cpp
index b74c4e0a67d..3820c2a6ef3 100644
--- a/libs/flake/tests/TestShapePainting.cpp
+++ b/libs/flake/tests/TestShapePainting.cpp
@@ -289,7 +289,7 @@ void TestShapePainting::testGroupUngroup()
group->setName("group");
KUndo2Command groupingCommand;
- canvas.shapeController()->addShapeDirect(group, &groupingCommand);
+ canvas.shapeController()->addShapeDirect(group, 0, &groupingCommand);
new KoShapeGroupCommand(group, groupedShapes, false, true, true, &groupingCommand);
groupingCommand.redo();
diff --git a/libs/flake/tools/KoCreateShapeStrategy.cpp b/libs/flake/tools/KoCreateShapeStrategy.cpp
index 054a56b7c4e..83fbc4424e8 100644
--- a/libs/flake/tools/KoCreateShapeStrategy.cpp
+++ b/libs/flake/tools/KoCreateShapeStrategy.cpp
@@ -80,7 +80,7 @@ KUndo2Command* KoCreateShapeStrategy::createCommand()
if (newSize.width() > 1.0 && newSize.height() > 1.0)
shape->setSize(newSize);
- KUndo2Command * cmd = parent->canvas()->shapeController()->addShape(shape);
+ KUndo2Command * cmd = parent->canvas()->shapeController()->addShape(shape, 0);
if (cmd) {
KoSelection *selection = parent->canvas()->shapeManager()->selection();
selection->deselectAll();
diff --git a/libs/ui/actions/KisPasteActionFactory.cpp b/libs/ui/actions/KisPasteActionFactory.cpp
index 5a23f23456c..3f5c66c8dea 100644
--- a/libs/ui/actions/KisPasteActionFactory.cpp
+++ b/libs/ui/actions/KisPasteActionFactory.cpp
@@ -83,7 +83,7 @@ bool tryPasteShapes(bool pasteAtCursorPosition, KisViewManager *view)
shapeManager->selection()->deselectAll();
KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18n("Paste shapes"));
- canvas->shapeController()->addShapesDirect(shapes, parentCommand);
+ canvas->shapeController()->addShapesDirect(shapes, 0, parentCommand);
QPointF finalShapesOffset;
diff --git a/libs/ui/actions/kis_selection_action_factories.cpp b/libs/ui/actions/kis_selection_action_factories.cpp
index e11d6124cb7..b0660b5debb 100644
--- a/libs/ui/actions/kis_selection_action_factories.cpp
+++ b/libs/ui/actions/kis_selection_action_factories.cpp
@@ -437,7 +437,7 @@ void KisSelectionToVectorActionFactory::run(KisViewManager *view)
KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Convert to Vector Selection"));
- ap->applyCommand(view->canvasBase()->shapeController()->addShape(shape),
+ ap->applyCommand(view->canvasBase()->shapeController()->addShape(shape, 0),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
diff --git a/libs/ui/tool/kis_selection_tool_helper.cpp b/libs/ui/tool/kis_selection_tool_helper.cpp
index db14dc4223d..d39f241b451 100644
--- a/libs/ui/tool/kis_selection_tool_helper.cpp
+++ b/libs/ui/tool/kis_selection_tool_helper.cpp
@@ -205,7 +205,7 @@ void KisSelectionToolHelper::addSelectionShapes(QList< KoShape* > shapes)
m_shape->setUserData(new KisShapeSelectionMarker);
}
- return m_view->canvasBase()->shapeController()->addShape(m_shape);
+ return m_view->canvasBase()->shapeController()->addShape(m_shape, 0);
}
};
diff --git a/libs/ui/tool/kis_tool_shape.cc b/libs/ui/tool/kis_tool_shape.cc
index 306a84a9143..34c62383548 100644
--- a/libs/ui/tool/kis_tool_shape.cc
+++ b/libs/ui/tool/kis_tool_shape.cc
@@ -179,7 +179,7 @@ void KisToolShape::addShape(KoShape* shape)
shape->setBackground(QSharedPointer<KoShapeBackground>(0));
break;
}
- KUndo2Command * cmd = canvas()->shapeController()->addShape(shape);
+ KUndo2Command * cmd = canvas()->shapeController()->addShape(shape, 0);
canvas()->addCommand(cmd);
}
diff --git a/plugins/flake/artistictextshape/ArtisticTextTool.cpp b/plugins/flake/artistictextshape/ArtisticTextTool.cpp
index a502f8695b5..4e207d75e72 100644
--- a/plugins/flake/artistictextshape/ArtisticTextTool.cpp
+++ b/plugins/flake/artistictextshape/ArtisticTextTool.cpp
@@ -600,14 +600,13 @@ void ArtisticTextTool::convertText()
}
KoPathShape *path = KoPathShape::createShapeFromPainterPath(m_currentShape->outline());
- path->setParent(m_currentShape->parent());
path->setZIndex(m_currentShape->zIndex());
path->setStroke(m_currentShape->stroke());
path->setBackground(m_currentShape->background());
path->setTransformation(m_currentShape->transformation());
path->setShapeId(KoPathShapeId);
- KUndo2Command *cmd = canvas()->shapeController()->addShapeDirect(path);
+ KUndo2Command *cmd = canvas()->shapeController()->addShapeDirect(path, 0);
cmd->setText(kundo2_i18n("Convert to Path"));
canvas()->shapeController()->removeShape(m_currentShape, cmd);
canvas()->addCommand(cmd);
diff --git a/plugins/tools/basictools/kis_tool_line.cc b/plugins/tools/basictools/kis_tool_line.cc
index 770498df962..358efd9451c 100644
--- a/plugins/tools/basictools/kis_tool_line.cc
+++ b/plugins/tools/basictools/kis_tool_line.cc
@@ -288,7 +288,7 @@ void KisToolLine::endStroke()
KoShapeStrokeSP border(new KoShapeStroke(currentStrokeWidth(), currentFgColor().toQColor()));
path->setStroke(border);
- KUndo2Command * cmd = canvas()->shapeController()->addShape(path);
+ KUndo2Command * cmd = canvas()->shapeController()->addShape(path, 0);
canvas()->addCommand(cmd);
}
diff --git a/plugins/tools/defaulttool/connectionTool/ConnectionTool.cpp b/plugins/tools/defaulttool/connectionTool/ConnectionTool.cpp
index fa347dc9a34..8ce68e366a7 100644
--- a/plugins/tools/defaulttool/connectionTool/ConnectionTool.cpp
+++ b/plugins/tools/defaulttool/connectionTool/ConnectionTool.cpp
@@ -445,7 +445,7 @@ void ConnectionTool::mouseReleaseEvent(KoPointerEvent *event)
return;
} else {
// finalize adding the new connection shape with an undo command
- KUndo2Command *cmd = canvas()->shapeController()->addShape(m_currentShape);
+ KUndo2Command *cmd = canvas()->shapeController()->addShape(m_currentShape, 0);
canvas()->addCommand(cmd);
setEditMode(EditConnection, m_currentShape, KoConnectionShape::StartHandle);
}
diff --git a/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp b/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp
index 7ff84602726..20807c71846 100644
--- a/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp
+++ b/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp
@@ -40,6 +40,7 @@
#include <KoShapeGroup.h>
#include <KoShapeLayer.h>
#include <KoShapeOdfSaveHelper.h>
+#include <KoPathShape.h>
#include <KoDrag.h>
#include <KoCanvasBase.h>
#include <KoCanvasResourceManager.h>
@@ -51,6 +52,7 @@
#include <commands/KoShapeGroupCommand.h>
#include <commands/KoShapeUngroupCommand.h>
#include <commands/KoShapeDistributeCommand.h>
+#include <commands/KoKeepShapesSelectedCommand.h>
#include <KoSnapGuide.h>
#include <KoStrokeConfigWidget.h>
#include "kis_action_registry.h"
@@ -95,6 +97,12 @@ enum TransformActionType {
TransformReset
};
+enum BooleanOp {
+ BooleanUnion,
+ BooleanIntersection,
+ BooleanSubtraction
+};
+
}
QPolygonF selectionPolygon(KoSelection *selection)
@@ -435,6 +443,13 @@ void DefaultTool::setupActions()
addMappedAction(transformSignalsMapper, "object_transform_mirror_vertically", TransformMirrorY);
addMappedAction(transformSignalsMapper, "object_transform_reset", TransformReset);
+ QSignalMapper *booleanSignalsMapper = new QSignalMapper(this);
+ connect(booleanSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionBooleanOp(int)));
+
+ addMappedAction(booleanSignalsMapper, "object_unite", BooleanUnion);
+ addMappedAction(booleanSignalsMapper, "object_intersect", BooleanIntersection);
+ addMappedAction(booleanSignalsMapper, "object_subtract", BooleanSubtraction);
+
m_contextMenu.reset(new QMenu());
}
@@ -999,7 +1014,7 @@ void DefaultTool::selectionGroup()
KoShapeGroup *group = new KoShapeGroup();
// TODO what if only one shape is left?
KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Group shapes"));
- canvas()->shapeController()->addShapeDirect(group, cmd);
+ canvas()->shapeController()->addShapeDirect(group, 0, cmd);
new KoShapeGroupCommand(group, selectedShapes, false, true, true, cmd);
canvas()->addCommand(cmd);
@@ -1112,6 +1127,89 @@ void DefaultTool::selectionTransform(int transformAction)
canvas()->addCommand(cmd);
}
+void DefaultTool::selectionBooleanOp(int booleanOp)
+{
+ KoSelection *selection = koSelection();
+ if (!selection) return;
+
+ QList<KoShape *> editableShapes = selection->selectedEditableShapes();
+ if (editableShapes.isEmpty()) {
+ return;
+ }
+
+ QVector<QPainterPath> srcOutlines;
+ QPainterPath dstOutline;
+ KUndo2MagicString actionName = kundo2_noi18n("BUG: boolean action name");
+
+ // TODO: implement a reference shape selection dialog!
+ const int referenceShapeIndex = 0;
+ KoShape *referenceShape = editableShapes[referenceShapeIndex];
+
+ Q_FOREACH (KoShape *shape, editableShapes) {
+ srcOutlines << shape->absoluteTransformation(0).map(shape->outline());
+ }
+
+ if (booleanOp == BooleanUnion) {
+ Q_FOREACH (const QPainterPath &path, srcOutlines) {
+ dstOutline |= path;
+ }
+ actionName = kundo2_i18n("Unite Shapes");
+ } else if (booleanOp == BooleanIntersection) {
+ for (int i = 0; i < srcOutlines.size(); i++) {
+ if (i == 0) {
+ dstOutline = srcOutlines[i];
+ } else {
+ dstOutline &= srcOutlines[i];
+ }
+ }
+
+ // there is a bug in Qt, sometimes it leaves the resulting
+ // outline open, so just close it explicitly.
+ dstOutline.closeSubpath();
+
+ actionName = kundo2_i18n("Intersect Shapes");
+
+ } else if (booleanOp == BooleanSubtraction) {
+ for (int i = 0; i < srcOutlines.size(); i++) {
+ dstOutline = srcOutlines[referenceShapeIndex];
+ if (i != referenceShapeIndex) {
+ dstOutline -= srcOutlines[i];
+ }
+ }
+
+ actionName = kundo2_i18n("Subtract Shapes");
+ }
+
+ KoShape *newShape = 0;
+
+ if (!dstOutline.isEmpty()) {
+ newShape = KoPathShape::createShapeFromPainterPath(dstOutline);
+ }
+
+ KUndo2Command *cmd = new KUndo2Command(actionName);
+
+ new KoKeepShapesSelectedCommand(editableShapes, {}, selection, false, cmd);
+
+ QList<KoShape*> newSelectedShapes;
+
+ if (newShape) {
+ newShape->setBackground(referenceShape->background());
+ newShape->setStroke(referenceShape->stroke());
+ newShape->setZIndex(referenceShape->zIndex());
+
+ KoShapeContainer *parent = referenceShape->parent();
+ canvas()->shapeController()->addShapeDirect(newShape, parent, cmd);
+
+ newSelectedShapes << newShape;
+ }
+
+ canvas()->shapeController()->removeShapes(editableShapes, cmd);
+
+ new KoKeepShapesSelectedCommand({}, newSelectedShapes, selection, true, cmd);
+
+ canvas()->addCommand(cmd);
+}
+
void DefaultTool::selectionAlign(int _align)
{
KoShapeAlignCommand::Align align =
@@ -1376,8 +1474,10 @@ void DefaultTool::updateActions()
action("object_transform_mirror_vertically")->setEnabled(hasEditableShapes);
action("object_transform_reset")->setEnabled(hasEditableShapes);
+ const bool multipleSelected = editableShapes.size() > 1;
+
const bool alignmentEnabled =
- editableShapes.size() > 1 ||
+ multipleSelected ||
(!editableShapes.isEmpty() &&
canvas()->resourceManager()->hasResource(KoCanvasResourceManager::PageSize));
@@ -1388,7 +1488,12 @@ void DefaultTool::updateActions()
action("object_align_vertical_center")->setEnabled(alignmentEnabled);
action("object_align_vertical_bottom")->setEnabled(alignmentEnabled);
- action("object_group")->setEnabled(editableShapes.size() > 1);
+
+ action("object_group")->setEnabled(multipleSelected);
+
+ action("object_unite")->setEnabled(multipleSelected);
+ action("object_intersect")->setEnabled(multipleSelected);
+ action("object_subtract")->setEnabled(multipleSelected);
const bool distributionEnabled = editableShapes.size() > 2;
@@ -1444,6 +1549,8 @@ QMenu* DefaultTool::popupActionsMenu()
m_contextMenu->addAction(action("object_ungroup"));
}
+ m_contextMenu->addSeparator();
+
QMenu *transform = m_contextMenu->addMenu(i18n("Transform"));
transform->addAction(action("object_transform_rotate_90_cw"));
transform->addAction(action("object_transform_rotate_90_ccw"));
@@ -1453,6 +1560,16 @@ QMenu* DefaultTool::popupActionsMenu()
transform->addAction(action("object_transform_mirror_vertically"));
transform->addSeparator();
transform->addAction(action("object_transform_reset"));
+
+ if (action("object_unite")->isEnabled() ||
+ action("object_intersect")->isEnabled() ||
+ action("object_subtract")->isEnabled()) {
+
+ QMenu *transform = m_contextMenu->addMenu(i18n("Logical Operations"));
+ transform->addAction(action("object_unite"));
+ transform->addAction(action("object_intersect"));
+ transform->addAction(action("object_subtract"));
+ }
}
return m_contextMenu.data();
diff --git a/plugins/tools/defaulttool/defaulttool/DefaultTool.h b/plugins/tools/defaulttool/defaulttool/DefaultTool.h
index 4f41fb9ceae..bd1b33d0368 100644
--- a/plugins/tools/defaulttool/defaulttool/DefaultTool.h
+++ b/plugins/tools/defaulttool/defaulttool/DefaultTool.h
@@ -107,6 +107,7 @@ private Q_SLOTS:
void selectionUngroup();
void selectionTransform(int transformAction);
+ void selectionBooleanOp(int booleanOp);
void slotActivateEditFillGradient(bool value);
void slotActivateEditStrokeGradient(bool value);
diff --git a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.cpp b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.cpp
index 1f88b06b35c..7e031832fc7 100644
--- a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.cpp
+++ b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.cpp
@@ -148,7 +148,7 @@ void KarbonCalligraphyTool::mouseReleaseEvent(KoPointerEvent *event)
m_shape->simplifyGuidePath();
- KUndo2Command *cmd = canvas()->shapeController()->addShape(m_shape);
+ KUndo2Command *cmd = canvas()->shapeController()->addShape(m_shape, 0);
if (cmd) {
m_lastShape = m_shape;
canvas()->addCommand(cmd);
More information about the kimageshop
mailing list