X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FModel%2FModel_Document.cpp;h=ee8cebc14c9d0274a1dd486a1638fca540450c17;hb=7cb6ac084270943d926ec7ef35bf9e63d6a3eaf5;hp=304df74cbe8771daa53aa1a79c7d29b642eb77c0;hpb=a7f44a41bdf29e7bd978ff3b8c8bd514d1223be7;p=modules%2Fshaper.git diff --git a/src/Model/Model_Document.cpp b/src/Model/Model_Document.cpp index 304df74cb..e0ae73e76 100644 --- a/src/Model/Model_Document.cpp +++ b/src/Model/Model_Document.cpp @@ -1,17 +1,21 @@ +// Copyright (C) 2014-20xx CEA/DEN, EDF R&D + // File: Model_Document.cxx // Created: 28 Feb 2014 // Author: Mikhail PONIKAROV #include #include +#include #include #include #include -#include -#include -#include -#include +#include #include +#include +#include +#include + #include #include @@ -24,6 +28,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #ifndef WIN32 @@ -36,38 +47,44 @@ # 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) -static const int TAG_HISTORY = 3; // tag of the history sub-tree (python dump) -// feature sub-labels -static const int TAG_FEATURE_ARGUMENTS = 1; ///< where the arguments are located -static const int TAG_FEATURE_RESULTS = 2; ///< where the results are located +// general sub-labels +static const int TAG_CURRENT_FEATURE = 1; ///< where the reference to the current feature label is located (or no attribute if null feature) +static const int TAG_CURRENT_TRANSACTION = 2; ///< integer, index of the cransaction +static const int TAG_SELECTION_FEATURE = 3; ///< integer, tag of the selection feature label Model_Document::Model_Document(const std::string theID, const std::string theKind) - : myID(theID), myKind(theKind), + : myID(theID), myKind(theKind), myIsActive(false), myDoc(new TDocStd_Document("BinOcaf")) // binary OCAF format { + myObjs = new Model_Objects(myDoc->Main()); 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 myDoc->NewCommand(); TDataStd_Integer::Set(myDoc->Main().Father(), 0); + // this to avoid creation of integer attribute outside the transaction after undo + transactionID(); myDoc->CommitCommand(); } +void Model_Document::setThis(DocumentPtr theDoc) +{ + myObjs->setOwner(theDoc); +} + /// Returns the file name of this document by the nameof directory and identifuer of a document static TCollection_ExtendedString DocFileName(const char* theFileName, const std::string& theID) { TCollection_ExtendedString aPath((const Standard_CString) theFileName); // remove end-separators - while(aPath.Length() && (aPath.Value(aPath.Length()) == '\\' || aPath.Value(aPath.Length()) == '/')) + while(aPath.Length() && + (aPath.Value(aPath.Length()) == '\\' || aPath.Value(aPath.Length()) == '/')) aPath.Remove(aPath.Length()); aPath += _separator_; aPath += theID.c_str(); @@ -75,16 +92,22 @@ static TCollection_ExtendedString DocFileName(const char* theFileName, const std return aPath; } -bool Model_Document::load(const char* theFileName) +bool Model_Document::isRoot() const +{ + return this == Model_Session::get()->moduleDocument().get(); +} + +bool Model_Document::load(const char* theFileName, DocumentPtr theThis) { Handle(Model_Application) anApp = Model_Application::getApplication(); - if (this == Model_Session::get()->moduleDocument().get()) { + if (isRoot()) { anApp->setLoadPath(theFileName); } TCollection_ExtendedString aPath(DocFileName(theFileName, myID)); PCDM_ReaderStatus aStatus = (PCDM_ReaderStatus) -1; + Handle(TDocStd_Document) aLoaded; try { - aStatus = anApp->Open(aPath, myDoc); + aStatus = anApp->Open(aPath, aLoaded); } catch (Standard_Failure) { Handle(Standard_Failure) aFail = Standard_Failure::Caught(); Events_Error::send( @@ -95,7 +118,7 @@ bool Model_Document::load(const char* theFileName) if (isError) { switch (aStatus) { case PCDM_RS_UnknownDocument: - Events_Error::send(std::string("Can not open document: unknown format")); + Events_Error::send(std::string("Can not open document")); break; case PCDM_RS_AlreadyRetrieved: Events_Error::send(std::string("Can not open document: already opened")); @@ -145,21 +168,35 @@ bool Model_Document::load(const char* theFileName) break; } } + std::shared_ptr aSession = + std::dynamic_pointer_cast(Model_Session::get()); if (!isError) { + myDoc = aLoaded; 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); - synchronizeFeatures(false, true); - Model_Session::get()->setActiveDocument(Model_Session::get()->moduleDocument(), false); - Model_Session::get()->setActiveDocument(anApp->getDocument(myID), true); - } + aSession->setActiveDocument(anApp->getDocument(myID), false); + aSession->setCheckTransactions(false); + if (myObjs) + delete myObjs; + myObjs = new Model_Objects(myDoc->Main()); // synchronisation is inside + myObjs->setOwner(theThis); + // update the current features status + setCurrentFeature(currentFeature(false), false); + aSession->setCheckTransactions(true); + aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false); + // this is done in Part result "activate", so no needed here. Causes not-blue active part. + // aSession->setActiveDocument(anApp->getDocument(myID), true); + } else { // open failed, but new documnet was created to work with it: inform the model + aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false); + } return !isError; } bool Model_Document::save(const char* theFileName, std::list& theResults) { // create a directory in the root document if it is not yet exist - if (this == Model_Session::get()->moduleDocument().get()) { + Handle(Model_Application) anApp = Model_Application::getApplication(); + if (isRoot()) { #ifdef WIN32 CreateDirectory(theFileName, NULL); #else @@ -170,7 +207,7 @@ bool Model_Document::save(const char* theFileName, std::list& theRe TCollection_ExtendedString aPath(DocFileName(theFileName, myID)); PCDM_StoreStatus aStatus; try { - aStatus = Model_Application::getApplication()->SaveAs(myDoc, aPath); + aStatus = anApp->SaveAs(myDoc, aPath); } catch (Standard_Failure) { Handle(Standard_Failure) aFail = Standard_Failure::Caught(); Events_Error::send( @@ -192,12 +229,33 @@ 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()); - std::set::iterator aSubIter = mySubs.begin(); - for (; aSubIter != mySubs.end() && isDone; aSubIter++) { - isDone = subDoc(*aSubIter)->save(theFileName, theResults); + const std::set aSubs = subDocuments(false); + std::set::iterator aSubIter = aSubs.begin(); + for (; aSubIter != aSubs.end() && isDone; aSubIter++) { + 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); + } } } return isDone; @@ -206,39 +264,34 @@ bool Model_Document::save(const char* theFileName, std::list& theRe void Model_Document::close(const bool theForever) { std::shared_ptr aPM = Model_Session::get(); - if (this != aPM->moduleDocument().get() && this == aPM->activeDocument().get()) { + if (!isRoot() && this == aPM->activeDocument().get()) { aPM->setActiveDocument(aPM->moduleDocument()); + } else if (isRoot()) { + // erase the active document if root is closed + aPM->setActiveDocument(DocumentPtr()); } // close all subs - std::set::iterator aSubIter = mySubs.begin(); - for (; aSubIter != mySubs.end(); aSubIter++) - subDoc(*aSubIter)->close(theForever); - mySubs.clear(); + const std::set aSubs = subDocuments(true); + std::set::iterator aSubIter = aSubs.begin(); + for (; aSubIter != aSubs.end(); aSubIter++) { + std::shared_ptr aSub = subDoc(*aSubIter); + if (aSub->myObjs) // if it was not closed before + aSub->close(theForever); + } // close for thid document needs no transaction in this document std::static_pointer_cast(Model_Session::get())->setCheckTransactions(false); - // delete all features of this document - std::shared_ptr aThis = - Model_Application::getApplication()->getDocument(myID); - Events_Loop* aLoop = Events_Loop::loop(); - NCollection_DataMap::Iterator aFeaturesIter(myObjs); - for(; aFeaturesIter.More(); aFeaturesIter.Next()) { - FeaturePtr aFeature = aFeaturesIter.Value(); - static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY); - ModelAPI_EventCreator::get()->sendDeleted(aThis, ModelAPI_Feature::group()); - ModelAPI_EventCreator::get()->sendUpdated(aFeature, EVENT_DISP); - aFeature->eraseResults(); - aFeature->erase(); - } - myObjs.Clear(); - aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED)); - aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY)); - // close all only if it is really asked, otherwise it can be undoed/redoed if (theForever) { + // flush everything to avoid messages with bad objects + delete myObjs; + myObjs = 0; if (myDoc->CanClose() == CDM_CCS_OK) myDoc->Close(); + mySelectionFeature.reset(); + } else { + setCurrentFeature(FeaturePtr(), false); // disables all features } std::static_pointer_cast(Model_Session::get())->setCheckTransactions(true); @@ -247,62 +300,57 @@ 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()->myOCAFNum++; // 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 + incrementTransactionID(); + myTransactions.push_back(Transaction()); + if (!myNestedNum.empty()) + (*myNestedNum.rbegin())++; + myRedos.clear(); // new command for all subs - std::set::iterator aSubIter = mySubs.begin(); - for (; aSubIter != mySubs.end(); aSubIter++) + const std::set aSubs = subDocuments(true); + std::set::iterator aSubIter = aSubs.begin(); + for (; aSubIter != aSubs.end(); aSubIter++) 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()->myOCAFNum; + myTransactions.pop_back(); } - myIsEmptyTr.erase(myTransactionsCounter); - myNestedNum--; + // the latest transaction is the start of lower-level operation which startes the nested + myTransactions.rbegin()->myOCAFNum += 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 - synchronizeBackRefs(); + bool isNestedClosed = !myDoc->HasOpenCommand() && !myNestedNum.empty(); + static std::shared_ptr aSession = + std::static_pointer_cast(Model_Session::get()); + myObjs->synchronizeBackRefs(); Events_Loop* aLoop = Events_Loop::loop(); aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED)); 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)); aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED)); // this must be here just after everything is finished but before real transaction stop // to avoid messages about modifications outside of the transaction // and to rebuild everything after all updates and creates - if (Model_Session::get()->moduleDocument().get() == this) { // once for root document + if (isRoot()) { // once for root document Events_Loop::loop()->autoFlush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED)); static std::shared_ptr aFinishMsg (new Events_Message(Events_Loop::eventByName("FinishOperation"))); @@ -312,52 +360,107 @@ 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 - std::set::iterator aSubIter = mySubs.begin(); - for (; aSubIter != mySubs.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++; + bool aResult = false; + const std::set aSubs = subDocuments(true); + std::set::iterator aSubIter = aSubs.begin(); + for (; aSubIter != aSubs.end(); aSubIter++) + 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()->myOCAFNum++; + aResult = true; + } + + if (isNestedClosed) { + compactNested(); + } + if (!aResult && !myTransactions.empty() /* it can be for just created part document */) + aResult = myTransactions.rbegin()->myOCAFNum != 0; + + if (!aResult && isRoot()) { + // nothing inside in all documents, so remove this transaction from the transactions list + undoInternal(true, false); + } + // on finish clear redos in any case (issue 446) and for all subs (issue 408) + myDoc->ClearRedos(); + myRedos.clear(); + for (aSubIter = aSubs.begin(); aSubIter != aSubs.end(); aSubIter++) { + subDoc(*aSubIter)->myDoc->ClearRedos(); + subDoc(*aSubIter)->myRedos.clear(); + } + + return aResult; +} + +/// Returns in theDelta labels that has been modified in the latest transaction of theDoc +static void modifiedLabels(const Handle(TDocStd_Document)& theDoc, TDF_LabelList& theDelta, + const bool isRedo = false) { + Handle(TDF_Delta) aDelta; + if (isRedo) + aDelta = theDoc->GetRedos().First(); + else + aDelta = theDoc->GetUndos().Last(); + TDF_LabelList aDeltaList; + aDelta->Labels(aDeltaList); // it clears list, so, use new one and then append to the result + for(TDF_ListIteratorOfLabelList aListIter(aDeltaList); aListIter.More(); aListIter.Next()) { + theDelta.Append(aListIter.Value()); + } + // add also label of the modified attributes + const TDF_AttributeDeltaList& anAttrs = aDelta->AttributeDeltas(); + for (TDF_ListIteratorOfAttributeDeltaList anAttr(anAttrs); anAttr.More(); anAttr.Next()) { + theDelta.Append(anAttr.Value()->Label()); } } 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 + TDF_LabelList aDeltaLabels; // labels that are updated during "abort" + if (!myNestedNum.empty() && !myDoc->HasOpenCommand()) { // abort all what was done in nested + compactNested(); + // store undo-delta here as undo actually does in the method later + int a, aNumTransactions = myTransactions.rbegin()->myOCAFNum; + for(a = 0; a < aNumTransactions; a++) { + modifiedLabels(myDoc, aDeltaLabels); + myDoc->Undo(); + } + for(a = 0; a < aNumTransactions; a++) { + myDoc->Redo(); + } + + undoInternal(false, false); + myDoc->ClearRedos(); + myRedos.clear(); + } else { // abort the current + int aNumTransactions = myTransactions.rbegin()->myOCAFNum; + myTransactions.pop_back(); + if (!myNestedNum.empty()) + (*myNestedNum.rbegin())--; + // roll back the needed number of transactions + // make commit/undo to get the modification delta + //myDoc->AbortCommand(); + if (myDoc->CommitCommand()) { + modifiedLabels(myDoc, aDeltaLabels); + myDoc->Undo(); + } + for(int a = 0; a < aNumTransactions; a++) { + modifiedLabels(myDoc, aDeltaLabels); + myDoc->Undo(); } myDoc->ClearRedos(); - myTransactionsCounter--; - myIsEmptyTr.erase(myTransactionsCounter); - } else { - if (myNestedNum == 0) // abort only high-level - myNestedNum = -1; - myDoc->AbortCommand(); } - synchronizeFeatures(true, false); // references were not changed since transaction start - // abort for all subs - std::set::iterator aSubIter = mySubs.begin(); - for (; aSubIter != mySubs.end(); aSubIter++) + // abort for all subs, flushes will be later, in the end of root abort + const std::set aSubs = subDocuments(true); + std::set::iterator aSubIter = aSubs.begin(); + for (; aSubIter != aSubs.end(); aSubIter++) subDoc(*aSubIter)->abortOperation(); + // references may be changed because they are set in attributes on the fly + myObjs->synchronizeFeatures(aDeltaLabels, true, isRoot()); } -bool Model_Document::isOperation() +bool Model_Document::isOperation() const { // operation is opened for all documents: no need to check subs return myDoc->HasOpenCommand() == Standard_True ; @@ -366,107 +469,181 @@ 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 */) + // issue 406 : if transaction is opened, but nothing to undo behind, can not undo + int aCurrentNum = isOperation() ? 1 : 0; + if (myDoc->GetAvailableUndos() > 0 && + (myNestedNum.empty() || *myNestedNum.rbegin() - aCurrentNum > 0) && // there is something to undo in nested + myTransactions.size() - aCurrentNum > 0 /* for omitting the first useless transaction */) return true; // check other subs contains operation that can be undoed - std::set::iterator aSubIter = mySubs.begin(); - for (; aSubIter != mySubs.end(); aSubIter++) - if (subDoc(*aSubIter)->canUndo()) - return true; + const std::set aSubs = subDocuments(true); + std::set::iterator aSubIter = aSubs.begin(); + for (; aSubIter != aSubs.end(); aSubIter++) { + std::shared_ptr aSub = subDoc(*aSubIter); + if (aSub->myObjs) {// if it was not closed before + if (aSub->canUndo()) + return true; + } + } + 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()->myOCAFNum; + myRedos.push_back(*myTransactions.rbegin()); + myTransactions.pop_back(); + if (!myNestedNum.empty()) + (*myNestedNum.rbegin())--; + // roll back the needed number of transactions + TDF_LabelList aDeltaLabels; + for(int a = 0; a < aNumTransactions; a++) { + if (theSynchronize) + modifiedLabels(myDoc, aDeltaLabels); myDoc->Undo(); - synchronizeFeatures(true, true); - // undo for all subs - std::set::iterator aSubIter = mySubs.begin(); - for (; aSubIter != mySubs.end(); aSubIter++) - subDoc(*aSubIter)->undo(); + } + + if (theWithSubs) { + // undo for all subs + const std::set aSubs = subDocuments(true); + std::set::iterator aSubIter = aSubs.begin(); + for (; aSubIter != aSubs.end(); aSubIter++) { + if (!subDoc(*aSubIter)->myObjs) + continue; + subDoc(*aSubIter)->undoInternal(theWithSubs, theSynchronize); + } + } + // after undo of all sub-documents to avoid updates on not-modified data (issue 370) + if (theSynchronize) { + myObjs->synchronizeFeatures(aDeltaLabels, true, isRoot()); + // update the current features status + setCurrentFeature(currentFeature(false), false); + } +} + +void Model_Document::undo() +{ + undoInternal(true, true); } bool Model_Document::canRedo() { - if (myDoc->GetAvailableRedos() > 0) + if (!myRedos.empty()) return true; // check other subs contains operation that can be redoed - std::set::iterator aSubIter = mySubs.begin(); - for (; aSubIter != mySubs.end(); aSubIter++) + const std::set aSubs = subDocuments(true); + std::set::iterator aSubIter = aSubs.begin(); + for (; aSubIter != aSubs.end(); aSubIter++) { + if (!subDoc(*aSubIter)->myObjs) + continue; if (subDoc(*aSubIter)->canRedo()) return true; + } return false; } void Model_Document::redo() { - if (myNestedNum != -1) - myNestedNum++; - if (!myIsEmptyTr[myTransactionsCounter]) + if (!myNestedNum.empty()) + (*myNestedNum.rbegin())++; + int aNumRedos = myRedos.rbegin()->myOCAFNum; + myTransactions.push_back(*myRedos.rbegin()); + myRedos.pop_back(); + TDF_LabelList aDeltaLabels; + for(int a = 0; a < aNumRedos; a++) { + modifiedLabels(myDoc, aDeltaLabels, true); myDoc->Redo(); - myTransactionsCounter++; - synchronizeFeatures(true, true); + } + // redo for all subs - std::set::iterator aSubIter = mySubs.begin(); - for (; aSubIter != mySubs.end(); aSubIter++) + const std::set aSubs = subDocuments(true); + std::set::iterator aSubIter = aSubs.begin(); + for (; aSubIter != aSubs.end(); aSubIter++) subDoc(*aSubIter)->redo(); + + // after redo of all sub-documents to avoid updates on not-modified data (issue 370) + myObjs->synchronizeFeatures(aDeltaLabels, true, isRoot()); + // update the current features status + setCurrentFeature(currentFeature(false), false); } -/// Appenad to the array of references a new referenced label -static void AddToRefArray(TDF_Label& theArrayLab, TDF_Label& theReferenced) +std::list Model_Document::undoList() const { - Handle(TDataStd_ReferenceArray) aRefs; - if (!theArrayLab.FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) { - aRefs = TDataStd_ReferenceArray::Set(theArrayLab, 0, 0); - aRefs->SetValue(0, theReferenced); - } else { // extend array by one more element - Handle(TDataStd_HLabelArray1) aNewArray = new TDataStd_HLabelArray1(aRefs->Lower(), - aRefs->Upper() + 1); - for (int a = aRefs->Lower(); a <= aRefs->Upper(); a++) { - aNewArray->SetValue(a, aRefs->Value(a)); - } - aNewArray->SetValue(aRefs->Upper() + 1, theReferenced); - aRefs->SetInternalArray(aNewArray); + std::list aResult; + // the number of skipped current operations (on undo they will be aborted) + int aSkipCurrent = isOperation() ? 1 : 0; + std::list::const_reverse_iterator aTrIter = myTransactions.crbegin(); + int aNumUndo = myTransactions.size(); + if (!myNestedNum.empty()) + aNumUndo = *myNestedNum.rbegin(); + for( ; aNumUndo > 0; aTrIter++, aNumUndo--) { + if (aSkipCurrent == 0) aResult.push_back(aTrIter->myId); + else aSkipCurrent--; + } + return aResult; +} + +std::list Model_Document::redoList() const +{ + std::list aResult; + std::list::const_reverse_iterator aTrIter = myRedos.crbegin(); + for( ; aTrIter != myRedos.crend(); aTrIter++) { + aResult.push_back(aTrIter->myId); } + return aResult; +} + +void Model_Document::operationId(const std::string& theId) +{ + myTransactions.rbegin()->myId = theId; } -FeaturePtr Model_Document::addFeature(std::string theID) +FeaturePtr Model_Document::addFeature(std::string theID, const bool theMakeCurrent) { - TDF_Label anEmptyLab; - FeaturePtr anEmptyFeature; - FeaturePtr aFeature = ModelAPI_Session::get()->createFeature(theID); + std::shared_ptr aSession = + std::dynamic_pointer_cast(ModelAPI_Session::get()); + FeaturePtr aFeature = aSession->createFeature(theID, this); if (!aFeature) return aFeature; - std::shared_ptr aDocToAdd = std::dynamic_pointer_cast( - aFeature->documentToAdd()); + aFeature->init(); + Model_Document* aDocToAdd; + if (!aFeature->documentToAdd().empty()) { // use the customized document to add + if (aFeature->documentToAdd() != kind()) { // the root document by default + aDocToAdd = std::dynamic_pointer_cast(aSession->moduleDocument()).get(); + } else { + aDocToAdd = this; + } + } else { // if customized is not presented, add to "this" document + aDocToAdd = this; + } if (aFeature) { - TDF_Label aFeatureLab; - if (!aFeature->isAction()) { // do not add action to the data model - TDF_Label aFeaturesLab = aDocToAdd->featuresLabel(); - aFeatureLab = aFeaturesLab.NewChild(); - aDocToAdd->initData(aFeature, aFeatureLab, TAG_FEATURE_ARGUMENTS); - // keep the feature ID to restore document later correctly - TDataStd_Comment::Set(aFeatureLab, aFeature->getKind().c_str()); - aDocToAdd->myObjs.Bind(aFeatureLab, aFeature); - // store feature in the history of features array - if (aFeature->isInHistory()) { - AddToRefArray(aFeaturesLab, aFeatureLab); + // searching for feature after which must be added the next feature: this is the current feature + // but also all sub-features of this feature + FeaturePtr aCurrent = aDocToAdd->currentFeature(false); + bool isModified = true; + for(CompositeFeaturePtr aComp = std::dynamic_pointer_cast(aCurrent); + aComp.get() && isModified; + aComp = std::dynamic_pointer_cast(aCurrent)) { + isModified = false; + int aSubs = aComp->numberOfSubs(false); + for(int a = 0; a < aSubs; a++) { + FeaturePtr aSub = aComp->subFeature(a, false); + if (myObjs->isLater(aSub, aCurrent)) { + isModified = true; + aCurrent = aSub; + } } } + aDocToAdd->myObjs->addFeature(aFeature, aCurrent); if (!aFeature->isAction()) { // do not add action to the data model - // event: feature is added - static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED); - ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent); + if (theMakeCurrent) // after all this feature stays in the document, so make it current + aDocToAdd->setCurrentFeature(aFeature, false); } else { // feature must be executed // no creation event => updater not working, problem with remove part aFeature->execute(); @@ -475,587 +652,421 @@ FeaturePtr Model_Document::addFeature(std::string theID) return aFeature; } -/// Appenad to the array of references a new referenced label. -/// If theIndex is not -1, removes element at thisindex, not theReferenced. -/// \returns the index of removed element -static int RemoveFromRefArray(TDF_Label theArrayLab, TDF_Label theReferenced, const int theIndex = - -1) + +void Model_Document::refsToFeature(FeaturePtr theFeature, + std::set >& theRefs, const bool isSendError) { - int aResult = -1; // no returned - Handle(TDataStd_ReferenceArray) aRefs; - if (theArrayLab.FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) { - if (aRefs->Length() == 1) { // just erase an array - if ((theIndex == -1 && aRefs->Value(0) == theReferenced) || theIndex == 0) { - theArrayLab.ForgetAttribute(TDataStd_ReferenceArray::GetID()); - } - aResult = 0; - } else { // reduce the array - Handle(TDataStd_HLabelArray1) aNewArray = new TDataStd_HLabelArray1(aRefs->Lower(), - aRefs->Upper() - 1); - int aCount = aRefs->Lower(); - for (int a = aCount; a <= aRefs->Upper(); a++, aCount++) { - if ((theIndex == -1 && aRefs->Value(a) == theReferenced) || theIndex == a) { - aCount--; - aResult = a; - } else { - aNewArray->SetValue(aCount, aRefs->Value(a)); - } - } - aRefs->SetInternalArray(aNewArray); - } - } - return aResult; + myObjs->refsToFeature(theFeature, theRefs, isSendError); } -void Model_Document::removeFeature(FeaturePtr theFeature, const bool theCheck) +void Model_Document::removeFeature(FeaturePtr theFeature) { - if (theCheck) { - // check the feature: it must have no depended objects on it - std::list::const_iterator aResIter = theFeature->results().cbegin(); - for(; aResIter != theFeature->results().cend(); aResIter++) { - std::shared_ptr aData = - std::dynamic_pointer_cast((*aResIter)->data()); - if (aData && !aData->refsToMe().empty()) { - Events_Error::send( - "Feature '" + theFeature->data()->name() + "' is used and can not be deleted"); - return; - } - } - } + myObjs->removeFeature(theFeature); +} - std::shared_ptr aData = std::static_pointer_cast(theFeature->data()); - if (aData) { - TDF_Label aFeatureLabel = aData->label().Father(); - if (myObjs.IsBound(aFeatureLabel)) - myObjs.UnBind(aFeatureLabel); - else - return; // not found feature => do not remove - // erase fields - theFeature->erase(); - // erase all attributes under the label of feature - aFeatureLabel.ForgetAllAttributes(); - // remove it from the references array - if (theFeature->isInHistory()) { - RemoveFromRefArray(featuresLabel(), aFeatureLabel); - } - } - // event: feature is deleted - ModelAPI_EventCreator::get()->sendDeleted(theFeature->document(), ModelAPI_Feature::group()); +void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis) +{ + myObjs->moveFeature(theMoved, theAfterThis); + if (theAfterThis == currentFeature(true)) + setCurrentFeature(theMoved, true); } -FeaturePtr Model_Document::feature(TDF_Label& theLabel) +void Model_Document::updateHistory(const std::shared_ptr theObject) { - if (myObjs.IsBound(theLabel)) - return myObjs.Find(theLabel); - return FeaturePtr(); // not found + myObjs->updateHistory(theObject); } -ObjectPtr Model_Document::object(TDF_Label theLabel) +void Model_Document::updateHistory(const std::string theGroup) { - // try feature by label - FeaturePtr aFeature = feature(theLabel); - if (aFeature) - return feature(theLabel); - TDF_Label aFeatureLabel = theLabel.Father().Father(); // let's suppose it is result - aFeature = feature(aFeatureLabel); - if (aFeature) { - const std::list >& aResults = aFeature->results(); - std::list >::const_iterator aRIter = aResults.cbegin(); - for (; aRIter != aResults.cend(); aRIter++) { - std::shared_ptr aResData = std::dynamic_pointer_cast( - (*aRIter)->data()); - if (aResData->label().Father().IsEqual(theLabel)) - return *aRIter; - } - } - return FeaturePtr(); // not found + myObjs->updateHistory(theGroup); } std::shared_ptr Model_Document::subDocument(std::string theDocID) { - // just store sub-document identifier here to manage it later - if (mySubs.find(theDocID) == mySubs.end()) - mySubs.insert(theDocID); return Model_Application::getApplication()->getDocument(theDocID); } +const std::set Model_Document::subDocuments(const bool theActivatedOnly) const +{ + std::set aResult; + std::list aPartResults; + myObjs->allResults(ModelAPI_ResultPart::group(), aPartResults); + std::list::iterator aPartRes = aPartResults.begin(); + for(; aPartRes != aPartResults.end(); aPartRes++) { + ResultPartPtr aPart = std::dynamic_pointer_cast(*aPartRes); + if (aPart && (!theActivatedOnly || aPart->isActivated())) { + aResult.insert(aPart->original()->data()->name()); + } + } + return aResult; +} + std::shared_ptr Model_Document::subDoc(std::string theDocID) { // just store sub-document identifier here to manage it later - if (mySubs.find(theDocID) == mySubs.end()) - mySubs.insert(theDocID); return std::dynamic_pointer_cast( Model_Application::getApplication()->getDocument(theDocID)); } -ObjectPtr Model_Document::object(const std::string& theGroupID, const int theIndex, - const bool theHidden) +ObjectPtr Model_Document::object(const std::string& theGroupID, const int theIndex) { - if (theGroupID == ModelAPI_Feature::group()) { - if (theHidden) { - int anIndex = 0; - TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID()); - for (; aLabIter.More(); aLabIter.Next()) { - if (theIndex == anIndex) { - TDF_Label aFLabel = aLabIter.Value()->Label(); - return feature(aFLabel); - } - anIndex++; - } - } else { - Handle(TDataStd_ReferenceArray) aRefs; - if (!featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) - return ObjectPtr(); - if (aRefs->Lower() > theIndex || aRefs->Upper() < theIndex) - return ObjectPtr(); - TDF_Label aFeatureLabel = aRefs->Value(theIndex); - return feature(aFeatureLabel); - } - } else { - // comment must be in any feature: it is kind - int anIndex = 0; - TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID()); - 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() != theGroupID) continue; - bool isIn = theHidden && (*aRIter)->isInHistory(); - if (!isIn && (*aRIter)->isInHistory()) { // check that there is nobody references this result - isIn = !(*aRIter)->isConcealed(); - } - if (isIn) { - if (anIndex == theIndex) - return *aRIter; - anIndex++; - } - } - } - } - // not found - return ObjectPtr(); + return myObjs->object(theGroupID, theIndex); } -int Model_Document::size(const std::string& theGroupID, const bool theHidden) +std::shared_ptr Model_Document::objectByName( + const std::string& theGroupID, const std::string& theName) { - int aResult = 0; - if (theGroupID == ModelAPI_Feature::group()) { - if (theHidden) { - return myObjs.Size(); - } else { - Handle(TDataStd_ReferenceArray) aRefs; - if (featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) - return aRefs->Length(); - } - } else { - // comment must be in any feature: it is kind - TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID()); - for (; aLabIter.More(); aLabIter.Next()) { - TDF_Label aFLabel = aLabIter.Value()->Label(); - FeaturePtr aFeature = feature(aFLabel); - 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)->groupName() != theGroupID) continue; - bool isIn = theHidden; - if (!isIn && (*aRIter)->isInHistory()) { // check that there is nobody references this result - isIn = !(*aRIter)->isConcealed(); - } - if (isIn) - aResult++; - } - } - } - // group is not found - return aResult; + return myObjs->objectByName(theGroupID, theName); } -TDF_Label Model_Document::featuresLabel() +const int Model_Document::index(std::shared_ptr theObject) { - return myDoc->Main().FindChild(TAG_OBJECTS); + return myObjs->index(theObject); } -void Model_Document::setUniqueName(FeaturePtr theFeature) +int Model_Document::size(const std::string& theGroupID) { - if (!theFeature->data()->name().empty()) - return; // not needed, name is already defined - std::string aName; // result - // first count all objects of such kind to start with index = count + 1 - int aNumObjects = 0; - NCollection_DataMap::Iterator aFIter(myObjs); - for (; aFIter.More(); aFIter.Next()) { - if (aFIter.Value()->getKind() == theFeature->getKind()) - aNumObjects++; - } - // generate candidate name - std::stringstream aNameStream; - aNameStream << theFeature->getKind() << "_" << aNumObjects + 1; - aName = aNameStream.str(); - // check this is unique, if not, increase index by 1 - for (aFIter.Initialize(myObjs); aFIter.More();) { - FeaturePtr aFeature = aFIter.Value(); - bool isSameName = aFeature->data()->name() == aName; - if (!isSameName) { // check also results to avoid same results names (actual for Parts) - const std::list >& aResults = aFeature->results(); - std::list >::const_iterator aRIter = aResults.begin(); - for (; aRIter != aResults.cend(); aRIter++) { - isSameName = (*aRIter)->data()->name() == aName; - } - } - if (isSameName) { - aNumObjects++; - std::stringstream aNameStream; - aNameStream << theFeature->getKind() << "_" << aNumObjects + 1; - aName = aNameStream.str(); - // reinitialize iterator to make sure a new name is unique - aFIter.Initialize(myObjs); - } else - aFIter.Next(); - } - theFeature->data()->setName(aName); + return myObjs->size(theGroupID); } -void Model_Document::initData(ObjectPtr theObj, TDF_Label theLab, const int theTag) +std::shared_ptr Model_Document::currentFeature(const bool theVisible) { - std::shared_ptr aThis = Model_Application::getApplication()->getDocument( - myID); - std::shared_ptr aData(new Model_Data); - aData->setLabel(theLab.FindChild(theTag)); - aData->setObject(theObj); - theObj->setDoc(aThis); - theObj->setData(aData); - FeaturePtr aFeature = std::dynamic_pointer_cast(theObj); - if (aFeature) { - setUniqueName(aFeature); // must be before "initAttributes" because duplicate part uses name - aFeature->initAttributes(); + if (!myObjs) // on close document feature destruction it may call this method + return std::shared_ptr(); + TDF_Label aRefLab = generalLabel().FindChild(TAG_CURRENT_FEATURE); + Handle(TDF_Reference) aRef; + if (aRefLab.FindAttribute(TDF_Reference::GetID(), aRef)) { + TDF_Label aLab = aRef->Get(); + FeaturePtr aResult = myObjs->feature(aLab); + if (theVisible) { // get nearest visible (in history) going up + while(aResult.get() && // sub-composites are never in history + (!aResult->isInHistory() || ModelAPI_Tools::compositeOwner(aResult).get())) { + aResult = myObjs->nextFeature(aResult, true); + } + } + return aResult; } + return std::shared_ptr(); // null feature means the higher than first } -void Model_Document::synchronizeFeatures(const bool theMarkUpdated, const bool theUpdateReferences) +void Model_Document::setCurrentFeature( + std::shared_ptr theCurrent, const bool theVisible) { - 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); + // blocks the flush signals to avoid each objects visualization in the viewer + // they should not be shown once after all modifications are performed Events_Loop* aLoop = Events_Loop::loop(); - aLoop->activateFlushes(false); - - // update all objects by checking are they of labels or not - std::set aNewFeatures, aKeptFeatures; - TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID()); - for (; aLabIter.More(); aLabIter.Next()) { - TDF_Label aFeatureLabel = aLabIter.Value()->Label(); - FeaturePtr aFeature; - if (!myObjs.IsBound(aFeatureLabel)) { // a new feature is inserted - // create a feature - aFeature = ModelAPI_Session::get()->createFeature( - TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(aLabIter.Value())->Get()) - .ToCString()); - if (!aFeature) { // somethig is wrong, most probably, the opened document has invalid structure - Events_Error::send("Invalid type of object in the document"); - aLabIter.Value()->Label().ForgetAllAttributes(); - continue; - } - // this must be before "setData" to redo the sketch line correctly - myObjs.Bind(aFeatureLabel, aFeature); - aNewFeatures.insert(aFeature); - initData(aFeature, aFeatureLabel, TAG_FEATURE_ARGUMENTS); - - // event: model is updated - static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED); - ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent); - } else { // nothing is changed, both iterators are incremented - aFeature = myObjs.Find(aFeatureLabel); - aKeptFeatures.insert(aFeature); - if (theMarkUpdated) { - static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED); - ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent); + bool isActive = aLoop->activateFlushes(false); + + TDF_Label aRefLab = generalLabel().FindChild(TAG_CURRENT_FEATURE); + CompositeFeaturePtr aMain; // main feature that may nest the new current + if (theCurrent.get()) { + aMain = std::dynamic_pointer_cast(theCurrent); + if (!aMain.get()) { + // if feature nests into compisite feature, make the composite feature as current + const std::set& aRefsToMe = theCurrent->data()->refsToMe(); + std::set::const_iterator aRefToMe = aRefsToMe.begin(); + for(; aRefToMe != aRefsToMe.end(); aRefToMe++) { + CompositeFeaturePtr aComposite = + std::dynamic_pointer_cast((*aRefToMe)->owner()); + if (aComposite.get() && aComposite->isSub(theCurrent)) { + aMain = aComposite; + break; + } } } } - // update results of thefeatures (after features created because they may be connected, like sketch and sub elements) - 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); - updateResults(aFeature); - } - } - // check all features are checked: if not => it was removed - NCollection_DataMap::Iterator aFIter(myObjs); - while (aFIter.More()) { - if (aKeptFeatures.find(aFIter.Value()) == aKeptFeatures.end() - && aNewFeatures.find(aFIter.Value()) == aNewFeatures.end()) { - FeaturePtr aFeature = aFIter.Value(); - // event: model is updated - //if (aFeature->isInHistory()) { - ModelAPI_EventCreator::get()->sendDeleted(aThis, ModelAPI_Feature::group()); - //} - // results of this feature must be redisplayed (hided) - static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY); - const std::list >& aResults = aFeature->results(); - std::list >::const_iterator aRIter = aResults.begin(); - // redisplay also removed feature (used for sketch and AISObject) - 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); - } else - aFIter.Next(); + if (theVisible) { // make features below which are not in history also enabled: sketch subs + FeaturePtr aNext = + theCurrent.get() ? myObjs->nextFeature(theCurrent) : myObjs->firstFeature(); + for (; aNext.get(); aNext = myObjs->nextFeature(theCurrent)) { + if (aNext->isInHistory()) { + break; // next in history is not needed + } else { // next not in history is good for making current + theCurrent = aNext; + } + } } + if (theCurrent.get()) { + std::shared_ptr aData = std::static_pointer_cast(theCurrent->data()); + if (!aData.get() || !aData->isValid()) { + aLoop->activateFlushes(isActive); + return; + } + TDF_Label aFeatureLabel = aData->label().Father(); - if (theUpdateReferences) { - synchronizeBackRefs(); + Handle(TDF_Reference) aRef; + if (aRefLab.FindAttribute(TDF_Reference::GetID(), aRef)) { + aRef->Set(aFeatureLabel); + } else { + aRef = TDF_Reference::Set(aRefLab, aFeatureLabel); + } + } else { // remove reference for the null feature + aRefLab.ForgetAttribute(TDF_Reference::GetID()); } + // make all features after this feature disabled in reversed order (to remove results without deps) + static Events_ID aRedispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY); + static Events_ID aCreateEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED); + static Events_ID aDeleteEvent = aLoop->eventByName(EVENT_OBJECT_DELETED); - myExecuteFeatures = false; - aLoop->activateFlushes(true); + bool aPassed = false; // flag that the current object is already passed in cycle + FeaturePtr anIter = myObjs->lastFeature(); + bool aWasChanged = false; + for(; anIter.get(); anIter = myObjs->nextFeature(anIter, true)) { + // check this before passed become enabled: the current feature is enabled! + if (anIter == theCurrent) aPassed = true; - aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED)); - aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED)); - 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; -} - -void Model_Document::synchronizeBackRefs() -{ - std::shared_ptr aThis = - Model_Application::getApplication()->getDocument(myID); - // keeps the concealed flags of result to catch the change and create created/deleted events - std::list > aConcealed; - // first cycle: erase all data about back-references - NCollection_DataMap::Iterator aFeatures(myObjs); - for(; aFeatures.More(); aFeatures.Next()) { - FeaturePtr aFeature = aFeatures.Value(); - std::shared_ptr aFData = - std::dynamic_pointer_cast(aFeature->data()); - if (aFData) { - aFData->eraseBackReferences(); - } - const std::list >& aResults = aFeature->results(); - std::list >::const_iterator aRIter = aResults.begin(); - for (; aRIter != aResults.cend(); aRIter++) { - std::shared_ptr aResData = - std::dynamic_pointer_cast((*aRIter)->data()); - if (aResData) { - aConcealed.push_back(std::pair(*aRIter, (*aRIter)->isConcealed())); - aResData->eraseBackReferences(); - } + bool aDisabledFlag = !aPassed; + if (aMain.get() && aMain->isSub(anIter)) // sub-elements of not-disabled feature are not disabled + aDisabledFlag = false; + if (anIter->getKind() == "Parameter") {// parameters are always out of the history of features, but not parameters + if (theCurrent.get() && theCurrent->getKind() != "Parameter") + aDisabledFlag = false; } - } - - // second cycle: set new back-references: only features may have reference, iterate only them - ModelAPI_ValidatorsFactory* aValidators = ModelAPI_Session::get()->validators(); - for(aFeatures.Initialize(myObjs); aFeatures.More(); aFeatures.Next()) { - FeaturePtr aFeature = aFeatures.Value(); - std::shared_ptr aFData = - std::dynamic_pointer_cast(aFeature->data()); - if (aFData) { - std::list > > aRefs; - aFData->referencesToObjects(aRefs); - std::list > >::iterator aRefsIter = aRefs.begin(); - for(; aRefsIter != aRefs.end(); aRefsIter++) { - std::list::iterator aRefTo = aRefsIter->second.begin(); - for(; aRefTo != aRefsIter->second.end(); aRefTo++) { - if (*aRefTo) { - std::shared_ptr aRefData = - std::dynamic_pointer_cast((*aRefTo)->data()); - aRefData->addBackReference(aFeature, aRefsIter->first); // here the Concealed flag is updated - } - } - } + if (anIter->setDisabled(aDisabledFlag)) { + // state of feature is changed => so feature become updated + static Events_ID anUpdateEvent = aLoop->eventByName(EVENT_OBJECT_UPDATED); + ModelAPI_EventCreator::get()->sendUpdated(anIter, anUpdateEvent); + // flush is in the end of this method + ModelAPI_EventCreator::get()->sendUpdated(anIter, aRedispEvent /*, false*/); + aWasChanged = true; } - } - std::list >::iterator aCIter = aConcealed.begin(); - for(; aCIter != aConcealed.end(); aCIter++) { - if (aCIter->first->isConcealed() != aCIter->second) { // somethign is changed => produce event - if (aCIter->second) { // was concealed become not => creation event - static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED); - ModelAPI_EventCreator::get()->sendUpdated(aCIter->first, anEvent); - } else { // was not concealed become concealed => delete event - ModelAPI_EventCreator::get()->sendDeleted(aThis, aCIter->first->groupName()); - // redisplay for the viewer (it must be disappeared also) - static Events_ID EVENT_DISP = - Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY); - ModelAPI_EventCreator::get()->sendUpdated(aCIter->first, EVENT_DISP); + // update for everyone the concealment flag immideately: on edit feature in the midle of history + if (aWasChanged) { + const std::list >& aResList = anIter->results(); + std::list >::const_iterator aRes = aResList.begin(); + for(; aRes != aResList.end(); aRes++) { + if ((*aRes).get() && (*aRes)->data()->isValid() && !(*aRes)->isDisabled()) + std::dynamic_pointer_cast((*aRes)->data())->updateConcealmentFlag(); } + } } + // unblock the flush signals and up them after this + aLoop->activateFlushes(isActive); } -TDF_Label Model_Document::resultLabel( - const std::shared_ptr& theFeatureData, const int theResultIndex) +void Model_Document::setCurrentFeatureUp() { - const std::shared_ptr& aData = - std::dynamic_pointer_cast(theFeatureData); - return aData->label().Father().FindChild(TAG_FEATURE_RESULTS).FindChild(theResultIndex + 1); + // on remove just go up for minimum step: highlight external objects in sketch causes + // problems if it is true: here and in "setCurrentFeature" + FeaturePtr aCurrent = currentFeature(false); + if (aCurrent.get()) { // if not, do nothing because null is the upper + FeaturePtr aPrev = myObjs->nextFeature(aCurrent, true); + // do not flush: it is called only on remove, it will be flushed in the end of transaction + setCurrentFeature(aPrev, false); + } } -void Model_Document::storeResult(std::shared_ptr theFeatureData, - std::shared_ptr theResult, - const int theResultIndex) +TDF_Label Model_Document::generalLabel() const { - std::shared_ptr aThis = - Model_Application::getApplication()->getDocument(myID); - theResult->setDoc(aThis); - initData(theResult, resultLabel(theFeatureData, theResultIndex), TAG_FEATURE_ARGUMENTS); - if (theResult->data()->name().empty()) { // if was not initialized, generate event and set a name - theResult->data()->setName(theFeatureData->name()); - } + return myDoc->Main().FindChild(TAG_GENERAL); } std::shared_ptr Model_Document::createConstruction( const std::shared_ptr& theFeatureData, const int theIndex) { - TDF_Label aLab = resultLabel(theFeatureData, theIndex); - TDataStd_Comment::Set(aLab, ModelAPI_ResultConstruction::group().c_str()); - ObjectPtr anOldObject = object(aLab); - std::shared_ptr aResult; - if (anOldObject) { - aResult = std::dynamic_pointer_cast(anOldObject); - } - if (!aResult) { - aResult = std::shared_ptr(new Model_ResultConstruction); - storeResult(theFeatureData, aResult, theIndex); - } - return aResult; + return myObjs->createConstruction(theFeatureData, theIndex); } std::shared_ptr Model_Document::createBody( const std::shared_ptr& theFeatureData, const int theIndex) { - TDF_Label aLab = resultLabel(theFeatureData, theIndex); - TDataStd_Comment::Set(aLab, ModelAPI_ResultBody::group().c_str()); - ObjectPtr anOldObject = object(aLab); - std::shared_ptr aResult; - if (anOldObject) { - aResult = std::dynamic_pointer_cast(anOldObject); - } - if (!aResult) { - aResult = std::shared_ptr(new Model_ResultBody); - storeResult(theFeatureData, aResult, theIndex); - } - return aResult; + return myObjs->createBody(theFeatureData, theIndex); } std::shared_ptr Model_Document::createPart( const std::shared_ptr& theFeatureData, const int theIndex) { - TDF_Label aLab = resultLabel(theFeatureData, theIndex); - TDataStd_Comment::Set(aLab, ModelAPI_ResultPart::group().c_str()); - ObjectPtr anOldObject = object(aLab); - std::shared_ptr aResult; - if (anOldObject) { - aResult = std::dynamic_pointer_cast(anOldObject); - } - if (!aResult) { - aResult = std::shared_ptr(new Model_ResultPart); - storeResult(theFeatureData, aResult, theIndex); - } - return aResult; + return myObjs->createPart(theFeatureData, theIndex); +} + +std::shared_ptr Model_Document::copyPart( + const std::shared_ptr& theOrigin, + const std::shared_ptr& theFeatureData, const int theIndex) +{ + return myObjs->copyPart(theOrigin, theFeatureData, theIndex); } std::shared_ptr Model_Document::createGroup( const std::shared_ptr& theFeatureData, const int theIndex) { - TDF_Label aLab = resultLabel(theFeatureData, theIndex); - TDataStd_Comment::Set(aLab, ModelAPI_ResultGroup::group().c_str()); - ObjectPtr anOldObject = object(aLab); - std::shared_ptr aResult; - if (anOldObject) { - aResult = std::dynamic_pointer_cast(anOldObject); - } - if (!aResult) { - aResult = std::shared_ptr(new Model_ResultGroup(theFeatureData)); - storeResult(theFeatureData, aResult, theIndex); - } - return aResult; + return myObjs->createGroup(theFeatureData, theIndex); +} + +std::shared_ptr Model_Document::createParameter( + const std::shared_ptr& theFeatureData, const int theIndex) +{ + return myObjs->createParameter(theFeatureData, theIndex); } std::shared_ptr Model_Document::feature( const std::shared_ptr& theResult) { - std::shared_ptr aData = std::dynamic_pointer_cast(theResult->data()); - if (aData) { - TDF_Label aFeatureLab = aData->label().Father().Father().Father(); - return feature(aFeatureLab); - } - return FeaturePtr(); + return myObjs->feature(theResult); } -void Model_Document::updateResults(FeaturePtr theFeature) +Standard_Integer HashCode(const TDF_Label& theLab, const Standard_Integer theUpper) { - // for not persistent is will be done by parametric updater automatically - //if (!theFeature->isPersistentResult()) return; - // check the existing results and remove them if there is nothing on the label - std::list::const_iterator aResIter = theFeature->results().cbegin(); - while(aResIter != theFeature->results().cend()) { - ResultPtr aBody = std::dynamic_pointer_cast(*aResIter); - if (aBody) { - if (!aBody->data()->isValid()) { - // found a disappeared result => remove it - theFeature->removeResult(aBody); - // start iterate from beginning because iterator is corrupted by removing - aResIter = theFeature->results().cbegin(); - continue; - } - } - aResIter++; - } - // it may be on undo - if (!theFeature->data() || !theFeature->data()->isValid()) - return; - // check that results are presented on all labels - int aResSize = theFeature->results().size(); - TDF_ChildIterator aLabIter(resultLabel(theFeature->data(), 0).Father()); - for(; aLabIter.More(); aLabIter.Next()) { - // here must be GUID of the feature - int aResIndex = aLabIter.Value().Tag() - 1; - ResultPtr aNewBody; - if (aResSize <= aResIndex) { - TDF_Label anArgLab = aLabIter.Value(); - Handle(TDataStd_Comment) aGroup; - if (anArgLab.FindAttribute(TDataStd_Comment::GetID(), aGroup)) { - if (aGroup->Get() == ModelAPI_ResultBody::group().c_str()) { - aNewBody = createBody(theFeature->data(), aResIndex); - } else if (aGroup->Get() == ModelAPI_ResultPart::group().c_str()) { - aNewBody = createPart(theFeature->data(), aResIndex); - } else if (aGroup->Get() == ModelAPI_ResultConstruction::group().c_str()) { - theFeature->execute(); // construction shapes are needed for sketch solver - break; - } else if (aGroup->Get() == ModelAPI_ResultGroup::group().c_str()) { - aNewBody = createGroup(theFeature->data(), aResIndex); - } else { - Events_Error::send(std::string("Unknown type of result is found in the document:") + - TCollection_AsciiString(aGroup->Get()).ToCString()); + return TDF_LabelMapHasher::HashCode(theLab, theUpper); + +} +Standard_Boolean IsEqual(const TDF_Label& theLab1, const TDF_Label& theLab2) +{ + return TDF_LabelMapHasher::IsEqual(theLab1, theLab2); +} + +void Model_Document::addNamingName(const TDF_Label theLabel, std::string theName) +{ + myNamingNames[theName] = theLabel; +} + +TDF_Label Model_Document::findNamingName(std::string theName) +{ + std::map::iterator aFind = myNamingNames.find(theName); + if (aFind == myNamingNames.end()) + return TDF_Label(); // not found + return aFind->second; +} + +ResultPtr Model_Document::findByName(const std::string theName) +{ + return myObjs->findByName(theName); +} + +std::list > Model_Document::allFeatures() +{ + return myObjs->allFeatures(); +} + +void Model_Document::setActive(const bool theFlag) +{ + if (theFlag != myIsActive) { + myIsActive = theFlag; + // redisplay all the objects of this part + static Events_Loop* aLoop = Events_Loop::loop(); + static Events_ID aRedispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY); + + for(int a = size(ModelAPI_Feature::group()) - 1; a >= 0; a--) { + FeaturePtr aFeature = std::dynamic_pointer_cast( + object(ModelAPI_Feature::group(), a)); + if (aFeature.get() && aFeature->data()->isValid()) { + const std::list >& aResList = aFeature->results(); + std::list >::const_iterator aRes = aResList.begin(); + for(; aRes != aResList.end(); aRes++) { + ModelAPI_EventCreator::get()->sendUpdated(*aRes, aRedispEvent); } } - if (aNewBody) { - theFeature->setResult(aNewBody, aResIndex); - } } } } -Standard_Integer HashCode(const TDF_Label& theLab, const Standard_Integer theUpper) +bool Model_Document::isActive() const { - return TDF_LabelMapHasher::HashCode(theLab, theUpper); + return myIsActive; +} +int Model_Document::transactionID() +{ + Handle(TDataStd_Integer) anIndex; + if (!generalLabel().FindChild(TAG_CURRENT_TRANSACTION). + FindAttribute(TDataStd_Integer::GetID(), anIndex)) { + anIndex = TDataStd_Integer::Set(generalLabel().FindChild(TAG_CURRENT_TRANSACTION), 1); + } + return anIndex->Get(); } -Standard_Boolean IsEqual(const TDF_Label& theLab1, const TDF_Label& theLab2) + +void Model_Document::incrementTransactionID() { - return TDF_LabelMapHasher::IsEqual(theLab1, theLab2); + int aNewVal = transactionID() + 1; + TDataStd_Integer::Set(generalLabel().FindChild(TAG_CURRENT_TRANSACTION), aNewVal); +} +void Model_Document::decrementTransactionID() +{ + int aNewVal = transactionID() - 1; + TDataStd_Integer::Set(generalLabel().FindChild(TAG_CURRENT_TRANSACTION), aNewVal); +} + +bool Model_Document::isOpened() +{ + return myObjs && !myDoc.IsNull(); +} + +int Model_Document::numInternalFeatures() +{ + return myObjs->numInternalFeatures(); +} + +std::shared_ptr Model_Document::internalFeature(const int theIndex) +{ + return myObjs->internalFeature(theIndex); +} + +void Model_Document::synchronizeTransactions() +{ + Model_Document* aRoot = + std::dynamic_pointer_cast(ModelAPI_Session::get()->moduleDocument()).get(); + if (aRoot == this) + return; // don't need to synchronise root with root + + std::shared_ptr aSession = + std::dynamic_pointer_cast(Model_Session::get()); + while(myRedos.size() > aRoot->myRedos.size()) { // remove redos in this + aSession->setCheckTransactions(false); + redo(); + aSession->setCheckTransactions(true); + } + /* this case can not be reproduced in any known case for the current moment, so, just comment + while(myRedos.size() < aRoot->myRedos.size()) { // add more redos in this + undoInternal(false, true); + }*/ +} + +/// Feature that is used for selection in the Part document by the external request +class Model_SelectionInPartFeature : public ModelAPI_Feature { +public: + /// Nothing to do in constructor + Model_SelectionInPartFeature() : ModelAPI_Feature() {} + + /// Returns the unique kind of a feature + virtual const std::string& getKind() { + static std::string MY_KIND("InternalSelectionInPartFeature"); + return MY_KIND; + } + /// Request for initialization of data model of the object: adding all attributes + virtual void initAttributes() { + data()->addAttribute("selection", ModelAPI_AttributeSelectionList::typeId()); + } + /// Nothing to do in the execution function + virtual void execute() {} + +}; + +//! Returns the feature that is used for calculation of selection externally from the document +AttributeSelectionListPtr Model_Document::selectionInPartFeature() +{ + // return already created, otherwise create + if (!mySelectionFeature.get() || !mySelectionFeature->data()->isValid()) { + // create a new one + mySelectionFeature = FeaturePtr(new Model_SelectionInPartFeature); + + TDF_Label aFeatureLab = generalLabel().FindChild(TAG_SELECTION_FEATURE); + std::shared_ptr aData(new Model_Data); + aData->setLabel(aFeatureLab.FindChild(1)); + aData->setObject(mySelectionFeature); + mySelectionFeature->setDoc(myObjs->owner()); + mySelectionFeature->setData(aData); + std::string aName = id() + "_Part"; + mySelectionFeature->data()->setName(aName); + mySelectionFeature->setDoc(myObjs->owner()); + mySelectionFeature->initAttributes(); + } + return mySelectionFeature->selectionList("selection"); +} + +FeaturePtr Model_Document::lastFeature() +{ + if (myObjs) + return myObjs->lastFeature(); + return FeaturePtr(); }