X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FModel%2FModel_Document.cpp;h=89c596d6d2026e2df305d0709adba247570732d2;hb=409ad39d4655a87a91986bf9927e47bfc08b5341;hp=28fda6f314c0c20abf921d4d4e7619ca0afb94c1;hpb=8c56aa1d9ce74e35df11472ab3542f6653a5ba51;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 28fda6f31..89c596d6d --- a/src/Model/Model_Document.cpp +++ b/src/Model/Model_Document.cpp @@ -15,26 +15,42 @@ #include #include #include - +#include #include -#include +#include #include #include #include #include +#include +#include #include #include +#include #include #include #include #include -#include -#include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include #include #ifndef WIN32 @@ -52,16 +68,21 @@ static const int UNDO_LIMIT = 1000; // number of possible undo operations (big 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 cransaction +/// where the reference to the current feature label is located (or no attribute if null feature) +static const int TAG_CURRENT_FEATURE = 1; ///< reference to the current 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 +static const int TAG_NODES_STATE = 4; ///< array, tag of the Object Browser nodes states -Model_Document::Model_Document(const std::string theID, const std::string 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 { +#ifdef TINSPECTOR + CDF_Session::CurrentSession()->Directory()->Add(myDoc); +#endif myObjs = new Model_Objects(myDoc->Main()); - myDoc->SetUndoLimit(UNDO_LIMIT); + myDoc->SetUndoLimit(UNDO_LIMIT); myTransactionSave = 0; myExecuteFeatures = true; // to have something in the document and avoid empty doc open/save problem @@ -78,12 +99,12 @@ 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() && + while(aPath.Length() && (aPath.Value(aPath.Length()) == '\\' || aPath.Value(aPath.Length()) == '/')) aPath.Remove(aPath.Length()); aPath += _separator_; @@ -97,84 +118,88 @@ 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, aLoaded); } catch (Standard_Failure) { Handle(Standard_Failure) aFail = Standard_Failure::Caught(); - Events_Error::send( - std::string("Exception in opening of document: ") + aFail->GetMessageString()); + Events_InfoMessage("Model_Document", + "Exception in opening of document: %1").arg(aFail->GetMessageString()).send(); return false; } bool isError = aStatus != PCDM_RS_OK; if (isError) { switch (aStatus) { case PCDM_RS_UnknownDocument: - Events_Error::send(std::string("Can not open document")); + Events_InfoMessage("Model_Document", "Can not open document").send(); break; case PCDM_RS_AlreadyRetrieved: - Events_Error::send(std::string("Can not open document: already opened")); + Events_InfoMessage("Model_Document", "Can not open document: already opened").send(); break; case PCDM_RS_AlreadyRetrievedAndModified: - Events_Error::send( - std::string("Can not open document: already opened and modified")); + Events_InfoMessage("Model_Document", + "Can not open document: already opened and modified").send(); break; case PCDM_RS_NoDriver: - Events_Error::send(std::string("Can not open document: driver library is not found")); + Events_InfoMessage("Model_Document", + "Can not open document: driver library is not found").send(); break; case PCDM_RS_UnknownFileDriver: - Events_Error::send(std::string("Can not open document: unknown driver for opening")); + Events_InfoMessage("Model_Document", + "Can not open document: unknown driver for opening").send(); break; case PCDM_RS_OpenError: - Events_Error::send(std::string("Can not open document: file open error")); + Events_InfoMessage("Model_Document", "Can not open document: file open error").send(); break; case PCDM_RS_NoVersion: - Events_Error::send(std::string("Can not open document: invalid version")); + Events_InfoMessage("Model_Document", "Can not open document: invalid version").send(); break; case PCDM_RS_NoModel: - Events_Error::send(std::string("Can not open document: no data model")); + Events_InfoMessage("Model_Document", "Can not open document: no data model").send(); break; case PCDM_RS_NoDocument: - Events_Error::send(std::string("Can not open document: no document inside")); + Events_InfoMessage("Model_Document", "Can not open document: no document inside").send(); break; case PCDM_RS_FormatFailure: - Events_Error::send(std::string("Can not open document: format failure")); + Events_InfoMessage("Model_Document", "Can not open document: format failure").send(); break; case PCDM_RS_TypeNotFoundInSchema: - Events_Error::send(std::string("Can not open document: invalid object")); + Events_InfoMessage("Model_Document", "Can not open document: invalid object").send(); break; case PCDM_RS_UnrecognizedFileFormat: - Events_Error::send(std::string("Can not open document: unrecognized file format")); + Events_InfoMessage("Model_Document", + "Can not open document: unrecognized file format").send(); break; case PCDM_RS_MakeFailure: - Events_Error::send(std::string("Can not open document: make failure")); + Events_InfoMessage("Model_Document", "Can not open document: make failure").send(); break; case PCDM_RS_PermissionDenied: - Events_Error::send(std::string("Can not open document: permission denied")); + Events_InfoMessage("Model_Document", "Can not open document: permission denied").send(); break; case PCDM_RS_DriverFailure: - Events_Error::send(std::string("Can not open document: driver failure")); + Events_InfoMessage("Model_Document", "Can not open document: driver failure").send(); break; default: - Events_Error::send(std::string("Can not open document: unknown error")); + Events_InfoMessage("Model_Document", "Can not open document: unknown error").send(); break; } } - std::shared_ptr aSession = + 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 - aSession->setActiveDocument(anApp->getDocument(myID), false); + aSession->setActiveDocument(anApp->document(myID), false); aSession->setCheckTransactions(false); if (myObjs) delete myObjs; @@ -186,75 +211,92 @@ 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(); if (isRoot()) { #ifdef WIN32 - CreateDirectory(theFileName, NULL); + CreateDirectory(theDirName, NULL); #else - mkdir(theFileName, 0x1ff); + mkdir(theDirName, 0x1ff); #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); } catch (Standard_Failure) { Handle(Standard_Failure) aFail = Standard_Failure::Caught(); - Events_Error::send( - std::string("Exception in saving of document: ") + aFail->GetMessageString()); + Events_InfoMessage("Model_Document", + "Exception in saving of document: %1").arg(aFail->GetMessageString()).send(); return false; } bool isDone = aStatus == PCDM_SS_OK || aStatus == PCDM_SS_No_Obj; if (!isDone) { switch (aStatus) { case PCDM_SS_DriverFailure: - Events_Error::send(std::string("Can not save document: save driver-library failure")); + Events_InfoMessage("Model_Document", + "Can not save document: save driver-library failure").send(); break; case PCDM_SS_WriteFailure: - Events_Error::send(std::string("Can not save document: file writing failure")); + Events_InfoMessage("Model_Document", "Can not save document: file writing failure").send(); break; case PCDM_SS_Failure: default: - Events_Error::send(std::string("Can not save document")); + Events_InfoMessage("Model_Document", "Can not save document").send(); 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()); } else { - Events_Error::send( - std::string("Can not open file ") + aSubPath.ToCString() + " for saving"); + Events_InfoMessage("Model_Document", + "Can not open file %1 for saving").arg(aSubPath.ToCString()).send(); } } } 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); } } } @@ -271,8 +313,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 @@ -292,6 +334,10 @@ void Model_Document::close(const bool theForever) 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); @@ -299,8 +345,10 @@ 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 + 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 } myNestedNum.push_back(0); // start of nested operation with zero transactions inside yet @@ -309,14 +357,13 @@ void Model_Document::startOperation() myDoc->NewCommand(); } // starts a new operation - incrementTransactionID(); myTransactions.push_back(Transaction()); if (!myNestedNum.empty()) (*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(); } @@ -336,17 +383,165 @@ void Model_Document::compactNested() } } +/// Compares the content of the 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 && // second is for display + anArr2->Label().Tag() == 1 && (anArr2->Label().Depth() == 4 || + anArr2->Label().Depth() == 6)) + continue; + if (anArr1->Value(a) != anArr2->Value(a)) + 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 = + static std::shared_ptr aSession = std::static_pointer_cast(Model_Session::get()); + + // open transaction if nested is closed to fit inside + // all synchronizeBackRefs and flushed consequences + if (isNestedClosed) { + myDoc->OpenCommand(); + } + // 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)); aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED)); aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY)); aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED)); + + if (isNestedClosed) { + if (myDoc->CommitCommand()) + myTransactions.rbegin()->myOCAFNum++; + } + // 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 @@ -362,16 +557,22 @@ 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 (!myTransactions.empty() && myDoc->CommitCommand()) { + // if commit is successfull, just increment counters + if (isEmptyTransaction(myDoc)) { // erase this transaction + myDoc->Undo(); + myDoc->ClearRedos(); + } else { + myTransactions.rbegin()->myOCAFNum++; + aResult = true; + } } if (isNestedClosed) { @@ -401,7 +602,7 @@ static void modifiedLabels(const Handle(TDocStd_Document)& theDoc, TDF_LabelList Handle(TDF_Delta) aDelta; if (isRedo) aDelta = theDoc->GetRedos().First(); - else + else aDelta = theDoc->GetUndos().Last(); TDF_LabelList aDeltaList; aDelta->Labels(aDeltaList); // it clears list, so, use new one and then append to the result @@ -410,8 +611,32 @@ static void modifiedLabels(const Handle(TDocStd_Document)& theDoc, TDF_LabelList } // add also label of the modified attributes const TDF_AttributeDeltaList& anAttrs = aDelta->AttributeDeltas(); + /// named shape evolution also modifies integer on this label: exclude it + TDF_LabelMap anExcludedInt; for (TDF_ListIteratorOfAttributeDeltaList anAttr(anAttrs); anAttr.More(); anAttr.Next()) { - theDelta.Append(anAttr.Value()->Label()); + if (anAttr.Value()->Attribute()->ID() == TDataStd_BooleanArray::GetID()) { + // Boolean array is used for feature auxiliary attributes only, feature args are not modified + continue; + } + if (anAttr.Value()->Attribute()->ID() == TNaming_NamedShape::GetID()) { + anExcludedInt.Add(anAttr.Value()->Label()); + // named shape evolution is changed in history update => skip them, + // they are not the features arguents + continue; + } + if (anAttr.Value()->Attribute()->ID() == TDataStd_Integer::GetID()) { + if (anExcludedInt.Contains(anAttr.Value()->Label())) + continue; + } + theDelta.Append(anAttr.Value()->Label()); + } + TDF_ListIteratorOfLabelList aDeltaIter(theDelta); + for(; aDeltaIter.More(); aDeltaIter.Next()) { + if (anExcludedInt.Contains(aDeltaIter.Value())) { + theDelta.Remove(aDeltaIter); + if (!aDeltaIter.More()) + break; + } } } @@ -439,9 +664,9 @@ void Model_Document::abortOperation() 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()) { + // instead of abort, do commit and undo: to get the delta of modifications + if (myDoc->CommitCommand()) { modifiedLabels(myDoc, aDeltaLabels); myDoc->Undo(); } @@ -452,12 +677,12 @@ void Model_Document::abortOperation() 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(aDeltaLabels, true, isRoot()); + myObjs->synchronizeFeatures(aDeltaLabels, true, false, false, isRoot()); } bool Model_Document::isOperation() const @@ -476,13 +701,14 @@ bool Model_Document::canUndo() { // 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 + if (myDoc->GetAvailableUndos() > 0 && + // there is something to undo in nested + (myNestedNum.empty() || *myNestedNum.rbegin() - aCurrentNum > 0) && 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 @@ -496,6 +722,8 @@ 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(); @@ -511,8 +739,8 @@ void Model_Document::undoInternal(const bool theWithSubs, const bool theSynchron 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; @@ -521,7 +749,7 @@ void Model_Document::undoInternal(const bool theWithSubs, const bool theSynchron } // after undo of all sub-documents to avoid updates on not-modified data (issue 370) if (theSynchronize) { - myObjs->synchronizeFeatures(aDeltaLabels, true, isRoot()); + myObjs->synchronizeFeatures(aDeltaLabels, true, false, false, isRoot()); // update the current features status setCurrentFeature(currentFeature(false), false); } @@ -537,8 +765,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; @@ -562,13 +790,13 @@ void Model_Document::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(aDeltaLabels, true, isRoot()); + myObjs->synchronizeFeatures(aDeltaLabels, true, false, false, isRoot()); // update the current features status setCurrentFeature(currentFeature(false), false); } @@ -579,7 +807,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--) { @@ -606,7 +834,7 @@ void Model_Document::operationId(const std::string& theId) FeaturePtr Model_Document::addFeature(std::string theID, const bool theMakeCurrent) { - std::shared_ptr aSession = + std::shared_ptr aSession = std::dynamic_pointer_cast(ModelAPI_Session::get()); FeaturePtr aFeature = aSession->createFeature(theID, this); if (!aFeature) @@ -628,7 +856,7 @@ FeaturePtr Model_Document::addFeature(std::string theID, const bool theMakeCurre FeaturePtr aCurrent = aDocToAdd->currentFeature(false); bool isModified = true; for(CompositeFeaturePtr aComp = std::dynamic_pointer_cast(aCurrent); - aComp.get() && isModified; + aComp.get() && isModified; aComp = std::dynamic_pointer_cast(aCurrent)) { isModified = false; int aSubs = aComp->numberOfSubs(false); @@ -664,11 +892,47 @@ void Model_Document::removeFeature(FeaturePtr theFeature) myObjs->removeFeature(theFeature); } +// 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::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis) { + bool aCurrentUp = theMoved == currentFeature(false); + if (aCurrentUp) { + setCurrentFeatureUp(); + } + // if user adds after high-level feature with nested, + // add it after all nested (otherwise the nested will be disabled) + CompositeFeaturePtr aCompositeAfter = + std::dynamic_pointer_cast(theAfterThis); + if (aCompositeAfter.get()) { + FeaturePtr aSub = aCompositeAfter; + do { + FeaturePtr aNext = myObjs->nextFeature(aSub); + if (!isSub(aCompositeAfter, aNext)) { + theAfterThis = aSub; + break; + } + aSub = aNext; + } while (aSub.get()); + } + myObjs->moveFeature(theMoved, theAfterThis); - if (theAfterThis == currentFeature(true)) + if (aCurrentUp) { // make the moved feature enabled or disabled due to the real status + setCurrentFeature(currentFeature(false), false); + } else if (theAfterThis == currentFeature(false)) { + // must be after move to make enabled all features which are before theMoved setCurrentFeature(theMoved, true); + } } void Model_Document::updateHistory(const std::shared_ptr theObject) @@ -681,31 +945,26 @@ void Model_Document::updateHistory(const std::string theGroup) myObjs->updateHistory(theGroup); } -std::shared_ptr Model_Document::subDocument(std::string theDocID) -{ - return Model_Application::getApplication()->getDocument(theDocID); -} - -const std::set Model_Document::subDocuments(const bool theActivatedOnly) const +const std::set Model_Document::subDocuments() 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->original()->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) @@ -726,6 +985,8 @@ 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); } @@ -771,8 +1032,9 @@ void Model_Document::setCurrentFeature( } } - if (theVisible) { // make features below which are not in history also enabled: sketch subs - FeaturePtr aNext = + if (theVisible && !theCurrent.get()) { + // needed to avoid disabling of PartSet initial constructions + FeaturePtr aNext = theCurrent.get() ? myObjs->nextFeature(theCurrent) : myObjs->firstFeature(); for (; aNext.get(); aNext = myObjs->nextFeature(theCurrent)) { if (aNext->isInHistory()) { @@ -799,47 +1061,62 @@ void Model_Document::setCurrentFeature( } 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) + // 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); bool aPassed = false; // flag that the current object is already passed in cycle FeaturePtr anIter = myObjs->lastFeature(); bool aWasChanged = false; + bool isCurrentParameter = theCurrent.get() && theCurrent->getKind() == "Parameter"; 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()) { - if (aMain->isSub(anIter)) // sub-elements of not-disabled feature are not disabled + 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 + 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") + if (anIter->getKind() == "Parameter") { + // parameters are always out of the history of features, but not parameters + // due to the issue 1491 all parameters are kept enabled any time + //if (!isCurrentParameter) aDisabledFlag = false; + } else if (isCurrentParameter) { + // if paramater is active, all other features become enabled (issue 1307) + 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); + // state of feature is changed => so inform that it must be updated if it has such state + if (!aDisabledFlag && + (anIter->data()->execState() == ModelAPI_StateMustBeUpdated || + anIter->data()->execState() == ModelAPI_StateInvalidArgument)) + 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) { - const std::list >& aResList = anIter->results(); - std::list >::const_iterator aRes = aResList.begin(); - for(; aRes != aResList.end(); aRes++) { + 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(); + } } } // unblock the flush signals and up them after this @@ -848,11 +1125,19 @@ void Model_Document::setCurrentFeature( void Model_Document::setCurrentFeatureUp() { - // on remove just go up for minimum step: highlight external objects in sketch causes + // 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); + // make the higher level composite as current (sketch becomes disabled if line is enabled) + if (aPrev.get()) { + FeaturePtr aComp = ModelAPI_Tools::compositeOwner(aPrev); + // without cycle (issue 1555): otherwise extrusion fuse + // will be enabled and displayed whaen inside sketch + if (aComp.get()) + aPrev = aComp; + } // do not flush: it is called only on remove, it will be flushed in the end of transaction setCurrentFeature(aPrev, false); } @@ -894,6 +1179,12 @@ std::shared_ptr Model_Document::createGroup( return myObjs->createGroup(theFeatureData, theIndex); } +std::shared_ptr Model_Document::createField( + const std::shared_ptr& theFeatureData, const int theIndex) +{ + return myObjs->createField(theFeatureData, theIndex); +} + std::shared_ptr Model_Document::createParameter( const std::shared_ptr& theFeatureData, const int theIndex) { @@ -921,12 +1212,52 @@ void Model_Document::addNamingName(const TDF_Label theLabel, std::string theName myNamingNames[theName] = theLabel; } +void Model_Document::changeNamingName(const std::string theOldName, const std::string theNewName) +{ + std::map::iterator aFind = myNamingNames.find(theOldName); + if (aFind != myNamingNames.end()) { + myNamingNames[theNewName] = aFind->second; + myNamingNames.erase(theOldName); + } +} + 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; + if (aFind != myNamingNames.end()) { + return aFind->second; + } + // not found exact name, try to find by sub-components + std::string::size_type aSlash = theName.rfind('/'); + if (aSlash != std::string::npos) { + std::string anObjName = theName.substr(0, aSlash); + aFind = myNamingNames.find(anObjName); + if (aFind != myNamingNames.end()) { + TCollection_ExtendedString aSubName(theName.substr(aSlash + 1).c_str()); + // searching sub-labels with this name + TDF_ChildIDIterator aNamesIter(aFind->second, TDataStd_Name::GetID(), Standard_True); + for(; aNamesIter.More(); aNamesIter.Next()) { + Handle(TDataStd_Name) aName = Handle(TDataStd_Name)::DownCast(aNamesIter.Value()); + if (aName->Get() == aSubName) + return aName->Label(); + } + // If not found child label with the exact sub-name, then try to find compound with + // such sub-name without suffix. + Standard_Integer aSuffixPos = aSubName.SearchFromEnd('_'); + if (aSuffixPos != -1) { + TCollection_ExtendedString anIndexStr = aSubName.Split(aSuffixPos); + aSubName.Remove(aSuffixPos); + aNamesIter.Initialize(aFind->second, TDataStd_Name::GetID(), Standard_True); + for(; aNamesIter.More(); aNamesIter.Next()) { + Handle(TDataStd_Name) aName = Handle(TDataStd_Name)::DownCast(aNamesIter.Value()); + if (aName->Get() == aSubName) { + return aName->Label(); + } + } + } + } + } + return TDF_Label(); // not found } ResultPtr Model_Document::findByName(const std::string theName) @@ -955,6 +1286,17 @@ void Model_Document::setActive(const bool theFlag) 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); + } + } + } } } } @@ -1002,14 +1344,19 @@ std::shared_ptr Model_Document::internalFeature(const int theI return myObjs->internalFeature(theIndex); } +std::shared_ptr Model_Document::featureById(const int theId) +{ + return myObjs->featureById(theId); +} + void Model_Document::synchronizeTransactions() { - Model_Document* aRoot = + 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::shared_ptr aSession = std::dynamic_pointer_cast(Model_Session::get()); while(myRedos.size() > aRoot->myRedos.size()) { // remove redos in this aSession->setCheckTransactions(false); @@ -1049,7 +1396,7 @@ AttributeSelectionListPtr Model_Document::selectionInPartFeature() 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)); @@ -1060,6 +1407,9 @@ AttributeSelectionListPtr Model_Document::selectionInPartFeature() 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"); } @@ -1070,3 +1420,170 @@ FeaturePtr Model_Document::lastFeature() return myObjs->lastFeature(); return FeaturePtr(); } + +static Handle(TNaming_NamedShape) searchForOriginalShape(TopoDS_Shape theShape, TDF_Label aMain) { + Handle(TNaming_NamedShape) aResult; + while(!theShape.IsNull()) { // searching for the very initial shape that produces this one + TopoDS_Shape aShape = theShape; + theShape.Nullify(); + // to avoid crash of TNaming_SameShapeIterator if pure shape does not exists + if (!TNaming_Tool::HasLabel(aMain, aShape)) + break; + for(TNaming_SameShapeIterator anIter(aShape, aMain); anIter.More(); anIter.Next()) { + TDF_Label aNSLab = anIter.Label(); + Handle(TNaming_NamedShape) aNS; + if (aNSLab.FindAttribute(TNaming_NamedShape::GetID(), aNS)) { + for(TNaming_Iterator aShapesIter(aNS); aShapesIter.More(); aShapesIter.Next()) { + if (aShapesIter.Evolution() == TNaming_SELECTED || + aShapesIter.Evolution() == TNaming_DELETE) + continue; // don't use the selection evolution + if (aShapesIter.NewShape().IsSame(aShape)) { // found the original shape + aResult = aNS; + if (aResult->Evolution() == TNaming_MODIFY) + theShape = aShapesIter.OldShape(); + // otherwise may me searching for another item of this shape with longer history + if (!theShape.IsNull()) + break; + } + } + } + } + } + return aResult; +} + +std::shared_ptr Model_Document::producedByFeature( + std::shared_ptr theResult, + const std::shared_ptr& theShape) +{ + ResultBodyPtr aBody = std::dynamic_pointer_cast(theResult); + if (!aBody.get()) { + return feature(theResult); // for not-body just returns the feature that produced this result + } + // otherwise get the shape and search the very initial label for it + TopoDS_Shape aShape = theShape->impl(); + if (aShape.IsNull()) + return FeaturePtr(); + + // for comsolids and compounds all the naming is located in the main object, so, try to use + // it first + ResultCompSolidPtr aMain = ModelAPI_Tools::compSolidOwner(theResult); + if (aMain.get()) { + FeaturePtr aMainRes = producedByFeature(aMain, theShape); + if (aMainRes) + return aMainRes; + } + + std::shared_ptr aBodyData = std::dynamic_pointer_cast(theResult->data()); + if (!aBodyData.get() || !aBodyData->isValid()) + return FeaturePtr(); + + TopoDS_Shape anOldShape; // old shape in the pair oldshape->theShape in the named shape + TopoDS_Shape aShapeContainer; // old shape of the shape that contains aShape as sub-element + Handle(TNaming_NamedShape) aCandidatInThis, aCandidatContainer; + TDF_Label aBodyLab = aBodyData->label(); + // use childs and this label (the lowest priority) + TDF_ChildIDIterator aNSIter(aBodyLab, TNaming_NamedShape::GetID(), Standard_True); + bool aUseThis = !aNSIter.More(); + while(anOldShape.IsNull() && (aNSIter.More() || aUseThis)) { + Handle(TNaming_NamedShape) aNS; + if (aUseThis) { + if (!aBodyLab.FindAttribute(TNaming_NamedShape::GetID(), aNS)) + break; + } else { + aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value()); + } + for(TNaming_Iterator aShapesIter(aNS); aShapesIter.More(); aShapesIter.Next()) { + if (aShapesIter.Evolution() == TNaming_SELECTED || aShapesIter.Evolution() == TNaming_DELETE) + continue; // don't use the selection evolution + if (aShapesIter.NewShape().IsSame(aShape)) { // found the original shape + aCandidatInThis = aNS; + if (aCandidatInThis->Evolution() == TNaming_MODIFY) + anOldShape = aShapesIter.OldShape(); + // otherwise may me searching for another item of this shape with longer history + if (!anOldShape.IsNull()) + break; + } + // check that the shape contains aShape as sub-shape to fill container + if (aShapesIter.NewShape().ShapeType() < aShape.ShapeType() && aCandidatContainer.IsNull()) { + TopExp_Explorer anExp(aShapesIter.NewShape(), aShape.ShapeType()); + for(; anExp.More(); anExp.Next()) { + if (aShape.IsSame(anExp.Current())) { + aCandidatContainer = aNS; + aShapeContainer = aShapesIter.NewShape(); + } + } + } + } + // iterate to the next label or to the body label in the end + if (!aUseThis) + aNSIter.Next(); + if (!aNSIter.More()) { + if (aUseThis) + break; + aUseThis = true; + } + } + if (aCandidatInThis.IsNull()) { + // to fix 1512: searching for original shape of this shape + // if modification of it is not in this result + aCandidatInThis = searchForOriginalShape(aShape, myDoc->Main()); + if (aCandidatInThis.IsNull()) { + if (aCandidatContainer.IsNull()) + return FeaturePtr(); + // with the lower priority use the higher level shape that contains aShape + aCandidatInThis = aCandidatContainer; + anOldShape = aShapeContainer; + } else { + // to stop the searching by the following searchForOriginalShape + anOldShape.Nullify(); + } + } + + Handle(TNaming_NamedShape) aNS = searchForOriginalShape(anOldShape, myDoc->Main()); + if (!aNS.IsNull()) + aCandidatInThis = aNS; + + FeaturePtr aResult; + TDF_Label aResultLab = aCandidatInThis->Label(); + while(aResultLab.Depth() > 3) + aResultLab = aResultLab.Father(); + FeaturePtr aFeature = myObjs->feature(aResultLab); + if (aFeature.get()) { + if (!aResult.get() || myObjs->isLater(aResult, aFeature)) { + aResult = aFeature; + } + } + return aResult; +} + +bool Model_Document::isLater(FeaturePtr theLater, FeaturePtr theCurrent) const +{ + return myObjs->isLater(theLater, theCurrent); +} + +void Model_Document::storeNodesState(const std::list& theStates) +{ + TDF_Label aLab = generalLabel().FindChild(TAG_NODES_STATE); + aLab.ForgetAllAttributes(); + if (!theStates.empty()) { + Handle(TDataStd_BooleanArray) anArray = + TDataStd_BooleanArray::Set(aLab, 0, int(theStates.size()) - 1); + std::list::const_iterator aState = theStates.begin(); + for(int anIndex = 0; aState != theStates.end(); aState++, anIndex++) { + anArray->SetValue(anIndex, *aState); + } + } +} + +void Model_Document::restoreNodesState(std::list& theStates) const +{ + TDF_Label aLab = generalLabel().FindChild(TAG_NODES_STATE); + Handle(TDataStd_BooleanArray) anArray; + if (aLab.FindAttribute(TDataStd_BooleanArray::GetID(), anArray)) { + int anUpper = anArray->Upper(); + for(int anIndex = 0; anIndex <= anUpper; anIndex++) { + theStates.push_back(anArray->Value(anIndex) == Standard_True); + } + } +}