Salome HOME
Fix for the issue #370 : activization of updater only once per undo/redo for all...
[modules/shaper.git] / src / Model / Model_Document.cpp
index 15983188d822ba65cc920976f75a969a671d04f4..ee8cebc14c9d0274a1dd486a1638fca540450c17 100644 (file)
@@ -1,3 +1,5 @@
+// Copyright (C) 2014-20xx CEA/DEN, EDF R&D
+
 // File:        Model_Document.cxx
 // Created:     28 Feb 2014
 // Author:      Mikhail PONIKAROV
@@ -12,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>
 
@@ -24,6 +27,8 @@
 #include <TDF_Reference.hxx>
 #include <TDF_ChildIDIterator.hxx>
 #include <TDF_LabelMapHasher.hxx>
+#include <OSD_File.hxx>
+#include <OSD_Path.hxx>
 
 #include <climits>
 #ifndef WIN32
@@ -36,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)
@@ -46,13 +51,17 @@ static const int TAG_HISTORY = 3;  // tag of the history sub-tree (python dump)
 static const int TAG_FEATURE_ARGUMENTS = 1;  ///< where the arguments are located
 static const int TAG_FEATURE_RESULTS = 2;  ///< where the results are located
 
+///
+/// 0:1:2 - where features are located
+/// 0:1:2:N:1 - data of the feature N
+/// 0:1:2:N:2:K:1 - data of the K result of the feature N
+
 Model_Document::Model_Document(const std::string theID, const std::string theKind)
     : myID(theID), myKind(theKind),
       myDoc(new TDocStd_Document("BinOcaf"))  // binary OCAF format
 {
   myDoc->SetUndoLimit(UNDO_LIMIT);  
-  myTransactionsAfterSave = 0;
-  myNestedNum = -1;
+  myTransactionSave = 0;
   myExecuteFeatures = true;
   // to have something in the document and avoid empty doc open/save problem
   // in transaction for nesting correct working
@@ -66,7 +75,8 @@ static TCollection_ExtendedString DocFileName(const char* theFileName, const std
 {
   TCollection_ExtendedString aPath((const Standard_CString) theFileName);
   // remove end-separators
-  while(aPath.Length() && (aPath.Value(aPath.Length()) == '\\' || aPath.Value(aPath.Length()) == '/'))
+  while(aPath.Length() && 
+        (aPath.Value(aPath.Length()) == '\\' || aPath.Value(aPath.Length()) == '/'))
     aPath.Remove(aPath.Length());
   aPath += _separator_;
   aPath += theID.c_str();
@@ -74,10 +84,15 @@ static TCollection_ExtendedString DocFileName(const char* theFileName, const std
   return aPath;
 }
 
+bool Model_Document::isRoot() const
+{
+  return this == Model_Session::get()->moduleDocument().get();
+}
+
 bool Model_Document::load(const char* theFileName)
 {
   Handle(Model_Application) anApp = Model_Application::getApplication();
-  if (this == Model_Session::get()->moduleDocument().get()) {
+  if (isRoot()) {
     anApp->setLoadPath(theFileName);
   }
   TCollection_ExtendedString aPath(DocFileName(theFileName, myID));
@@ -94,7 +109,7 @@ bool Model_Document::load(const char* theFileName)
   if (isError) {
     switch (aStatus) {
       case PCDM_RS_UnknownDocument:
-        Events_Error::send(std::string("Can not open document: unknown format"));
+        Events_Error::send(std::string("Can not open document"));
         break;
       case PCDM_RS_AlreadyRetrieved:
         Events_Error::send(std::string("Can not open document: already opened"));
@@ -147,10 +162,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);
-    synchronizeFeatures(false, true);
-    Model_Session::get()->setActiveDocument(Model_Session::get()->moduleDocument(), false);
-    Model_Session::get()->setActiveDocument(anApp->getDocument(myID), true);
+    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, true);
+    aSession->setCheckTransactions(true);
+    aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false);
+    aSession->setActiveDocument(anApp->getDocument(myID), true);
   }
   return !isError;
 }
@@ -158,7 +177,8 @@ bool Model_Document::load(const char* theFileName)
 bool Model_Document::save(const char* theFileName, std::list<std::string>& theResults)
 {
   // create a directory in the root document if it is not yet exist
-  if (this == Model_Session::get()->moduleDocument().get()) {
+  Handle(Model_Application) anApp = Model_Application::getApplication();
+  if (isRoot()) {
 #ifdef WIN32
     CreateDirectory(theFileName, NULL);
 #else
@@ -169,7 +189,7 @@ bool Model_Document::save(const char* theFileName, std::list<std::string>& theRe
   TCollection_ExtendedString aPath(DocFileName(theFileName, myID));
   PCDM_StoreStatus aStatus;
   try {
-    aStatus = Model_Application::getApplication()->SaveAs(myDoc, aPath);
+    aStatus = anApp->SaveAs(myDoc, aPath);
   } catch (Standard_Failure) {
     Handle(Standard_Failure) aFail = Standard_Failure::Caught();
     Events_Error::send(
@@ -191,78 +211,131 @@ bool Model_Document::save(const char* theFileName, std::list<std::string>& theRe
         break;
     }
   }
-  myTransactionsAfterSave = 0;
+  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);
+    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);
+      }
     }
   }
   return isDone;
 }
 
-void Model_Document::close()
+void Model_Document::close(const bool theForever)
 {
-  boost::shared_ptr<ModelAPI_Session> aPM = Model_Session::get();
-  if (this != aPM->moduleDocument().get() && this == aPM->activeDocument().get()) {
+  std::shared_ptr<ModelAPI_Session> aPM = Model_Session::get();
+  if (!isRoot() && this == aPM->activeDocument().get()) {
     aPM->setActiveDocument(aPM->moduleDocument());
+  } else if (isRoot()) {
+    // 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++)
-    subDoc(*aSubIter)->close();
-  mySubs.clear();
-  // close this only if it is module document, otherwise it can be undoed
-  if (this == aPM->moduleDocument().get()) {
+  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);
+
+  // close for thid document needs no transaction in this document
+  std::static_pointer_cast<Model_Session>(Model_Session::get())->setCheckTransactions(false);
+
+  // delete all features of this document
+  std::shared_ptr<ModelAPI_Document> aThis = 
+    Model_Application::getApplication()->getDocument(myID);
+  Events_Loop* aLoop = Events_Loop::loop();
+  NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator aFeaturesIter(myObjs);
+  for(; aFeaturesIter.More(); aFeaturesIter.Next()) {
+    FeaturePtr aFeature = aFeaturesIter.Value();
+    static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
+    ModelAPI_EventCreator::get()->sendDeleted(aThis, ModelAPI_Feature::group());
+    ModelAPI_EventCreator::get()->sendUpdated(aFeature, EVENT_DISP);
+    aFeature->eraseResults();
+    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();
+  }
+  aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
+  aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
+
+  // close all only if it is really asked, otherwise it can be undoed/redoed
+  if (theForever) {
     if (myDoc->CanClose() == CDM_CCS_OK)
       myDoc->Close();
-    Model_Application::getApplication()->deleteDocument(myID);
   }
+
+  std::static_pointer_cast<Model_Session>(Model_Session::get())->setCheckTransactions(true);
 }
 
 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()->myOCAFNum++; // if has open command, the list is not empty
     }
-    myIsEmptyTr[myTransactionsAfterSave] = !myDoc->CommitCommand();
-    myTransactionsAfterSave++;
+    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(Transaction());
+  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) {
-    myTransactionsAfterSave--;
-    if (!myIsEmptyTr[myTransactionsAfterSave]) {
-      allWasEmpty = false;
+  if (!myNestedNum.empty()) {
+    int aNumToCompact = *(myNestedNum.rbegin());
+    int aSumOfTransaction = 0;
+    for(int a = 0; a < aNumToCompact; a++) {
+      aSumOfTransaction += myTransactions.rbegin()->myOCAFNum;
+      myTransactions.pop_back();
     }
-    myIsEmptyTr.erase(myTransactionsAfterSave);
-    myNestedNum--;
+    // the latest transaction is the start of lower-level operation which startes the nested
+    myTransactions.rbegin()->myOCAFNum += aSumOfTransaction;
+    myNestedNum.pop_back();
   }
-  myIsEmptyTr[myTransactionsAfterSave] = allWasEmpty;
-  myTransactionsAfterSave++;
-  myDoc->PerformDeltaCompaction();
-  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)
-    boost::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));
@@ -273,9 +346,9 @@ void Model_Document::finishOperation()
   // 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 (Model_Session::get()->moduleDocument().get() == this) { // once for root document
