X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FModel%2FModel_Document.cpp;h=ec10e79632777634a6ca203b35b9d2f7edced0b1;hb=4b07d0ae015bc7ed510f8795d9d57d408e0367eb;hp=1e38773f03f964aa93d42e1b23b65273b6c0d61a;hpb=d57ad65dd41c26cd7c937cc68bc32a23bffd644f;p=modules%2Fshaper.git diff --git a/src/Model/Model_Document.cpp b/src/Model/Model_Document.cpp index 1e38773f0..ec10e7963 100644 --- a/src/Model/Model_Document.cpp +++ b/src/Model/Model_Document.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -40,7 +41,7 @@ # define _separator_ '/' #endif -static const int UNDO_LIMIT = 10; // number of possible undo operations +static const int UNDO_LIMIT = 1000; // number of possible undo operations (big for sketcher) static const int TAG_GENERAL = 1; // general properties tag static const int TAG_OBJECTS = 2; // tag of the objects sub-tree (features, results) @@ -60,9 +61,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 @@ -157,10 +156,14 @@ bool Model_Document::load(const char* theFileName) if (!isError) { myDoc->SetUndoLimit(UNDO_LIMIT); // to avoid the problem that feature is created in the current, not this, document - Model_Session::get()->setActiveDocument(anApp->getDocument(myID), false); + std::shared_ptr aSession = + std::dynamic_pointer_cast(Model_Session::get()); + aSession->setActiveDocument(anApp->getDocument(myID), false); + aSession->setCheckTransactions(false); synchronizeFeatures(false, true); - Model_Session::get()->setActiveDocument(Model_Session::get()->moduleDocument(), false); - Model_Session::get()->setActiveDocument(anApp->getDocument(myID), true); + aSession->setCheckTransactions(true); + aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false); + aSession->setActiveDocument(anApp->getDocument(myID), true); } return !isError; } @@ -202,35 +205,32 @@ 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(true); + const std::set aSubs = subDocuments(false); std::set::iterator aSubIter = aSubs.begin(); for (; aSubIter != aSubs.end() && isDone; aSubIter++) { - isDone = subDoc(*aSubIter)->save(theFileName, theResults); - } - const std::set allSubs = subDocuments(false); - if (isDone) { // also try to copy the not-activated sub-documents - for(aSubIter = allSubs.begin(); aSubIter != allSubs.end(); aSubIter++) { - if (aSubs.find(*aSubIter) == aSubs.end()) { // filter out the active subs - std::string aDocName = *aSubIter; - if (!aDocName.empty() && aSubs.find(aDocName) == aSubs.end()) { - // just copy file - TCollection_AsciiString aSubPath(DocFileName(anApp->loadPath().c_str(), aDocName)); - OSD_Path aPath(aSubPath); - OSD_File aFile(aPath); - if (aFile.Exists()) { - TCollection_AsciiString aDestinationDir(DocFileName(theFileName, aDocName)); - OSD_Path aDestination(aDestinationDir); - aFile.Copy(aDestination); - theResults.push_back(aDestinationDir.ToCString()); - } else { - Events_Error::send( - std::string("Can not open file ") + aSubPath.ToCString() + " for saving"); - } + if (anApp->isLoadByDemand(*aSubIter)) { + // copy not-activated document that is not in the memory + std::string aDocName = *aSubIter; + if (!aDocName.empty()) { + // just copy file + TCollection_AsciiString aSubPath(DocFileName(anApp->loadPath().c_str(), aDocName)); + OSD_Path aPath(aSubPath); + OSD_File aFile(aPath); + if (aFile.Exists()) { + TCollection_AsciiString aDestinationDir(DocFileName(theFileName, aDocName)); + OSD_Path aDestination(aDestinationDir); + aFile.Copy(aDestination); + theResults.push_back(aDestinationDir.ToCString()); + } else { + Events_Error::send( + std::string("Can not open file ") + aSubPath.ToCString() + " for saving"); } } + } else { // simply save opened document + isDone = subDoc(*aSubIter)->save(theFileName, theResults); } } } @@ -242,6 +242,9 @@ void Model_Document::close(const bool theForever) std::shared_ptr aPM = Model_Session::get(); if (this != aPM->moduleDocument().get() && this == aPM->activeDocument().get()) { aPM->setActiveDocument(aPM->moduleDocument()); + } else if (this == aPM->moduleDocument().get()) { + // erase the active document if root is closed + aPM->setActiveDocument(DocumentPtr()); } // close all subs const std::set aSubs = subDocuments(true); @@ -263,9 +266,15 @@ void Model_Document::close(const bool theForever) ModelAPI_EventCreator::get()->sendDeleted(aThis, ModelAPI_Feature::group()); ModelAPI_EventCreator::get()->sendUpdated(aFeature, EVENT_DISP); aFeature->eraseResults(); - aFeature->erase(); + if (theForever) { // issue #294: do not delete content of the document until it can be redone + aFeature->erase(); + } else { + aFeature->data()->execState(ModelAPI_StateMustBeUpdated); + } + } + if (theForever) { + myObjs.Clear(); } - myObjs.Clear(); aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED)); aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY)); @@ -281,16 +290,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(); @@ -298,35 +310,26 @@ 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 Model_Document::finishOperation() { - // 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 + bool isNestedClosed = !myDoc->HasOpenCommand() && !myNestedNum.empty(); + static std::shared_ptr aSession = + std::static_pointer_cast(Model_Session::get()); synchronizeBackRefs(); Events_Loop* aLoop = Events_Loop::loop(); aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED)); @@ -347,44 +350,52 @@ 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 - // finish for all subs first: to avoid nested finishing and "isOperation" calls problems inside + bool aResult = false; const std::set aSubs = subDocuments(true); std::set::iterator aSubIter = aSubs.begin(); 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 (subDoc(*aSubIter)->finishOperation()) + aResult = true; + + // transaction may be empty if this document was created during this transaction (create part) + if (!myTransactions.empty() && myDoc->CommitCommand()) { // if commit is successfull, just increment counters + (*myTransactions.rbegin())++; + aResult = true; + } + + if (isNestedClosed) { + compactNested(); } + if (!aResult && !myTransactions.empty() /* it can be for just created part document */) + aResult = *(myTransactions.rbegin()) != 0; + + if (!aResult && Model_Session::get()->moduleDocument().get() == this) { + // nothing inside in all documents, so remove this transaction from the transactions list + undoInternal(true, false); + myDoc->ClearRedos(); + myRedos.clear(); + } + return aResult; } 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(); + undoInternal(false, false); 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 @@ -403,13 +414,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); @@ -420,19 +431,31 @@ bool Model_Document::canUndo() return false; } -void Model_Document::undo() +void Model_Document::undoInternal(const bool theWithSubs, const bool theSynchronize) { - 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); - std::set::iterator aSubIter = aSubs.begin(); - for (; aSubIter != aSubs.end(); aSubIter++) - subDoc(*aSubIter)->undo(); + + if (theSynchronize) + synchronizeFeatures(true, true); + if (theWithSubs) { + // undo for all subs + const std::set aSubs = subDocuments(true); + std::set::iterator aSubIter = aSubs.begin(); + for (; aSubIter != aSubs.end(); aSubIter++) + subDoc(*aSubIter)->undoInternal(theWithSubs, theSynchronize); + } +} + +void Model_Document::undo() +{ + undoInternal(true, true); } bool Model_Document::canRedo() @@ -450,11 +473,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); @@ -463,7 +489,7 @@ void Model_Document::redo() subDoc(*aSubIter)->redo(); } -/// Appenad to the array of references a new referenced label +/// Append to the array of references a new referenced label static void AddToRefArray(TDF_Label& theArrayLab, TDF_Label& theReferenced) { Handle(TDataStd_ReferenceArray) aRefs; @@ -626,14 +652,16 @@ const std::set Model_Document::subDocuments(const bool theActivated for (; aLabIter.More(); aLabIter.Next()) { TDF_Label aFLabel = aLabIter.Value()->Label(); FeaturePtr aFeature = feature(aFLabel); - const std::list >& aResults = aFeature->results(); - std::list >::const_iterator aRIter = aResults.begin(); - for (; aRIter != aResults.cend(); aRIter++) { - if ((*aRIter)->groupName() != ModelAPI_ResultPart::group()) continue; - if ((*aRIter)->isInHistory()) { - ResultPartPtr aPart = std::dynamic_pointer_cast(*aRIter); - if (aPart && (!theActivatedOnly || aPart->isActivated())) - aResult.insert(aPart->data()->name()); + if (aFeature.get()) { // if document is closed the feature may be not in myObjs map + const std::list >& aResults = aFeature->results(); + std::list >::const_iterator aRIter = aResults.begin(); + for (; aRIter != aResults.cend(); aRIter++) { + if ((*aRIter)->groupName() != ModelAPI_ResultPart::group()) continue; + if ((*aRIter)->isInHistory()) { + ResultPartPtr aPart = std::dynamic_pointer_cast(*aRIter); + if (aPart && (!theActivatedOnly || aPart->isActivated())) + aResult.insert(aPart->data()->name()); + } } } } @@ -799,8 +827,6 @@ void Model_Document::synchronizeFeatures(const bool theMarkUpdated, const bool t std::shared_ptr aThis = Model_Application::getApplication()->getDocument(myID); // after all updates, sends a message that groups of features were created or updated - std::static_pointer_cast(Model_Session::get()) - ->setCheckTransactions(false); Events_Loop* aLoop = Events_Loop::loop(); aLoop->activateFlushes(false); @@ -838,14 +864,21 @@ void Model_Document::synchronizeFeatures(const bool theMarkUpdated, const bool t } } // update results of thefeatures (after features created because they may be connected, like sketch and sub elements) + std::list aComposites; // composites must be updated after their subs (issue 360) TDF_ChildIDIterator aLabIter2(featuresLabel(), TDataStd_Comment::GetID()); for (; aLabIter2.More(); aLabIter2.Next()) { TDF_Label aFeatureLabel = aLabIter2.Value()->Label(); if (myObjs.IsBound(aFeatureLabel)) { // a new feature is inserted FeaturePtr aFeature = myObjs.Find(aFeatureLabel); + if (std::dynamic_pointer_cast(aFeature).get()) + aComposites.push_back(aFeature); updateResults(aFeature); } } + std::list::iterator aComposite = aComposites.begin(); + for(; aComposite != aComposites.end(); aComposite++) { + updateResults(*aComposite); + } // check all features are checked: if not => it was removed NCollection_DataMap::Iterator aFIter(myObjs); @@ -865,9 +898,9 @@ void Model_Document::synchronizeFeatures(const bool theMarkUpdated, const bool t ModelAPI_EventCreator::get()->sendUpdated(aFeature, EVENT_DISP); aFeature->erase(); // unbind after the "erase" call: on abort sketch is removes sub-objects that corrupts aFIter - TDF_Label aLab = aFIter.Key(); - aFIter.Next(); - myObjs.UnBind(aLab); + myObjs.UnBind(aFIter.Key()); + // reinitialize iterator because unbind may corrupt the previous order in the map + aFIter.Initialize(myObjs); } else aFIter.Next(); } @@ -884,8 +917,6 @@ void Model_Document::synchronizeFeatures(const bool theMarkUpdated, const bool t aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED)); aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY)); aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TOHIDE)); - std::static_pointer_cast(Model_Session::get()) - ->setCheckTransactions(true); myExecuteFeatures = true; } @@ -1131,3 +1162,23 @@ TDF_Label Model_Document::findNamingName(std::string theName) return TDF_Label(); // not found return aFind->second; } + +ResultPtr Model_Document::findByName(const std::string theName) +{ + NCollection_DataMap::Iterator anObjIter(myObjs); + for(; anObjIter.More(); anObjIter.Next()) { + FeaturePtr& aFeature = anObjIter.ChangeValue(); + if (!aFeature) // may be on close + continue; + const std::list >& aResults = aFeature->results(); + std::list >::const_iterator aRIter = aResults.begin(); + for (; aRIter != aResults.cend(); aRIter++) { + if (aRIter->get() && (*aRIter)->data() && (*aRIter)->data()->isValid() && + (*aRIter)->data()->name() == theName) { + return *aRIter; + } + } + } + // not found + return ResultPtr(); +}