Salome HOME
Revert "Issue #3044: Undo list contains empty string for Load python script"
[modules/shaper.git] / src / Model / Model_Session.cpp
index c0dde0f287ea7bea7ff7f529b0ed201c658cd845..89f90ee4aff07fa7f3799e7dc193dab01b471437 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2017  CEA/DEN, EDF R&D
+// 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
 //
 // 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
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 //
-// See http://www.salome-platform.org/ or
-// email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
 //
 
 #include <Model_Session.h>
@@ -27,6 +26,7 @@
 #include <Model_Application.h>
 #include <Model_Events.h>
 #include <Model_Validator.h>
+#include <Model_FiltersFactory.h>
 #include <ModelAPI_Events.h>
 #include <Events_Loop.h>
 #include <Events_InfoMessage.h>
@@ -55,7 +55,7 @@
 
 static Model_Session* myImpl = new Model_Session();
 
-// t oredirect all calls to the root document
+// tredirect all calls to the root document
 #define ROOT_DOC std::dynamic_pointer_cast<Model_Document>(moduleDocument())
 
 bool Model_Session::load(const char* theFileName)
@@ -72,11 +72,15 @@ bool Model_Session::save(const char* theFileName, std::list<std::string>& theRes
 void Model_Session::closeAll()
 {
   Model_Application::getApplication()->deleteAllDocuments();
+  static const Events_ID aDocChangeEvent = Events_Loop::eventByName(EVENT_DOCUMENT_CHANGED);
+  static std::shared_ptr<Events_Message> aMsg(new Events_Message(aDocChangeEvent));
+  Events_Loop::loop()->send(aMsg);
+  Events_Loop::loop()->flush(aDocChangeEvent);
 }
 
 void Model_Session::startOperation(const std::string& theId, const bool theAttachedToNested)
 {
-  myOperationAttachedToNext = theAttachedToNested;
+  myOperationAttachedToNext.push_back(theAttachedToNested);
   ROOT_DOC->startOperation();
   ROOT_DOC->operationId(theId);
   static std::shared_ptr<Events_Message> aStartedMsg
@@ -94,9 +98,13 @@ void Model_Session::finishOperation()
 {
   setCheckTransactions(false);
   ROOT_DOC->finishOperation();
-  if (myOperationAttachedToNext) { // twice, with nested
-    ROOT_DOC->finishOperation();
-    myOperationAttachedToNext = false;
+  if (!myOperationAttachedToNext.empty()) {
+    while (myOperationAttachedToNext.back()) {
+      // with nested, the first transaction can not be attached
+      ROOT_DOC->finishOperation();
+      myOperationAttachedToNext.pop_back();
+    }
+    myOperationAttachedToNext.pop_back();
   }
   setCheckTransactions(true);
 }
@@ -105,12 +113,16 @@ void Model_Session::abortOperation()
 {
   setCheckTransactions(false);
   ROOT_DOC->abortOperation();
-  if (myOperationAttachedToNext) { // twice, with nested
-    ROOT_DOC->abortOperation();
-    myOperationAttachedToNext = false;
+  if (!myOperationAttachedToNext.empty()) {
+    while (myOperationAttachedToNext.back()) {
+      // with nested, the first transaction can not be attached
+      ROOT_DOC->abortOperation();
+      myOperationAttachedToNext.pop_back();
+    }
+    myOperationAttachedToNext.pop_back();
   }
   setCheckTransactions(true);
-  // here the update mechanism may work after abort, so, supress the warnings about
+  // here the update mechanism may work after abort, so, suppress the warnings about
   // modifications outside of the transactions
   bool aWasCheck = myCheckTransactions;
   myCheckTransactions = false;
@@ -290,7 +302,7 @@ void Model_Session::setActiveDocument(
     std::shared_ptr<ModelAPI_Document> aPrevious = myCurrentDoc;
     myCurrentDoc = theDoc;
     if (theDoc.get() && theSendSignal) {
-      // this must be before the synchronisation call because features in PartSet lower than this
+      // this must be before the synchronization call because features in PartSet lower than this
       // part feature must be disabled and don't recomputed anymore (issue 1156,
       // translation feature is failed on activation of Part 2)
       if (isOperation()) { // do it only in transaction, not on opening of document
@@ -302,7 +314,7 @@ void Model_Session::setActiveDocument(
           }
         }
       }
-      // syncronize the document: it may be just opened or opened but removed before
+      // synchronize the document: it may be just opened or opened but removed before
       std::shared_ptr<Model_Document> aDoc = std::dynamic_pointer_cast<Model_Document>(theDoc);
       if (aDoc.get()) {
         bool aWasChecked = myCheckTransactions;
@@ -316,7 +328,7 @@ void Model_Session::setActiveDocument(
           new Events_Message(Events_Loop::eventByName(EVENT_DOCUMENT_CHANGED)));
       Events_Loop::loop()->send(aMsg);
     }
-    // make the current state correct and synchronised in the module and sub-documents
+    // make the current state correct and synchronized in the module and sub-documents
     if (isOperation()) { // do it only in transaction, not on opening of document
       if (myCurrentDoc == moduleDocument()) {
         // make the current feature the latest in root, in previous root current become also last
@@ -423,7 +435,6 @@ Model_Session::Model_Session()
 {
   myPluginsInfoLoaded = false;
   myCheckTransactions = true;
-  myOperationAttachedToNext = false;
   ModelAPI_Session::setSession(std::shared_ptr<ModelAPI_Session>(this));
   // register the configuration reading listener
   Events_Loop* aLoop = Events_Loop::loop();
@@ -471,6 +482,10 @@ void Model_Session::processEvent(const std::shared_ptr<Events_Message>& theMessa
         if (!aCases.empty()) {
           validators()->registerCase(aMsgAttr->featureId(), aMsgAttr->attributeId(), aCases);
         }
+        if (aMsgAttr->isGeometricalSelection()) {
+          validators()->registerGeometricalSelection(aMsgAttr->featureId(),
+                                                     aMsgAttr->attributeId());
+        }
       }
     }
     // plugins information was started to load, so, it will be loaded
@@ -496,9 +511,33 @@ void Model_Session::processEvent(const std::shared_ptr<Events_Message>& theMessa
       }
     }
   } else {  // create/update/delete
-    if (myCheckTransactions && !isOperation())
-      Events_InfoMessage("Model_Session",
-        "Modification of data structure outside of the transaction").send();
+    if (myCheckTransactions && !isOperation()) {
+      // check it is done in real opened document: 2958
+      bool aIsActual = true;
+      static const Events_ID kDeletedEvent = Events_Loop::eventByName(EVENT_OBJECT_DELETED);
+      if (theMessage->eventID() == kDeletedEvent) {
+        aIsActual = false;
+        std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleted =
+          std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
+        std::list<std::shared_ptr<ModelAPI_Document> > allOpened =
+          Model_Session::allOpenedDocuments();
+        std::list<std::pair<std::shared_ptr<ModelAPI_Document>, std::string>>::const_iterator
+          aGIter = aDeleted->groups().cbegin();
+        for (; !aIsActual && aGIter != aDeleted->groups().cend(); aGIter++) {
+          std::list<std::shared_ptr<ModelAPI_Document> >::iterator anOpened = allOpened.begin();
+          for(; anOpened != allOpened.end(); anOpened++) {
+            if (aGIter->first == *anOpened) {
+              aIsActual = true;
+              break;
+            }
+          }
+        }
+      }
+
+      if (aIsActual)
+        Events_InfoMessage("Model_Session",
+          "Modification of data structure outside of the transaction").send();
+    }
     // if part is deleted, make the root as the current document (on undo of Parts creations)
     static const Events_ID kDeletedEvent = Events_Loop::eventByName(EVENT_OBJECT_DELETED);
     if (theMessage->eventID() == kDeletedEvent) {
@@ -573,7 +612,52 @@ ModelAPI_ValidatorsFactory* Model_Session::validators()
   return aFactory;
 }
 
+ModelAPI_FiltersFactory* Model_Session::filters()
+{
+  static Model_FiltersFactory* aFactory = new Model_FiltersFactory;
+  return aFactory;
+}
+
 int Model_Session::transactionID()
 {
   return ROOT_DOC->transactionID();
 }
+
+bool Model_Session::isAutoUpdateBlocked()
+{
+  Handle(Model_Application) anApp = Model_Application::getApplication();
+  if (!anApp->hasRoot()) // when document is not yet created, do not create it by such simple call
+    return false;
+  return !ROOT_DOC->autoRecomutationState();
+}
+
+void Model_Session::blockAutoUpdate(const bool theBlock)
+{
+  bool aCurrentState = isAutoUpdateBlocked();
+  if (aCurrentState != theBlock) {
+    // if there is no operation, start it to avoid modifications outside of transaction
+    bool isOperation = this->isOperation();
+    if (!isOperation)
+      startOperation("Auto update");
+    ROOT_DOC->setAutoRecomutationState(!theBlock);
+    static Events_Loop* aLoop = Events_Loop::loop();
+    if (theBlock) {
+      static const Events_ID kAutoOff = aLoop->eventByName(EVENT_AUTOMATIC_RECOMPUTATION_DISABLE);
+      std::shared_ptr<Events_Message> aMsg(new Events_Message(kAutoOff));
+      aLoop->send(aMsg);
+    } else {
+      // if there is no operation, start it to avoid modifications outside of transaction
+      bool isOperation = this->isOperation();
+      if (!isOperation)
+        startOperation("Auto update enabling");
+      static const Events_ID kAutoOn = aLoop->eventByName(EVENT_AUTOMATIC_RECOMPUTATION_ENABLE);
+      std::shared_ptr<Events_Message> aMsg(new Events_Message(kAutoOn));
+      aLoop->send(aMsg);
+    }
+    if (!isOperation) {
+      finishOperation();
+      // append this transaction to the previous one: don't need this separated operation in list
+      ROOT_DOC->appendTransactionToPrevious();
+    }
+  }
+}