+  if (isRoot()) { // once for root document
     Events_Loop::loop()->autoFlush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
-    static boost::shared_ptr<Events_Message> aFinishMsg
+    static std::shared_ptr<Events_Message> aFinishMsg
       (new Events_Message(Events_Loop::eventByName("FinishOperation")));
     Events_Loop::loop()->send(aFinishMsg);
     Events_Loop::loop()->autoFlush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED), false);
@@ -283,53 +356,63 @@ void Model_Document::finishOperation()
   // to avoid "updated" message appearance by updater
   //aLoop->clear(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
 
-  if (!myDoc->HasOpenCommand() && myNestedNum != -1)
-    boost::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();
+  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;
 
-  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[myTransactionsAfterSave] = !myDoc->CommitCommand();  // && (myNestedNum == -1);
-    myTransactionsAfterSave++;
+  // transaction may be empty if this document was created during this transaction (create part)
+  if (!myTransactions.empty() && myDoc->CommitCommand()) { // if commit is successfull, just increment counters
+    myTransactions.rbegin()->myOCAFNum++;
+    aResult = true;
+  }
+
+  if (isNestedClosed) {
+    compactNested();
   }
+  if (!aResult && !myTransactions.empty() /* it can be for just created part document */)
+    aResult = myTransactions.rbegin()->myOCAFNum != 0;
+
+  if (!aResult && isRoot()) {
+    // 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()) {
-      // for nested it is undo and clear redos
-      myDoc->Undo();
-    }
+  if (!myNestedNum.empty() && !myDoc->HasOpenCommand()) {  // abort all what was done in nested
+    compactNested();
+    undoInternal(false, false);
     myDoc->ClearRedos();
-    myTransactionsAfterSave--;
-    myIsEmptyTr.erase(myTransactionsAfterSave);
-  } else {
-    if (myNestedNum == 0)  // abort only high-level
-      myNestedNum = -1;
+    myRedos.clear();
+  } else { // abort the current
+    int aNumTransactions = myTransactions.rbegin()->myOCAFNum;
+    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
+  // references were not changed since transaction start
+  synchronizeFeatures(true, false, isRoot());
   // 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();
 }
 
