From b2874ec0dce77a1f1cc72a54ef420b9cdc223f6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Tr=C3=B6ger?= Date: Tue, 8 Aug 2017 18:16:41 +0200 Subject: [PATCH] Keep backlink consitent during undo/redo. issue 0003150 --- src/App/Transactions.cpp | 18 ++++++++-- src/Mod/Test/Document.py | 71 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/src/App/Transactions.cpp b/src/App/Transactions.cpp index 414891be79..6a1c6378cf 100644 --- a/src/App/Transactions.cpp +++ b/src/App/Transactions.cpp @@ -339,9 +339,18 @@ TransactionDocumentObject::~TransactionDocumentObject() void TransactionDocumentObject::applyDel(Document &Doc, TransactionalObject *pcObj) { if (status == Del) { - // simply filling in the saved object + DocumentObject* obj = static_cast(pcObj); - Doc._remObject(obj); + + //Make sure the backlinks of all linked objects are updated. As the links of the removed + //object are never set to [] they also do not remove the backlink. But as they are + //not in the document anymore we need to remove them anyway to ensure a correct graph + auto list = obj->getOutList(); + for(auto link : list) + link->_removeBackLink(obj); + + // simply filling in the saved object + Doc._remObject(obj); } } @@ -350,6 +359,11 @@ void TransactionDocumentObject::applyNew(Document &Doc, TransactionalObject *pcO if (status == New) { DocumentObject* obj = static_cast(pcObj); Doc._addObject(obj, _NameInDocument.c_str()); + + //make sure the backlinks of all linked objects are updated + auto list = obj->getOutList(); + for(auto link : list) + link->_addBackLink(obj); } } diff --git a/src/Mod/Test/Document.py b/src/Mod/Test/Document.py index e05b54ec4d..353a55fe46 100644 --- a/src/Mod/Test/Document.py +++ b/src/Mod/Test/Document.py @@ -764,6 +764,77 @@ class UndoRedoCases(unittest.TestCase): self.assertEqual(self.Doc.RedoNames,[]) self.assertEqual(self.Doc.RedoCount,0) + def testUndoInList(self): + + self.Doc.UndoMode = 1 + + self.Doc.openTransaction("Box") + self.Box = self.Doc.addObject('Part::Box') + self.Doc.commitTransaction() + + self.Doc.openTransaction("Cylinder") + self.Cylinder = self.Doc.addObject('Part::Cylinder') + self.Doc.commitTransaction() + + self.Doc.openTransaction("Fuse") + self.Fuse1 = self.Doc.addObject('Part::MultiFuse', 'Fuse') + self.Fuse1.Shapes = [self.Box, self.Cylinder] + self.Doc.commitTransaction() + + print self.Box.InList + self.Doc.undo() + print self.Box.InList + self.failUnless(len(self.Box.InList) == 0) + self.failUnless(len(self.Cylinder.InList) == 0) + + self.Doc.redo() + self.failUnless(len(self.Box.InList) == 1) + self.failUnless(self.Box.InList[0] == self.Doc.Fuse) + self.failUnless(len(self.Cylinder.InList) == 1) + self.failUnless(self.Cylinder.InList[0] == self.Doc.Fuse) + + def testUndoIssue0003150(self): + + self.Doc.UndoMode = 1 + + self.Doc.openTransaction("Box") + self.Box = self.Doc.addObject('Part::Box') + self.Doc.commitTransaction() + + self.Doc.openTransaction("Cylinder") + self.Cylinder = self.Doc.addObject('Part::Cylinder') + self.Doc.commitTransaction() + + self.Doc.openTransaction("Fuse") + self.Fuse1 = self.Doc.addObject('Part::MultiFuse') + self.Fuse1.Shapes = [self.Box, self.Cylinder] + self.Doc.commitTransaction() + self.Doc.recompute() + + self.Doc.openTransaction("Sphere") + self.Sphere = self.Doc.addObject('Part::Sphere') + self.Doc.commitTransaction() + + self.Doc.openTransaction("Fuse") + self.Fuse2 = self.Doc.addObject('Part::MultiFuse') + self.Fuse2.Shapes = [self.Fuse1, self.Sphere] + self.Doc.commitTransaction() + self.Doc.recompute() + + self.Doc.openTransaction("Part") + self.Part = self.Doc.addObject('App::Part') + self.Doc.commitTransaction() + + self.Doc.openTransaction("Drag") + self.Part.addObject(self.Fuse2) + self.Doc.commitTransaction() + + #3 undos show the problem of failing recompute + self.Doc.undo() + self.Doc.undo() + self.Doc.undo() + self.failUnless(self.Doc.recompute() >= 0) + def tearDown(self): # closing doc FreeCAD.closeDocument("UndoTest")