Salome HOME
Issue #661: on abort, undo or redo of operation, only the updated features are update...
authormpv <mpv@opencascade.com>
Mon, 17 Aug 2015 13:50:54 +0000 (16:50 +0300)
committermpv <mpv@opencascade.com>
Mon, 17 Aug 2015 13:50:54 +0000 (16:50 +0300)
src/Model/Model_Document.cpp
src/Model/Model_Objects.cpp
src/Model/Model_Objects.h
src/Model/Model_Session.cpp

index efdc253aa3f678fa7591117861cd333d6c74c277..3a2cb0d2c77fb9c4770fb0ca1dfb687cedf12a72 100644 (file)
 #include <TDF_Reference.hxx>
 #include <TDF_ChildIDIterator.hxx>
 #include <TDF_LabelMapHasher.hxx>
+#include <TDF_Delta.hxx>
 #include <OSD_File.hxx>
 #include <OSD_Path.hxx>
+#include <TDF_AttributeDelta.hxx>
+#include <TDF_AttributeDeltaList.hxx>
+#include <TDF_ListIteratorOfAttributeDeltaList.hxx>
 
 #include <climits>
 #ifndef WIN32
@@ -383,10 +387,37 @@ bool Model_Document::finishOperation()
   return aResult;
 }
 
+/// Returns in theDelta labels that has been modified in the latest transaction of theDoc
+static void modifiedLabels(const Handle(TDocStd_Document)& theDoc, TDF_LabelList& theDelta,
+  const bool isRedo = false) {
+  Handle(TDF_Delta) aDelta;
+  if (isRedo)
+    aDelta = theDoc->GetRedos().First();
+  else 
+    aDelta = theDoc->GetUndos().Last();
+  aDelta->Labels(theDelta);
+  // add also label of the modified attributes
+  const TDF_AttributeDeltaList& anAttrs = aDelta->AttributeDeltas();
+  for (TDF_ListIteratorOfAttributeDeltaList anAttr(anAttrs); anAttr.More(); anAttr.Next()) {
+    theDelta.Append(anAttr.Value()->Label());
+  }
+}
+
 void Model_Document::abortOperation()
 {
+  TDF_LabelList aDeltaLabels; // labels that are updated during "abort"
   if (!myNestedNum.empty() && !myDoc->HasOpenCommand()) {  // abort all what was done in nested
     compactNested();
+    // store undo-delta here as undo actually does in the method later
+    int a, aNumTransactions = myTransactions.rbegin()->myOCAFNum;
+    for(a = 0; a < aNumTransactions; a++) {
+      modifiedLabels(myDoc, aDeltaLabels);
+      myDoc->Undo();
+    }
+    for(a = 0; a < aNumTransactions; a++) {
+      myDoc->Redo();
+    }
+
     undoInternal(false, false);
     myDoc->ClearRedos();
     myRedos.clear();
@@ -397,8 +428,10 @@ void Model_Document::abortOperation()
       (*myNestedNum.rbegin())--;
     // roll back the needed number of transactions
     myDoc->AbortCommand();
-    for(int a = 0; a < aNumTransactions; a++)
+    for(int a = 0; a < aNumTransactions; a++) {
+      modifiedLabels(myDoc, aDeltaLabels);
       myDoc->Undo();
+    }
     myDoc->ClearRedos();
   }
   // abort for all subs, flushes will be later, in the end of root abort
@@ -407,7 +440,7 @@ void Model_Document::abortOperation()
   for (; aSubIter != aSubs.end(); aSubIter++)
     subDoc(*aSubIter)->abortOperation();
   // references may be changed because they are set in attributes on the fly
-  myObjs->synchronizeFeatures(true, true, isRoot());
+  myObjs->synchronizeFeatures(aDeltaLabels, true, isRoot());
 }
 
 bool Model_Document::isOperation() const