-bool Model_Document::isOperation()
+bool Model_Document::isOperation() const
 {
   // operation is opened for all documents: no need to check subs
   return myDoc->HasOpenCommand() == Standard_True ;
@@ -338,43 +421,62 @@ bool Model_Document::isOperation()
 bool Model_Document::isModified()
 {
   // is modified if at least one operation was commited and not undoed
-  return myTransactionsAfterSave > 0 || isOperation();
+  return myTransactions.size() != myTransactionSave || isOperation();
 }
 
 bool Model_Document::canUndo()
 {
-  if (myDoc->GetAvailableUndos() > 0 && myNestedNum != 0
-      && myTransactionsAfterSave != 0 /* for omitting the first useless transaction */)
+  // 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
+      myTransactions.size() - aCurrentNum > 0 /* 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)
 {
-  myTransactionsAfterSave--;
-  if (myNestedNum > 0)
-    myNestedNum--;
-  if (!myIsEmptyTr[myTransactionsAfterSave])
+  int aNumTransactions = myTransactions.rbegin()->myOCAFNum;
+  myRedos.push_back(*myTransactions.rbegin());
+  myTransactions.pop_back();
+  if (!myNestedNum.empty())
+    (*myNestedNum.rbegin())--;
+  // roll back the needed number of transactions
+  for(int a = 0; a < aNumTransactions; a++)
     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 (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);
+  }
+  // after redo of all sub-documents to avoid updates on not-modified data (issue 370)
+  if (theSynchronize)
+    synchronizeFeatures(true, true, isRoot());
+}
+
+void Model_Document::undo()
+{
+  undoInternal(true, true);
 }
 
 bool Model_Document::canRedo()
 {
-  if (myDoc->GetAvailableRedos() > 0)
+  if (!myRedos.empty())
     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;
@@ -382,19 +484,56 @@ bool Model_Document::canRedo()
 
 void Model_Document::redo()
 {
-  if (myNestedNum != -1)
-    myNestedNum++;
-  if (!myIsEmptyTr[myTransactionsAfterSave])
+  if (!myNestedNum.empty())
+    (*myNestedNum.rbegin())++;
+  int aNumRedos = myRedos.rbegin()->myOCAFNum;
+  myTransactions.push_back(*myRedos.rbegin());
+  myRedos.pop_back();
+  for(int a = 0; a < aNumRedos; a++)
     myDoc->Redo();
-  myTransactionsAfterSave++;
-  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();
+
+  // after redo of all sub-documents to avoid updates on not-modified data (issue 370)
+  synchronizeFeatures(true, true, isRoot());
+}
+
+std::list<std::string> Model_Document::undoList() const
+{
+  std::list<std::string> aResult;
+  // the number of skipped current operations (on undo they will be aborted)
+  int aSkipCurrent = isOperation() ? 1 : 0;
+  std::list<Transaction>::const_reverse_iterator aTrIter = myTransactions.crbegin();
+  int aNumUndo = myTransactions.size();
+  if (!myNestedNum.empty())
+    aNumUndo = *myNestedNum.rbegin();
+  for( ; aNumUndo > 0; aTrIter++, aNumUndo--) {
+    if (aSkipCurrent == 0) aResult.push_back(aTrIter->myId);
+    else aSkipCurrent--;
+  }
+  return aResult;
+}
+
+std::list<std::string> Model_Document::redoList() const
+{
+  std::list<std::string> aResult;
+  std::list<Transaction>::const_reverse_iterator aTrIter = myRedos.crbegin();
+  for( ; aTrIter != myRedos.crend(); aTrIter++) {
+    aResult.push_back(aTrIter->myId);
+  }
+  return aResult;
 }
 
-/// Appenad to the array of references a new referenced label
+void Model_Document::operationId(const std::string& theId)
+{
+  myTransactions.rbegin()->myId = theId;
+}
+
+/// Append to the array of references a new referenced label
 static void AddToRefArray(TDF_Label& theArrayLab, TDF_Label& theReferenced)
 {
   Handle(TDataStd_ReferenceArray) aRefs;
@@ -419,8 +558,12 @@ FeaturePtr Model_Document::addFeature(std::string theID)
   FeaturePtr aFeature = ModelAPI_Session::get()->createFeature(theID);
   if (!aFeature)
     return aFeature;
-  boost::shared_ptr<Model_Document> aDocToAdd = boost::dynamic_pointer_cast<Model_Document>(
-      aFeature->documentToAdd());
+  Model_Document* aDocToAdd;
+  if (aFeature->documentToAdd().get()) { // use the customized document to add
+    aDocToAdd = std::dynamic_pointer_cast<Model_Document>(aFeature->documentToAdd()).get();
+  } else { // if customized is not presented, add to "this" document
+    aDocToAdd = this;
+  }
   if (aFeature) {
     TDF_Label aFeatureLab;
     if (!aFeature->isAction()) {  // do not add action to the data model
@@ -448,10 +591,10 @@ FeaturePtr Model_Document::addFeature(std::string theID)
 }
 
 /// Appenad to the array of references a new referenced label.
-/// If theIndex is not -1, removes element at thisindex, not theReferenced.
+/// If theIndex is not -1, removes element at this index, not theReferenced.
 /// \returns the index of removed element
-static int RemoveFromRefArray(TDF_Label theArrayLab, TDF_Label theReferenced, const int theIndex =
-                                  -1)
+static int RemoveFromRefArray(TDF_Label theArrayLab, TDF_Label theReferenced, 
+  const int theIndex = -1)
 {
   int aResult = -1;  // no returned
   Handle(TDataStd_ReferenceArray) aRefs;
@@ -479,41 +622,100 @@ static int RemoveFromRefArray(TDF_Label theArrayLab, TDF_Label theReferenced, co
   return aResult;
 }
 
-void Model_Document::removeFeature(FeaturePtr theFeature, const bool theCheck)
+void Model_Document::refsToFeature(FeaturePtr theFeature,
+                                   std::set<std::shared_ptr<ModelAPI_Feature> >& theRefs,
+                                   const bool isSendError)
 {
-  if (theCheck) {
-    // check the feature: it must have no depended objects on it
-    std::list<ResultPtr>::const_iterator aResIter = theFeature->results().cbegin();
-    for(; aResIter != theFeature->results().cend(); aResIter++) {
-      boost::shared_ptr<Model_Data> aData = 
-        boost::dynamic_pointer_cast<Model_Data>((*aResIter)->data());
-      if (aData && !aData->refsToMe().empty()) {
-        Events_Error::send(
-          "Feature '" + theFeature->data()->name() + "' is used and can not be deleted");
-        return;
+  // check the feature: it must have no depended objects on it
+  // the dependencies can be in the feature results
+  std::list<ResultPtr>::const_iterator aResIter = theFeature->results().cbegin();
+  for(; aResIter != theFeature->results().cend(); aResIter++) {
+    ResultPtr aResult = (*aResIter);
+    std::shared_ptr<Model_Data> aData = 
+      std::dynamic_pointer_cast<Model_Data>(aResult->data());
+    if (aData.get() != NULL) {
+      const std::set<AttributePtr>& aRefs = aData->refsToMe();
+      std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin(), aRefLast = aRefs.end();
+      for(; aRefIt != aRefLast; aRefIt++) {
+        FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRefIt)->owner());
+        if (aFeature.get() != NULL)
+          theRefs.insert(aFeature);
       }
     }
   }
+  // the dependencies can be in the feature itself
+  std::shared_ptr<Model_Data> aData = 
+      std::dynamic_pointer_cast<Model_Data>(theFeature->data());
+  if (aData && !aData->refsToMe().empty()) {
+    const std::set<AttributePtr>& aRefs = aData->refsToMe();
+    std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin(), aRefLast = aRefs.end();
+    for(; aRefIt != aRefLast; aRefIt++) {
+      FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRefIt)->owner());
+      if (aFeature.get() != NULL)
+        theRefs.insert(aFeature);
+    }
+  }
 
-  boost::shared_ptr<Model_Data> aData = boost::static_pointer_cast<Model_Data>(theFeature->data());
+  if (!theRefs.empty() && isSendError) {
+    Events_Error::send(
+      "Feature '" + theFeature->data()->name() + "' is used and can not be deleted");
+  }
+}
+
+void Model_Document::removeFeature(FeaturePtr theFeature/*, const bool theCheck*/)
+{
+  std::shared_ptr<Model_Data> aData = std::static_pointer_cast<Model_Data>(theFeature->data());
+  if (aData) {
+    TDF_Label aFeatureLabel = aData->label().Father();
+    if (myObjs.IsBound(aFeatureLabel))
+      myObjs.UnBind(aFeatureLabel);
+    else
+      return;  // not found feature => do not remove
+
+    // checking that the sub-element of composite feature is removed: if yes, inform the owner
+    std::set<std::shared_ptr<ModelAPI_Feature> > aRefs;
+    refsToFeature(theFeature, aRefs, false);
+    std::set<std::shared_ptr<ModelAPI_Feature> >::iterator aRefIter = aRefs.begin();
+    for(; aRefIter != aRefs.end(); aRefIter++) {
+      std::shared_ptr<ModelAPI_CompositeFeature> aComposite = 
+        std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aRefIter);
+      if (aComposite.get()) {
+        aComposite->removeFeature(theFeature);
+      }
+    }
+
+    // erase fields
+    theFeature->erase();
+    static Events_ID EVENT_DISP = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
+    ModelAPI_EventCreator::get()->sendUpdated(theFeature, EVENT_DISP);
+    // erase all attributes under the label of feature
+    aFeatureLabel.ForgetAllAttributes();
+    // remove it from the references array
+    if (theFeature->isInHistory()) {
+      RemoveFromRefArray(featuresLabel(), aFeatureLabel);
+    }
+    // event: feature is deleted
+    ModelAPI_EventCreator::get()->sendDeleted(theFeature->document(), ModelAPI_Feature::group());
+  }
+}
+
+void Model_Document::addToHistory(const std::shared_ptr<ModelAPI_Object> theObject)
+{
+  TDF_Label aFeaturesLab = featuresLabel();
+  std::shared_ptr<Model_Data> aData = std::static_pointer_cast<Model_Data>(theObject->data());
+  if (!aData) {
+      return;  // not found feature => do not remove
+  }
   TDF_Label aFeatureLabel = aData->label().Father();
-  if (myObjs.IsBound(aFeatureLabel))
-    myObjs.UnBind(aFeatureLabel);
-  else
-    return;  // not found feature => do not remove
-  // erase fields
-  theFeature->erase();
-  // erase all attributes under the label of feature
-  aFeatureLabel.ForgetAllAttributes();
-  // remove it from the references array
-  if (theFeature->isInHistory()) {
-    RemoveFromRefArray(featuresLabel(), aFeatureLabel);
+  // store feature in the history of features array
+  if (theObject->isInHistory()) {
+    AddToRefArray(aFeaturesLab, aFeatureLabel);
+  } else {
+    RemoveFromRefArray(aFeaturesLab, aFeatureLabel);
   }
-  // event: feature is deleted
-  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);
@@ -529,10 +731,10 @@ ObjectPtr Model_Document::object(TDF_Label theLabel)
   TDF_Label aFeatureLabel = theLabel.Father().Father();  // let's suppose it is result
   aFeature = feature(aFeatureLabel);
   if (aFeature) {
-    const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
-    std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.cbegin();
+    const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
+    std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.cbegin();
     for (; aRIter != aResults.cend(); aRIter++) {
-      boost::shared_ptr<Model_Data> aResData = boost::dynamic_pointer_cast<Model_Data>(
+      std::shared_ptr<Model_Data> aResData = std::dynamic_pointer_cast<Model_Data>(
           (*aRIter)->data());
       if (aResData->label().Father().IsEqual(theLabel))
         return *aRIter;
@@ -541,20 +743,40 @@ ObjectPtr Model_Document::object(TDF_Label theLabel)
   return FeaturePtr();  // not found
 }
 
-boost::shared_ptr<ModelAPI_Document> Model_Document::subDocument(std::string theDocID)
+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);
 }
 
-boost::shared_ptr<Model_Document> Model_Document::subDoc(std::string 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 boost::dynamic_pointer_cast<Model_Document>(
+  return std::dynamic_pointer_cast<Model_Document>(
     Model_Application::getApplication()->getDocument(theDocID));
 }
 
@@ -588,8 +810,8 @@ ObjectPtr Model_Document::object(const std::string& theGroupID, const int theInd
     for (; aLabIter.More(); aLabIter.Next()) {
       TDF_Label aFLabel = aLabIter.Value()->Label();
       FeaturePtr aFeature = feature(aFLabel);
-      const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
-      std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
+      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() != theGroupID) continue;
         bool isIn = theHidden && (*aRIter)->isInHistory();
@@ -608,6 +830,37 @@ ObjectPtr Model_Document::object(const std::string& theGroupID, const int theInd
   return ObjectPtr();
 }
 
+std::shared_ptr<ModelAPI_Object> Model_Document::objectByName(
+    const std::string& theGroupID, const std::string& theName)
+{
+  if (theGroupID == ModelAPI_Feature::group()) {
+    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 && aFeature->name() == theName)
+        return aFeature;
+    }
+  } else {
+    // 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);
+      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() == theGroupID && (*aRIter)->data()->name() == theName)
+          return *aRIter;
+      }
+    }
+  }
+  // not found
+  return ObjectPtr();
+}
+
 int Model_Document::size(const std::string& theGroupID, const bool theHidden)
 {
   int aResult = 0;
@@ -625,8 +878,10 @@ int Model_Document::size(const std::string& theGroupID, const bool theHidden)
     for (; aLabIter.More(); aLabIter.Next()) {
       TDF_Label aFLabel = aLabIter.Value()->Label();
       FeaturePtr aFeature = feature(aFLabel);
-      const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
-      std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
+      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)->groupName() != theGroupID) continue;
         bool isIn = theHidden;
