Salome HOME
Fix for the problem when referenced object is updated by modified-feature result...
[modules/shaper.git] / src / Model / Model_Objects.cpp
index 7324960c650a448bab7a7845a518116d4de3b45e..b4e516cd64e75305d523ad0c750635d14376c1f0 100644 (file)
@@ -60,6 +60,22 @@ static const std::string& groupNameFoldering(const std::string& theGroupID,
   return theGroupID;
 }
 
+// Check theFeature is a first or last feature in folder and return this folder
+static FolderPtr inFolder(const FeaturePtr& theFeature, const std::string& theFolderAttr)
+{
+  const std::set<AttributePtr>& aRefs = theFeature->data()->refsToMe();
+  for (std::set<AttributePtr>::iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt) {
+    if ((*anIt)->id() != theFolderAttr)
+      continue;
+
+    ObjectPtr anOwner = (*anIt)->owner();
+    FolderPtr aFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(anOwner);
+    if (aFolder.get())
+      return aFolder;
+  }
+  return FolderPtr();
+}
+
 
 static const int TAG_OBJECTS = 2;  // tag of the objects sub-tree (features, results)
 
@@ -81,7 +97,7 @@ void Model_Objects::setOwner(DocumentPtr theDoc)
   myDoc = theDoc;
   // update all fields and recreate features and result objects if needed
   TDF_LabelList aNoUpdated;
-  synchronizeFeatures(aNoUpdated, true, true, true, true);
+  synchronizeFeatures(aNoUpdated, true, false, true, true);
   myHistory.clear();
 }
 
@@ -160,12 +176,24 @@ void Model_Objects::addFeature(FeaturePtr theFeature, const FeaturePtr theAfterT
     // store feature in the features array: before "initData" because in macro features
     // in initData it creates new features, appeared later than this
     TDF_Label aPrevFeateureLab;
+    FolderPtr aParentFolder;
     if (theAfterThis.get()) { // searching for the previous feature label
       std::shared_ptr<Model_Data> aPrevData =
         std::dynamic_pointer_cast<Model_Data>(theAfterThis->data());
       if (aPrevData.get()) {
         aPrevFeateureLab = aPrevData->label().Father();
       }
+      // Check if the previous feature is the last feature in a folder,
+      // then the folder should be updated to contain additional feature.
+      // Macro features are not stored in folder.
+      if (!theFeature->isMacro()) {
+        // If the last feature is a sub-feature of composite, use parent feature
+        // to check belonging to a folder.
+        FeaturePtr afterThis = ModelAPI_Tools::compositeOwner(theAfterThis);
+        if (!afterThis)
+          afterThis = theAfterThis;
+        aParentFolder = inFolder(afterThis, ModelAPI_Folder::LAST_FEATURE_ID());
+      }
     }
     AddToRefArray(aFeaturesLab, aFeatureLab, aPrevFeateureLab);
 
@@ -183,6 +211,12 @@ void Model_Objects::addFeature(FeaturePtr theFeature, const FeaturePtr theAfterT
     // must be after binding to the map because of "Box" macro feature that
     // creates other features in "initData"
     initData(theFeature, aFeatureLab, TAG_FEATURE_ARGUMENTS);
+    // put feature to the end of folder if it is added while
+    // the history line is set to the last feature from the folder
+    if (aParentFolder) {
+      aParentFolder->reference(ModelAPI_Folder::LAST_FEATURE_ID())->setValue(theFeature);
+      updateHistory(ModelAPI_Folder::group());
+    }
     // event: feature is added, mist be before "initData" to update OB correctly on Duplicate:
     // first new part, then the content
     static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
@@ -281,6 +315,8 @@ void Model_Objects::removeFeature(FeaturePtr theFeature)
         aComposite->removeFeature(theFeature);
       }
     }
+    // remove feature from folder
+    removeFromFolder(std::list<FeaturePtr>(1, theFeature));
     // this must be before erase since theFeature erasing removes all information about
     // the feature results and groups of results
     // To reproduce: create sketch, extrusion, remove sketch => constructions tree is not updated
