From 91b83ac171f01da6be4ae0ded00e9f70494f06d5 Mon Sep 17 00:00:00 2001 From: mpv Date: Wed, 14 Jan 2015 12:06:38 +0300 Subject: [PATCH] Fix for issue #154. Management of any number of nested operations without help of OCCT. --- src/Model/Model_Document.cpp | 129 ++++++++++++++++++----------------- src/Model/Model_Document.h | 19 +++--- 2 files changed, 76 insertions(+), 72 deletions(-) diff --git a/src/Model/Model_Document.cpp b/src/Model/Model_Document.cpp index 7b1b6db85..915ce9365 100644 --- a/src/Model/Model_Document.cpp +++ b/src/Model/Model_Document.cpp @@ -60,9 +60,7 @@ Model_Document::Model_Document(const std::string theID, const std::string theKin myDoc(new TDocStd_Document("BinOcaf")) // binary OCAF format { myDoc->SetUndoLimit(UNDO_LIMIT); - myTransactionsCounter = 0; myTransactionSave = 0; - myNestedNum = -1; myExecuteFeatures = true; // to have something in the document and avoid empty doc open/save problem // in transaction for nesting correct working @@ -202,7 +200,7 @@ bool Model_Document::save(const char* theFileName, std::list& theRe break; } } - myTransactionSave = myTransactionsCounter; + myTransactionSave = myTransactions.size(); if (isDone) { // save also sub-documents if any theResults.push_back(TCollection_AsciiString(aPath).ToCString()); const std::set aSubs = subDocuments(false); @@ -278,16 +276,19 @@ void Model_Document::close(const bool theForever) void Model_Document::startOperation() { if (myDoc->HasOpenCommand()) { // start of nested command - if (myNestedNum == -1) { - myNestedNum = 0; - myDoc->InitDeltaCompaction(); + if (myDoc->CommitCommand()) { // commit the current: it will contain all nested after compactification + (*myTransactions.rbegin())++; // if has open command, the list is not empty } - myIsEmptyTr[myTransactionsCounter] = !myDoc->CommitCommand(); - myTransactionsCounter++; + myNestedNum.push_back(0); // start of nested operation with zero transactions inside yet myDoc->OpenCommand(); } else { // start the simple command myDoc->NewCommand(); } + // starts a new operation + myTransactions.push_back(0); + if (!myNestedNum.empty()) + (*myNestedNum.rbegin())++; + myRedos.clear(); // new command for all subs const std::set aSubs = subDocuments(true); std::set::iterator aSubIter = aSubs.begin(); @@ -295,35 +296,30 @@ void Model_Document::startOperation() subDoc(*aSubIter)->startOperation(); } -bool Model_Document::compactNested() +void Model_Document::compactNested() { - bool allWasEmpty = true; - while (myNestedNum != -1) { - myTransactionsCounter--; - if (!myIsEmptyTr[myTransactionsCounter]) { - allWasEmpty = false; + if (!myNestedNum.empty()) { + int aNumToCompact = *(myNestedNum.rbegin()); + int aSumOfTransaction = 0; + for(int a = 0; a < aNumToCompact; a++) { + aSumOfTransaction += *(myTransactions.rbegin()); + myTransactions.pop_back(); } - myIsEmptyTr.erase(myTransactionsCounter); - myNestedNum--; + // the latest transaction is the start of lower-level operation which startes the nested + *(myTransactions.rbegin()) += aSumOfTransaction; + myNestedNum.pop_back(); } - myIsEmptyTr[myTransactionsCounter] = allWasEmpty; - myTransactionsCounter++; - if (allWasEmpty) { - // Issue 151: if everything is empty, it is a problem for OCCT to work with it, - // just commit the empty that returns nothing - myDoc->CommitCommand(); - } else { - myDoc->PerformDeltaCompaction(); - } - return !allWasEmpty; } void Model_Document::finishOperation() { + bool isNestedClosed = !myDoc->HasOpenCommand() && !myNestedNum.empty(); + static std::shared_ptr aSession = + std::static_pointer_cast(Model_Session::get()); // just to be sure that everybody knows that changes were performed - if (!myDoc->HasOpenCommand() && myNestedNum != -1) - std::static_pointer_cast(Model_Session::get()) - ->setCheckTransactions(false); // for nested transaction commit + if (isNestedClosed) { + aSession->setCheckTransactions(false); // for nested transaction commit + } synchronizeBackRefs(); Events_Loop* aLoop = Events_Loop::loop(); aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED)); @@ -344,9 +340,9 @@ void Model_Document::finishOperation() // to avoid "updated" message appearance by updater //aLoop->clear(Events_Loop::eventByName(EVENT_OBJECT_UPDATED)); - if (!myDoc->HasOpenCommand() && myNestedNum != -1) - std::static_pointer_cast(Model_Session::get()) - ->setCheckTransactions(true); // for nested transaction commit + if (isNestedClosed) { + aSession->setCheckTransactions(true); // for nested transaction commit + } // finish for all subs first: to avoid nested finishing and "isOperation" calls problems inside const std::set aSubs = subDocuments(true); @@ -354,34 +350,32 @@ void Model_Document::finishOperation() for (; aSubIter != aSubs.end(); aSubIter++) subDoc(*aSubIter)->finishOperation(); - if (myNestedNum != -1) // this nested transaction is owervritten - myNestedNum++; - if (!myDoc->HasOpenCommand()) { - if (myNestedNum != -1) { - myNestedNum--; - compactNested(); - } - } else { - // returns false if delta is empty and no transaction was made - myIsEmptyTr[myTransactionsCounter] = !myDoc->CommitCommand(); // && (myNestedNum == -1); - myTransactionsCounter++; + if (myDoc->CommitCommand()) { // if commit is successfull, just increment counters + (*myTransactions.rbegin())++; + } + + if (isNestedClosed) { + compactNested(); } } void Model_Document::abortOperation() { - if (myNestedNum > 0 && !myDoc->HasOpenCommand()) { // abort all what was done in nested - // first compact all nested - if (compactNested()) { - myDoc->Undo(); // undo only compacted, if not: do not undo the empty transaction - } + if (!myNestedNum.empty() && !myDoc->HasOpenCommand()) { // abort all what was done in nested + compactNested(); + undo(); myDoc->ClearRedos(); - myTransactionsCounter--; - myIsEmptyTr.erase(myTransactionsCounter); - } else { - if (myNestedNum == 0) // abort only high-level - myNestedNum = -1; + myRedos.clear(); + } else { // abort the current + int aNumTransactions = *myTransactions.rbegin(); + myTransactions.pop_back(); + if (!myNestedNum.empty()) + (*myNestedNum.rbegin())--; + // roll back the needed number of transactions myDoc->AbortCommand(); + for(int a = 0; a < aNumTransactions; a++) + myDoc->Undo(); + myDoc->ClearRedos(); } synchronizeFeatures(true, false); // references were not changed since transaction start // abort for all subs @@ -400,13 +394,13 @@ bool Model_Document::isOperation() bool Model_Document::isModified() { // is modified if at least one operation was commited and not undoed - return myTransactionsCounter != myTransactionSave || isOperation(); + return myTransactions.size() != myTransactionSave || isOperation(); } bool Model_Document::canUndo() { - if (myDoc->GetAvailableUndos() > 0 && myNestedNum != 0 - && myTransactionsCounter != 0 /* for omitting the first useless transaction */) + if (myDoc->GetAvailableUndos() > 0 && (myNestedNum.empty() || *myNestedNum.rbegin() != 0) && + !myTransactions.empty() /* for omitting the first useless transaction */) return true; // check other subs contains operation that can be undoed const std::set aSubs = subDocuments(true); @@ -419,11 +413,15 @@ bool Model_Document::canUndo() void Model_Document::undo() { - myTransactionsCounter--; - if (myNestedNum > 0) - myNestedNum--; - if (!myIsEmptyTr[myTransactionsCounter]) + int aNumTransactions = *myTransactions.rbegin(); + myTransactions.pop_back(); + myRedos.push_back(aNumTransactions); + if (!myNestedNum.empty()) + (*myNestedNum.rbegin())--; + // roll back the needed number of transactions + for(int a = 0; a < aNumTransactions; a++) myDoc->Undo(); + synchronizeFeatures(true, true); // undo for all subs const std::set aSubs = subDocuments(true); @@ -447,11 +445,14 @@ bool Model_Document::canRedo() void Model_Document::redo() { - if (myNestedNum != -1) - myNestedNum++; - if (!myIsEmptyTr[myTransactionsCounter]) + if (!myNestedNum.empty()) + (*myNestedNum.rbegin())++; + int aNumRedos = *myRedos.rbegin(); + myRedos.pop_back(); + myTransactions.push_back(aNumRedos); + for(int a = 0; a < aNumRedos; a++) myDoc->Redo(); - myTransactionsCounter++; + synchronizeFeatures(true, true); // redo for all subs const std::set aSubs = subDocuments(true); diff --git a/src/Model/Model_Document.h b/src/Model/Model_Document.h index ad309ce94..2c9dd2123 100644 --- a/src/Model/Model_Document.h +++ b/src/Model/Model_Document.h @@ -170,7 +170,7 @@ class Model_Document : public ModelAPI_Document //! performs compactification of all nested operations into one //! \returns true if resulting transaction is not empty and can be undoed - bool compactNested(); + void compactNested(); //! Initializes the data fields of the feature void initData(ObjectPtr theObj, TDF_Label theLab, const int theTag); @@ -199,20 +199,23 @@ class Model_Document : public ModelAPI_Document std::string myID; ///< identifier of the document in the application std::string myKind; ///< kind of the document in the application Handle_TDocStd_Document myDoc; ///< OCAF document - /// counter of transactions - int myTransactionsCounter; + /// counter value of transaction on the last "save" call, used for "IsModified" method int myTransactionSave; - /// number of nested transactions performed (or -1 if not nested) - int myNestedNum; + /// number of nested transactions performed (list becasue may be nested inside of nested) + /// the list is empty if not nested transaction is performed + std::list myNestedNum; + + /// transaction indexes (related to myTransactionsAfterSave) and number of real transactions + /// in myDocument connected to this operation (may be zero for empty transaction) + std::list myTransactions; + /// list of numbers of real document transactions undone (first is oldest undone) + std::list myRedos; /// All features managed by this document (not only in history of OB) /// For optimization mapped by labels NCollection_DataMap myObjs; /// Optimization for finding the shape-label by topological naming names std::map myNamingNames; - - /// transaction indexes (related to myTransactionsAfterSave) which were empty in this doc - std::map myIsEmptyTr; /// If it is true, features are not executed on update (on abort, undo, redo) bool myExecuteFeatures; }; -- 2.39.2