@@ -642,7 +897,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);
 }
@@ -668,8 +923,8 @@ void Model_Document::setUniqueName(FeaturePtr theFeature)
     FeaturePtr aFeature = aFIter.Value();
     bool isSameName = aFeature->data()->name() == aName;
     if (!isSameName) {  // check also results to avoid same results names (actual for Parts)
-      const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
-      std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
+      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++) {
         isSameName = (*aRIter)->data()->name() == aName;
       }
@@ -689,31 +944,36 @@ void Model_Document::setUniqueName(FeaturePtr theFeature)
 
 void Model_Document::initData(ObjectPtr theObj, TDF_Label theLab, const int theTag)
 {
-  boost::shared_ptr<ModelAPI_Document> aThis = Model_Application::getApplication()->getDocument(
+  std::shared_ptr<ModelAPI_Document> aThis = Model_Application::getApplication()->getDocument(
       myID);
-  boost::shared_ptr<Model_Data> aData(new Model_Data);
+  std::shared_ptr<Model_Data> aData(new Model_Data);
   aData->setLabel(theLab.FindChild(theTag));
   aData->setObject(theObj);
   theObj->setDoc(aThis);
   theObj->setData(aData);
-  FeaturePtr aFeature = boost::dynamic_pointer_cast<ModelAPI_Feature>(theObj);
+  FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObj);
   if (aFeature) {
     setUniqueName(aFeature);  // must be before "initAttributes" because duplicate part uses name
-    aFeature->initAttributes();
   }
+  theObj->initAttributes();
 }
 
