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 8f4ac4cb17ef7eb62a3dc7ee4db6b4bcd8e64802..ee8cebc14c9d0274a1dd486a1638fca540450c17 100644 (file)
@@ -75,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();
@@ -83,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));
@@ -160,7 +166,7 @@ bool Model_Document::load(const char* theFileName)
       std::dynamic_pointer_cast<Model_Session>(Model_Session::get());
     aSession->setActiveDocument(anApp->getDocument(myID), false);
     aSession->setCheckTransactions(false);
-    synchronizeFeatures(false, true);
+    synchronizeFeatures(false, true, true);
     aSession->setCheckTransactions(true);
     aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false);
     aSession->setActiveDocument(anApp->getDocument(myID), true);
@@ -172,7 +178,7 @@ bool Model_Document::save(const char* theFileName, std::list<std::string>& theRe
 {
   // create a directory in the root document if it is not yet exist
   Handle(Model_Application) anApp = Model_Application::getApplication();
-  if (this == Model_Session::get()->moduleDocument().get()) {
+  if (isRoot()) {
 #ifdef WIN32
     CreateDirectory(theFileName, NULL);
 #else
@@ -240,9 +246,9 @@ bool Model_Document::save(const char* theFileName, std::list<std::string>& theRe
 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()) {
+  if (!isRoot() && this == aPM->activeDocument().get()) {
     aPM->setActiveDocument(aPM->moduleDocument());
-  } else if (this == aPM->moduleDocument().get()) {
+  } else if (isRoot()) {
     // erase the active document if root is closed
     aPM->setActiveDocument(DocumentPtr());
   }
@@ -340,7 +346,7 @@ bool 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 std::shared_ptr<Events_Message> aFinishMsg
       (new Events_Message(Events_Loop::eventByName("FinishOperation")));
@@ -370,7 +376,7 @@ bool Model_Document::finishOperation()
   if (!aResult && !myTransactions.empty() /* it can be for just created part document */)
     aResult = myTransactions.rbegin()->myOCAFNum != 0;
 
-  if (!aResult && Model_Session::get()->moduleDocument().get() == this) {
+  if (!aResult && isRoot()) {
     // nothing inside in all documents, so remove this transaction from the transactions list
     undoInternal(true, false);
     myDoc->ClearRedos();
@@ -397,7 +403,8 @@ void Model_Document::abortOperation()
       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
   const std::set<std::string> aSubs = subDocuments(true);
   std::set<std::string>::iterator aSubIter = aSubs.begin();
@@ -445,8 +452,6 @@ void Model_Document::undoInternal(const bool theWithSubs, const bool theSynchron
   for(int a = 0; a < aNumTransactions; a++)
     myDoc->Undo();
 
-  if (theSynchronize)
-    synchronizeFeatures(true, true);
   if (theWithSubs) {
     // undo for all subs
     const std::set<std::string> aSubs = subDocuments(true);
@@ -454,6 +459,9 @@ void Model_Document::undoInternal(const bool theWithSubs, const bool theSynchron
     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()
@@ -484,12 +492,14 @@ void Model_Document::redo()
   for(int a = 0; a < aNumRedos; a++)
     myDoc->Redo();
 
-  synchronizeFeatures(true, true);
   // redo 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)->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
@@ -583,8 +593,8 @@ 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 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;
@@ -820,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;
@@ -917,15 +958,22 @@ void Model_Document::initData(ObjectPtr theObj, TDF_Label theLab, const int theT
   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)
 {
   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
   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()) {
@@ -934,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();
@@ -947,18 +995,16 @@ 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()) {
@@ -979,23 +1025,20 @@ void Model_Document::synchronizeFeatures(const bool theMarkUpdated, const bool t
   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<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
-      std::list<std::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
-      myObjs.UnBind(aFIter.Key());
-      // reinitialize iterator because unbind may corrupt the previous order in the map
-      aFIter.Initialize(myObjs);
+        //}
+        // 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();
   }
@@ -1007,11 +1050,13 @@ 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));
+  if (theFlush) {
+    aLoop->flush(aCreateEvent);
+    aLoop->flush(aDeleteEvent);
+    aLoop->flush(anUpdateEvent);
+    aLoop->flush(aRedispEvent);
+    aLoop->flush(aToHideEvent);
+  }
   myExecuteFeatures = true;
 }
 
@@ -1051,7 +1096,8 @@ void Model_Document::synchronizeBackRefs()
     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++) {
@@ -1098,7 +1144,11 @@ void Model_Document::storeResult(std::shared_ptr<ModelAPI_Data> theFeatureData,
   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());
   }
 }