Salome HOME
According to this branch modifications, sub-composites can be in history.
[modules/shaper.git] / src / Model / Model_Document.cpp
index 51e0782e42c19f64cecdf4a246c9acf035001421..28fda6f314c0c20abf921d4d4e7619ca0afb94c1 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 <TDF_ListIteratorOfLabelList.hxx>
 
 #include <climits>
 #ifndef WIN32
@@ -63,6 +68,8 @@ Model_Document::Model_Document(const std::string theID, const std::string theKin
   // in transaction for nesting correct working
   myDoc->NewCommand();
   TDataStd_Integer::Set(myDoc->Main().Father(), 0);
+  // this to avoid creation of integer attribute outside the transaction after undo
+  transactionID();
   myDoc->CommitCommand();
 }
 
@@ -98,8 +105,9 @@ bool Model_Document::load(const char* theFileName, DocumentPtr theThis)
   }
   TCollection_ExtendedString aPath(DocFileName(theFileName, myID));
   PCDM_ReaderStatus aStatus = (PCDM_ReaderStatus) -1;
+  Handle(TDocStd_Document) aLoaded;
   try {
-    aStatus = anApp->Open(aPath, myDoc);
+    aStatus = anApp->Open(aPath, aLoaded);
   } catch (Standard_Failure) {
     Handle(Standard_Failure) aFail = Standard_Failure::Caught();
     Events_Error::send(
@@ -160,11 +168,12 @@ bool Model_Document::load(const char* theFileName, DocumentPtr theThis)
         break;
     }
   }
+  std::shared_ptr<Model_Session> aSession = 
+    std::dynamic_pointer_cast<Model_Session>(Model_Session::get());
   if (!isError) {
+    myDoc = aLoaded;
     myDoc->SetUndoLimit(UNDO_LIMIT);
     // to avoid the problem that feature is created in the current, not this, document
-    std::shared_ptr<Model_Session> aSession = 
-      std::dynamic_pointer_cast<Model_Session>(Model_Session::get());
     aSession->setActiveDocument(anApp->getDocument(myID), false);
     aSession->setCheckTransactions(false);
     if (myObjs)
@@ -177,7 +186,9 @@ bool Model_Document::load(const char* theFileName, DocumentPtr theThis)
     aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false);
     // this is done in Part result "activate", so no needed here. Causes not-blue active part.
     // aSession->setActiveDocument(anApp->getDocument(myID), true);
-  }
+  } else { // open failed, but new documnet was created to work with it: inform the model
+    aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false);
+  } 
   return !isError;
 }
 
@@ -273,6 +284,7 @@ void Model_Document::close(const bool theForever)
 
   // close all only if it is really asked, otherwise it can be undoed/redoed
   if (theForever) {
+    // flush everything to avoid messages with bad objects
     delete myObjs;
     myObjs = 0;
     if (myDoc->CanClose() == CDM_CCS_OK)
@@ -383,10 +395,41 @@ 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();
+  TDF_LabelList aDeltaList;
+  aDelta->Labels(aDeltaList); // it clears list, so, use new one and then append to the result
+  for(TDF_ListIteratorOfLabelList aListIter(aDeltaList); aListIter.More(); aListIter.Next()) {
+    theDelta.Append(aListIter.Value());
+  }
+  // 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();
@@ -396,9 +439,16 @@ void Model_Document::abortOperation()
     if (!myNestedNum.empty())
       (*myNestedNum.rbegin())--;
     // roll back the needed number of transactions
-    myDoc->AbortCommand();
-    for(int a = 0; a < aNumTransactions; a++)
+    // make commit/undo to get the modification delta
+    //myDoc->AbortCommand();
+    if (myDoc->CommitCommand()) {
+      modifiedLabels(myDoc, aDeltaLabels);
       myDoc->Undo();
+    }
+    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 +457,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 +502,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
@@ -465,9 +519,9 @@ void Model_Document::undoInternal(const bool theWithSubs, const bool theSynchron
       subDoc(*aSubIter)->undoInternal(theWithSubs, theSynchronize);
     }
   }
-  // after redo of all sub-documents to avoid updates on not-modified data (issue 370)
+  // after undo 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 +555,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 +568,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);
 }