-void Model_Document::synchronizeFeatures(const bool theMarkUpdated, const bool theUpdateReferences)
+void Model_Document::synchronizeFeatures(
+  const bool theMarkUpdated, const bool theUpdateReferences, const bool theFlush)
 {
-  boost::shared_ptr<ModelAPI_Document> aThis = 
+  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
-  boost::static_pointer_cast<Model_Session>(Model_Session::get())
-    ->setCheckTransactions(false);
   Events_Loop* aLoop = Events_Loop::loop();
+  static Events_ID aDispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
+  static Events_ID aCreateEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
+  static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
+  static Events_ID aRedispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
+  static Events_ID aDeleteEvent = Events_Loop::eventByName(EVENT_OBJECT_DELETED);
+  static Events_ID aToHideEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
   aLoop->activateFlushes(false);
 
-  // update all objects by checking are they of labels or not
+  // update all objects by checking are they on labels or not
   std::set<FeaturePtr> aNewFeatures, aKeptFeatures;
   TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID());
   for (; aLabIter.More(); aLabIter.Next()) {
@@ -722,8 +982,8 @@ void Model_Document::synchronizeFeatures(const bool theMarkUpdated, const bool t
     if (!myObjs.IsBound(aFeatureLabel)) {  // a new feature is inserted
       // create a feature
       aFeature = ModelAPI_Session::get()->createFeature(
-          TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(aLabIter.Value())->Get())
-              .ToCString());
+        TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(aLabIter.Value())->Get())
+        .ToCString());
       if (!aFeature) {  // somethig is wrong, most probably, the opened document has invalid structure
         Events_Error::send("Invalid type of object in the document");
         aLabIter.Value()->Label().ForgetAllAttributes();
@@ -735,48 +995,50 @@ void Model_Document::synchronizeFeatures(const bool theMarkUpdated, const bool t
       initData(aFeature, aFeatureLabel, TAG_FEATURE_ARGUMENTS);
 
       // event: model is updated
-      static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
-      ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent);
+      ModelAPI_EventCreator::get()->sendUpdated(aFeature, aCreateEvent);
     } else {  // nothing is changed, both iterators are incremented
       aFeature = myObjs.Find(aFeatureLabel);
       aKeptFeatures.insert(aFeature);
       if (theMarkUpdated) {
-        static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
-        ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent);
+        ModelAPI_EventCreator::get()->sendUpdated(aFeature, anUpdateEvent);
       }
     }
   }
