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 65d4cb70bb76d66c9d87d07d71f9dcbe82702e46..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>
 
@@ -155,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;
 }
@@ -237,6 +242,9 @@ 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
   const std::set<std::string> aSubs = subDocuments(true);
@@ -317,15 +325,11 @@ void Model_Document::compactNested()
   }
 }
 
-void Model_Document::finishOperation()
+bool Model_Document::finishOperation()
 {
   bool isNestedClosed = !myDoc->HasOpenCommand() && !myNestedNum.empty();
   static std::shared_ptr<Model_Session> aSession = 
     std::static_pointer_cast<Model_Session>(Model_Session::get());
-  // just to be sure that everybody knows that changes were performed
-  if (isNestedClosed) {
-    aSession->setCheckTransactions(false);  // for nested transaction commit
-  }
   synchronizeBackRefs();
   Events_Loop* aLoop = Events_Loop::loop();
   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED));
@@ -346,30 +350,40 @@ void Model_Document::finishOperation()
   // to avoid "updated" message appearance by updater
   //aLoop->clear(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
 
-  if (isNestedClosed) {
-    aSession->setCheckTransactions(true);  // for nested transaction commit
-  }
-
   // finish for all subs first: to avoid nested finishing and "isOperation" calls problems inside
+  bool aResult = false;
   const std::set<std::string> aSubs = subDocuments(true);
   std::set<std::string>::iterator aSubIter = aSubs.begin();
   for (; aSubIter != aSubs.end(); aSubIter++)
-    subDoc(*aSubIter)->finishOperation();
+    if (subDoc(*aSubIter)->finishOperation())
+      aResult = true;
 
-  if (myDoc->CommitCommand()) { // if commit is successfull, just increment counters
+  // 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.empty() && !myDoc->HasOpenCommand()) {  // abort all what was done in nested
     compactNested();
-    undo();
+    undoInternal(false, false);
     myDoc->ClearRedos();
     myRedos.clear();
   } else { // abort the current
@@ -417,7 +431,7 @@ bool Model_Document::canUndo()
   return false;
 }
 
-void Model_Document::undo()
+void Model_Document::undoInternal(const bool theWithSubs, const bool theSynchronize)
 {
   int aNumTransactions = *myTransactions.rbegin();
   myTransactions.pop_back();
@@ -428,12 +442,20 @@ void Model_Document::undo()
   for(int a = 0; a < aNumTransactions; a++)
     myDoc->Undo();
 
-  synchronizeFeatures(true, true);
-  // 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)->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()
@@ -805,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);
 
@@ -844,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);
@@ -871,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();
   }
@@ -890,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;
 }