@@ -452,8 +485,12 @@ void Model_Document::undoInternal(const bool theWithSubs, const bool theSynchron
   if (!myNestedNum.empty())
     (*myNestedNum.rbegin())--;
   // roll back the needed number of transactions
-  for(int a = 0; a < aNumTransactions; a++)
+  TDF_LabelList aDeltaLabels;
+  for(int a = 0; a < aNumTransactions; a++) {
+    if (theSynchronize)
+      modifiedLabels(myDoc, aDeltaLabels);
     myDoc->Undo();
+  }
 
   if (theWithSubs) {
     // undo for all subs
@@ -467,7 +504,7 @@ void Model_Document::undoInternal(const bool theWithSubs, const bool theSynchron
   }
   // after redo of all sub-documents to avoid updates on not-modified data (issue 370)
   if (theSynchronize) {
-    myObjs->synchronizeFeatures(true, true, isRoot());
+    myObjs->synchronizeFeatures(aDeltaLabels, true, isRoot());
     // update the current features status
     setCurrentFeature(currentFeature(false), false);
   }
@@ -501,8 +538,11 @@ void Model_Document::redo()
   int aNumRedos = myRedos.rbegin()->myOCAFNum;
   myTransactions.push_back(*myRedos.rbegin());
   myRedos.pop_back();
-  for(int a = 0; a < aNumRedos; a++)
+  TDF_LabelList aDeltaLabels;
+  for(int a = 0; a < aNumRedos; a++) {
+    modifiedLabels(myDoc, aDeltaLabels, true);
     myDoc->Redo();
+  }
 
   // redo for all subs
   const std::set<std::string> aSubs = subDocuments(true);
@@ -511,7 +551,7 @@ void Model_Document::redo()
     subDoc(*aSubIter)->redo();
 
   // after redo of all sub-documents to avoid updates on not-modified data (issue 370)
-  myObjs->synchronizeFeatures(true, true, isRoot());
+  myObjs->synchronizeFeatures(aDeltaLabels, true, isRoot());
   // update the current features status
   setCurrentFeature(currentFeature(false), false);
 }
index 6375e4e2cbd133dffe8e3507a5101889d9af20cb..49e922a804d8cb4a2cab873bcdf69955d2e01b77 100644 (file)
@@ -31,6 +31,8 @@
 #include <TDF_Reference.hxx>
 #include <TDF_ChildIDIterator.hxx>
 #include <TDF_LabelMapHasher.hxx>
+#include <TDF_LabelMap.hxx>
+#include <TDF_ListIteratorOfLabelList.hxx>
 
 static const int TAG_OBJECTS = 2;  // tag of the objects sub-tree (features, results)
 
@@ -51,7 +53,8 @@ void Model_Objects::setOwner(DocumentPtr theDoc)
 {
   myDoc = theDoc;
   // update all fields and recreate features and result objects if needed
-  synchronizeFeatures(false, true, true);
+  TDF_LabelList aNoUpdated;
+  synchronizeFeatures(aNoUpdated, true, true);
   myHistory.clear();
 }
 
@@ -542,7 +545,7 @@ void Model_Objects::initData(ObjectPtr theObj, TDF_Label theLab, const int theTa
 }
 
 void Model_Objects::synchronizeFeatures(
-  const bool theMarkUpdated, const bool theUpdateReferences, const bool theFlush)
+  const TDF_LabelList& theUpdated, const bool theUpdateReferences, const bool theFlush)
 {
   Model_Document* anOwner = std::dynamic_pointer_cast<Model_Document>(myDoc).get();
   if (!anOwner) // this may happen on creation of document: nothing there, so nothing to synchronize
@@ -557,6 +560,17 @@ void Model_Objects::synchronizeFeatures(
   static Events_ID aToHideEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
   bool isActive = aLoop->activateFlushes(false);
 
+  // collect all updated labels map
+  TDF_LabelMap anUpdatedMap;
+  TDF_ListIteratorOfLabelList anUpdatedIter(theUpdated);
+  for(; anUpdatedIter.More(); anUpdatedIter.Next()) {
+    TDF_Label& aFeatureLab = anUpdatedIter.Value();
+    while(aFeatureLab.Depth() > 3)
+      aFeatureLab = aFeatureLab.Father();
+    if (myFeatures.IsBound(aFeatureLab))
+      anUpdatedMap.Add(aFeatureLab);
+  }
+
   // update all objects by checking are they on labels or not
   std::set<FeaturePtr> aNewFeatures, aKeptFeatures;
   TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID());
@@ -585,7 +599,7 @@ void Model_Objects::synchronizeFeatures(
     } else {  // nothing is changed, both iterators are incremented
       aFeature = myFeatures.Find(aFeatureLabel);
       aKeptFeatures.insert(aFeature);
-      if (theMarkUpdated) {
+      if (anUpdatedMap.Contains(aFeatureLabel)) {
         ModelAPI_EventCreator::get()->sendUpdated(aFeature, anUpdateEvent);
       }
     }
@@ -639,7 +653,7 @@ void Model_Objects::synchronizeFeatures(
   if (theUpdateReferences) {
     synchronizeBackRefs();
   }
-  if (theMarkUpdated) { // this means there is no control what was modified => remove history cash
+  if (!theUpdated.IsEmpty()) { // this means there is no control what was modified => remove history cash
     myHistory.clear();
   }
 
index 91124d483b0fb2e9a25b1666fc38606ec1173b46..114be09dffe82d11715af1eb4670fcad791cbf7f 100644 (file)
@@ -16,6 +16,7 @@
 #include <TDocStd_Document.hxx>
 #include <NCollection_DataMap.hxx>
 #include <TDF_Label.hxx>
+#include <TDF_LabelList.hxx>
 #include <map>
 #include <set>
 #include <vector>
@@ -137,10 +138,10 @@ class Model_Objects
   void setUniqueName(FeaturePtr theFeature);
 
   //! Synchronizes myFeatures list with the updated document
-  //! \param theMarkUpdated causes the "update" event for all features
+  //! \param theUpdated list of labels that are marked as modified, so featrues must be also
   //! \param theUpdateReferences causes the update of back-references
   //! \param theFlush makes flush all events in the end of all modifications of this method
-  void synchronizeFeatures(const bool theMarkUpdated, const bool theUpdateReferences,
+  void synchronizeFeatures(const TDF_LabelList& theUpdated, const bool theUpdateReferences,
     const bool theFlush);
   //! Synchronizes the BackReferences list in Data of Features and Results
   void synchronizeBackRefs();
index ff74e5bf3ad2b095e274c7cbdbfb9ff7c9739a91..e66e434a1348e12a4f3fef8cc3b7e043a8ee2dea 100644 (file)
@@ -240,7 +240,8 @@ void Model_Session::setActiveDocument(
       if (aDoc.get()) {
         bool aWasChecked = myCheckTransactions;
         setCheckTransactions(false);
-        aDoc->objects()->synchronizeFeatures(false, true, true);
+        TDF_LabelList anEmptyUpdated;
+        aDoc->objects()->synchronizeFeatures(anEmptyUpdated, true, true);
         if (aWasChecked)
             setCheckTransactions(true);
       }
@@ -314,7 +315,8 @@ std::shared_ptr<ModelAPI_Document> Model_Session::copy(
   aRT->SetRelocation(aSourceRoot, aTargetRoot);
   TDF_CopyTool::Copy(aDS, aRT);
 
-  aNew->objects()->synchronizeFeatures(false, true, true);
+  TDF_LabelList anEmptyUpdated;
+  aNew->objects()->synchronizeFeatures(anEmptyUpdated, true, true);
   return aNew;
 }