-  // update results of thefeatures (after features created because they may be connected, like sketch and sub elements)
+  // update results of the features (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);
   while (aFIter.More()) {
     if (aKeptFeatures.find(aFIter.Value()) == aKeptFeatures.end()
-        && aNewFeatures.find(aFIter.Value()) == aNewFeatures.end()) {
-      FeaturePtr aFeature = aFIter.Value();
-      // event: model is updated
-      //if (aFeature->isInHistory()) {
+      && aNewFeatures.find(aFIter.Value()) == aNewFeatures.end()) {
+        FeaturePtr aFeature = aFIter.Value();
+        // event: model is updated
+        //if (aFeature->isInHistory()) {
         ModelAPI_EventCreator::get()->sendDeleted(aThis, ModelAPI_Feature::group());
-      //}
-      // results of this feature must be redisplayed (hided)
-      static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
-      const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
-      std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
-      // redisplay also removed feature (used for sketch and AISObject)
-      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);
+        //}
+        // results of this feature must be redisplayed (hided)
+        // redisplay also removed feature (used for sketch and AISObject)
+        ModelAPI_EventCreator::get()->sendUpdated(aFeature, aRedispEvent);
+        aFeature->erase();
+        // unbind after the "erase" call: on abort sketch is removes sub-objects that corrupts aFIter
+        myObjs.UnBind(aFIter.Key());
+        // reinitialize iterator because unbind may corrupt the previous order in the map
+        aFIter.Initialize(myObjs);
     } else
       aFIter.Next();
   }
