X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FModel%2FModel_Document.cpp;h=21823b4e05b73ba391dff15922d5fd5f5c77caab;hb=ba8ee992b99097165d62ac9a93e842a47787216a;hp=8cc427ce8d88564a9450b7eeadc4c072b295cf31;hpb=61cd0845b41710ad4e7eae07cc6106904be67b9f;p=modules%2Fshaper.git diff --git a/src/Model/Model_Document.cpp b/src/Model/Model_Document.cpp old mode 100644 new mode 100755 index 8cc427ce8..21823b4e0 --- a/src/Model/Model_Document.cpp +++ b/src/Model/Model_Document.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include @@ -21,13 +23,21 @@ #include #include #include +#include +#include #include #include +#include #include #include #include +#include #include #include +#include +#include +#include +#include #include #ifndef WIN32 @@ -46,9 +56,11 @@ static const int TAG_GENERAL = 1; // general properties tag // 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 transaction +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), +Model_Document::Model_Document(const int theID, const std::string theKind) + : myID(theID), myKind(theKind), myIsActive(false), myDoc(new TDocStd_Document("BinOcaf")) // binary OCAF format { myObjs = new Model_Objects(myDoc->Main()); @@ -59,6 +71,8 @@ Model_Document::Model_Document(const std::string theID, const std::string theKin // 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(); } @@ -67,10 +81,10 @@ 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) +/// Returns the file name of this document by the name of directory and identifier of a document +static TCollection_ExtendedString DocFileName(const char* theDirName, const std::string& theID) { - TCollection_ExtendedString aPath((const Standard_CString) theFileName); + TCollection_ExtendedString aPath((const Standard_CString) theDirName); // remove end-separators while(aPath.Length() && (aPath.Value(aPath.Length()) == '\\' || aPath.Value(aPath.Length()) == '/')) @@ -86,16 +100,17 @@ bool Model_Document::isRoot() const return this == Model_Session::get()->moduleDocument().get(); } -bool Model_Document::load(const char* theFileName, DocumentPtr theThis) +bool Model_Document::load(const char* theDirName, const char* theFileName, DocumentPtr theThis) { Handle(Model_Application) anApp = Model_Application::getApplication(); if (isRoot()) { - anApp->setLoadPath(theFileName); + anApp->setLoadPath(theDirName); } - TCollection_ExtendedString aPath(DocFileName(theFileName, myID)); + TCollection_ExtendedString aPath(DocFileName(theDirName, theFileName)); 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( @@ -156,12 +171,13 @@ bool Model_Document::load(const char* theFileName, DocumentPtr theThis) 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 - std::shared_ptr aSession = - std::dynamic_pointer_cast(Model_Session::get()); - aSession->setActiveDocument(anApp->getDocument(myID), false); + aSession->setActiveDocument(anApp->document(myID), false); aSession->setCheckTransactions(false); if (myObjs) delete myObjs; @@ -173,11 +189,25 @@ bool Model_Document::load(const char* theFileName, DocumentPtr theThis) 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); - } + + // make sub-parts as loaded by demand + 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.get()) + anApp->setLoadByDemand(aPart->data()->name()); + } + + } 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) +bool Model_Document::save( + const char* theDirName, const char* theFileName, std::list& theResults) { // create a directory in the root document if it is not yet exist Handle(Model_Application) anApp = Model_Application::getApplication(); @@ -189,7 +219,7 @@ bool Model_Document::save(const char* theFileName, std::list& theRe #endif } // filename in the dir is id of document inside of the given directory - TCollection_ExtendedString aPath(DocFileName(theFileName, myID)); + TCollection_ExtendedString aPath(DocFileName(theDirName, theFileName)); PCDM_StoreStatus aStatus; try { aStatus = anApp->SaveAs(myDoc, aPath); @@ -214,22 +244,25 @@ bool Model_Document::save(const char* theFileName, std::list& theRe break; } } - myTransactionSave = myTransactions.size(); + myTransactionSave = int(myTransactions.size()); if (isDone) { // save also sub-documents if any theResults.push_back(TCollection_AsciiString(aPath).ToCString()); - const std::set aSubs = subDocuments(false); - std::set::iterator aSubIter = aSubs.begin(); - for (; aSubIter != aSubs.end() && isDone; aSubIter++) { - if (anApp->isLoadByDemand(*aSubIter)) { + // iterate all result parts to find all loaded or not yet loaded documents + 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->isActivated()) { // copy not-activated document that is not in the memory - std::string aDocName = *aSubIter; + std::string aDocName = aPart->data()->name(); 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)); + TCollection_AsciiString aDestinationDir(DocFileName(theDirName, aDocName)); OSD_Path aDestination(aDestinationDir); aFile.Copy(aDestination); theResults.push_back(aDestinationDir.ToCString()); @@ -239,7 +272,8 @@ bool Model_Document::save(const char* theFileName, std::list& theRe } } } else { // simply save opened document - isDone = subDoc(*aSubIter)->save(theFileName, theResults); + isDone = std::dynamic_pointer_cast(aPart->partDoc())-> + save(theDirName, aPart->data()->name().c_str(), theResults); } } } @@ -256,8 +290,8 @@ void Model_Document::close(const bool theForever) aPM->setActiveDocument(DocumentPtr()); } // close all subs - const std::set aSubs = subDocuments(true); - std::set::iterator aSubIter = aSubs.begin(); + const std::set aSubs = subDocuments(); + 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 @@ -269,12 +303,18 @@ void Model_Document::close(const bool theForever) // 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 + // update the OB: features are disabled (on remove of Part) + Events_Loop* aLoop = Events_Loop::loop(); + static Events_ID aDeleteEvent = Events_Loop::eventByName(EVENT_OBJECT_DELETED); + aLoop->flush(aDeleteEvent); } std::static_pointer_cast(Model_Session::get())->setCheckTransactions(true); @@ -282,6 +322,7 @@ void Model_Document::close(const bool theForever) void Model_Document::startOperation() { + incrementTransactionID(); // outside of transaction in order to avoid empty transactions keeping if (myDoc->HasOpenCommand()) { // start of nested command 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 @@ -297,8 +338,8 @@ void Model_Document::startOperation() (*myNestedNum.rbegin())++; myRedos.clear(); // new command for all subs - const std::set aSubs = subDocuments(true); - std::set::iterator aSubIter = aSubs.begin(); + const std::set aSubs = subDocuments(); + std::set::iterator aSubIter = aSubs.begin(); for (; aSubIter != aSubs.end(); aSubIter++) subDoc(*aSubIter)->startOperation(); } @@ -318,11 +359,141 @@ void Model_Document::compactNested() } } +/// Compares the content ofthe given attributes, returns true if equal. +/// This method is used to avoid empty transactions when only "current" is changed +/// to some value and then comes back in this transaction, so, it compares only +/// references and Boolean and Integer Arrays for the current moment. +static bool isEqualContent(Handle(TDF_Attribute) theAttr1, Handle(TDF_Attribute) theAttr2) +{ + if (Standard_GUID::IsEqual(theAttr1->ID(), TDF_Reference::GetID())) { // reference + Handle(TDF_Reference) aRef1 = Handle(TDF_Reference)::DownCast(theAttr1); + Handle(TDF_Reference) aRef2 = Handle(TDF_Reference)::DownCast(theAttr2); + if (aRef1.IsNull() && aRef2.IsNull()) + return true; + if (aRef1.IsNull() || aRef2.IsNull()) + return false; + return aRef1->Get().IsEqual(aRef2->Get()) == Standard_True; + } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDataStd_BooleanArray::GetID())) { + Handle(TDataStd_BooleanArray) anArr1 = Handle(TDataStd_BooleanArray)::DownCast(theAttr1); + Handle(TDataStd_BooleanArray) anArr2 = Handle(TDataStd_BooleanArray)::DownCast(theAttr2); + if (anArr1.IsNull() && anArr2.IsNull()) + return true; + if (anArr1.IsNull() || anArr2.IsNull()) + return false; + if (anArr1->Lower() == anArr2->Lower() && anArr1->Upper() == anArr2->Upper()) { + for(int a = anArr1->Lower(); a <= anArr1->Upper(); a++) + if (a != 1 && anArr1->Value(a) != anArr2->Value(a)) // second is for display + return false; + return true; + } + } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDataStd_IntegerArray::GetID())) { + Handle(TDataStd_IntegerArray) anArr1 = Handle(TDataStd_IntegerArray)::DownCast(theAttr1); + Handle(TDataStd_IntegerArray) anArr2 = Handle(TDataStd_IntegerArray)::DownCast(theAttr2); + if (anArr1.IsNull() && anArr2.IsNull()) + return true; + if (anArr1.IsNull() || anArr2.IsNull()) + return false; + if (anArr1->Lower() == anArr2->Lower() && anArr1->Upper() == anArr2->Upper()) { + for(int a = anArr1->Lower(); a <= anArr1->Upper(); a++) + if (anArr1->Value(a) != anArr2->Value(a)) { + // avoid the transaction ID checking + if (a == 2 && anArr1->Upper() == 2 && anArr2->Label().Tag() == 1 && + (anArr2->Label().Depth() == 4 || anArr2->Label().Depth() == 6)) + continue; + return false; + } + return true; + } + } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDataStd_ReferenceArray::GetID())) { + Handle(TDataStd_ReferenceArray) anArr1 = Handle(TDataStd_ReferenceArray)::DownCast(theAttr1); + Handle(TDataStd_ReferenceArray) anArr2 = Handle(TDataStd_ReferenceArray)::DownCast(theAttr2); + if (anArr1.IsNull() && anArr2.IsNull()) + return true; + if (anArr1.IsNull() || anArr2.IsNull()) + return false; + if (anArr1->Lower() == anArr2->Lower() && anArr1->Upper() == anArr2->Upper()) { + for(int a = anArr1->Lower(); a <= anArr1->Upper(); a++) + if (anArr1->Value(a) != anArr2->Value(a)) { + // avoid the transaction ID checking + if (a == 2 && anArr1->Upper() == 2 && anArr2->Label().Tag() == 1 && + (anArr2->Label().Depth() == 4 || anArr2->Label().Depth() == 6)) + continue; + return false; + } + return true; + } + } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDataStd_ReferenceList::GetID())) { + Handle(TDataStd_ReferenceList) aList1 = Handle(TDataStd_ReferenceList)::DownCast(theAttr1); + Handle(TDataStd_ReferenceList) aList2= Handle(TDataStd_ReferenceList)::DownCast(theAttr2); + if (aList1.IsNull() && aList2.IsNull()) + return true; + if (aList1.IsNull() || aList2.IsNull()) + return false; + const TDF_LabelList& aLList1 = aList1->List(); + const TDF_LabelList& aLList2 = aList2->List(); + TDF_ListIteratorOfLabelList aLIter1(aLList1); + TDF_ListIteratorOfLabelList aLIter2(aLList2); + for(; aLIter1.More() && aLIter2.More(); aLIter1.Next(), aLIter2.Next()) { + if (aLIter1.Value() != aLIter2.Value()) + return false; + } + return !aLIter1.More() && !aLIter2.More(); // both lists are with the same size + } else if (Standard_GUID::IsEqual(theAttr1->ID(), TDF_TagSource::GetID())) { + return true; // it just for created and removed feature: nothing is changed + } + return false; +} + +/// Returns true if the last transaction is actually empty: modification to te same values +/// were performed only +static bool isEmptyTransaction(const Handle(TDocStd_Document)& theDoc) { + Handle(TDF_Delta) aDelta; + 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()) { + return false; + } + // add also label of the modified attributes + const TDF_AttributeDeltaList& anAttrs = aDelta->AttributeDeltas(); + for (TDF_ListIteratorOfAttributeDeltaList anAttr(anAttrs); anAttr.More(); anAttr.Next()) { + Handle(TDF_AttributeDelta)& anADelta = anAttr.Value(); + Handle(TDF_DeltaOnAddition) anAddition = Handle(TDF_DeltaOnAddition)::DownCast(anADelta); + if (anAddition.IsNull()) { // if the attribute was added, transaction is not empty + if (!anADelta->Label().IsNull() && !anADelta->Attribute().IsNull()) { + Handle(TDF_Attribute) aCurrentAttr; + if (anADelta->Label().FindAttribute(anADelta->Attribute()->ID(), aCurrentAttr)) { + if (isEqualContent(anADelta->Attribute(), aCurrentAttr)) { + continue; // attribute is not changed actually + } + } else if (Standard_GUID::IsEqual(anADelta->Attribute()->ID(), TDataStd_AsciiString::GetID())) { + continue; // error message is disappeared + } + } + } + return false; + } + return true; +} + bool Model_Document::finishOperation() { bool isNestedClosed = !myDoc->HasOpenCommand() && !myNestedNum.empty(); static std::shared_ptr aSession = std::static_pointer_cast(Model_Session::get()); + // do it before flashes to enable and recompute nesting features correctly + if (myNestedNum.empty() || (isNestedClosed && myNestedNum.size() == 1)) { + // if all nested operations are closed, make current the higher level objects (to perform + // it in the python scripts correctly): sketch become current after creation ofsub-elements + FeaturePtr aCurrent = currentFeature(false); + CompositeFeaturePtr aMain, aNext = ModelAPI_Tools::compositeOwner(aCurrent); + while(aNext.get()) { + aMain = aNext; + aNext = ModelAPI_Tools::compositeOwner(aMain); + } + if (aMain.get() && aMain != aCurrent) + setCurrentFeature(aMain, false); + } myObjs->synchronizeBackRefs(); Events_Loop* aLoop = Events_Loop::loop(); aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED)); @@ -344,16 +515,21 @@ bool Model_Document::finishOperation() // 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(); + const std::set aSubs = subDocuments(); + 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 (isEmptyTransaction(myDoc)) { // erase this transaction + myDoc->Undo(); + myDoc->ClearRedos(); + } else { + myTransactions.rbegin()->myOCAFNum++; + aResult = true; + } } if (isNestedClosed) { @@ -377,10 +553,41 @@ bool Model_Document::finishOperation() 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() { + 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(); @@ -390,18 +597,25 @@ void Model_Document::abortOperation() if (!myNestedNum.empty()) (*myNestedNum.rbegin())--; // roll back the needed number of transactions - myDoc->AbortCommand(); - for(int a = 0; a < aNumTransactions; a++) + //myDoc->AbortCommand(); + // instead of abort, do commit and undo: to get the delta of modifications + if (myDoc->CommitCommand()) { + modifiedLabels(myDoc, aDeltaLabels); myDoc->Undo(); + } + for(int a = 0; a < aNumTransactions; a++) { + modifiedLabels(myDoc, aDeltaLabels); + myDoc->Undo(); + } myDoc->ClearRedos(); } // 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(); + const std::set aSubs = subDocuments(); + 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(true, true, isRoot()); + myObjs->synchronizeFeatures(aDeltaLabels, true, isRoot()); } bool Model_Document::isOperation() const @@ -425,8 +639,8 @@ bool Model_Document::canUndo() myTransactions.size() - aCurrentNum > 0 /* for omitting the first useless transaction */) return true; // check other subs contains operation that can be undoed - const std::set aSubs = subDocuments(true); - std::set::iterator aSubIter = aSubs.begin(); + const std::set aSubs = subDocuments(); + 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 @@ -440,28 +654,34 @@ bool Model_Document::canUndo() void Model_Document::undoInternal(const bool theWithSubs, const bool theSynchronize) { + if (myTransactions.empty()) + return; 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 - for(int a = 0; a < aNumTransactions; a++) + TDF_LabelList aDeltaLabels; + for(int a = 0; a < aNumTransactions; a++) { + if (theSynchronize) + modifiedLabels(myDoc, aDeltaLabels); myDoc->Undo(); + } if (theWithSubs) { // undo for all subs - const std::set aSubs = subDocuments(true); - std::set::iterator aSubIter = aSubs.begin(); + const std::set aSubs = subDocuments(); + std::set::iterator aSubIter = aSubs.begin(); for (; aSubIter != aSubs.end(); aSubIter++) { if (!subDoc(*aSubIter)->myObjs) continue; subDoc(*aSubIter)->undoInternal(theWithSubs, theSynchronize); } } - // after redo of all sub-documents to avoid updates on not-modified data (issue 370) + // after undo of all sub-documents to avoid updates on not-modified data (issue 370) if (theSynchronize) { - myObjs->synchronizeFeatures(true, true, isRoot()); + myObjs->synchronizeFeatures(aDeltaLabels, true, isRoot()); // update the current features status setCurrentFeature(currentFeature(false), false); } @@ -477,8 +697,8 @@ bool Model_Document::canRedo() if (!myRedos.empty()) return true; // check other subs contains operation that can be redoed - const std::set aSubs = subDocuments(true); - std::set::iterator aSubIter = aSubs.begin(); + const std::set aSubs = subDocuments(); + std::set::iterator aSubIter = aSubs.begin(); for (; aSubIter != aSubs.end(); aSubIter++) { if (!subDoc(*aSubIter)->myObjs) continue; @@ -495,17 +715,20 @@ void Model_Document::redo() int aNumRedos = myRedos.rbegin()->myOCAFNum; myTransactions.push_back(*myRedos.rbegin()); myRedos.pop_back(); - for(int a = 0; a < aNumRedos; a++) + TDF_LabelList aDeltaLabels; + for(int a = 0; a < aNumRedos; a++) { + modifiedLabels(myDoc, aDeltaLabels, true); myDoc->Redo(); + } // redo for all subs - const std::set aSubs = subDocuments(true); - std::set::iterator aSubIter = aSubs.begin(); + const std::set aSubs = subDocuments(); + 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(true, true, isRoot()); + myObjs->synchronizeFeatures(aDeltaLabels, true, isRoot()); // update the current features status setCurrentFeature(currentFeature(false), false); } @@ -516,7 +739,7 @@ std::list Model_Document::undoList() const // 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(); + int aNumUndo = int(myTransactions.size()); if (!myNestedNum.empty()) aNumUndo = *myNestedNum.rbegin(); for( ; aNumUndo > 0; aTrIter++, aNumUndo--) { @@ -548,6 +771,7 @@ FeaturePtr Model_Document::addFeature(std::string theID, const bool theMakeCurre FeaturePtr aFeature = aSession->createFeature(theID, this); if (!aFeature) return aFeature; + aFeature->init(); Model_Document* aDocToAdd; if (!aFeature->documentToAdd().empty()) { // use the customized document to add if (aFeature->documentToAdd() != kind()) { // the root document by default @@ -559,7 +783,24 @@ FeaturePtr Model_Document::addFeature(std::string theID, const bool theMakeCurre aDocToAdd = this; } if (aFeature) { - aDocToAdd->myObjs->addFeature(aFeature, aDocToAdd->currentFeature(false)); + // 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 if (theMakeCurrent) // after all this feature stays in the document, so make it current aDocToAdd->setCurrentFeature(aFeature, false); @@ -583,6 +824,13 @@ void Model_Document::removeFeature(FeaturePtr theFeature) myObjs->removeFeature(theFeature); } +void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis) +{ + myObjs->moveFeature(theMoved, theAfterThis); + if (theAfterThis == currentFeature(true)) + setCurrentFeature(theMoved, true); +} + void Model_Document::updateHistory(const std::shared_ptr theObject) { myObjs->updateHistory(theObject); @@ -593,30 +841,26 @@ void Model_Document::updateHistory(const std::string theGroup) myObjs->updateHistory(theGroup); } -std::shared_ptr Model_Document::subDocument(std::string theDocID) +const std::set Model_Document::subDocuments() const { - return Model_Application::getApplication()->getDocument(theDocID); -} - -const std::set Model_Document::subDocuments(const bool theActivatedOnly) const -{ - std::set aResult; + 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->data()->name()); + if (aPart && aPart->isActivated()) { + aResult.insert(aPart->original()->partDoc()->id()); + } } return aResult; } -std::shared_ptr Model_Document::subDoc(std::string theDocID) +std::shared_ptr Model_Document::subDoc(int theDocID) { // just store sub-document identifier here to manage it later return std::dynamic_pointer_cast( - Model_Application::getApplication()->getDocument(theDocID)); + Model_Application::getApplication()->document(theDocID)); } ObjectPtr Model_Document::object(const std::string& theGroupID, const int theIndex) @@ -637,18 +881,22 @@ const int Model_Document::index(std::shared_ptr theObject) int Model_Document::size(const std::string& theGroupID) { + if (myObjs == 0) // may be on close + return 0; return myObjs->size(theGroupID); } std::shared_ptr Model_Document::currentFeature(const bool theVisible) { + 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() && !aResult->isInHistory()) { + while(aResult.get() && !aResult->isInHistory()) { aResult = myObjs->nextFeature(aResult, true); } } @@ -657,25 +905,37 @@ std::shared_ptr Model_Document::currentFeature(const bool theV return std::shared_ptr(); // null feature means the higher than first } -void Model_Document::setCurrentFeature(std::shared_ptr theCurrent, - const bool theVisible) +// recursive function to check if theSub is a child of theMain composite feature +// through all the hierarchy of parents +static bool isSub(const CompositeFeaturePtr theMain, const FeaturePtr theSub) { + CompositeFeaturePtr aParent = ModelAPI_Tools::compositeOwner(theSub); + if (!aParent.get()) + return false; + if (aParent == theMain) + return true; + return isSub(theMain, aParent); +} + +void Model_Document::setCurrentFeature( + std::shared_ptr theCurrent, const bool theVisible) { + // 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(); + bool isActive = aLoop->activateFlushes(false); + TDF_Label aRefLab = generalLabel().FindChild(TAG_CURRENT_FEATURE); CompositeFeaturePtr aMain; // main feature that may nest the new current + std::set anOwners; // composites that contain theCurrent (with any level of nesting) 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; - } + CompositeFeaturePtr anOwner = ModelAPI_Tools::compositeOwner(theCurrent); + while(anOwner.get()) { + if (!aMain.get()) { + aMain = anOwner; } + anOwners.insert(anOwner); + anOwner = ModelAPI_Tools::compositeOwner(anOwner); } } @@ -692,7 +952,10 @@ void Model_Document::setCurrentFeature(std::shared_ptr theCurr } if (theCurrent.get()) { std::shared_ptr aData = std::static_pointer_cast(theCurrent->data()); - if (!aData.get() || !aData->isValid()) return; + if (!aData.get() || !aData->isValid()) { + aLoop->activateFlushes(isActive); + return; + } TDF_Label aFeatureLabel = aData->label().Father(); Handle(TDF_Reference) aRef; @@ -705,39 +968,66 @@ void Model_Document::setCurrentFeature(std::shared_ptr theCurr aRefLab.ForgetAttribute(TDF_Reference::GetID()); } // make all features after this feature disabled in reversed order (to remove results without deps) - static Events_Loop* aLoop = Events_Loop::loop(); 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); 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; 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 - aDisabledFlag = false; + if (aMain.get()) { + if (isSub(aMain, anIter)) // sub-elements of not-disabled feature are not disabled + aDisabledFlag = false; + else if (anOwners.find(anIter) != anOwners.end()) // disable the higher-level feature is the nested is the current + aDisabledFlag = true; + } + + if (anIter->getKind() == "Parameter") {// parameters are always out of the history of features, but not parameters + if (theCurrent.get() && theCurrent->getKind() != "Parameter") + aDisabledFlag = false; + } 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; + } + // update for everyone the concealment flag immideately: on edit feature in the midle of history + if (aWasChanged) { + std::list aResults; + ModelAPI_Tools::allResults(anIter, aResults); + std::list::const_iterator aRes = aResults.begin(); + for(; aRes != aResults.end(); aRes++) { + if ((*aRes).get() && (*aRes)->data()->isValid() && !(*aRes)->isDisabled()) + std::dynamic_pointer_cast((*aRes)->data())->updateConcealmentFlag(); + } + // update the concealment status for disply in isConcealed of ResultBody + for(aRes = aResults.begin(); aRes != aResults.end(); aRes++) { + if ((*aRes).get() && (*aRes)->data()->isValid() && !(*aRes)->isDisabled()) + (*aRes)->isConcealed(); + } } } - aLoop->flush(aRedispEvent); - aLoop->flush(aDeleteEvent); + // unblock the flush signals and up them after this + aLoop->activateFlushes(isActive); } void Model_Document::setCurrentFeatureUp() { - FeaturePtr aCurrent = currentFeature(true); + // 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); - setCurrentFeature(aPrev, true); + // do not flush: it is called only on remove, it will be flushed in the end of transaction + setCurrentFeature(aPrev, false); } } @@ -764,6 +1054,13 @@ std::shared_ptr Model_Document::createPart( 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) { @@ -779,12 +1076,7 @@ std::shared_ptr Model_Document::createParameter( 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 myObjs->feature(aFeatureLab); - } - return FeaturePtr(); + return myObjs->feature(theResult); } Standard_Integer HashCode(const TDF_Label& theLab, const Standard_Integer theUpper) @@ -814,3 +1106,159 @@ 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); + // #issue 1048: sub-compsolids also + ResultCompSolidPtr aCompRes = std::dynamic_pointer_cast(*aRes); + if (aCompRes.get()) { + int aNumSubs = aCompRes->numberOfSubs(); + for(int a = 0; a < aNumSubs; a++) { + ResultPtr aSub = aCompRes->subResult(a); + if (aSub.get()) { + ModelAPI_EventCreator::get()->sendUpdated(aSub, aRedispEvent); + } + } + } + } + } + } + } +} + +bool Model_Document::isActive() const +{ + 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(); +} + +void Model_Document::incrementTransactionID() +{ + 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); +} + +std::shared_ptr Model_Document::featureById(const int theId) +{ + return myObjs->featureById(theId); +} + +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(); + mySelectionFeature->init(); // to make it enabled and Update correctly + // this update may cause recomputation of the part after selection on it, that is not needed + mySelectionFeature->data()->blockSendAttributeUpdated(true); + } + return mySelectionFeature->selectionList("selection"); +} + +FeaturePtr Model_Document::lastFeature() +{ + if (myObjs) + return myObjs->lastFeature(); + return FeaturePtr(); +}