Salome HOME
Explicit initialization of myDisabled and myConcealed fields of base objects (to...
[modules/shaper.git] / src / Model / Model_Document.cpp
index 517b4231f5695d97fe0e7aaba24849cf97b8b46c..a657f75ef8680bbfdd118eaa418ae15fb3e71cad 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
@@ -46,9 +53,11 @@ 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),
+    : myID(theID), myKind(theKind), myIsActive(false),
       myDoc(new TDocStd_Document("BinOcaf"))  // binary OCAF format
 {
   myObjs = new Model_Objects(myDoc->Main());
@@ -59,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();
 }
 
@@ -269,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
   }
@@ -292,6 +305,7 @@ void Model_Document::startOperation()
     myDoc->NewCommand();
   }
   // starts a new operation
+  incrementTransactionID();
   myTransactions.push_back(Transaction());
   if (!myNestedNum.empty())
     (*myNestedNum.rbegin())++;
@@ -377,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();
@@ -390,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
@@ -401,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
@@ -446,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
@@ -459,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);
   }
@@ -495,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);
@@ -505,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);
 }
@@ -548,6 +607,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
@@ -559,7 +619,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);
@@ -583,6 +660,13 @@ void Model_Document::removeFeature(FeaturePtr theFeature)
   myObjs->removeFeature(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)
 {
   myObjs->updateHistory(theObject);
@@ -606,8 +690,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;
 }
@@ -650,7 +735,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);
       }
     }
