Salome HOME
Fix for random order of faces in selection name (groups on extrusion after aborting).
[modules/shaper.git] / src / Model / Model_Document.cpp
index 3dedf9e1a7b8e8580c58eb6941a18eb80f91daae..ec10e79632777634a6ca203b35b9d2f7edced0b1 100644 (file)
@@ -14,6 +14,7 @@
 #include <Model_ResultBody.h>
 #include <Model_ResultGroup.h>
 #include <ModelAPI_Validator.h>
+#include <ModelAPI_CompositeFeature.h>
 #include <Events_Loop.h>
 #include <Events_Error.h>
 
@@ -40,7 +41,7 @@
 # define _separator_ '/'
 #endif
 
-static const int UNDO_LIMIT = 10;  // number of possible undo operations
+static const int UNDO_LIMIT = 1000;  // number of possible undo operations (big for sketcher)
 
 static const int TAG_GENERAL = 1;  // general properties tag
 static const int TAG_OBJECTS = 2;  // tag of the objects sub-tree (features, results)
@@ -60,9 +61,7 @@ Model_Document::Model_Document(const std::string theID, const std::string theKin
       myDoc(new TDocStd_Document("BinOcaf"))  // binary OCAF format
 {
   myDoc->SetUndoLimit(UNDO_LIMIT);  
-  myTransactionsCounter = 0;
   myTransactionSave = 0;
-  myNestedNum = -1;
   myExecuteFeatures = true;
   // to have something in the document and avoid empty doc open/save problem
   // in transaction for nesting correct working
@@ -157,10 +156,14 @@ bool Model_Document::load(const char* theFileName)
   if (!isError) {
     myDoc->SetUndoLimit(UNDO_LIMIT);
     // to avoid the problem that feature is created in the current, not this, document
-    Model_Session::get()->setActiveDocument(anApp->getDocument(myID), false);
+    std::shared_ptr<Model_Session> aSession = 
+      std::dynamic_pointer_cast<Model_Session>(Model_Session::get());
+    aSession->setActiveDocument(anApp->getDocument(myID), false);
+    aSession->setCheckTransactions(false);
     synchronizeFeatures(false, true);
-    Model_Session::get()->setActiveDocument(Model_Session::get()->moduleDocument(), false);
-    Model_Session::get()->setActiveDocument(anApp->getDocument(myID), true);
+    aSession->setCheckTransactions(true);
+    aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false);
+    aSession->setActiveDocument(anApp->getDocument(myID), true);
   }
   return !isError;
 }
@@ -202,37 +205,32 @@ bool Model_Document::save(const char* theFileName, std::list<std::string>& theRe
         break;
     }
   }