@@ -451,7 +487,7 @@ void Model_Objects::createHistory(const std::string& theGroupID)
 
         } else {
           // it may be a folder
-          ObjectPtr aFolder = folder(aRefs->Value(a));
+          const ObjectPtr& aFolder = folder(aRefs->Value(a));
           if (aFolder.get()) {
             // store folder information for the Features group only
             if (isFeature || isFolder) {
@@ -499,11 +535,12 @@ void Model_Objects::updateHistory(const std::string theGroup)
   }
 }
 
-ObjectPtr Model_Objects::folder(TDF_Label theLabel) const
+const ObjectPtr& Model_Objects::folder(TDF_Label theLabel) const
 {
   if (myFolders.IsBound(theLabel))
     return myFolders.Find(theLabel);
-  return ObjectPtr();
+  static ObjectPtr anEmptyResult;
+  return anEmptyResult;
 }
 
 FeaturePtr Model_Objects::feature(TDF_Label theLabel) const
@@ -564,6 +601,9 @@ ObjectPtr Model_Objects::object(const std::string& theGroupID,
     return ObjectPtr();
   createHistory(theGroupID);
   const std::string& aGroupID = groupNameFoldering(theGroupID, theAllowFolder);
+  const std::vector<ObjectPtr>& aVec = myHistory[theGroupID];
+  //if (aVec.size() <= theIndex)
+  //  return aVec[aVec.size() - 1]; // too high index requested (to avoid crash in #2360)
   return aGroupID.empty() ? myHistory[theGroupID][theIndex] : myHistory[aGroupID][theIndex];
 }
 
@@ -784,7 +824,7 @@ void Model_Objects::synchronizeFeatures(
     TDF_Label& aFeatureLab = anUpdatedIter.Value();
     while(aFeatureLab.Depth() > 3)
       aFeatureLab = aFeatureLab.Father();
-    if (myFeatures.IsBound(aFeatureLab))
+    if (myFeatures.IsBound(aFeatureLab) || myFolders.IsBound(aFeatureLab))
       anUpdatedMap.Add(aFeatureLab);
   }
 
@@ -898,6 +938,8 @@ void Model_Objects::synchronizeFeatures(
       // unbind after the "erase" call: on abort sketch
       // is removes sub-objects that corrupts aFIter
       myFolders.UnBind(aFldIt.Key());
+      // reinitialize iterator because unbind may corrupt the previous order in the map
+      aFldIt.Initialize(myFolders);
     }
   }
 
@@ -927,8 +969,8 @@ void Model_Objects::synchronizeFeatures(
     myHistory.clear();
   }
 
-  if (theExecuteFeatures)
-    anOwner->executeFeatures() = false;
+  if (!theExecuteFeatures)
+    anOwner->setExecuteFeatures(false);
   aLoop->activateFlushes(isActive);
 
   if (theFlush) {
@@ -941,8 +983,8 @@ void Model_Objects::synchronizeFeatures(
     aLoop->flush(aRedispEvent);
     aLoop->flush(aToHideEvent);
   }
-  if (theExecuteFeatures)
-    anOwner->executeFeatures() = true;
+  if (!theExecuteFeatures)
+    anOwner->setExecuteFeatures(true);
 }
 
 /// synchronises back references for the given object basing on the collected data
@@ -957,7 +999,10 @@ void Model_Objects::synchronizeBackRefsForObject(const std::set<AttributePtr>& t
   for(; aNewIter != theNewRefs.end(); aNewIter++) {
     if (aData->refsToMe().find(*aNewIter) == aData->refsToMe().end()) {
       FeaturePtr aRefFeat = std::dynamic_pointer_cast<ModelAPI_Feature>((*aNewIter)->owner());
-      aData->addBackReference(aRefFeat, (*aNewIter)->id());
+      if (aRefFeat)
+        aData->addBackReference(aRefFeat, (*aNewIter)->id());
+      else // add back reference to a folder
+        aData->addBackReference((*aNewIter)->owner(), (*aNewIter)->id());
     }
   }
   if (theNewRefs.size() != aData->refsToMe().size()) { // some back ref must be removed
@@ -993,6 +1038,35 @@ void Model_Objects::synchronizeBackRefsForObject(const std::set<AttributePtr>& t
       } else aCurrentIter++;
     }
   }