@@ -788,19 +1050,19 @@ void Model_Document::synchronizeFeatures(const bool theMarkUpdated, const bool t
   myExecuteFeatures = false;
   aLoop->activateFlushes(true);
 
-  aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED));
-  aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
-  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));
-  boost::static_pointer_cast<Model_Session>(Model_Session::get())
-    ->setCheckTransactions(true);
+  if (theFlush) {
+    aLoop->flush(aCreateEvent);
+    aLoop->flush(aDeleteEvent);
+    aLoop->flush(anUpdateEvent);
+    aLoop->flush(aRedispEvent);
+    aLoop->flush(aToHideEvent);
+  }
   myExecuteFeatures = true;
 }
 
 void Model_Document::synchronizeBackRefs()
 {
-  boost::shared_ptr<ModelAPI_Document> aThis = 
+  std::shared_ptr<ModelAPI_Document> aThis = 
     Model_Application::getApplication()->getDocument(myID);
   // keeps the concealed flags of result to catch the change and create created/deleted events
   std::list<std::pair<ResultPtr, bool> > aConcealed;
@@ -808,16 +1070,16 @@ void Model_Document::synchronizeBackRefs()
   NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator aFeatures(myObjs);
   for(; aFeatures.More(); aFeatures.Next()) {
     FeaturePtr aFeature = aFeatures.Value();
-    boost::shared_ptr<Model_Data> aFData = 
-      boost::dynamic_pointer_cast<Model_Data>(aFeature->data());
+    std::shared_ptr<Model_Data> aFData = 
+      std::dynamic_pointer_cast<Model_Data>(aFeature->data());
     if (aFData) {
       aFData->eraseBackReferences();
     }
-    const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
-    std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
+    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++) {
-      boost::shared_ptr<Model_Data> aResData = 
-        boost::dynamic_pointer_cast<Model_Data>((*aRIter)->data());
+      std::shared_ptr<Model_Data> aResData = 
+        std::dynamic_pointer_cast<Model_Data>((*aRIter)->data());
       if (aResData) {
         aConcealed.push_back(std::pair<ResultPtr, bool>(*aRIter, (*aRIter)->isConcealed()));
         aResData->eraseBackReferences();
@@ -829,18 +1091,19 @@ void Model_Document::synchronizeBackRefs()
   ModelAPI_ValidatorsFactory* aValidators = ModelAPI_Session::get()->validators();
   for(aFeatures.Initialize(myObjs); aFeatures.More(); aFeatures.Next()) {
     FeaturePtr aFeature = aFeatures.Value();
-    boost::shared_ptr<Model_Data> aFData = 
-      boost::dynamic_pointer_cast<Model_Data>(aFeature->data());
+    std::shared_ptr<Model_Data> aFData = 
+      std::dynamic_pointer_cast<Model_Data>(aFeature->data());
     if (aFData) {
       std::list<std::pair<std::string, std::list<ObjectPtr> > > aRefs;
       aFData->referencesToObjects(aRefs);
-      std::list<std::pair<std::string, std::list<ObjectPtr> > >::iterator aRefsIter = aRefs.begin();
+      std::list<std::pair<std::string, std::list<ObjectPtr> > >::iterator 
+        aRefsIter = aRefs.begin();
       for(; aRefsIter != aRefs.end(); aRefsIter++) {
         std::list<ObjectPtr>::iterator aRefTo = aRefsIter->second.begin();
         for(; aRefTo != aRefsIter->second.end(); aRefTo++) {
           if (*aRefTo) {
-            boost::shared_ptr<Model_Data> aRefData = 
-              boost::dynamic_pointer_cast<Model_Data>((*aRefTo)->data());
+            std::shared_ptr<Model_Data> aRefData = 
+              std::dynamic_pointer_cast<Model_Data>((*aRefTo)->data());
             aRefData->addBackReference(aFeature, aRefsIter->first); // here the Concealed flag is updated
           }
         }
@@ -855,104 +1118,112 @@ void Model_Document::synchronizeBackRefs()
         ModelAPI_EventCreator::get()->sendUpdated(aCIter->first, anEvent);
       } else { // was not concealed become concealed => delete event
         ModelAPI_EventCreator::get()->sendDeleted(aThis, aCIter->first->groupName());
+        // redisplay for the viewer (it must be disappeared also)
+        static Events_ID EVENT_DISP = 
+          Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
+        ModelAPI_EventCreator::get()->sendUpdated(aCIter->first, EVENT_DISP);
       }
     }
   }
 }
 
 TDF_Label Model_Document::resultLabel(
-  const boost::shared_ptr<ModelAPI_Data>& theFeatureData, const int theResultIndex) 
+  const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theResultIndex) 
 {
-  const boost::shared_ptr<Model_Data>& aData = 
-    boost::dynamic_pointer_cast<Model_Data>(theFeatureData);
+  const std::shared_ptr<Model_Data>& aData = 
+    std::dynamic_pointer_cast<Model_Data>(theFeatureData);
   return aData->label().Father().FindChild(TAG_FEATURE_RESULTS).FindChild(theResultIndex + 1);
 }
 
-void Model_Document::storeResult(boost::shared_ptr<ModelAPI_Data> theFeatureData,
-                                 boost::shared_ptr<ModelAPI_Result> theResult,
+void Model_Document::storeResult(std::shared_ptr<ModelAPI_Data> theFeatureData,
+                                 std::shared_ptr<ModelAPI_Result> theResult,
                                  const int theResultIndex)
 {
-  boost::shared_ptr<ModelAPI_Document> aThis = 
+  std::shared_ptr<ModelAPI_Document> aThis = 
     Model_Application::getApplication()->getDocument(myID);
   theResult->setDoc(aThis);
   initData(theResult, resultLabel(theFeatureData, theResultIndex), TAG_FEATURE_ARGUMENTS);
   if (theResult->data()->name().empty()) {  // if was not initialized, generate event and set a name
-    theResult->data()->setName(theFeatureData->name());
+    std::stringstream aNewName;
+    aNewName<<theFeatureData->name();
+    if (theResultIndex > 0) // if there are several results, add unique prefix starting from second
+      aNewName<<"_"<<theResultIndex + 1;
+    theResult->data()->setName(aNewName.str());
   }
 }
 
-boost::shared_ptr<ModelAPI_ResultConstruction> Model_Document::createConstruction(
-    const boost::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
+std::shared_ptr<ModelAPI_ResultConstruction> Model_Document::createConstruction(
+    const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
 {
   TDF_Label aLab = resultLabel(theFeatureData, theIndex);
   TDataStd_Comment::Set(aLab, ModelAPI_ResultConstruction::group().c_str());
   ObjectPtr anOldObject = object(aLab);
-  boost::shared_ptr<ModelAPI_ResultConstruction> aResult;
+  std::shared_ptr<ModelAPI_ResultConstruction> aResult;
   if (anOldObject) {
-    aResult = boost::dynamic_pointer_cast<ModelAPI_ResultConstruction>(anOldObject);
+    aResult = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(anOldObject);
   }
   if (!aResult) {
-    aResult = boost::shared_ptr<ModelAPI_ResultConstruction>(new Model_ResultConstruction);
+    aResult = std::shared_ptr<ModelAPI_ResultConstruction>(new Model_ResultConstruction);
     storeResult(theFeatureData, aResult, theIndex);
   }
   return aResult;
 }
 
-boost::shared_ptr<ModelAPI_ResultBody> Model_Document::createBody(
-    const boost::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
+std::shared_ptr<ModelAPI_ResultBody> Model_Document::createBody(
+    const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
 {
   TDF_Label aLab = resultLabel(theFeatureData, theIndex);
   TDataStd_Comment::Set(aLab, ModelAPI_ResultBody::group().c_str());
   ObjectPtr anOldObject = object(aLab);
-  boost::shared_ptr<ModelAPI_ResultBody> aResult;
+  std::shared_ptr<ModelAPI_ResultBody> aResult;
   if (anOldObject) {
-    aResult = boost::dynamic_pointer_cast<ModelAPI_ResultBody>(anOldObject);
+    aResult = std::dynamic_pointer_cast<ModelAPI_ResultBody>(anOldObject);
   }
   if (!aResult) {
-    aResult = boost::shared_ptr<ModelAPI_ResultBody>(new Model_ResultBody);
+    aResult = std::shared_ptr<ModelAPI_ResultBody>(new Model_ResultBody);
     storeResult(theFeatureData, aResult, theIndex);
   }
   return aResult;
 }
 
-boost::shared_ptr<ModelAPI_ResultPart> Model_Document::createPart(
-    const boost::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
+std::shared_ptr<ModelAPI_ResultPart> Model_Document::createPart(
+    const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
 {
   TDF_Label aLab = resultLabel(theFeatureData, theIndex);
   TDataStd_Comment::Set(aLab, ModelAPI_ResultPart::group().c_str());
   ObjectPtr anOldObject = object(aLab);
-  boost::shared_ptr<ModelAPI_ResultPart> aResult;
+  std::shared_ptr<ModelAPI_ResultPart> aResult;
   if (anOldObject) {
-    aResult = boost::dynamic_pointer_cast<ModelAPI_ResultPart>(anOldObject);
+    aResult = std::dynamic_pointer_cast<ModelAPI_ResultPart>(anOldObject);
   }
   if (!aResult) {
-    aResult = boost::shared_ptr<ModelAPI_ResultPart>(new Model_ResultPart);
+    aResult = std::shared_ptr<ModelAPI_ResultPart>(new Model_ResultPart);
     storeResult(theFeatureData, aResult, theIndex);
   }
   return aResult;
 }
 
-boost::shared_ptr<ModelAPI_ResultGroup> Model_Document::createGroup(
-    const boost::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
+std::shared_ptr<ModelAPI_ResultGroup> Model_Document::createGroup(
+    const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
 {
   TDF_Label aLab = resultLabel(theFeatureData, theIndex);
   TDataStd_Comment::Set(aLab, ModelAPI_ResultGroup::group().c_str());
   ObjectPtr anOldObject = object(aLab);
-  boost::shared_ptr<ModelAPI_ResultGroup> aResult;
+  std::shared_ptr<ModelAPI_ResultGroup> aResult;
   if (anOldObject) {
-    aResult = boost::dynamic_pointer_cast<ModelAPI_ResultGroup>(anOldObject);
+    aResult = std::dynamic_pointer_cast<ModelAPI_ResultGroup>(anOldObject);
   }
   if (!aResult) {
-    aResult = boost::shared_ptr<ModelAPI_ResultGroup>(new Model_ResultGroup(theFeatureData));
+    aResult = std::shared_ptr<ModelAPI_ResultGroup>(new Model_ResultGroup(theFeatureData));
     storeResult(theFeatureData, aResult, theIndex);
   }
   return aResult;
 }
 
-boost::shared_ptr<ModelAPI_Feature> Model_Document::feature(
-    const boost::shared_ptr<ModelAPI_Result>& theResult)
+std::shared_ptr<ModelAPI_Feature> Model_Document::feature(
+    const std::shared_ptr<ModelAPI_Result>& theResult)
 {
-  boost::shared_ptr<Model_Data> aData = boost::dynamic_pointer_cast<Model_Data>(theResult->data());
+  std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(theResult->data());
   if (aData) {
     TDF_Label aFeatureLab = aData->label().Father().Father().Father();
     return feature(aFeatureLab);
@@ -967,7 +1238,7 @@ void Model_Document::updateResults(FeaturePtr theFeature)
   // check the existing results and remove them if there is nothing on the label
   std::list<ResultPtr>::const_iterator aResIter = theFeature->results().cbegin();
   while(aResIter != theFeature->results().cend()) {
-    ResultPtr aBody = boost::dynamic_pointer_cast<ModelAPI_Result>(*aResIter);
+    ResultPtr aBody = std::dynamic_pointer_cast<ModelAPI_Result>(*aResIter);
     if (aBody) {
       if (!aBody->data()->isValid()) { 
         // found a disappeared result => remove it
@@ -979,6 +1250,9 @@ void Model_Document::updateResults(FeaturePtr theFeature)
     }
     aResIter++;
   }
+  // it may be on undo
+  if (!theFeature->data() || !theFeature->data()->isValid())
+    return;
   // check that results are presented on all labels
   int aResSize = theFeature->results().size();
   TDF_ChildIterator aLabIter(resultLabel(theFeature->data(), 0).Father());
@@ -1020,3 +1294,36 @@ Standard_Boolean IsEqual(const TDF_Label& theLab1, const TDF_Label& theLab2)
 {
   return TDF_LabelMapHasher::IsEqual(theLab1, theLab2);
 }
+
+void Model_Document::addNamingName(const TDF_Label theLabel, std::string theName)
+{
+  myNamingNames[theName] = theLabel;
+}
+
+TDF_Label Model_Document::findNamingName(std::string theName)
+{
+  std::map<std::string, TDF_Label>::iterator aFind = myNamingNames.find(theName);
+  if (aFind == myNamingNames.end())
+    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();
+}