-  myTransactionSave = myTransactionsCounter;
+  myTransactionSave = myTransactions.size();
   if (isDone) {  // save also sub-documents if any
     theResults.push_back(TCollection_AsciiString(aPath).ToCString());
-    std::set<std::string>::iterator aSubIter = mySubs.begin();
-    for (; aSubIter != mySubs.end() && isDone; aSubIter++) {
-      isDone = subDoc(*aSubIter)->save(theFileName, theResults);
-    }
-    if (isDone) { // also try to copy the not-activated sub-documents
-      // they are not in mySubs but as ResultParts
-      int aPartsNum = size(ModelAPI_ResultPart::group());
-      for(int aPart = 0; aPart < aPartsNum; aPart++) {
-        ResultPartPtr aPartRes = std::dynamic_pointer_cast<ModelAPI_ResultPart>
-          (object(ModelAPI_ResultPart::group(), aPart));
-        if (aPartRes) {
-          std::string aDocName = aPartRes->data()->name();
-          if (!aDocName.empty() && mySubs.find(aDocName) == mySubs.end()) {
-            // just copy file
-            TCollection_AsciiString aSubPath(DocFileName(anApp->loadPath().c_str(), aDocName));
-            OSD_Path aPath(aSubPath);
-            OSD_File aFile(aPath);
-            if (aFile.Exists()) {
-              TCollection_AsciiString aDestinationDir(DocFileName(theFileName, aDocName));
-              OSD_Path aDestination(aDestinationDir);
-              aFile.Copy(aDestination);
-              theResults.push_back(aDestinationDir.ToCString());
-            } else {
-              Events_Error::send(
-                std::string("Can not open file ") + aSubPath.ToCString() + " for saving");
-            }
+    const std::set<std::string> aSubs = subDocuments(false);
+    std::set<std::string>::iterator aSubIter = aSubs.begin();
+    for (; aSubIter != aSubs.end() && isDone; aSubIter++) {
+      if (anApp->isLoadByDemand(*aSubIter)) { 
+        // copy not-activated document that is not in the memory
+        std::string aDocName = *aSubIter;
+        if (!aDocName.empty()) {
+          // just copy file
+          TCollection_AsciiString aSubPath(DocFileName(anApp->loadPath().c_str(), aDocName));
+          OSD_Path aPath(aSubPath);
+          OSD_File aFile(aPath);
+          if (aFile.Exists()) {
+            TCollection_AsciiString aDestinationDir(DocFileName(theFileName, aDocName));
+            OSD_Path aDestination(aDestinationDir);
+            aFile.Copy(aDestination);
+            theResults.push_back(aDestinationDir.ToCString());
+          } else {
+            Events_Error::send(
+              std::string("Can not open file ") + aSubPath.ToCString() + " for saving");
           }
         }
+      } else { // simply save opened document
+        isDone = subDoc(*aSubIter)->save(theFileName, theResults);
       }
     }
   }
@@ -244,12 +242,15 @@ void Model_Document::close(const bool theForever)
   std::shared_ptr<ModelAPI_Session> aPM = Model_Session::get();
   if (this != aPM->moduleDocument().get() && this == aPM->activeDocument().get()) {
     aPM->setActiveDocument(aPM->moduleDocument());
+  } else if (this == aPM->moduleDocument().get()) {
+    // erase the active document if root is closed
+    aPM->setActiveDocument(DocumentPtr());
   }
   // close all subs
-  std::set<std::string>::iterator aSubIter = mySubs.begin();
-  for (; aSubIter != mySubs.end(); aSubIter++)
+  const std::set<std::string> aSubs = subDocuments(true);
+  std::set<std::string>::iterator aSubIter = aSubs.begin();
+  for (; aSubIter != aSubs.end(); aSubIter++)
     subDoc(*aSubIter)->close(theForever);
-  mySubs.clear();
 
   // close for thid document needs no transaction in this document
   std::static_pointer_cast<Model_Session>(Model_Session::get())->setCheckTransactions(false);
@@ -265,9 +266,15 @@ void Model_Document::close(const bool theForever)
     ModelAPI_EventCreator::get()->sendDeleted(aThis, ModelAPI_Feature::group());
     ModelAPI_EventCreator::get()->sendUpdated(aFeature, EVENT_DISP);
     aFeature->eraseResults();
-    aFeature->erase();
+    if (theForever) { // issue #294: do not delete content of the document until it can be redone
+      aFeature->erase();
+    } else {
+      aFeature->data()->execState(ModelAPI_StateMustBeUpdated);
+    }
+  }
+  if (theForever) {
+    myObjs.Clear();
   }
-  myObjs.Clear();
   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
 
@@ -283,51 +290,46 @@ void Model_Document::close(const bool theForever)
 void Model_Document::startOperation()
 {
   if (myDoc->HasOpenCommand()) {  // start of nested command
-    if (myNestedNum == -1) {
-      myNestedNum = 0;
-      myDoc->InitDeltaCompaction();
+    if (myDoc->CommitCommand()) { // commit the current: it will contain all nested after compactification
+      (*myTransactions.rbegin())++; // if has open command, the list is not empty
     }
-    myIsEmptyTr[myTransactionsCounter] = !myDoc->CommitCommand();
-    myTransactionsCounter++;
+    myNestedNum.push_back(0); // start of nested operation with zero transactions inside yet
     myDoc->OpenCommand();
   } else {  // start the simple command
     myDoc->NewCommand();
   }
+  // starts a new operation
+  myTransactions.push_back(0);
+  if (!myNestedNum.empty())
+    (*myNestedNum.rbegin())++;
+  myRedos.clear();
   // new command for all subs
-  std::set<std::string>::iterator aSubIter = mySubs.begin();
-  for (; aSubIter != mySubs.end(); aSubIter++)
+  const std::set<std::string> aSubs = subDocuments(true);
+  std::set<std::string>::iterator aSubIter = aSubs.begin();
+  for (; aSubIter != aSubs.end(); aSubIter++)
     subDoc(*aSubIter)->startOperation();
 }
 
-bool Model_Document::compactNested()
+void Model_Document::compactNested()
 {
-  bool allWasEmpty = true;
-  while (myNestedNum != -1) {
-    myTransactionsCounter--;
-    if (!myIsEmptyTr[myTransactionsCounter]) {
-      allWasEmpty = false;
+  if (!myNestedNum.empty()) {
+    int aNumToCompact = *(myNestedNum.rbegin());
+    int aSumOfTransaction = 0;
+    for(int a = 0; a < aNumToCompact; a++) {
+      aSumOfTransaction += *(myTransactions.rbegin());
+      myTransactions.pop_back();
     }
-    myIsEmptyTr.erase(myTransactionsCounter);
-    myNestedNum--;
-  }
-  myIsEmptyTr[myTransactionsCounter] = allWasEmpty;
-  myTransactionsCounter++;
-  if (allWasEmpty) {
-    // Issue 151: if everything is empty, it is a problem for OCCT to work with it, 
-    // just commit the empty that returns nothing
-    myDoc->CommitCommand();
-  } else {
-    myDoc->PerformDeltaCompaction();
+    // the latest transaction is the start of lower-level operation which startes the nested
+    *(myTransactions.rbegin()) += aSumOfTransaction;
+    myNestedNum.pop_back();
   }
-  return !allWasEmpty;
 }
 
-void Model_Document::finishOperation()
+bool Model_Document::finishOperation()
 {
-  // just to be sure that everybody knows that changes were performed
-  if (!myDoc->HasOpenCommand() && myNestedNum != -1)
-    std::static_pointer_cast<Model_Session>(Model_Session::get())
-        ->setCheckTransactions(false);  // for nested transaction commit
+  bool isNestedClosed = !myDoc->HasOpenCommand() && !myNestedNum.empty();
+  static std::shared_ptr<Model_Session> aSession = 
+    std::static_pointer_cast<Model_Session>(Model_Session::get());
   synchronizeBackRefs();
   Events_Loop* aLoop = Events_Loop::loop();
   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED));
@@ -348,48 +350,58 @@ void Model_Document::finishOperation()
   // to avoid "updated" message appearance by updater
   //aLoop->clear(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
 
-  if (!myDoc->HasOpenCommand() && myNestedNum != -1)
-    std::static_pointer_cast<Model_Session>(Model_Session::get())
-        ->setCheckTransactions(true);  // for nested transaction commit
-
   // finish for all subs first: to avoid nested finishing and "isOperation" calls problems inside
-  std::set<std::string>::iterator aSubIter = mySubs.begin();
-  for (; aSubIter != mySubs.end(); aSubIter++)
-    subDoc(*aSubIter)->finishOperation();
-
-  if (myNestedNum != -1)  // this nested transaction is owervritten
-    myNestedNum++;
-  if (!myDoc->HasOpenCommand()) {
-    if (myNestedNum != -1) {
-      myNestedNum--;
-      compactNested();
-    }
-  } else {
-    // returns false if delta is empty and no transaction was made
-    myIsEmptyTr[myTransactionsCounter] = !myDoc->CommitCommand();  // && (myNestedNum == -1);
-    myTransactionsCounter++;
+  bool aResult = false;
+  const std::set<std::string> aSubs = subDocuments(true);
+  std::set<std::string>::iterator aSubIter = aSubs.begin();
+  for (; aSubIter != aSubs.end(); aSubIter++)
+    if (subDoc(*aSubIter)->finishOperation())
+      aResult = true;
+
+  // transaction may be empty if this document was created during this transaction (create part)
+  if (!myTransactions.empty() && myDoc->CommitCommand()) { // if commit is successfull, just increment counters
+    (*myTransactions.rbegin())++;
+    aResult = true;
   }
+
+  if (isNestedClosed) {
+    compactNested();
+  }
+  if (!aResult && !myTransactions.empty() /* it can be for just created part document */)
+    aResult = *(myTransactions.rbegin()) != 0;
+
+  if (!aResult && Model_Session::get()->moduleDocument().get() == this) {
+    // nothing inside in all documents, so remove this transaction from the transactions list
+    undoInternal(true, false);
+    myDoc->ClearRedos();
+    myRedos.clear();
+  }
+  return aResult;
 }
 
 void Model_Document::abortOperation()
 {
-  if (myNestedNum > 0 && !myDoc->HasOpenCommand()) {  // abort all what was done in nested
-      // first compact all nested
-    if (compactNested()) {
-      myDoc->Undo(); // undo only compacted, if not: do not undo the empty transaction
-    }
+  if (!myNestedNum.empty() && !myDoc->HasOpenCommand()) {  // abort all what was done in nested
+    compactNested();
+    undoInternal(false, false);
     myDoc->ClearRedos();
-    myTransactionsCounter--;
-    myIsEmptyTr.erase(myTransactionsCounter);
-  } else {
-    if (myNestedNum == 0)  // abort only high-level
-      myNestedNum = -1;
+    myRedos.clear();
+  } else { // abort the current
+    int aNumTransactions = *myTransactions.rbegin();
+    myTransactions.pop_back();
+    if (!myNestedNum.empty())
+      (*myNestedNum.rbegin())--;
+    // roll back the needed number of transactions
     myDoc->AbortCommand();
+    for(int a = 0; a < aNumTransactions; a++)
+      myDoc->Undo();
+    myDoc->ClearRedos();
   }
   synchronizeFeatures(true, false); // references were not changed since transaction start
   // abort for all subs
-  std::set<std::string>::iterator aSubIter = mySubs.begin();
-  for (; aSubIter != mySubs.end(); aSubIter++)
+  const std::set<std::string> aSubs = subDocuments(true);
+  std::set<std::string>::iterator aSubIter = aSubs.begin();
+  for (; aSubIter != aSubs.end(); aSubIter++)
     subDoc(*aSubIter)->abortOperation();
 }
 
@@ -402,34 +414,48 @@ bool Model_Document::isOperation()
 bool Model_Document::isModified()
 {
   // is modified if at least one operation was commited and not undoed
-  return myTransactionsCounter != myTransactionSave || isOperation();
+  return myTransactions.size() != myTransactionSave || isOperation();
 }
 
 bool Model_Document::canUndo()
 {
-  if (myDoc->GetAvailableUndos() > 0 && myNestedNum != 0
-      && myTransactionsCounter != 0 /* for omitting the first useless transaction */)
+  if (myDoc->GetAvailableUndos() > 0 && (myNestedNum.empty() || *myNestedNum.rbegin() != 0) &&
+      !myTransactions.empty() /* for omitting the first useless transaction */)
     return true;
   // check other subs contains operation that can be undoed
-  std::set<std::string>::iterator aSubIter = mySubs.begin();
-  for (; aSubIter != mySubs.end(); aSubIter++)
+  const std::set<std::string> aSubs = subDocuments(true);
+  std::set<std::string>::iterator aSubIter = aSubs.begin();
+  for (; aSubIter != aSubs.end(); aSubIter++)
     if (subDoc(*aSubIter)->canUndo())
       return true;
   return false;
 }
 
-void Model_Document::undo()
+void Model_Document::undoInternal(const bool theWithSubs, const bool theSynchronize)
 {
-  myTransactionsCounter--;
-  if (myNestedNum > 0)
-    myNestedNum--;
-  if (!myIsEmptyTr[myTransactionsCounter])
+  int aNumTransactions = *myTransactions.rbegin();
+  myTransactions.pop_back();
+  myRedos.push_back(aNumTransactions);
+  if (!myNestedNum.empty())
+    (*myNestedNum.rbegin())--;
+  // roll back the needed number of transactions
+  for(int a = 0; a < aNumTransactions; a++)
     myDoc->Undo();
-  synchronizeFeatures(true, true);
-  // undo for all subs
-  std::set<std::string>::iterator aSubIter = mySubs.begin();
-  for (; aSubIter != mySubs.end(); aSubIter++)
-    subDoc(*aSubIter)->undo();
+
+  if (theSynchronize)
+    synchronizeFeatures(true, true);
+  if (theWithSubs) {
+    // undo for all subs
+    const std::set<std::string> aSubs = subDocuments(true);
+    std::set<std::string>::iterator aSubIter = aSubs.begin();
+    for (; aSubIter != aSubs.end(); aSubIter++)
+      subDoc(*aSubIter)->undoInternal(theWithSubs, theSynchronize);
+  }
+}
+
+void Model_Document::undo()
+{
+  undoInternal(true, true);
 }
 
 bool Model_Document::canRedo()
@@ -437,8 +463,9 @@ bool Model_Document::canRedo()
   if (myDoc->GetAvailableRedos() > 0)
     return true;
   // check other subs contains operation that can be redoed
-  std::set<std::string>::iterator aSubIter = mySubs.begin();
-  for (; aSubIter != mySubs.end(); aSubIter++)
+  const std::set<std::string> aSubs = subDocuments(true);
+  std::set<std::string>::iterator aSubIter = aSubs.begin();
+  for (; aSubIter != aSubs.end(); aSubIter++)
     if (subDoc(*aSubIter)->canRedo())
       return true;
   return false;
@@ -446,19 +473,23 @@ bool Model_Document::canRedo()
 
 void Model_Document::redo()
 {
-  if (myNestedNum != -1)
-    myNestedNum++;
-  if (!myIsEmptyTr[myTransactionsCounter])
+  if (!myNestedNum.empty())
+    (*myNestedNum.rbegin())++;
+  int aNumRedos = *myRedos.rbegin();
+  myRedos.pop_back();
+  myTransactions.push_back(aNumRedos);
+  for(int a = 0; a < aNumRedos; a++)
     myDoc->Redo();
-  myTransactionsCounter++;
+
   synchronizeFeatures(true, true);
   // redo for all subs
-  std::set<std::string>::iterator aSubIter = mySubs.begin();
-  for (; aSubIter != mySubs.end(); aSubIter++)
+  const std::set<std::string> aSubs = subDocuments(true);
+  std::set<std::string>::iterator aSubIter = aSubs.begin();
+  for (; aSubIter != aSubs.end(); aSubIter++)
     subDoc(*aSubIter)->redo();
 }
 
-/// Appenad to the array of references a new referenced label
+/// Append to the array of references a new referenced label
 static void AddToRefArray(TDF_Label& theArrayLab, TDF_Label& theReferenced)
 {
   Handle(TDataStd_ReferenceArray) aRefs;
@@ -579,7 +610,7 @@ void Model_Document::removeFeature(FeaturePtr theFeature, const bool theCheck)
   ModelAPI_EventCreator::get()->sendDeleted(theFeature->document(), ModelAPI_Feature::group());
 }
 
-FeaturePtr Model_Document::feature(TDF_Label& theLabel)
+FeaturePtr Model_Document::feature(TDF_Label& theLabel) const
 {
   if (myObjs.IsBound(theLabel))
     return myObjs.Find(theLabel);
@@ -609,17 +640,37 @@ ObjectPtr Model_Document::object(TDF_Label theLabel)
 
 std::shared_ptr<ModelAPI_Document> Model_Document::subDocument(std::string theDocID)
 {
-  // just store sub-document identifier here to manage it later
-  if (mySubs.find(theDocID) == mySubs.end())
-    mySubs.insert(theDocID);
   return Model_Application::getApplication()->getDocument(theDocID);
 }
 
+const std::set<std::string> Model_Document::subDocuments(const bool theActivatedOnly) const
+{
+  std::set<std::string> aResult;
+  // comment must be in any feature: it is kind
+  int anIndex = 0;
+  TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID());
+  for (; aLabIter.More(); aLabIter.Next()) {
+    TDF_Label aFLabel = aLabIter.Value()->Label();
+    FeaturePtr aFeature = feature(aFLabel);
+    if (aFeature.get()) { // if document is closed the feature may be not in myObjs map
+      const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
+      std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
+      for (; aRIter != aResults.cend(); aRIter++) {
+        if ((*aRIter)->groupName() != ModelAPI_ResultPart::group()) continue;
+        if ((*aRIter)->isInHistory()) {
+          ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aRIter);
+          if (aPart && (!theActivatedOnly || aPart->isActivated()))
+            aResult.insert(aPart->data()->name());
+        }
+      }
+    }
+  }
+  return aResult;
+}
+
 std::shared_ptr<Model_Document> Model_Document::subDoc(std::string theDocID)
 {
   // just store sub-document identifier here to manage it later
-  if (mySubs.find(theDocID) == mySubs.end())
-    mySubs.insert(theDocID);
   return std::dynamic_pointer_cast<Model_Document>(
     Model_Application::getApplication()->getDocument(theDocID));
 }
@@ -710,7 +761,7 @@ int Model_Document::size(const std::string& theGroupID, const bool theHidden)
   return aResult;
 }
 
-TDF_Label Model_Document::featuresLabel()
+TDF_Label Model_Document::featuresLabel() const
 {
   return myDoc->Main().FindChild(TAG_OBJECTS);
 }
@@ -776,8 +827,6 @@ void Model_Document::synchronizeFeatures(const bool theMarkUpdated, const bool t
   std::shared_ptr<ModelAPI_Document> aThis = 
     Model_Application::getApplication()->getDocument(myID);
   // after all updates, sends a message that groups of features were created or updated
-  std::static_pointer_cast<Model_Session>(Model_Session::get())
-    ->setCheckTransactions(false);
   Events_Loop* aLoop = Events_Loop::loop();
   aLoop->activateFlushes(false);
 
@@ -815,14 +864,21 @@ void Model_Document::synchronizeFeatures(const bool theMarkUpdated, const bool t
     }
   }
   // update results of thefeatures (after features created because they may be connected, like sketch and sub elements)
+  std::list<FeaturePtr> aComposites; // composites must be updated after their subs (issue 360)
   TDF_ChildIDIterator aLabIter2(featuresLabel(), TDataStd_Comment::GetID());
   for (; aLabIter2.More(); aLabIter2.Next()) {
     TDF_Label aFeatureLabel = aLabIter2.Value()->Label();
     if (myObjs.IsBound(aFeatureLabel)) {  // a new feature is inserted
       FeaturePtr aFeature = myObjs.Find(aFeatureLabel);
+      if (std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aFeature).get())
+        aComposites.push_back(aFeature);
       updateResults(aFeature);
     }
   }
+  std::list<FeaturePtr>::iterator aComposite = aComposites.begin();
+  for(; aComposite != aComposites.end(); aComposite++) {
+    updateResults(*aComposite);
+  }
 
   // check all features are checked: if not => it was removed
   NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator aFIter(myObjs);
@@ -842,9 +898,9 @@ void Model_Document::synchronizeFeatures(const bool theMarkUpdated, const bool t
       ModelAPI_EventCreator::get()->sendUpdated(aFeature, EVENT_DISP);
       aFeature->erase();
       // unbind after the "erase" call: on abort sketch is removes sub-objects that corrupts aFIter
-      TDF_Label aLab = aFIter.Key();
-      aFIter.Next();
-      myObjs.UnBind(aLab);
+      myObjs.UnBind(aFIter.Key());
+      // reinitialize iterator because unbind may corrupt the previous order in the map
+      aFIter.Initialize(myObjs);
     } else
       aFIter.Next();
   }
@@ -861,8 +917,6 @@ void Model_Document::synchronizeFeatures(const bool theMarkUpdated, const bool t
   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TOHIDE));
-  std::static_pointer_cast<Model_Session>(Model_Session::get())
-    ->setCheckTransactions(true);
   myExecuteFeatures = true;
 }
 
@@ -1108,3 +1162,23 @@ TDF_Label Model_Document::findNamingName(std::string theName)
     return TDF_Label(); // not found
   return aFind->second;
 }
+
+ResultPtr Model_Document::findByName(const std::string theName)
+{
+  NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator anObjIter(myObjs);
+  for(; anObjIter.More(); anObjIter.Next()) {
+    FeaturePtr& aFeature = anObjIter.ChangeValue();
+    if (!aFeature) // may be on close
+      continue;
+    const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
+    std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
+    for (; aRIter != aResults.cend(); aRIter++) {
+      if (aRIter->get() && (*aRIter)->data() && (*aRIter)->data()->isValid() &&
+          (*aRIter)->data()->name() == theName) {
+        return *aRIter;
+      }
+    }
+  }
+  // not found
+  return ResultPtr();
+}