@@ -554,6 +611,7 @@ FeaturePtr Model_Document::addFeature(std::string theID, const bool theMakeCurre
   FeaturePtr aFeature = aSession->createFeature(theID, this);
   if (!aFeature)
     return aFeature;
+  aFeature->init();
   Model_Document* aDocToAdd;
   if (!aFeature->documentToAdd().empty()) { // use the customized document to add
     if (aFeature->documentToAdd() != kind()) { // the root document by default
@@ -565,7 +623,24 @@ FeaturePtr Model_Document::addFeature(std::string theID, const bool theMakeCurre
     aDocToAdd = this;
   }
   if (aFeature) {
-    aDocToAdd->myObjs->addFeature(aFeature, aDocToAdd->currentFeature(false));
+    // searching for feature after which must be added the next feature: this is the current feature
+    // but also all sub-features of this feature
+    FeaturePtr aCurrent = aDocToAdd->currentFeature(false);
+    bool isModified = true;
+    for(CompositeFeaturePtr aComp = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aCurrent);
+        aComp.get() && isModified; 
+        aComp = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aCurrent)) {
+      isModified =  false;
+      int aSubs = aComp->numberOfSubs(false);
+      for(int a = 0; a < aSubs; a++) {
+        FeaturePtr aSub = aComp->subFeature(a, false);
+        if (myObjs->isLater(aSub, aCurrent)) {
+          isModified =  true;
+          aCurrent = aSub;
+        }
+      }
+    }
+    aDocToAdd->myObjs->addFeature(aFeature, aCurrent);
     if (!aFeature->isAction()) {  // do not add action to the data model
       if (theMakeCurrent)  // after all this feature stays in the document, so make it current
         aDocToAdd->setCurrentFeature(aFeature, false);
@@ -592,6 +667,8 @@ void Model_Document::removeFeature(FeaturePtr theFeature)
 void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis)
 {
   myObjs->moveFeature(theMoved, theAfterThis);
+  if (theAfterThis == currentFeature(true))
+    setCurrentFeature(theMoved, true);
 }
 
 void Model_Document::updateHistory(const std::shared_ptr<ModelAPI_Object> theObject)
@@ -617,8 +694,9 @@ const std::set<std::string> Model_Document::subDocuments(const bool theActivated
   std::list<ResultPtr>::iterator aPartRes = aPartResults.begin();
   for(; aPartRes != aPartResults.end(); aPartRes++) {
     ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aPartRes);
-    if (aPart && (!theActivatedOnly || aPart->isActivated()))
-      aResult.insert(aPart->data()->name());
+    if (aPart && (!theActivatedOnly || aPart->isActivated())) {
+      aResult.insert(aPart->original()->data()->name());
+    }
   }
   return aResult;
 }
@@ -661,8 +739,7 @@ std::shared_ptr<ModelAPI_Feature> Model_Document::currentFeature(const bool theV
     TDF_Label aLab = aRef->Get();
     FeaturePtr aResult = myObjs->feature(aLab);
     if (theVisible) { // get nearest visible (in history) going up
-      while(aResult.get() &&  // sub-composites are never in history
-             (!aResult->isInHistory() || ModelAPI_Tools::compositeOwner(aResult).get())) {
+      while(aResult.get() &&  !aResult->isInHistory()) {
         aResult = myObjs->nextFeature(aResult, true);
       }
     }
@@ -671,8 +748,8 @@ std::shared_ptr<ModelAPI_Feature> Model_Document::currentFeature(const bool theV
   return std::shared_ptr<ModelAPI_Feature>(); // null feature means the higher than first
 }
 
-void Model_Document::setCurrentFeature(std::shared_ptr<ModelAPI_Feature> theCurrent,
-  const bool theVisible)
+void Model_Document::setCurrentFeature(
+  std::shared_ptr<ModelAPI_Feature> theCurrent, const bool theVisible)
 {
   // blocks the flush signals to avoid each objects visualization in the viewer
   // they should not be shown once after all modifications are performed
@@ -681,20 +758,16 @@ void Model_Document::setCurrentFeature(std::shared_ptr<ModelAPI_Feature> theCurr
 
   TDF_Label aRefLab = generalLabel().FindChild(TAG_CURRENT_FEATURE);
   CompositeFeaturePtr aMain; // main feature that may nest the new current
+  std::set<FeaturePtr> anOwners; // composites that contain theCurrent (with any level of nesting)
   if (theCurrent.get()) {
     aMain = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theCurrent);
-    if (!aMain.get()) {
-      // if feature nests into compisite feature, make the composite feature as current
-      const std::set<AttributePtr>& aRefsToMe = theCurrent->data()->refsToMe();
-      std::set<AttributePtr>::const_iterator aRefToMe = aRefsToMe.begin();
-      for(; aRefToMe != aRefsToMe.end(); aRefToMe++) {
-        CompositeFeaturePtr aComposite = 
-          std::dynamic_pointer_cast<ModelAPI_CompositeFeature>((*aRefToMe)->owner());
-        if (aComposite.get() && aComposite->isSub(theCurrent)) {
-          aMain = aComposite;
-          break;
-        }
+    CompositeFeaturePtr anOwner = ModelAPI_Tools::compositeOwner(theCurrent);
+    while(anOwner.get()) {
+      if (!aMain.get()) {
+        aMain = anOwner;
       }
+      anOwners.insert(anOwner);
+      anOwner = ModelAPI_Tools::compositeOwner(anOwner);
     }
   }
 
@@ -739,8 +812,13 @@ void Model_Document::setCurrentFeature(std::shared_ptr<ModelAPI_Feature> theCurr
     if (anIter == theCurrent) aPassed = true;
 
     bool aDisabledFlag = !aPassed;
-    if (aMain.get() && aMain->isSub(anIter)) // sub-elements of not-disabled feature are not disabled
-      aDisabledFlag = false;
+    if (aMain.get()) {
+      if (aMain->isSub(anIter)) // sub-elements of not-disabled feature are not disabled
+        aDisabledFlag = false;
+      else if (anOwners.find(anIter) != anOwners.end()) // disable the higher-level feature is the nested is the current
+        aDisabledFlag = true;
+    }
+
     if (anIter->getKind() == "Parameter") {// parameters are always out of the history of features, but not parameters
       if (theCurrent.get() && theCurrent->getKind() != "Parameter")
         aDisabledFlag = false;
@@ -766,10 +844,6 @@ void Model_Document::setCurrentFeature(std::shared_ptr<ModelAPI_Feature> theCurr
   }
   // unblock  the flush signals and up them after this
   aLoop->activateFlushes(isActive);
-
-  aLoop->flush(aCreateEvent);
-  aLoop->flush(aRedispEvent);
-  aLoop->flush(aDeleteEvent);
 }
 
 void Model_Document::setCurrentFeatureUp()
@@ -779,6 +853,7 @@ void Model_Document::setCurrentFeatureUp()
   FeaturePtr aCurrent = currentFeature(false);
   if (aCurrent.get()) { // if not, do nothing because null is the upper
     FeaturePtr aPrev = myObjs->nextFeature(aCurrent, true);
+    // do not flush: it is called only on remove, it will be flushed in the end of transaction
     setCurrentFeature(aPrev, false);
   }
 }
@@ -807,11 +882,10 @@ std::shared_ptr<ModelAPI_ResultPart> Model_Document::createPart(
 }
 
 std::shared_ptr<ModelAPI_ResultPart> Model_Document::copyPart(
-      const std::shared_ptr<ModelAPI_Result>& theOldPart, 
-      const std::shared_ptr<ModelAPI_ResultPart>& theOrigin, 
-      const int theIndex)
+      const std::shared_ptr<ModelAPI_ResultPart>& theOrigin,
+      const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
 {
-  return myObjs->copyPart(theOldPart, theOrigin, theIndex);
+  return myObjs->copyPart(theOrigin, theFeatureData, theIndex);
 }
 
 std::shared_ptr<ModelAPI_ResultGroup> Model_Document::createGroup(
@@ -829,12 +903,7 @@ std::shared_ptr<ModelAPI_ResultParameter> Model_Document::createParameter(
 std::shared_ptr<ModelAPI_Feature> Model_Document::feature(
     const std::shared_ptr<ModelAPI_Result>& theResult)
 {
-  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 myObjs->feature(aFeatureLab);
-  }
-  return FeaturePtr();
+  return myObjs->feature(theResult);
 }
 
 Standard_Integer HashCode(const TDF_Label& theLab, const Standard_Integer theUpper)
@@ -933,7 +1002,27 @@ std::shared_ptr<ModelAPI_Feature> Model_Document::internalFeature(const int theI
   return myObjs->internalFeature(theIndex);
 }
 
-// Feature that is used for selection in the Part document by the external request
+void Model_Document::synchronizeTransactions()
+{
+  Model_Document* aRoot = 
+    std::dynamic_pointer_cast<Model_Document>(ModelAPI_Session::get()->moduleDocument()).get();
+  if (aRoot == this)
+    return; // don't need to synchronise root with root
+
+  std::shared_ptr<Model_Session> aSession = 
+    std::dynamic_pointer_cast<Model_Session>(Model_Session::get());
+  while(myRedos.size() > aRoot->myRedos.size()) { // remove redos in this
+    aSession->setCheckTransactions(false);
+    redo();
+    aSession->setCheckTransactions(true);
+  }
+  /* this case can not be reproduced in any known case for the current moment, so, just comment
+  while(myRedos.size() < aRoot->myRedos.size()) { // add more redos in this
+    undoInternal(false, true);
+  }*/
+}
+
+/// Feature that is used for selection in the Part document by the external request
 class Model_SelectionInPartFeature : public ModelAPI_Feature {
 public:
   /// Nothing to do in constructor
@@ -974,3 +1063,10 @@ AttributeSelectionListPtr Model_Document::selectionInPartFeature()
   }
   return mySelectionFeature->selectionList("selection");
 }
+
+FeaturePtr Model_Document::lastFeature()
+{
+  if (myObjs)
+    return myObjs->lastFeature();
+  return FeaturePtr();
+}