X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2FModel%2FModel_Document.cpp;h=f61edd015f2185fbc9351c3be3533ab2ccf27a79;hb=72b9423caaa48805589d6ab87d366f79ecde5bfe;hp=852050fd9c6f789665590eb622128056faa50139;hpb=baebe67bf59028a972b781932680cb627b1a3d90;p=modules%2Fshaper.git diff --git a/src/Model/Model_Document.cpp b/src/Model/Model_Document.cpp index 852050fd9..f61edd015 100755 --- a/src/Model/Model_Document.cpp +++ b/src/Model/Model_Document.cpp @@ -1,8 +1,22 @@ -// Copyright (C) 2014-20xx CEA/DEN, EDF R&D - -// File: Model_Document.cxx -// Created: 28 Feb 2014 -// Author: Mikhail PONIKAROV +// Copyright (C) 2014-2017 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or +// email : webmaster.salome@opencascade.com +// #include #include @@ -16,6 +30,7 @@ #include #include #include +#include #include #include @@ -37,6 +52,8 @@ #include #include #include +#include +#include #include #include #include @@ -47,6 +64,8 @@ #include #include +#include +#include #include #ifndef WIN32 @@ -64,16 +83,23 @@ 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) +/// 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 +///< naming structures constructions selected from other document +static const int TAG_EXTERNAL_CONSTRUCTIONS = 5; 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 @@ -95,7 +121,7 @@ static TCollection_ExtendedString DocFileName(const char* theDirName, const std: { 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_; @@ -136,14 +162,16 @@ bool Model_Document::load(const char* theDirName, const char* theFileName, Docum Events_InfoMessage("Model_Document", "Can not open document: already opened").send(); break; case PCDM_RS_AlreadyRetrievedAndModified: - Events_InfoMessage("Model_Document", + Events_InfoMessage("Model_Document", "Can not open document: already opened and modified").send(); break; case PCDM_RS_NoDriver: - Events_InfoMessage("Model_Document", "Can not open document: driver library is not found").send(); + Events_InfoMessage("Model_Document", + "Can not open document: driver library is not found").send(); break; case PCDM_RS_UnknownFileDriver: - Events_InfoMessage("Model_Document", "Can not open document: unknown driver for opening").send(); + Events_InfoMessage("Model_Document", + "Can not open document: unknown driver for opening").send(); break; case PCDM_RS_OpenError: Events_InfoMessage("Model_Document", "Can not open document: file open error").send(); @@ -164,7 +192,8 @@ bool Model_Document::load(const char* theDirName, const char* theFileName, Docum Events_InfoMessage("Model_Document", "Can not open document: invalid object").send(); break; case PCDM_RS_UnrecognizedFileFormat: - Events_InfoMessage("Model_Document", "Can not open document: unrecognized file format").send(); + Events_InfoMessage("Model_Document", + "Can not open document: unrecognized file format").send(); break; case PCDM_RS_MakeFailure: Events_InfoMessage("Model_Document", "Can not open document: make failure").send(); @@ -180,11 +209,12 @@ bool Model_Document::load(const char* theDirName, const char* theFileName, Docum 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->document(myID), false); aSession->setCheckTransactions(false); @@ -206,18 +236,40 @@ bool Model_Document::load(const char* theDirName, const char* theFileName, Docum for(; aPartRes != aPartResults.end(); aPartRes++) { ResultPartPtr aPart = std::dynamic_pointer_cast(*aPartRes); if (aPart.get()) - anApp->setLoadByDemand(aPart->data()->name()); + anApp->setLoadByDemand(aPart->data()->name(), + aPart->data()->document(ModelAPI_ResultPart::DOC_REF())->docId()); } } 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* theDirName, const char* theFileName, std::list& theResults) { + // if the history line is not in the end, move it to the end before save, otherwise + // problems with results restore and (the most important) naming problems will appear + // due to change evolution to SELECTION (problems in NamedShape and Name) + FeaturePtr aWasCurrent; + std::shared_ptr aSession = + std::dynamic_pointer_cast(Model_Session::get()); + if (currentFeature(false) != lastFeature()) { + aSession->setCheckTransactions(false); + aWasCurrent = currentFeature(false); + // if last is nested into something else, make this something else as last: + // otherwise it will look like edition of sub-element, so, the main will be disabled + FeaturePtr aLast = lastFeature(); + if (aLast.get()) { + CompositeFeaturePtr aMain = ModelAPI_Tools::compositeOwner(aLast); + while(aMain.get()) { + aLast = aMain; + aMain = ModelAPI_Tools::compositeOwner(aLast); + } + } + setCurrentFeature(aLast, true); + } // create a directory in the root document if it is not yet exist Handle(Model_Application) anApp = Model_Application::getApplication(); if (isRoot()) { @@ -234,15 +286,20 @@ bool Model_Document::save( aStatus = anApp->SaveAs(myDoc, aPath); } catch (Standard_Failure) { Handle(Standard_Failure) aFail = Standard_Failure::Caught(); - Events_InfoMessage("Model_Document", + Events_InfoMessage("Model_Document", "Exception in saving of document: %1").arg(aFail->GetMessageString()).send(); + if (aWasCurrent.get()) { // return the current feature to the initial position + setCurrentFeature(aWasCurrent, false); + aSession->setCheckTransactions(true); + } return false; } bool isDone = aStatus == PCDM_SS_OK || aStatus == PCDM_SS_No_Obj; if (!isDone) { switch (aStatus) { case PCDM_SS_DriverFailure: - Events_InfoMessage("Model_Document", "Can not save document: save driver-library failure").send(); + Events_InfoMessage("Model_Document", + "Can not save document: save driver-library failure").send(); break; case PCDM_SS_WriteFailure: Events_InfoMessage("Model_Document", "Can not save document: file writing failure").send(); @@ -253,6 +310,12 @@ bool Model_Document::save( break; } } + + if (aWasCurrent.get()) { // return the current feature to the initial position + setCurrentFeature(aWasCurrent, false); + aSession->setCheckTransactions(true); + } + myTransactionSave = int(myTransactions.size()); if (isDone) { // save also sub-documents if any theResults.push_back(TCollection_AsciiString(aPath).ToCString()); @@ -276,7 +339,7 @@ bool Model_Document::save( aFile.Copy(aDestination); theResults.push_back(aDestinationDir.ToCString()); } else { - Events_InfoMessage("Model_Document", + Events_InfoMessage("Model_Document", "Can not open file %1 for saving").arg(aSubPath.ToCString()).send(); } } @@ -333,7 +396,8 @@ 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 @@ -390,9 +454,14 @@ static bool isEqualContent(Handle(TDF_Attribute) theAttr1, Handle(TDF_Attribute) 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 + 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())) { @@ -453,7 +522,7 @@ static bool isEqualContent(Handle(TDF_Attribute) theAttr1, Handle(TDF_Attribute) return false; } -/// Returns true if the last transaction is actually empty: modification to te same values +/// 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; @@ -475,8 +544,9 @@ static bool isEmptyTransaction(const Handle(TDocStd_Document)& theDoc) { 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 + } else + if (Standard_GUID::IsEqual(anADelta->Attribute()->ID(), TDataStd_AsciiString::GetID())) { + continue; // error message is disappeared } } } @@ -488,16 +558,17 @@ static bool isEmptyTransaction(const Handle(TDocStd_Document)& theDoc) { 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 + // 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 + // 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); @@ -510,26 +581,40 @@ bool Model_Document::finishOperation() } 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)); + static const Events_ID kCreatedEvent = Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED); + static const Events_ID kUpdatedEvent = Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED); + static const Events_ID kRedispEvent = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY); + static const Events_ID kDeletedEvent = Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED); + aLoop->flush(kCreatedEvent); + aLoop->flush(kUpdatedEvent); + aLoop->flush(kRedispEvent); + aLoop->flush(kDeletedEvent); 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 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"))); Events_Loop::loop()->send(aFinishMsg); - Events_Loop::loop()->autoFlush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED), false); } + + // for open of document with primitive box inside (finish transaction in initAttributes) + bool aWasActivatedFlushes = aLoop->activateFlushes(true); + while(aLoop->hasGrouppedEvent(kCreatedEvent) || aLoop->hasGrouppedEvent(kUpdatedEvent) || + aLoop->hasGrouppedEvent(kRedispEvent) || aLoop->hasGrouppedEvent(kDeletedEvent)) { + aLoop->flush(kCreatedEvent); + aLoop->flush(kUpdatedEvent); + aLoop->flush(kRedispEvent); + aLoop->flush(kDeletedEvent); + } + aLoop->activateFlushes(aWasActivatedFlushes); + // to avoid "updated" message appearance by updater //aLoop->clear(Events_Loop::eventByName(EVENT_OBJECT_UPDATED)); @@ -542,7 +627,8 @@ bool Model_Document::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 + if (!myTransactions.empty() && myDoc->CommitCommand()) { + // if commit is successfull, just increment counters if (isEmptyTransaction(myDoc)) { // erase this transaction myDoc->Undo(); myDoc->ClearRedos(); @@ -579,7 +665,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 @@ -588,14 +674,18 @@ static void modifiedLabels(const Handle(TDocStd_Document)& theDoc, TDF_LabelList } // add also label of the modified attributes const TDF_AttributeDeltaList& anAttrs = aDelta->AttributeDeltas(); - TDF_LabelMap anExcludedInt; /// named shape evolution also modifies integer on this label: exclude it + /// named shape evolution also modifies integer on this label: exclude it + TDF_LabelMap anExcludedInt; for (TDF_ListIteratorOfAttributeDeltaList anAttr(anAttrs); anAttr.More(); anAttr.Next()) { if (anAttr.Value()->Attribute()->ID() == TDataStd_BooleanArray::GetID()) { - continue; // Boolean array is used for feature auxiliary attributes only, feature args are not modified + // 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()); - continue; // named shape evolution is changed in history update => skip them, they are not the features arguents + // 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())) @@ -655,7 +745,7 @@ void Model_Document::abortOperation() 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, false, isRoot()); + myObjs->synchronizeFeatures(aDeltaLabels, true, false, false, isRoot()); } bool Model_Document::isOperation() const @@ -674,8 +764,9 @@ 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 @@ -721,7 +812,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, false, isRoot()); + myObjs->synchronizeFeatures(aDeltaLabels, true, false, false, isRoot()); // update the current features status setCurrentFeature(currentFeature(false), false); } @@ -768,7 +859,7 @@ void Model_Document::redo() subDoc(*aSubIter)->redo(); // after redo of all sub-documents to avoid updates on not-modified data (issue 370) - myObjs->synchronizeFeatures(aDeltaLabels, true, false, isRoot()); + myObjs->synchronizeFeatures(aDeltaLabels, true, false, false, isRoot()); // update the current features status setCurrentFeature(currentFeature(false), false); } @@ -806,7 +897,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) @@ -828,13 +919,13 @@ 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); for(int a = 0; a < aSubs; a++) { FeaturePtr aSub = aComp->subFeature(a, false); - if (myObjs->isLater(aSub, aCurrent)) { + if (aSub && myObjs->isLater(aSub, aCurrent)) { isModified = true; aCurrent = aSub; } @@ -882,24 +973,27 @@ void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis) 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 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); + FeaturePtr anAfterThisSub = theAfterThis; if (aCompositeAfter.get()) { FeaturePtr aSub = aCompositeAfter; do { FeaturePtr aNext = myObjs->nextFeature(aSub); if (!isSub(aCompositeAfter, aNext)) { - theAfterThis = aSub; + anAfterThisSub = aSub; break; } aSub = aNext; } while (aSub.get()); } - myObjs->moveFeature(theMoved, theAfterThis); + myObjs->moveFeature(theMoved, anAfterThisSub); if (aCurrentUp) { // make the moved feature enabled or disabled due to the real status setCurrentFeature(currentFeature(false), false); - } else if (theAfterThis == currentFeature(false)) { + } else if (theAfterThis == currentFeature(false) || anAfterThisSub == currentFeature(false)) { // must be after move to make enabled all features which are before theMoved setCurrentFeature(theMoved, true); } @@ -937,9 +1031,11 @@ std::shared_ptr Model_Document::subDoc(int theDocID) Model_Application::getApplication()->document(theDocID)); } -ObjectPtr Model_Document::object(const std::string& theGroupID, const int theIndex) +ObjectPtr Model_Document::object(const std::string& theGroupID, + const int theIndex, + const bool theAllowFolder) { - return myObjs->object(theGroupID, theIndex); + return myObjs->object(theGroupID, theIndex, theAllowFolder); } std::shared_ptr Model_Document::objectByName( @@ -953,11 +1049,11 @@ const int Model_Document::index(std::shared_ptr theObject) return myObjs->index(theObject); } -int Model_Document::size(const std::string& theGroupID) +int Model_Document::size(const std::string& theGroupID, const bool theAllowFolder) { if (myObjs == 0) // may be on close return 0; - return myObjs->size(theGroupID); + return myObjs->size(theGroupID, theAllowFolder); } std::shared_ptr Model_Document::currentFeature(const bool theVisible) @@ -1002,8 +1098,9 @@ void Model_Document::setCurrentFeature( } } - if (theVisible && !theCurrent.get()) { // needed to avoid disabling of PartSet initial constructions - 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()) { @@ -1030,7 +1127,8 @@ 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); bool aPassed = false; // flag that the current object is already passed in cycle @@ -1045,23 +1143,27 @@ void Model_Document::setCurrentFeature( 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 + else if (anOwners.find(anIter) != anOwners.end()) + // disable the higher-level feature if the nested is the current aDisabledFlag = true; } - if (anIter->getKind() == "Parameter") {// parameters are always out of the history of features, but not parameters + 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) + } else if (isCurrentParameter) { + // if paramater is active, all other features become enabled (issue 1307) aDisabledFlag = false; } if (anIter->setDisabled(aDisabledFlag)) { static Events_ID anUpdateEvent = aLoop->eventByName(EVENT_OBJECT_UPDATED); // 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)) + 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*/); @@ -1089,7 +1191,7 @@ 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 @@ -1097,8 +1199,9 @@ void Model_Document::setCurrentFeatureUp() // 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()) + // 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 @@ -1142,12 +1245,55 @@ 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) { return myObjs->createParameter(theFeatureData, theIndex); } +std::shared_ptr Model_Document::addFolder( + std::shared_ptr theAddBefore) +{ + return myObjs->createFolder(theAddBefore); +} + +void Model_Document::removeFolder(std::shared_ptr theFolder) +{ + if (theFolder) + myObjs->removeFolder(theFolder); +} + +std::shared_ptr Model_Document::findFolderAbove( + const std::list >& theFeatures) +{ + return myObjs->findFolder(theFeatures, false); +} + +std::shared_ptr Model_Document::findFolderBelow( + const std::list >& theFeatures) +{ + return myObjs->findFolder(theFeatures, true); +} + +bool Model_Document::moveToFolder( + const std::list >& theFeatures, + const std::shared_ptr& theFolder) +{ + return myObjs->moveToFolder(theFeatures, theFolder); +} + +bool Model_Document::removeFromFolder( + const std::list >& theFeatures) +{ + return myObjs->removeFromFolder(theFeatures); +} + std::shared_ptr Model_Document::feature( const std::shared_ptr& theResult) { @@ -1164,25 +1310,82 @@ Standard_Boolean IsEqual(const TDF_Label& theLab1, const TDF_Label& theLab2) return TDF_LabelMapHasher::IsEqual(theLab1, theLab2); } +// searches in this document feature that contains this label +FeaturePtr Model_Document::featureByLab(const TDF_Label& theLab) { + TDF_Label aCurrentLab = theLab; + while(aCurrentLab.Depth() > 3) + aCurrentLab = aCurrentLab.Father(); + return myObjs->feature(aCurrentLab); +} + void Model_Document::addNamingName(const TDF_Label theLabel, std::string theName) { - myNamingNames[theName] = theLabel; + std::map >::iterator aFind = myNamingNames.find(theName); + + if (aFind != myNamingNames.end()) { // to avoid duplicate-labels + // to keep correct order inspite of history line management + std::list::iterator anAddAfterThis = aFind->second.end(); + FeaturePtr anAddedFeature = featureByLab(theLabel); + std::list::iterator aLabIter = aFind->second.begin(); + while(aLabIter != aFind->second.end()) { + if (theLabel.IsEqual(*aLabIter)) { + std::list::iterator aTmpIter = aLabIter; + aLabIter++; + aFind->second.erase(aTmpIter); + } else { + FeaturePtr aCurFeature = featureByLab(*aLabIter); + if (aCurFeature.get() && anAddedFeature.get() && + myObjs->isLater(anAddedFeature, aCurFeature)) + anAddAfterThis = aLabIter; + + aLabIter++; + } + } + if (anAddAfterThis != aFind->second.end()) { + anAddAfterThis++; + if (anAddAfterThis != aFind->second.end()) { + myNamingNames[theName].insert(anAddAfterThis, theLabel); // inserts before anAddAfterThis + return; + } + } + } + myNamingNames[theName].push_back(theLabel); } -void Model_Document::changeNamingName(const std::string theOldName, const std::string theNewName) +void Model_Document::changeNamingName(const std::string theOldName, + const std::string theNewName, + const TDF_Label& theLabel) { - std::map::iterator aFind = myNamingNames.find(theOldName); + std::map >::iterator aFind = myNamingNames.find(theOldName); if (aFind != myNamingNames.end()) { - myNamingNames[theNewName] = aFind->second; - myNamingNames.erase(theOldName); + std::list::iterator aLabIter = aFind->second.begin(); + for(; aLabIter != aFind->second.end(); aLabIter++) { + if (theLabel.IsEqual(*aLabIter)) { // found the label + myNamingNames[theNewName].push_back(theLabel); + if (aFind->second.size() == 1) { // only one element, so, just change the name + myNamingNames.erase(theOldName); + } else { // remove from the list + aFind->second.erase(aLabIter); + } + return; + } + } } } -TDF_Label Model_Document::findNamingName(std::string theName) +TDF_Label Model_Document::findNamingName(std::string theName, ResultPtr theContext) { - std::map::iterator aFind = myNamingNames.find(theName); + std::map >::iterator aFind = myNamingNames.find(theName); if (aFind != myNamingNames.end()) { - return aFind->second; + std::list::reverse_iterator aLabIter = aFind->second.rbegin(); + for(; aLabIter != aFind->second.rend(); aLabIter++) { + if (theContext.get()) { + // context is defined and not like this, so, skip + if (theContext == myObjs->object(aLabIter->Father())) + return *aLabIter; + } + } + return *(aFind->second.rbegin()); // no more variannts, so, return the last } // not found exact name, try to find by sub-components std::string::size_type aSlash = theName.rfind('/'); @@ -1191,21 +1394,133 @@ TDF_Label Model_Document::findNamingName(std::string theName) 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(); + // iterate all possible same-named labels starting from the last one (the recent) + std::list::reverse_iterator aLabIter = aFind->second.rbegin(); + for(; aLabIter != aFind->second.rend(); aLabIter++) { + if (theContext.get()) { + // context is defined and not like this, so, skip + if (theContext != myObjs->object(aLabIter->Father())) + continue; + } + // searching sub-labels with this name + TDF_ChildIDIterator aNamesIter(*aLabIter, 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 && aSuffixPos != aSubName.Length()) { + TCollection_ExtendedString anIndexStr = aSubName.Split(aSuffixPos); + aSubName.Remove(aSuffixPos); + aNamesIter.Initialize(*aLabIter, 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) +bool Model_Document::isLaterByDep(FeaturePtr theThis, FeaturePtr theOther) { + // check dependencies first: if theOther depends on theThis, theThis is not later + std::list > > > aRefs; + theOther->data()->referencesToObjects(aRefs); + std::list > > >::iterator + aRefIt = aRefs.begin(); + for(; aRefIt != aRefs.end(); aRefIt++) { + std::list::iterator aRefObjIt = aRefIt->second.begin(); + for(; aRefObjIt != aRefIt->second.end(); aRefObjIt++) { + ObjectPtr aRefObj = *aRefObjIt; + if (aRefObj.get()) { + FeaturePtr aRefFeat = std::dynamic_pointer_cast(aRefObj); + if (!aRefFeat.get()) { // take feature of the result + aRefFeat = feature(std::dynamic_pointer_cast(aRefObj)); + } + if (aRefFeat.get() && aRefFeat == theThis) { + return false; // other references to this, so this later than other + } + } + } + } + return myObjs->isLater(theThis, theOther); +} + +int Model_Document::numberOfNameInHistory( + const ObjectPtr& theNameObject, const TDF_Label& theStartFrom) +{ + std::map >::iterator aFind = + myNamingNames.find(theNameObject->data()->name()); + if (aFind == myNamingNames.end() || aFind->second.size() < 2) { + return 1; // no need to specify the name by additional identifiers + } + // get the feature of the object for relative compare + FeaturePtr aStart = myObjs->feature(theStartFrom); + if (!aStart.get()) // strange, but can not find feature by the label + return 1; + // feature that contain result with this name + FeaturePtr aNameFeature; + ResultPtr aNameResult = std::dynamic_pointer_cast(theNameObject); + if (aNameResult) + aNameFeature = myObjs->feature(aNameResult); + else + aNameFeature = std::dynamic_pointer_cast(theNameObject); + // iterate all labels with this name to find the nearest just before or equal relative + std::list::reverse_iterator aLabIter = aFind->second.rbegin(); + for(; aLabIter != aFind->second.rend(); aLabIter++) { + FeaturePtr aLabFeat = featureByLab(*aLabIter); + if (!aLabFeat.get()) + continue; + if (isLaterByDep(aStart, aLabFeat)) // skip also start: its result don't used + break; + } + int aResIndex = 1; + for(; aLabIter != aFind->second.rend(); aLabIter++) { + FeaturePtr aLabFeat = featureByLab(*aLabIter); + if (!aLabFeat.get()) + continue; + if (aLabFeat == aNameFeature || isLaterByDep(aNameFeature, aLabFeat)) + return aResIndex; + aResIndex++; + } + return aResIndex; // strange +} + +ResultPtr Model_Document::findByName( + std::string& theName, std::string& theSubShapeName, bool& theUniqueContext) { - return myObjs->findByName(theName); + int aNumInHistory = 0; + std::string aName = theName; + ResultPtr aRes = myObjs->findByName(aName); + theUniqueContext = !(aRes.get() && myNamingNames.find(aName) != myNamingNames.end()); + while(!aRes.get() && aName[0] == '_') { // this may be thecontext with the history index + aNumInHistory++; + aName = aName.substr(1); + aRes = myObjs->findByName(aName); + } + if (aNumInHistory) { + std::map >::iterator aFind = myNamingNames.find(aName); + if (aFind != myNamingNames.end() && aFind->second.size() > aNumInHistory) { + std::list::reverse_iterator aLibIt = aFind->second.rbegin(); + for(; aNumInHistory != 0; aNumInHistory--) + aLibIt++; + const TDF_Label& aResultLab = *aLibIt; + aRes = std::dynamic_pointer_cast(myObjs->object(aResultLab.Father())); + if (aRes) { // modify the incoming names + if (!theSubShapeName.empty()) + theSubShapeName = theSubShapeName.substr(theName.size() - aName.size()); + theName = aName; + } + } + } + return aRes; } std::list > Model_Document::allFeatures() @@ -1213,6 +1528,11 @@ std::list > Model_Document::allFeatures() return myObjs->allFeatures(); } +std::list > Model_Document::allObjects() +{ + return myObjs->allObjects(); +} + void Model_Document::setActive(const bool theFlag) { if (theFlag != myIsActive) { @@ -1272,6 +1592,11 @@ void Model_Document::decrementTransactionID() TDataStd_Integer::Set(generalLabel().FindChild(TAG_CURRENT_TRANSACTION), aNewVal); } +TDF_Label Model_Document::extConstructionsLabel() const +{ + return myDoc->Main().FindChild(TAG_EXTERNAL_CONSTRUCTIONS); +} + bool Model_Document::isOpened() { return myObjs && !myDoc.IsNull(); @@ -1294,12 +1619,12 @@ std::shared_ptr Model_Document::featureById(const int 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); @@ -1339,7 +1664,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)); @@ -1369,20 +1694,23 @@ static Handle(TNaming_NamedShape) searchForOriginalShape(TopoDS_Shape theShape, while(!theShape.IsNull()) { // searching for the very initial shape that produces this one TopoDS_Shape aShape = theShape; theShape.Nullify(); - if (!TNaming_Tool::HasLabel(aMain, aShape)) // to avoid crash of TNaming_SameShapeIterator if pure shape does not exists + // 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) + 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(); - if (!theShape.IsNull()) // otherwise may me searching for another item of this shape with longer history + // otherwise may me searching for another item of this shape with longer history + if (!theShape.IsNull()) break; } } @@ -1440,7 +1768,8 @@ std::shared_ptr Model_Document::producedByFeature( aCandidatInThis = aNS; if (aCandidatInThis->Evolution() == TNaming_MODIFY) anOldShape = aShapesIter.OldShape(); - if (!anOldShape.IsNull()) // otherwise may me searching for another item of this shape with longer history + // 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 @@ -1464,7 +1793,8 @@ std::shared_ptr Model_Document::producedByFeature( } } if (aCandidatInThis.IsNull()) { - // to fix 1512: searching for original shape of this shape if modification of it is not in this result + // 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()) @@ -1499,3 +1829,35 @@ 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); + } + } +} + +void Model_Document::eraseAllFeatures() +{ + if (myObjs) + myObjs->eraseAllFeatures(); +}