@@ -662,6 +748,11 @@ std::shared_ptr<ModelAPI_Feature> Model_Document::currentFeature(const bool theV
 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
+  Events_Loop* aLoop = Events_Loop::loop();
+  bool isActive = aLoop->activateFlushes(false);
+
   TDF_Label aRefLab = generalLabel().FindChild(TAG_CURRENT_FEATURE);
   CompositeFeaturePtr aMain; // main feature that may nest the new current
   if (theCurrent.get()) {
@@ -694,7 +785,10 @@ void Model_Document::setCurrentFeature(std::shared_ptr<ModelAPI_Feature> theCurr
   }
   if (theCurrent.get()) {
     std::shared_ptr<Model_Data> aData = std::static_pointer_cast<Model_Data>(theCurrent->data());
-    if (!aData.get() || !aData->isValid()) return;
+    if (!aData.get() || !aData->isValid()) {
+      aLoop->activateFlushes(isActive);
+      return;
+    }
     TDF_Label aFeatureLabel = aData->label().Father();
 
     Handle(TDF_Reference) aRef;
@@ -707,12 +801,13 @@ void Model_Document::setCurrentFeature(std::shared_ptr<ModelAPI_Feature> theCurr
     aRefLab.ForgetAttribute(TDF_Reference::GetID());
   }
   // make all features after this feature disabled in reversed order (to remove results without deps)
-  static Events_Loop* aLoop = Events_Loop::loop();
   static Events_ID aRedispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
+  static Events_ID aCreateEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
   static Events_ID aDeleteEvent = aLoop->eventByName(EVENT_OBJECT_DELETED);
 
   bool aPassed = false; // flag that the current object is already passed in cycle
   FeaturePtr anIter = myObjs->lastFeature();
+  bool aWasChanged = false;
   for(; anIter.get(); anIter = myObjs->nextFeature(anIter, true)) {
     // check this before passed become enabled: the current feature is enabled!
     if (anIter == theCurrent) aPassed = true;
@@ -720,16 +815,33 @@ void Model_Document::setCurrentFeature(std::shared_ptr<ModelAPI_Feature> theCurr
     bool aDisabledFlag = !aPassed;
     if (aMain.get() && aMain->isSub(anIter)) // sub-elements of not-disabled feature are not disabled
       aDisabledFlag = false;
-    if (anIter->getKind() == "Parameter") // parameters are always out of the history
-      aDisabledFlag = false;
+    if (anIter->getKind() == "Parameter") {// parameters are always out of the history of features, but not parameters
+      if (theCurrent.get() && theCurrent->getKind() != "Parameter")
+        aDisabledFlag = false;
+    }
     if (anIter->setDisabled(aDisabledFlag)) {
       // state of feature is changed => so feature become updated
       static Events_ID anUpdateEvent = aLoop->eventByName(EVENT_OBJECT_UPDATED);
       ModelAPI_EventCreator::get()->sendUpdated(anIter, anUpdateEvent);
       // flush is in the end of this method
       ModelAPI_EventCreator::get()->sendUpdated(anIter, aRedispEvent /*, false*/);
+      aWasChanged = true;
+    }
+    // update for everyone the concealment flag immideately: on edit feature in the midle of history
+    if (aWasChanged) {
+      const std::list<std::shared_ptr<ModelAPI_Result> >& aResList = anIter->results();
+      std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRes = aResList.begin();
+      for(; aRes != aResList.end(); aRes++) {
+        if ((*aRes).get() && (*aRes)->data()->isValid() && !(*aRes)->isDisabled())
+          std::dynamic_pointer_cast<Model_Data>((*aRes)->data())->updateConcealmentFlag();
+      }
+
     }
   }
+  // unblock  the flush signals and up them after this
+  aLoop->activateFlushes(isActive);
+
+  aLoop->flush(aCreateEvent);
   aLoop->flush(aRedispEvent);
   aLoop->flush(aDeleteEvent);
 }
@@ -768,6 +880,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)
 {
@@ -783,12 +902,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)
@@ -818,3 +932,140 @@ ResultPtr Model_Document::findByName(const std::string theName)
 {
   return myObjs->findByName(theName);
 }
+
+std::list<std::shared_ptr<ModelAPI_Feature> > Model_Document::allFeatures()
+{
+  return myObjs->allFeatures();
+}
+
+void Model_Document::setActive(const bool theFlag)
+{
+  if (theFlag != myIsActive) {
+    myIsActive = theFlag;
+    // redisplay all the objects of this part
+    static Events_Loop* aLoop = Events_Loop::loop();
+    static Events_ID aRedispEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
+
+    for(int a = size(ModelAPI_Feature::group()) - 1; a >= 0; a--) {
+      FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(
+        object(ModelAPI_Feature::group(), a));
+      if (aFeature.get() && aFeature->data()->isValid()) {
+        const std::list<std::shared_ptr<ModelAPI_Result> >& aResList = aFeature->results();
+        std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRes = aResList.begin();
+        for(; aRes != aResList.end(); aRes++) {
+          ModelAPI_EventCreator::get()->sendUpdated(*aRes, aRedispEvent);
+        }
+      }
+    }
+  }
+}
+
+bool Model_Document::isActive() const
+{
+  return myIsActive;
+}
+
+int Model_Document::transactionID()
+{
+  Handle(TDataStd_Integer) anIndex;
+  if (!generalLabel().FindChild(TAG_CURRENT_TRANSACTION).
+      FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
+    anIndex = TDataStd_Integer::Set(generalLabel().FindChild(TAG_CURRENT_TRANSACTION), 1);
+  }
+  return anIndex->Get();
+}
+
+void Model_Document::incrementTransactionID()
+{
+  int aNewVal = transactionID() + 1;
+  TDataStd_Integer::Set(generalLabel().FindChild(TAG_CURRENT_TRANSACTION), aNewVal);
+}
+void Model_Document::decrementTransactionID()
+{
+  int aNewVal = transactionID() - 1;
+  TDataStd_Integer::Set(generalLabel().FindChild(TAG_CURRENT_TRANSACTION), aNewVal);
+}
+
+bool Model_Document::isOpened()
+{
+  return myObjs && !myDoc.IsNull();
+}
+
+int Model_Document::numInternalFeatures()
+{
+  return myObjs->numInternalFeatures();
+}
+
+std::shared_ptr<ModelAPI_Feature> Model_Document::internalFeature(const int theIndex)
+{
+  return myObjs->internalFeature(theIndex);
+}
+
+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
+  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();
+}