+  // for the last feature in the folder, check if it is a sub-feature,
+  // then refer the folder to a top-level parent composite feature
+  const std::set<AttributePtr>& aRefs = aData->refsToMe();
+  std::set<AttributePtr>::iterator anIt = aRefs.begin();
+  for (; anIt != aRefs.end(); ++anIt)
+    if ((*anIt)->id() == ModelAPI_Folder::LAST_FEATURE_ID())
+      break;
+  if (anIt != aRefs.end()) {
+    FeaturePtr aFeature = ModelAPI_Feature::feature(theObject);
+    if (aFeature) {
+      CompositeFeaturePtr aParent;
+      CompositeFeaturePtr aGrandParent = ModelAPI_Tools::compositeOwner(aFeature);
+      do {
+        aParent = aGrandParent;
+        if (aGrandParent)
+          aGrandParent = ModelAPI_Tools::compositeOwner(aParent);
+      } while (aGrandParent.get());
+      if (aParent) {
+        ObjectPtr aFolder = (*anIt)->owner();
+        // remove reference from the current feature
+        aData->removeBackReference(aFolder, ModelAPI_Folder::LAST_FEATURE_ID());
+        // set reference to a top-level parent
+        aFolder->data()->reference(ModelAPI_Folder::LAST_FEATURE_ID())->setValue(aParent);
+        std::shared_ptr<Model_Data> aParentData =
+            std::dynamic_pointer_cast<Model_Data>(aParent->data());
+        aParentData->addBackReference(aFolder, ModelAPI_Folder::LAST_FEATURE_ID());
+      }
+    }
+  }
   aData->updateConcealmentFlag();
 }
 
@@ -1114,8 +1188,10 @@ bool Model_Objects::hasCustomName(DataPtr theFeatureData,
     return false;
   }
 
