[graphics/krita/krita/6.0] libs/flake: Do not create a new path shape in KoMultiPathPointMergeCommand::redo() call
Dmitry Kazakov <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 478ddf4e6e5cebe3dbe0473d88d239c28e89ad01 by Dmitry Kazakov, on behalf of Elena Sagalaeva.
Committed on 27/07/2026 at 11:59.
Pushed by dkazakov into branch 'krita/6.0'.
Do not create a new path shape in KoMultiPathPointMergeCommand::redo() call
The subsequent operations don't know about the new shape and continue to
use the old one, which causes crashes.
BUG:523070
M +26 -20 libs/flake/commands/KoMultiPathPointMergeCommand.cpp
M +63 -0 libs/flake/tests/TestPointMergeCommand.cpp
M +3 -0 libs/flake/tests/TestPointMergeCommand.h
https://invent.kde.org/graphics/krita/-/commit/478ddf4e6e5cebe3dbe0473d88d239c28e89ad01
diff --git a/libs/flake/commands/KoMultiPathPointMergeCommand.cpp b/libs/flake/commands/KoMultiPathPointMergeCommand.cpp
index 86b15ce7367..f0c18f6f929 100644
--- a/libs/flake/commands/KoMultiPathPointMergeCommand.cpp
+++ b/libs/flake/commands/KoMultiPathPointMergeCommand.cpp
@@ -51,30 +51,38 @@ KUndo2Command *KoMultiPathPointMergeCommand::createMergeCommand(const KoPathPoin
void KoMultiPathPointMergeCommand::redo()
{
- KoShape *mergedShape = 0;
-
- if (m_d->pointData1.pathShape != m_d->pointData2.pathShape) {
- KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->controller);
-
- QList<KoPathShape*> shapes = {m_d->pointData1.pathShape, m_d->pointData2.pathShape};
- m_d->combineCommand.reset(new KoPathCombineCommand(m_d->controller, shapes));
- m_d->combineCommand->redo();
-
- KoPathPointData newPD1 = m_d->combineCommand->originalToCombined(m_d->pointData1);
- KoPathPointData newPD2 = m_d->combineCommand->originalToCombined(m_d->pointData2);
+ // Create shapes during the first call only.
+ if (!m_d->mergeCommand && !m_d->combineCommand)
+ {
+ if (m_d->pointData1.pathShape != m_d->pointData2.pathShape) {
+ KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->controller);
- m_d->mergeCommand.reset(createMergeCommand(newPD1, newPD2));
- m_d->mergeCommand->redo();
+ QList<KoPathShape*> shapes = { m_d->pointData1.pathShape, m_d->pointData2.pathShape };
+ m_d->combineCommand.reset(new KoPathCombineCommand(m_d->controller, shapes));
+
+ KoPathPointData newPD1 = m_d->combineCommand->originalToCombined(m_d->pointData1);
+ KoPathPointData newPD2 = m_d->combineCommand->originalToCombined(m_d->pointData2);
- mergedShape = m_d->combineCommand->combinedPath();
+ m_d->mergeCommand.reset(createMergeCommand(newPD1, newPD2));
+ }
+ else {
+ m_d->mergeCommand.reset(createMergeCommand(m_d->pointData1, m_d->pointData2));
+ }
+ }
- } else {
- m_d->mergeCommand.reset(createMergeCommand(m_d->pointData1, m_d->pointData2));
+ KoShape *mergedShape = 0;
+ if (m_d->mergeCommand)
+ {
m_d->mergeCommand->redo();
-
mergedShape = m_d->pointData1.pathShape;
}
+ if (m_d->combineCommand)
+ {
+ m_d->combineCommand->redo();
+ mergedShape = m_d->combineCommand->combinedPath();
+ }
+
if (m_d->selection) {
m_d->selection->select(mergedShape);
}
@@ -92,13 +100,11 @@ void KoMultiPathPointMergeCommand::undo()
KUndo2Command::undo();
if (m_d->mergeCommand) {
- m_d->mergeCommand->undo();
- m_d->mergeCommand.reset();
+ m_d->mergeCommand->undo();
}
if (m_d->combineCommand) {
m_d->combineCommand->undo();
- m_d->combineCommand.reset();
}
if (m_d->selection) {
diff --git a/libs/flake/tests/TestPointMergeCommand.cpp b/libs/flake/tests/TestPointMergeCommand.cpp
index 38e6481af3f..4eb0f324e0a 100644
--- a/libs/flake/tests/TestPointMergeCommand.cpp
+++ b/libs/flake/tests/TestPointMergeCommand.cpp
@@ -554,6 +554,69 @@ void TestPointMergeCommand::testMultipathJoinShapesSingleShapeStartToEnd()
}, true);
}
+#include "KoShapeDeleteCommand.h"
+void TestPointMergeCommand::undoRedo()
+{
+ KoPathShape path1, path2;
+ path1.moveTo(QPointF(40, 0));
+ path1.lineTo(QPointF(60, 0));
+
+ path2.lineTo(QPointF(60, 30));
+ path2.lineTo(QPointF(0, 30));
+
+ KoPathPointIndex index1(0, 0);
+ KoPathPointIndex index2(0, 0);
+
+ MockShapeController mockController;
+ MockCanvas canvas(&mockController);
+ MockContainer container1(nullptr);
+
+ path1.setParent(&container1);
+ path2.setParent(&container1);
+
+ KoPathPointData pd1(&path1, index1);
+ KoPathPointData pd2(&path2, index2);
+
+ // Test merge
+ {
+ KoMultiPathPointMergeCommand cmd(pd1, pd2, &mockController, canvas.shapeManager()->selection());
+ cmd.redo();
+
+ KoShapeDeleteCommand deleteCommand(&mockController, cmd.testingCombinedPath());
+ deleteCommand.redo();
+
+ deleteCommand.undo();
+ cmd.undo();
+
+ cmd.redo();
+ deleteCommand.redo();
+
+ deleteCommand.undo();
+ cmd.undo();
+ }
+
+
+ // Test join
+ {
+ KoMultiPathPointJoinCommand cmd(pd1, pd2, &mockController, canvas.shapeManager()->selection());
+ cmd.redo();
+
+ KoShapeDeleteCommand deleteCommand(&mockController, cmd.testingCombinedPath());
+ deleteCommand.redo();
+
+ deleteCommand.undo();
+ cmd.undo();
+
+ cmd.redo();
+ deleteCommand.redo();
+
+ deleteCommand.undo();
+ cmd.undo();
+ }
+
+ path1.setParent(nullptr);
+ path2.setParent(nullptr);
+}
KISTEST_MAIN(TestPointMergeCommand)
diff --git a/libs/flake/tests/TestPointMergeCommand.h b/libs/flake/tests/TestPointMergeCommand.h
index 0184c628ac1..95e32e0ef6a 100644
--- a/libs/flake/tests/TestPointMergeCommand.h
+++ b/libs/flake/tests/TestPointMergeCommand.h
@@ -35,6 +35,9 @@ private Q_SLOTS:
void testMultipathJoinShapesSingleShapeEndToStart();
void testMultipathJoinShapesSingleShapeStartToEnd();
+ // Regression test. Making sure that undo/redo operations don't cause crashes.
+ void undoRedo();
+
};
#endif // TESTPOINTMERGECOMMAND_H