Salome HOME
Fix for the issue #937 and crashes:
[modules/shaper.git] / src / Model / Model_Document.cpp
index 1c61d4f7c1047ebb53f5e53c48de7556fec1b505..d85fb7b292b9abcbdddf464d43ece287587726ac 100644 (file)
@@ -13,6 +13,8 @@
 #include <ModelAPI_ResultPart.h>
 #include <ModelAPI_Validator.h>
 #include <ModelAPI_CompositeFeature.h>
+#include <ModelAPI_AttributeSelectionList.h>
+#include <ModelAPI_Tools.h>
 
 #include <Events_Loop.h>
 #include <Events_Error.h>
 #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
@@ -47,6 +54,7 @@ static const int TAG_GENERAL = 1;  // general properties tag
 // general sub-labels
 static const int TAG_CURRENT_FEATURE = 1; ///< where the reference to the current feature label is located (or no attribute if null feature)
 static const int TAG_CURRENT_TRANSACTION = 2; ///< integer, index of the cransaction
+static const int TAG_SELECTION_FEATURE = 3; ///< integer, tag of the selection feature label
 
 Model_Document::Model_Document(const std::string theID, const std::string theKind)
     : myID(theID), myKind(theKind), myIsActive(false),
@@ -60,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();
 }
 
@@ -270,10 +280,12 @@ 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)
       myDoc->Close();
+    mySelectionFeature.reset();
   } else {
     setCurrentFeature(FeaturePtr(), false); // disables all features
   }
@@ -379,10 +391,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();
@@ -392,9 +435,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
@@ -403,7 +453,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
@@ -448,8 +498,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
@@ -461,9 +515,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);
   }
@@ -497,8 +551,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);
@@ -507,7 +564,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);
 }
@@ -561,7 +618,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);
@@ -588,6 +662,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)
@@ -613,8 +689,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;
 }
@@ -657,7 +734,8 @@ 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() && !aResult->isInHistory()) {
+      while(aResult.get() &&  // sub-composites are never in history
+             (!aResult->isInHistory() || ModelAPI_Tools::compositeOwner(aResult).get())) {
         aResult = myObjs->nextFeature(aResult, true);
       }
     }
@@ -801,6 +879,13 @@ std::shared_ptr<ModelAPI_ResultPart> Model_Document::createPart(
   return myObjs->createPart(theFeatureData, theIndex);
 }
 
+std::shared_ptr<ModelAPI_ResultPart> Model_Document::copyPart(
+      const std::shared_ptr<ModelAPI_ResultPart>& theOrigin,
+      const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
+{
+  return myObjs->copyPart(theOrigin, theFeatureData, theIndex);
+}
+
 std::shared_ptr<ModelAPI_ResultGroup> Model_Document::createGroup(
     const std::shared_ptr<ModelAPI_Data>& theFeatureData, const int theIndex)
 {
@@ -816,12 +901,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)
@@ -919,3 +999,52 @@ 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
+class Model_SelectionInPartFeature : public ModelAPI_Feature {
+public:
+  /// Nothing to do in constructor
+  Model_SelectionInPartFeature() : ModelAPI_Feature() {}
+
+  /// Returns the unique kind of a feature
+  virtual const std::string& getKind() {
+    static std::string MY_KIND("InternalSelectionInPartFeature");
+    return MY_KIND;
+  }
+  /// Request for initialization of data model of the object: adding all attributes
+  virtual void initAttributes() {
+    data()->addAttribute("selection", ModelAPI_AttributeSelectionList::typeId());
+  }
+  /// Nothing to do in the execution function
+  virtual void execute() {}
+
+};
+
+//! Returns the feature that is used for calculation of selection externally from the document
+AttributeSelectionListPtr Model_Document::selectionInPartFeature()
+{
+  // return already created, otherwise create
+  if (!mySelectionFeature.get() || !mySelectionFeature->data()->isValid()) {
+    // create a new one
+    mySelectionFeature = FeaturePtr(new Model_SelectionInPartFeature);
+  
+    TDF_Label aFeatureLab = generalLabel().FindChild(TAG_SELECTION_FEATURE);
+    std::shared_ptr<Model_Data> aData(new Model_Data);
+    aData->setLabel(aFeatureLab.FindChild(1));
+    aData->setObject(mySelectionFeature);
+    mySelectionFeature->setDoc(myObjs->owner());
+    mySelectionFeature->setData(aData);
+    std::string aName = id() + "_Part";
+    mySelectionFeature->data()->setName(aName);
+    mySelectionFeature->setDoc(myObjs->owner());
+    mySelectionFeature->initAttributes();
+  }
+  return mySelectionFeature->selectionList("selection");
+}
+
+FeaturePtr Model_Document::lastFeature()
+{
+  if (myObjs)
+    return myObjs->lastFeature();
+  return FeaturePtr();
+}