-  theParentName = ModelAPI_Tools::getDefaultName(theResult, theResultIndex);
-  return true;
+  std::pair<std::string, bool> aName = ModelAPI_Tools::getDefaultName(theResult, theResultIndex);
+  if (aName.second)
+    theParentName = aName.first;
+  return aName.second;
 }
 
 void Model_Objects::storeResult(std::shared_ptr<ModelAPI_Data> theFeatureData,
@@ -1128,7 +1204,11 @@ void Model_Objects::storeResult(std::shared_ptr<ModelAPI_Data> theFeatureData,
   if (theResult->data()->name().empty()) {
     // if was not initialized, generate event and set a name
     std::string aNewName = theFeatureData->name();
-    if (!hasCustomName(theFeatureData, theResult, theResultIndex, aNewName)) {
+    if (hasCustomName(theFeatureData, theResult, theResultIndex, aNewName)) {
+      // if the name of result is user-defined, then, at first time, assign name of the result
+      // by empty string to be sure that corresponding flag in the data model is set
+      theResult->data()->setName("");
+    } else {
       std::stringstream aName;
       aName << aNewName;
       // if there are several results (issue #899: any number of result),
@@ -1335,6 +1415,7 @@ void Model_Objects::removeFolder(std::shared_ptr<ModelAPI_Folder> theFolder)
   updateHistory(ModelAPI_Feature::group());
 }
 
+// Returns one of the limiting features of the list
 static FeaturePtr limitingFeature(std::list<FeaturePtr>& theFeatures, const bool isLast)
 {
   FeaturePtr aFeature;
@@ -1348,6 +1429,13 @@ static FeaturePtr limitingFeature(std::list<FeaturePtr>& theFeatures, const bool
   return aFeature;
 }
 
+// Verify the feature is sub-element in composite feature or it is not used in the history
+static bool isSkippedFeature(FeaturePtr theFeature)
+{
+  bool isSub = ModelAPI_Tools::compositeOwner(theFeature).get() != NULL;
+  return isSub || (theFeature && !theFeature->isInHistory());
+}
+
 std::shared_ptr<ModelAPI_Folder> Model_Objects::findFolder(
       const std::list<std::shared_ptr<ModelAPI_Feature> >& theFeatures,
       const bool theBelow)
@@ -1385,14 +1473,20 @@ std::shared_ptr<ModelAPI_Folder> Model_Objects::findFolder(
     if (theBelow)
       continue;
 
+    // if feature is in sub-component, skip it
+    FeaturePtr aCurFeature = feature(aCurLabel);
+    if (isSkippedFeature(aCurFeature))
+      continue;
+
     if (!aLastFeatureInFolder.IsNull()) {
       if (IsEqual(aCurLabel, aLastFeatureInFolder))
         aLastFeatureInFolder.Nullify(); // the last feature in the folder is achived
       continue;
     }
 
-    aFoundFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(folder(aCurLabel));
-    if (aFoundFolder) {
+    const ObjectPtr& aFolderObj = folder(aCurLabel);
+    if (aFolderObj.get()) {
+      aFoundFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(aFolderObj);
       AttributeReferencePtr aLastFeatAttr =
           aFoundFolder->reference(ModelAPI_Folder::LAST_FEATURE_ID());
       if (aLastFeatAttr) {
@@ -1408,8 +1502,15 @@ std::shared_ptr<ModelAPI_Folder> Model_Objects::findFolder(
   }
 
   if (theBelow && aRefIndex < aRefs->Upper()) {
+    TDF_Label aLabel;
+    // skip following features which are sub-components or not in history
+    for (int anIndex = aRefIndex + 1; anIndex <= aRefs->Upper(); ++anIndex) {
+      aLabel = aRefs->Value(anIndex);
+      FeaturePtr aCurFeature = feature(aLabel);
+      if (!isSkippedFeature(aCurFeature))
+        break;
+    }
     // check the next object is a folder
-    TDF_Label aLabel = aRefs->Value(aRefIndex + 1);
     aFoundFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(folder(aLabel));
   }
 
@@ -1423,14 +1524,13 @@ std::shared_ptr<ModelAPI_Folder> Model_Objects::findFolder(
        !aFeatures.empty() && aRefIndex >= aRefs->Lower() && aRefIndex <= aRefs->Upper();
        aRefIndex += aStep) {
     TDF_Label aCurLabel = aRefs->Value(aRefIndex);
-    TDF_Label aFeatureLabel;
+    // if feature is in sub-component, skip it
+    FeaturePtr aCurFeature = feature(aCurLabel);
+    if (isSkippedFeature(aCurFeature))
+      continue;
 
     aLimitingFeature = limitingFeature(aFeatures, theBelow);
-    aData = std::static_pointer_cast<Model_Data>(aLimitingFeature->data());
-    if (aData && aData->isValid())
-      aFeatureLabel = aData->label().Father();
-
-    if (!IsEqual(aCurLabel, aFeatureLabel))
+    if (!aCurFeature->data()->isEqual(aLimitingFeature->data()))
       return FolderPtr(); // not a sequential list
   }
 
@@ -1514,21 +1614,6 @@ bool Model_Objects::moveToFolder(
   return true;
 }
 
-static FolderPtr inFolder(const FeaturePtr& theFeature, const std::string& theFolderAttr)
-{
-  const std::set<AttributePtr>& aRefs = theFeature->data()->refsToMe();
-  for (std::set<AttributePtr>::iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt) {
-    if ((*anIt)->id() != theFolderAttr)
-      continue;
-
-    ObjectPtr anOwner = (*anIt)->owner();
-    FolderPtr aFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(anOwner);
-    if (aFolder.get())
-      return aFolder;
-  }
-  return FolderPtr();
-}
-
 static FolderPtr isExtractionCorrect(const FolderPtr& theFirstFeatureFolder,
                                      const FolderPtr& theLastFeatureFolder,
                                      bool& isExtractBefore)
@@ -1596,15 +1681,29 @@ bool Model_Objects::removeFromFolder(
     TDF_Label aFolderLabel = aData->label().Father();
     TDF_Label aPrevFeatureLabel = aRefs->Value(aRefIndex);
     // update start reference of the folder
-    if (aFolderStartFeature.get())
-      aFolderStartFeature = feature(aRefs->Value(aRefIndex + 1));
+    if (aFolderStartFeature.get()) {
+      FeaturePtr aNewStartFeature;
+      do { // skip all features placed in the composite features
+        aPrevFeatureLabel = aRefs->Value(aRefIndex++);
+        aNewStartFeature =
+            aRefIndex <= aRefs->Upper() ? feature(aRefs->Value(aRefIndex)) : FeaturePtr();
+      } while (aNewStartFeature && isSkippedFeature(aNewStartFeature));
+      aFolderStartFeature = aNewStartFeature;
+    }
     // move the folder in the list of references after the last feature from the list
     RemoveFromRefArray(aFeaturesLab, aFolderLabel);
     AddToRefArray(aFeaturesLab, aFolderLabel, aPrevFeatureLabel);
   } else {
     // update end reference of the folder
-    if (aFolderEndFeature.get())
-      aFolderEndFeature = feature(aRefs->Value(aRefIndex - 1));
+    if (aFolderEndFeature.get()) {
+      FeaturePtr aNewEndFeature;
+      do { // skip all features placed in the composite features
+        --aRefIndex;
+        aNewEndFeature =
+            aRefIndex >= aRefs->Lower() ? feature(aRefs->Value(aRefIndex)) : FeaturePtr();
+      } while (aNewEndFeature && isSkippedFeature(aNewEndFeature));
+      aFolderEndFeature = aNewEndFeature;
+    }
   }
 
   // update folder references
@@ -1634,17 +1733,32 @@ FolderPtr Model_Objects::findContainingFolder(const FeaturePtr& theFeature, int&
   TDF_Label aLastFeatureLabel;
 
   for (int aRefIndex = aRefs->Lower(); aRefIndex <= aRefs->Upper(); ++aRefIndex) {
+    TDF_Label aCurLabel = aRefs->Value(aRefIndex);
+
     if (aFoundFolder)
       ++theIndexInFolder;
 
-    TDF_Label aCurLabel = aRefs->Value(aRefIndex);
-    if (aCurLabel == aLabelToFind) // the feature is reached
+    if (aCurLabel == aLabelToFind) { // the feature is reached
+      if (aFoundFolder) {
+        if (isSkippedFeature(theFeature)) {
+          theIndexInFolder = -1;
+          return FolderPtr();
+        }
+        // decrease the index of the feature in the folder by the number of skipped features
+        for (int anIndex = theIndexInFolder - 1; anIndex > 0; anIndex--) {
+          aCurLabel = aRefs->Value(aRefIndex - anIndex);
+          if (isSkippedFeature(feature(aCurLabel)))
+            theIndexInFolder--;
+        }
+      }
       return aFoundFolder;
+    }
 
     if (!aFoundFolder) {
       // if the current label refers to a folder, feel all necessary data
-      aFoundFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(folder(aCurLabel));
-      if (aFoundFolder) {
+      const ObjectPtr& aFolderObj = folder(aCurLabel);
+      if (aFolderObj.get()) {
+        aFoundFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(aFolderObj);
         theIndexInFolder = -1;
 
         AttributeReferencePtr aLastRef =
@@ -1827,9 +1941,16 @@ FeaturePtr Model_Objects::nextFeature(FeaturePtr theCurrent, const bool theRever
   std::shared_ptr<Model_Data> aData = std::static_pointer_cast<Model_Data>(theCurrent->data());
   if (aData.get() && aData->isValid()) {
     TDF_Label aFeatureLabel = aData->label().Father();
-    TDF_Label aNextLabel = nextLabel(aFeatureLabel, theReverse);
-    if (!aNextLabel.IsNull())
-      return feature(aNextLabel);
+    do {
+      TDF_Label aNextLabel = nextLabel(aFeatureLabel, theReverse);
+      if (aNextLabel.IsNull())
+        break; // last or something is wrong
+      FeaturePtr aFound = feature(aNextLabel);
+      if (aFound)
+        return aFound; // the feature is found
+      // if the next label is a folder, skip it
+      aFeatureLabel = folder(aNextLabel).get() ? aNextLabel : TDF_Label();
+    } while (!aFeatureLabel.IsNull());
   }
   return FeaturePtr(); // not found, last, or something is wrong
 }
@@ -1847,6 +1968,11 @@ FeaturePtr Model_Objects::lastFeature()
 {
   Handle(TDataStd_ReferenceArray) aRefs;
   if (featuresLabel().FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs)) {
+    FeaturePtr aLast = feature(aRefs->Value(aRefs->Upper()));
+    if (!aLast.get() && aRefs->Length() != 0) { // erase the invalid feature from the array
+      RemoveFromRefArray(featuresLabel(), aRefs->Value(aRefs->Upper()));
+      return lastFeature(); // try once again, after the last was removed
+    }
     return feature(aRefs->Value(aRefs->Upper()));
   }
   return FeaturePtr(); // no features at all