X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FModel%2FModel_Document.cpp;h=c69e0c5cb0abe63fab1ab559f5353c295d74eb28;hb=f41052a2c5a9440c85bb126a0f72df546e8d2f1c;hp=d4e7b2d23a2bf5bdd88e30bd4fb5a62acf31ebc3;hpb=fe3678a85238df2b57ea18b341003ebef176e287;p=modules%2Fshaper.git diff --git a/src/Model/Model_Document.cpp b/src/Model/Model_Document.cpp old mode 100755 new mode 100644 index d4e7b2d23..c69e0c5cb --- a/src/Model/Model_Document.cpp +++ b/src/Model/Model_Document.cpp @@ -1,8 +1,21 @@ -// Copyright (C) 2014-20xx CEA/DEN, EDF R&D - -// File: Model_Document.cxx -// Created: 28 Feb 2014 -// Author: Mikhail PONIKAROV +// Copyright (C) 2014-2019 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 @@ -10,15 +23,16 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include +#include #include #include @@ -31,27 +45,35 @@ #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 @@ -71,14 +93,18 @@ static const int TAG_GENERAL = 1; // general properties tag // general sub-labels /// 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 +/// integer, index of the transaction + GUID for auto recomputation blocking +static const int TAG_CURRENT_TRANSACTION = 2; 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; +/// reference to the shape in external document: sting list attribute identifier +static const Standard_GUID kEXTERNAL_SHAPE_REF("9aa5dd14-6d34-4a8d-8786-05842fd7bbbd"); + Model_Document::Model_Document(const int theID, const std::string theKind) - : myID(theID), myKind(theKind), myIsActive(false), + : myID(theID), myKind(theKind), myIsActive(false), myIsSetCurrentFeature(false), myDoc(new TDocStd_Document("BinOcaf")) // binary OCAF format { #ifdef TINSPECTOR @@ -121,25 +147,99 @@ bool Model_Document::isRoot() const return this == Model_Session::get()->moduleDocument().get(); } -bool Model_Document::load(const char* theDirName, const char* theFileName, DocumentPtr theThis) +// LCOV_EXCL_START +/// Makes all modification and generation naming shapes that have old shapes corresponding to +/// shapes in a root document be equal to this root document +static void updateShapesFromRoot(const TDF_Label theThisAccess, const TDF_Label theRootAccess) { - Handle(Model_Application) anApp = Model_Application::getApplication(); - if (isRoot()) { - anApp->setLoadPath(theDirName); + TopTools_DataMapOfShapeShape aCurrentToRoot; // shapes that must be updated: from this to root + TDF_ChildIDIterator aThisIter(theThisAccess.Root(), kEXTERNAL_SHAPE_REF, true); + for(; aThisIter.More(); aThisIter.Next()) { + aCurrentToRoot.Clear(); + Handle(TNaming_NamedShape) aNS; + if (!aThisIter.Value()->Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) + continue; + if (aNS->Evolution() != TNaming_GENERATED && aNS->Evolution() != TNaming_MODIFY) + continue; + for (TNaming_Iterator aNSIter(aNS); aNSIter.More(); aNSIter.Next()) { + const TopoDS_Shape& anOld = aNSIter.OldShape(); + if (anOld.IsNull()) + continue; + TNaming_OldShapeIterator aNewIter(anOld, theThisAccess); + for (; aNewIter.More(); aNewIter.Next()) { + TNaming_Evolution anEvolution = aNewIter.NamedShape()->Evolution(); + if (anEvolution != TNaming_SELECTED && anEvolution != TNaming_DELETE) + break; + } + if (aNewIter.More()) + continue; + GeomShapePtr anOldShape(new GeomAPI_Shape), aRootShape(new GeomAPI_Shape); + anOldShape->setImpl(new TopoDS_Shape(anOld)); + anOldShape = GeomAPI_Tools::getTypedShape(anOldShape); + + // search the same shape in the root document + Handle(TDataStd_ExtStringList) anEntries = + Handle(TDataStd_ExtStringList)::DownCast(aThisIter.Value()); + TDataStd_ListOfExtendedString::Iterator anIter(anEntries->List()); + for (; anIter.More(); anIter.Next()) { + TDF_Label aRootLab; + TDF_Tool::Label(theRootAccess.Data(), anIter.Value(), aRootLab); + if (aRootLab.IsNull()) + continue; + Handle(TNaming_NamedShape) aRootNS; + if (!aRootLab.FindAttribute(TNaming_NamedShape::GetID(), aRootNS)) + continue; + TNaming_Iterator aRootShapes(aRootNS); + for (; aRootShapes.More(); aRootShapes.Next()) { + if (aRootShapes.NewShape().IsNull()) + continue; + aRootShape->setImpl(new TopoDS_Shape(aRootShapes.NewShape())); + aRootShape = GeomAPI_Tools::getTypedShape(aRootShape); + if (!anOldShape->isEqual(aRootShape)) // special checking by geometry + continue; + // found a good corresponded shape + if (!anOld.IsEqual(aRootShapes.NewShape())) + aCurrentToRoot.Bind(anOld, aRootShapes.NewShape()); + } + } + } + if (!aCurrentToRoot.IsEmpty()) { // update the whole named shape content + TopTools_ListOfShape anOld, aNew; + TNaming_Evolution anEvol = aNS->Evolution(); + for(TNaming_Iterator aNSIter(aNS); aNSIter.More(); aNSIter.Next()) { + anOld.Prepend(aCurrentToRoot.IsBound(aNSIter.OldShape()) ? + aCurrentToRoot.Find(aNSIter.OldShape()) : aNSIter.OldShape()); + aNew.Prepend(aNSIter.NewShape()); + } + TNaming_Builder aBuilder(aNS->Label()); + TopTools_ListOfShape::Iterator anOldIter(anOld), aNewIter(aNew); + for(; anOldIter.More(); anOldIter.Next(), aNewIter.Next()) { + if (anEvol == TNaming_GENERATED) { + aBuilder.Generated(anOldIter.Value(), aNewIter.Value()); + } else if (anEvol == TNaming_MODIFY) { + aBuilder.Modify(anOldIter.Value(), aNewIter.Value()); + } + } + } } - TCollection_ExtendedString aPath(DocFileName(theDirName, theFileName)); - PCDM_ReaderStatus aStatus = (PCDM_ReaderStatus) -1; - Handle(TDocStd_Document) aLoaded; +} +// LCOV_EXCL_STOP + +static bool loadDocument(Handle(Model_Application) theApp, + Handle(TDocStd_Document)& theDoc, + const TCollection_ExtendedString& theFilename) +{ + PCDM_ReaderStatus aStatus = (PCDM_ReaderStatus)-1; try { - aStatus = anApp->Open(aPath, aLoaded); - } catch (Standard_Failure) { - Handle(Standard_Failure) aFail = Standard_Failure::Caught(); + aStatus = theApp->Open(theFilename, theDoc); + } catch (Standard_Failure const& anException) { Events_InfoMessage("Model_Document", - "Exception in opening of document: %1").arg(aFail->GetMessageString()).send(); + "Exception in opening of document: %1").arg(anException.GetMessageString()).send(); return false; } - bool isError = aStatus != PCDM_RS_OK; - if (isError) { + bool isOk = aStatus == PCDM_RS_OK; + if (!isOk) { + // LCOV_EXCL_START switch (aStatus) { case PCDM_RS_UnknownDocument: Events_InfoMessage("Model_Document", "Can not open document").send(); @@ -194,10 +294,24 @@ bool Model_Document::load(const char* theDirName, const char* theFileName, Docum Events_InfoMessage("Model_Document", "Can not open document: unknown error").send(); break; } + // LCOV_EXCL_STOP + } + return isOk; +} + +bool Model_Document::load(const char* theDirName, const char* theFileName, DocumentPtr theThis) +{ + Handle(Model_Application) anApp = Model_Application::getApplication(); + if (isRoot()) { + anApp->setLoadPath(theDirName); } + TCollection_ExtendedString aPath(DocFileName(theDirName, theFileName)); + Handle(TDocStd_Document) aLoaded; + bool isOk = loadDocument(anApp, aLoaded, aPath); + std::shared_ptr aSession = std::dynamic_pointer_cast(Model_Session::get()); - if (!isError) { + if (isOk) { myDoc = aLoaded; myDoc->SetUndoLimit(UNDO_LIMIT); @@ -206,12 +320,12 @@ bool Model_Document::load(const char* theDirName, const char* theFileName, Docum aSession->setCheckTransactions(false); if (myObjs) delete myObjs; - myObjs = new Model_Objects(myDoc->Main()); // synchronisation is inside + myObjs = new Model_Objects(myDoc->Main()); // synchronization is inside myObjs->setOwner(theThis); // update the current features status setCurrentFeature(currentFeature(false), false); aSession->setCheckTransactions(true); - aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false); + aSession->setActiveDocument(aSession->moduleDocument(), false); // this is done in Part result "activate", so no needed here. Causes not-blue active part. // aSession->setActiveDocument(anApp->getDocument(myID), true); @@ -222,54 +336,154 @@ 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 + if (!isRoot()) { + updateShapesFromRoot(myDoc->Main(), + std::dynamic_pointer_cast(aSession->moduleDocument())->generalLabel()); + } + } else { // open failed, but new document was created to work with it: inform the model aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false); } - return !isError; + return isOk; +} + +bool Model_Document::import(const char* theFileName) +{ + Handle(Model_Application) anApp = Model_Application::getApplication(); + TCollection_ExtendedString aFormat; + if (!anApp->Format(theFileName, aFormat)) + return false; + + Handle(TDocStd_Document) aTempDoc; + bool isOk = loadDocument(anApp, aTempDoc, theFileName); + + // copy features from the temporary document to the current + Handle(TDF_RelocationTable) aRelocTable = new TDF_RelocationTable(Standard_True); + TDF_LabelList anAllNewFeatures; + // Perform the copying twice for correct references: + // 1. copy labels hierarchy and fill the relocation table + TDF_Label aMain = myDoc->Main(); + for (TDF_ChildIterator anIt(aTempDoc->Main()); anIt.More(); anIt.Next()) { + TDF_Label aCurrentLab = anIt.Value(); + Handle(TDataStd_Comment) aFeatureID; + TDF_Label aNewFeatuerLab; + if (aCurrentLab.FindAttribute(TDataStd_Comment::GetID(), aFeatureID)) { + TCollection_AsciiString anID(aFeatureID->Get()); + FeaturePtr aNewFeature = addFeature(anID.ToCString()); + std::shared_ptr aData = + std::dynamic_pointer_cast(aNewFeature->data()); + aNewFeatuerLab = aData->label().Father(); + Model_Tools::copyLabels(aCurrentLab, aNewFeatuerLab, aRelocTable); + } + anAllNewFeatures.Append(aNewFeatuerLab); + } + // 2. copy attributes + TDF_ListIteratorOfLabelList aNewIt(anAllNewFeatures); + for (TDF_ChildIterator anIt(aTempDoc->Main()); anIt.More(); anIt.Next()) { + TDF_Label aCurrentLab = anIt.Value(); + TDF_Label aFeatureLab = aNewIt.Value(); + if (aFeatureLab.IsNull()) + anAllNewFeatures.Remove(aNewIt); + else { + Model_Tools::copyAttrs(aCurrentLab, aFeatureLab, aRelocTable); + aNewIt.Next(); + } + } + + myObjs->synchronizeFeatures(anAllNewFeatures, true, false, false, true); + + if (aTempDoc->CanClose() == CDM_CCS_OK) + aTempDoc->Close(); + return isOk; +} + +static bool saveDocument(Handle(Model_Application) theApp, + Handle(TDocStd_Document) theDoc, + const TCollection_ExtendedString& theFilename) +{ + PCDM_StoreStatus aStatus; + try { + // create the directory to save the document + OSD_Path aPathToFile = UTL::Path(theFilename); + aPathToFile.SetName(""); + aPathToFile.SetExtension(""); + OSD_Directory aBaseDir(aPathToFile); + if (!aBaseDir.Exists()) + aBaseDir.Build(OSD_Protection()); + // save the document + aStatus = theApp->SaveAs(theDoc, theFilename); + } + catch (Standard_Failure const& anException) { + Events_InfoMessage("Model_Document", + "Exception in saving of document: %1").arg(anException.GetMessageString()).send(); + 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(); + break; + case PCDM_SS_WriteFailure: + Events_InfoMessage("Model_Document", "Can not save document: file writing failure").send(); + break; + case PCDM_SS_Failure: + default: + Events_InfoMessage("Model_Document", "Can not save document").send(); + break; + } + } + return isDone; } 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()) { #ifdef WIN32 - CreateDirectory(theDirName, NULL); + size_t aDirLen = strlen(theDirName); + std::wstring aWStr(aDirLen, L'#'); + mbstowcs(&aWStr[0], theDirName, aDirLen); + CreateDirectory(aWStr.c_str(), NULL); #else mkdir(theDirName, 0x1ff); #endif } // filename in the dir is id of document inside of the given directory 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_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_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(); - break; - case PCDM_SS_Failure: - default: - Events_InfoMessage("Model_Document", "Can not save document").send(); - break; - } + bool isDone = saveDocument(anApp, myDoc, aPath); + + 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()); @@ -306,6 +520,41 @@ bool Model_Document::save( return isDone; } +bool Model_Document::save(const char* theFilename, + const std::list& theExportFeatures) const +{ + Handle(Model_Application) anApp = Model_Application::getApplication(); + TCollection_ExtendedString aFormat; + if (!anApp->Format(theFilename, aFormat)) + return false; + + Handle(TDocStd_Document) aTempDoc = new TDocStd_Document(aFormat); + TDF_Label aMain = aTempDoc->Main(); + + Handle(TDF_RelocationTable) aRelocTable = new TDF_RelocationTable(Standard_True); + std::list::const_iterator anIt = theExportFeatures.begin(); + // Perform the copying twice for correct references: + // 1. copy labels hierarchy and fill the relocation table + for (; anIt != theExportFeatures.end(); ++anIt) { + TDF_Label aFeatureLab = aMain.NewChild(); + std::shared_ptr aData = std::dynamic_pointer_cast((*anIt)->data()); + Model_Tools::copyLabels(aData->label().Father(), aFeatureLab, aRelocTable); + } + // 2. copy attributes + TDF_ChildIterator aChildIt(aMain); + for (anIt = theExportFeatures.begin(); anIt != theExportFeatures.end(); ++anIt) { + TDF_Label aFeatureLab = aChildIt.Value(); + std::shared_ptr aData = std::dynamic_pointer_cast((*anIt)->data()); + Model_Tools::copyAttrs(aData->label().Father(), aFeatureLab, aRelocTable); + aChildIt.Next(); + } + + bool isDone = saveDocument(anApp, aTempDoc, theFilename); + if (aTempDoc->CanClose() == CDM_CCS_OK) + aTempDoc->Close(); + return isDone; +} + void Model_Document::close(const bool theForever) { std::shared_ptr aPM = Model_Session::get(); @@ -324,10 +573,10 @@ void Model_Document::close(const bool theForever) aSub->close(theForever); } - // close for thid document needs no transaction in this document + // close for this document needs no transaction in this document std::static_pointer_cast(Model_Session::get())->setCheckTransactions(false); - // close all only if it is really asked, otherwise it can be undoed/redoed + // close all only if it is really asked, otherwise it can be undone/redone if (theForever) { // flush everything to avoid messages with bad objects delete myObjs; @@ -380,7 +629,7 @@ void Model_Document::compactNested() aSumOfTransaction += myTransactions.rbegin()->myOCAFNum; myTransactions.pop_back(); } - // the latest transaction is the start of lower-level operation which startes the nested + // the latest transaction is the start of lower-level operation which starts the nested myTransactions.rbegin()->myOCAFNum += aSumOfTransaction; myNestedNum.pop_back(); } @@ -476,7 +725,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 the same values /// were performed only static bool isEmptyTransaction(const Handle(TDocStd_Document)& theDoc) { Handle(TDF_Delta) aDelta; @@ -523,7 +772,7 @@ bool Model_Document::finishOperation() // 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 + // it in the python scripts correctly): sketch become current after creation of sub-elements FeaturePtr aCurrent = currentFeature(false); CompositeFeaturePtr aMain, aNext = ModelAPI_Tools::compositeOwner(aCurrent); while(aNext.get()) { @@ -535,10 +784,14 @@ 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 = aLoop->eventByName(EVENT_OBJECT_CREATED); + static const Events_ID kUpdatedEvent = aLoop->eventByName(EVENT_OBJECT_UPDATED); + static const Events_ID kRedispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY); + static const Events_ID kDeletedEvent = aLoop->eventByName(EVENT_OBJECT_DELETED); + aLoop->flush(kCreatedEvent); + aLoop->flush(kUpdatedEvent); + aLoop->flush(kRedispEvent); + aLoop->flush(kDeletedEvent); if (isNestedClosed) { if (myDoc->CommitCommand()) @@ -549,12 +802,22 @@ bool Model_Document::finishOperation() // 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)); @@ -568,7 +831,7 @@ bool Model_Document::finishOperation() // 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 commit is successful, just increment counters if (isEmptyTransaction(myDoc)) { // erase this transaction myDoc->Undo(); myDoc->ClearRedos(); @@ -588,7 +851,7 @@ bool Model_Document::finishOperation() // nothing inside in all documents, so remove this transaction from the transactions list undoInternal(true, false); } - // on finish clear redos in any case (issue 446) and for all subs (issue 408) + // on finish clear redo in any case (issue 446) and for all subs (issue 408) myDoc->ClearRedos(); myRedos.clear(); for (aSubIter = aSubs.begin(); aSubIter != aSubs.end(); aSubIter++) { @@ -624,7 +887,7 @@ static void modifiedLabels(const Handle(TDocStd_Document)& theDoc, TDF_LabelList 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 + // they are not the features arguments continue; } if (anAttr.Value()->Attribute()->ID() == TDataStd_Integer::GetID()) { @@ -696,7 +959,7 @@ bool Model_Document::isOperation() const bool Model_Document::isModified() { - // is modified if at least one operation was commited and not undoed + // is modified if at least one operation was committed and not undone return myTransactions.size() != myTransactionSave || isOperation(); } @@ -709,7 +972,7 @@ bool Model_Document::canUndo() (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 + // check other subs contains operation that can be undone const std::set aSubs = subDocuments(); std::set::iterator aSubIter = aSubs.begin(); for (; aSubIter != aSubs.end(); aSubIter++) { @@ -740,9 +1003,10 @@ void Model_Document::undoInternal(const bool theWithSubs, const bool theSynchron myDoc->Undo(); } + std::set aSubs; if (theWithSubs) { // undo for all subs - const std::set aSubs = subDocuments(); + aSubs = subDocuments(); std::set::iterator aSubIter = aSubs.begin(); for (; aSubIter != aSubs.end(); aSubIter++) { if (!subDoc(*aSubIter)->myObjs) @@ -755,6 +1019,19 @@ void Model_Document::undoInternal(const bool theWithSubs, const bool theSynchron myObjs->synchronizeFeatures(aDeltaLabels, true, false, false, isRoot()); // update the current features status setCurrentFeature(currentFeature(false), false); + + if (theWithSubs) { + // undo for all subs + const std::set aNewSubs = subDocuments(); + std::set::iterator aNewSubIter = aNewSubs.begin(); + for (; aNewSubIter != aNewSubs.end(); aNewSubIter++) { + // synchronize only newly appeared documents + if (!subDoc(*aNewSubIter)->myObjs || aSubs.find(*aNewSubIter) != aSubs.end()) + continue; + TDF_LabelList anEmptyDeltas; + subDoc(*aNewSubIter)->myObjs->synchronizeFeatures(anEmptyDeltas, true, false, true, true); + } + } } } @@ -767,7 +1044,7 @@ bool Model_Document::canRedo() { if (!myRedos.empty()) return true; - // check other subs contains operation that can be redoed + // check other subs contains operation that can be redone const std::set aSubs = subDocuments(); std::set::iterator aSubIter = aSubs.begin(); for (; aSubIter != aSubs.end(); aSubIter++) { @@ -803,7 +1080,8 @@ void Model_Document::redo() // update the current features status setCurrentFeature(currentFeature(false), false); } - +// this is used for creation of undo/redo1-list by GUI +// LCOV_EXCL_START std::list Model_Document::undoList() const { std::list aResult; @@ -829,6 +1107,7 @@ std::list Model_Document::redoList() const } return aResult; } +// LCOV_EXCL_STOP void Model_Document::operationId(const std::string& theId) { @@ -865,12 +1144,21 @@ FeaturePtr Model_Document::addFeature(std::string theID, const bool theMakeCurre 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; } } } + // #2861,3029: if the parameter is added, add it after parameters existing in the list + if (aCurrent.get() && + (aFeature->getKind() == "Parameter" || aFeature->getKind() == "ParametersMgr")) { + int anIndex = kUNDEFINED_FEATURE_INDEX; + for(FeaturePtr aNextFeat = myObjs->nextFeature(aCurrent, anIndex); + aNextFeat.get() && aNextFeat->getKind() == "Parameter"; + aNextFeat = myObjs->nextFeature(aCurrent, anIndex)) + aCurrent = aNextFeat; + } 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 @@ -883,7 +1171,6 @@ FeaturePtr Model_Document::addFeature(std::string theID, const bool theMakeCurre return aFeature; } - void Model_Document::refsToFeature(FeaturePtr theFeature, std::set >& theRefs, const bool isSendError) { @@ -893,6 +1180,22 @@ void Model_Document::refsToFeature(FeaturePtr theFeature, void Model_Document::removeFeature(FeaturePtr theFeature) { myObjs->removeFeature(theFeature); + // fix for #2723: send signal that part is updated + if (!isRoot() && isOperation()) { + std::shared_ptr aRoot = + std::dynamic_pointer_cast(ModelAPI_Session::get()->moduleDocument()); + std::list allParts; + aRoot->objects()->allResults(ModelAPI_ResultPart::group(), allParts); + std::list::iterator aParts = allParts.begin(); + for(; aParts != allParts.end(); aParts++) { + ResultPartPtr aPart = std::dynamic_pointer_cast(*aParts); + if (aPart->partDoc().get() == this) { + static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED); + ModelAPI_EventCreator::get()->sendUpdated(aRoot->feature(aPart), anEvent); + break; + } + } + } } // recursive function to check if theSub is a child of theMain composite feature @@ -906,7 +1209,6 @@ static bool isSub(const CompositeFeaturePtr theMain, const FeaturePtr theSub) { return isSub(theMain, aParent); } - void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis) { bool aCurrentUp = theMoved == currentFeature(false); @@ -917,22 +1219,24 @@ void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis) // 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; + int anIndex = kUNDEFINED_FEATURE_INDEX; do { - FeaturePtr aNext = myObjs->nextFeature(aSub); + FeaturePtr aNext = myObjs->nextFeature(aSub, anIndex); 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); } @@ -940,12 +1244,14 @@ void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis) void Model_Document::updateHistory(const std::shared_ptr theObject) { - myObjs->updateHistory(theObject); + if (myObjs) + myObjs->updateHistory(theObject); } void Model_Document::updateHistory(const std::string theGroup) { - myObjs->updateHistory(theGroup); + if (myObjs) + myObjs->updateHistory(theGroup); } const std::set Model_Document::subDocuments() const @@ -970,9 +1276,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( @@ -981,16 +1289,25 @@ std::shared_ptr Model_Document::objectByName( return myObjs->objectByName(theGroupID, theName); } -const int Model_Document::index(std::shared_ptr theObject) +const int Model_Document::index(std::shared_ptr theObject, + const bool theAllowFolder) { - return myObjs->index(theObject); + return myObjs->index(theObject, theAllowFolder); } -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::parent( + const std::shared_ptr theChild) +{ + if(myObjs == 0) // may be on close + return ObjectPtr(); + return myObjs->parent(theChild); } std::shared_ptr Model_Document::currentFeature(const bool theVisible) @@ -1003,8 +1320,9 @@ std::shared_ptr Model_Document::currentFeature(const bool theV TDF_Label aLab = aRef->Get(); FeaturePtr aResult = myObjs->feature(aLab); if (theVisible) { // get nearest visible (in history) going up + int anIndex = kUNDEFINED_FEATURE_INDEX; while(aResult.get() && !aResult->isInHistory()) { - aResult = myObjs->nextFeature(aResult, true); + aResult = myObjs->nextFeature(aResult, anIndex, true); } } return aResult; @@ -1015,6 +1333,9 @@ std::shared_ptr Model_Document::currentFeature(const bool theV void Model_Document::setCurrentFeature( std::shared_ptr theCurrent, const bool theVisible) { + if (myIsSetCurrentFeature) + return; + myIsSetCurrentFeature = true; // 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(); @@ -1037,9 +1358,10 @@ void Model_Document::setCurrentFeature( if (theVisible && !theCurrent.get()) { // needed to avoid disabling of PartSet initial constructions + int anIndex = kUNDEFINED_FEATURE_INDEX; FeaturePtr aNext = - theCurrent.get() ? myObjs->nextFeature(theCurrent) : myObjs->firstFeature(); - for (; aNext.get(); aNext = myObjs->nextFeature(theCurrent)) { + theCurrent.get() ? myObjs->nextFeature(theCurrent, anIndex, false) : myObjs->firstFeature(); + for (; aNext.get(); aNext = myObjs->nextFeature(theCurrent, anIndex, false)) { if (aNext->isInHistory()) { break; // next in history is not needed } else { // next not in history is good for making current @@ -1047,10 +1369,23 @@ void Model_Document::setCurrentFeature( } } } + if (theVisible) { // make RemoveResults feature be active even it is performed after the current + int anIndex = kUNDEFINED_FEATURE_INDEX; + FeaturePtr aNext = + theCurrent.get() ? myObjs->nextFeature(theCurrent, anIndex, false) : myObjs->firstFeature(); + for (; aNext.get(); aNext = myObjs->nextFeature(theCurrent, anIndex, false)) { + if (aNext->isInHistory()) { + break; // next in history is not needed + } else if (aNext->getKind() == "RemoveResults"){ + theCurrent = aNext; + } + } + } if (theCurrent.get()) { std::shared_ptr aData = std::static_pointer_cast(theCurrent->data()); if (!aData.get() || !aData->isValid()) { aLoop->activateFlushes(isActive); + myIsSetCurrentFeature = false; return; } TDF_Label aFeatureLabel = aData->label().Father(); @@ -1065,14 +1400,15 @@ void Model_Document::setCurrentFeature( aRefLab.ForgetAttribute(TDF_Reference::GetID()); } // make all features after this feature disabled in reversed order - // (to remove results without deps) + // (to remove results without dependencies) static Events_ID aRedispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY); 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)) { + int anIndex = kUNDEFINED_FEATURE_INDEX; + for(; anIter.get(); anIter = myObjs->nextFeature(anIter, anIndex, true)) { // check this before passed become enabled: the current feature is enabled! if (anIter == theCurrent) aPassed = true; @@ -1081,8 +1417,9 @@ void Model_Document::setCurrentFeature( 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; + // disable the higher-level feature if the nested is the current + if (aMain->getKind() != "Import") // exception for the import XAO feature with Group (2430) + aDisabledFlag = true; } if (anIter->getKind() == "Parameter") { @@ -1091,7 +1428,7 @@ void Model_Document::setCurrentFeature( //if (!isCurrentParameter) aDisabledFlag = false; } else if (isCurrentParameter) { - // if paramater is active, all other features become enabled (issue 1307) + // if parameter is active, all other features become enabled (issue 1307) aDisabledFlag = false; } @@ -1106,7 +1443,7 @@ void Model_Document::setCurrentFeature( ModelAPI_EventCreator::get()->sendUpdated(anIter, aRedispEvent /*, false*/); aWasChanged = true; } - // update for everyone the concealment flag immideately: on edit feature in the midle of history + // update for everyone concealment flag immediately: on edit feature in the middle of history if (aWasChanged) { std::list aResults; ModelAPI_Tools::allResults(anIter, aResults); @@ -1115,13 +1452,14 @@ void Model_Document::setCurrentFeature( 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 + // update the concealment status for display in isConcealed of ResultBody for(aRes = aResults.begin(); aRes != aResults.end(); aRes++) { if ((*aRes).get() && (*aRes)->data()->isValid() && !(*aRes)->isDisabled()) (*aRes)->isConcealed(); } } } + myIsSetCurrentFeature = false; // unblock the flush signals and up them after this aLoop->activateFlushes(isActive); } @@ -1132,12 +1470,13 @@ void Model_Document::setCurrentFeatureUp() // 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); + int anIndex = kUNDEFINED_FEATURE_INDEX; + FeaturePtr aPrev = myObjs->nextFeature(aCurrent, anIndex, 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 + // will be enabled and displayed when inside sketch if (aComp.get()) aPrev = aComp; } @@ -1194,41 +1533,158 @@ std::shared_ptr Model_Document::createParameter( 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); +} + +std::shared_ptr Model_Document::findContainingFolder( + const std::shared_ptr& theFeature, + int& theIndexInFolder) +{ + return myObjs->findContainingFolder(theFeature, theIndexInFolder); +} + +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, + const bool theBefore) +{ + return myObjs->removeFromFolder(theFeatures, theBefore); +} + std::shared_ptr Model_Document::feature( const std::shared_ptr& theResult) { + if (myObjs == 0) // may be on close + return std::shared_ptr(); return myObjs->feature(theResult); } -Standard_Integer HashCode(const TDF_Label& theLab, const Standard_Integer theUpper) -{ - return TDF_LabelMapHasher::HashCode(theLab, theUpper); - +FeaturePtr Model_Document::featureByLab(const TDF_Label& theLab) { + TDF_Label aCurrentLab = theLab; + while(aCurrentLab.Depth() > 3) + aCurrentLab = aCurrentLab.Father(); + return myObjs->feature(aCurrentLab); } -Standard_Boolean IsEqual(const TDF_Label& theLab1, const TDF_Label& theLab2) + +ResultPtr Model_Document::resultByLab(const TDF_Label& theLab) { - return TDF_LabelMapHasher::IsEqual(theLab1, theLab2); + TDF_Label aCurrentLab = theLab; + while(aCurrentLab.Depth() > 3) { + ObjectPtr aResultObj = myObjs->object(aCurrentLab); + if (aResultObj.get()) { + return std::dynamic_pointer_cast(aResultObj); // this may be null if feature + } + aCurrentLab = aCurrentLab.Father(); + } + return ResultPtr(); // not found } 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 in spite 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); + } + // check the sketch vertex name located under renamed sketch line + TDF_ChildIDIterator aChild(theLabel, TDataStd_Name::GetID()); + for(; aChild.More(); aChild.Next()) { + Handle(TDataStd_Name) aSubName = Handle(TDataStd_Name)::DownCast(aChild.Value()); + std::string aName = TCollection_AsciiString(aSubName->Get()).ToCString(); + if (aName.find(theOldName) == 0) { // started from parent name + std::string aNewSubName = theNewName + aName.substr(theOldName.size()); + changeNamingName(aName, aNewSubName, aSubName->Label()); + aSubName->Set(aNewSubName.c_str()); + } + } + 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 variants, so, return the last } // not found exact name, try to find by sub-components std::string::size_type aSlash = theName.rfind('/'); @@ -1237,35 +1693,157 @@ 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(); - } - // 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); + // 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; + } + // copy aSubName to avoid incorrect further processing after its suffix cutting + TCollection_ExtendedString aSubNameCopy(aSubName); + // 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) { + if (aName->Get() == aSubNameCopy) 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 = aSubNameCopy.SearchFromEnd('_'); + if (aSuffixPos != -1 && aSuffixPos != aSubNameCopy.Length()) { + TCollection_ExtendedString anIndexStr = aSubNameCopy.Split(aSuffixPos); + aSubNameCopy.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() == aSubNameCopy) { + return aName->Label(); + } + } + // check also "this" label + Handle(TDataStd_Name) aName; + if (aLabIter->FindAttribute(TDataStd_Name::GetID(), aName)) { + if (aName->Get() == aSubNameCopy) { + return aName->Label(); + } } } } + // verify context's name is same as sub-component's and use context's label + if (aSubName.IsEqual(anObjName.c_str())) + return *(aFind->second.rbegin()); } } 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()) { + if (aRefFeat == theThis) + return false; // other references to this, so other later than this + //if (std::dynamic_pointer_cast(aRefFeat)) { + // if (!isLaterByDep(theThis, aRefFeat)) // nested composites: recursion + // return false; + //} + } + } + } + } + FeaturePtr aThisOwner = ModelAPI_Tools::compositeOwner(theThis); + if (aThisOwner.get()) { + if (aThisOwner == theOther) + return true; // composite owner is later that its sub + if (!isLaterByDep(aThisOwner, theOther)) + return false; + } + return myObjs->isLater(theThis, theOther); +} + +int Model_Document::numberOfNameInHistory( + const ObjectPtr& theNameObject, const TDF_Label& theStartFrom) { - return myObjs->findByName(theName); + 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) +{ + 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() @@ -1273,6 +1851,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) { @@ -1285,21 +1868,11 @@ void Model_Document::setActive(const bool theFlag) 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++) { + std::list aResults; + ModelAPI_Tools::allResults(aFeature, aResults); + for (std::list::iterator aRes = aResults.begin(); + aRes != aResults.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); - } - } - } } } } @@ -1326,11 +1899,6 @@ 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); -} TDF_Label Model_Document::extConstructionsLabel() const { @@ -1352,27 +1920,22 @@ 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 = std::dynamic_pointer_cast(ModelAPI_Session::get()->moduleDocument()).get(); if (aRoot == this) - return; // don't need to synchronise root with root + return; // don't need to synchronize root with root std::shared_ptr aSession = std::dynamic_pointer_cast(Model_Session::get()); - while(myRedos.size() > aRoot->myRedos.size()) { // remove redos in this + while(myRedos.size() > aRoot->myRedos.size()) { // remove redo 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 + while(myRedos.size() < aRoot->myRedos.size()) { // add more redo in this undoInternal(false, true); }*/ } @@ -1473,9 +2036,15 @@ std::shared_ptr Model_Document::producedByFeature( if (aShape.IsNull()) return FeaturePtr(); - // for comsolids and compounds all the naming is located in the main object, so, try to use + // for compsolids and compounds all the naming is located in the main object, so, try to use // it first - ResultCompSolidPtr aMain = ModelAPI_Tools::compSolidOwner(theResult); + ResultBodyPtr aMain = ModelAPI_Tools::bodyOwner(theResult); + while (aMain.get()) { // get the top-most main + ResultBodyPtr aNextMain = ModelAPI_Tools::bodyOwner(aMain); + if (aNextMain.get()) + aMain = aNextMain; + else break; + } if (aMain.get()) { FeaturePtr aMainRes = producedByFeature(aMain, theShape); if (aMainRes) @@ -1486,11 +2055,11 @@ std::shared_ptr Model_Document::producedByFeature( if (!aBodyData.get() || !aBodyData->isValid()) return FeaturePtr(); - TopoDS_Shape anOldShape; // old shape in the pair oldshape->theShape in the named shape + TopoDS_Shape anOldShape; // old shape in the pair old shape->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_Label aBodyLab = aBodyData->shapeLab(); + // use child 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)) { @@ -1570,6 +2139,8 @@ bool Model_Document::isLater(FeaturePtr theLater, FeaturePtr theCurrent) const return myObjs->isLater(theLater, theCurrent); } +// Object Browser nodes states +// LCOV_EXCL_START void Model_Document::storeNodesState(const std::list& theStates) { TDF_Label aLab = generalLabel().FindChild(TAG_NODES_STATE); @@ -1595,3 +2166,63 @@ void Model_Document::restoreNodesState(std::list& theStates) const } } } +// LCOV_EXCL_STOP + +void Model_Document::eraseAllFeatures() +{ + if (myObjs) + myObjs->eraseAllFeatures(); +} + +std::shared_ptr Model_Document::nextFeature( + std::shared_ptr theCurrent, const bool theReverse) const +{ + if (theCurrent.get() && myObjs) { + int anIndex = kUNDEFINED_FEATURE_INDEX; + return myObjs->nextFeature(theCurrent, anIndex, theReverse); + } + return FeaturePtr(); // nothing by default +} + +void Model_Document::setExecuteFeatures(const bool theFlag) +{ + myExecuteFeatures = theFlag; + const std::set aSubs = subDocuments(); + std::set::iterator aSubIter = aSubs.begin(); + for (; aSubIter != aSubs.end(); aSubIter++) { + if (!subDoc(*aSubIter)->myObjs) + continue; + subDoc(*aSubIter)->setExecuteFeatures(theFlag); + } +} + +void Model_Document::appendTransactionToPrevious() +{ + Transaction anAppended = myTransactions.back(); + myTransactions.pop_back(); + if (!myTransactions.empty()) { // if it is empty, just forget the appended + myTransactions.back().myOCAFNum += anAppended.myOCAFNum; + } + // propagate the same action to sub-documents + const std::set aSubs = subDocuments(); + for (std::set::iterator aSubIter = aSubs.begin(); aSubIter != aSubs.end(); aSubIter++) { + subDoc(*aSubIter)->appendTransactionToPrevious(); + } +} + +/// GUID for keeping information about the auto-recomputation state +static const Standard_GUID kAutoRecomputationID("8493fb74-0674-4912-a100-1cf46c7cfab3"); + +void Model_Document::setAutoRecomutationState(const bool theState) +{ + if (theState) + generalLabel().FindChild(TAG_CURRENT_TRANSACTION).ForgetAttribute(kAutoRecomputationID); + else + TDataStd_UAttribute::Set( + generalLabel().FindChild(TAG_CURRENT_TRANSACTION), kAutoRecomputationID); +} + +bool Model_Document::autoRecomutationState() const +{ + return !generalLabel().FindChild(TAG_CURRENT_TRANSACTION).IsAttribute(kAutoRecomputationID); +}