Salome HOME
Fix for the problem when referenced object is updated by modified-feature result...
[modules/shaper.git] / src / Model / Model_Objects.cpp
index 92b57970c79b32ad74c0852767437b952861d5d7..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,21 +824,28 @@ 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);
   }
 
   // update all objects by checking are they on labels or not
-  std::set<FeaturePtr> aNewFeatures, aKeptFeatures;
+  std::set<ObjectPtr> aNewFeatures, aKeptFeatures;
   TDF_ChildIDIterator aLabIter(featuresLabel(), TDataStd_Comment::GetID());
   for (; aLabIter.More(); aLabIter.Next()) {
     TDF_Label aFeatureLabel = aLabIter.Value()->Label();
-    FeaturePtr aFeature;
-    if (!myFeatures.IsBound(aFeatureLabel)) {  // a new feature is inserted
+    if (!myFeatures.IsBound(aFeatureLabel) && !myFolders.IsBound(aFeatureLabel)) {
+      // a new feature or folder is inserted
+
+      std::string aFeatureID = TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(
+                               aLabIter.Value())->Get()).ToCString();
+      bool isFolder = aFeatureID == ModelAPI_Folder::ID();
+
+      std::shared_ptr<Model_Session> aSession =
+          std::dynamic_pointer_cast<Model_Session>(ModelAPI_Session::get());
+
       // create a feature
-      aFeature = std::dynamic_pointer_cast<Model_Session>(ModelAPI_Session::get())->createFeature(
-        TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(aLabIter.Value())->Get())
-        .ToCString(), anOwner);
+      ObjectPtr aFeature = isFolder ? ObjectPtr(new ModelAPI_Folder)
+                                    : ObjectPtr(aSession->createFeature(aFeatureID, anOwner));
       if (!aFeature.get()) {
         // somethig is wrong, most probably, the opened document has invalid structure
         Events_InfoMessage("Model_Objects", "Invalid type of object in the document").send();
@@ -807,7 +854,10 @@ void Model_Objects::synchronizeFeatures(
       }
       aFeature->init();
       // this must be before "setData" to redo the sketch line correctly
-      myFeatures.Bind(aFeatureLabel, aFeature);
+      if (isFolder)
+        myFolders.Bind(aFeatureLabel, aFeature);
+      else
+        myFeatures.Bind(aFeatureLabel, std::dynamic_pointer_cast<ModelAPI_Feature>(aFeature));
       aNewFeatures.insert(aFeature);
       initData(aFeature, aFeatureLabel, TAG_FEATURE_ARGUMENTS);
       updateHistory(aFeature);
@@ -815,18 +865,25 @@ void Model_Objects::synchronizeFeatures(
       // event: model is updated
       ModelAPI_EventCreator::get()->sendUpdated(aFeature, aCreateEvent);
     } else {  // nothing is changed, both iterators are incremented
-      aFeature = myFeatures.Find(aFeatureLabel);
-      aKeptFeatures.insert(aFeature);
+      ObjectPtr anObject;
+      FeaturePtr aFeature;
+      if (myFeatures.Find(aFeatureLabel, aFeature)) {
+        aKeptFeatures.insert(aFeature);
+        anObject = aFeature;
+      } else
+        if (myFolders.Find(aFeatureLabel, anObject))
+          aKeptFeatures.insert(anObject);
+
       if (anUpdatedMap.Contains(aFeatureLabel)) {
         if (!theOpen) { // on abort/undo/redo reinitialize attributes if something is changed
           std::list<std::shared_ptr<ModelAPI_Attribute> > anAttrs =
-            aFeature->data()->attributes("");
+            anObject->data()->attributes("");
           std::list<std::shared_ptr<ModelAPI_Attribute> >::iterator anAttr = anAttrs.begin();
           for(; anAttr != anAttrs.end(); anAttr++)
             (*anAttr)->reinit();
         }
-        ModelAPI_EventCreator::get()->sendUpdated(aFeature, anUpdateEvent);
-        if (aFeature->getKind() == "Parameter") {
+        ModelAPI_EventCreator::get()->sendUpdated(anObject, anUpdateEvent);
+        if (aFeature && aFeature->getKind() == "Parameter") {
           // if parameters are changed, update the results (issue 937)
           const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
           std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
@@ -865,6 +922,26 @@ void Model_Objects::synchronizeFeatures(
     } else
       aFIter.Next();
   }
+  // verify folders are checked: if not => is was removed
+  for (NCollection_DataMap<TDF_Label, ObjectPtr>::Iterator aFldIt(myFolders);
+       aFldIt.More(); aFldIt.Next()) {
+    ObjectPtr aCurObj = aFldIt.Value();
+    if (aKeptFeatures.find(aCurObj) == aKeptFeatures.end() &&
+        aNewFeatures.find(aCurObj) == aNewFeatures.end()) {
+      ModelAPI_EventCreator::get()->sendDeleted(myDoc, ModelAPI_Folder::group());
+      // results of this feature must be redisplayed (hided)
+      // redisplay also removed feature (used for sketch and AISObject)
+      ModelAPI_EventCreator::get()->sendUpdated(aCurObj, aRedispEvent);
+      updateHistory(aCurObj);
+      aCurObj->erase();
+
+      // 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);
+    }
+  }
 
   if (theUpdateReferences) {
     synchronizeBackRefs();
@@ -892,8 +969,8 @@ void Model_Objects::synchronizeFeatures(
     myHistory.clear();
   }
 
-  if (theExecuteFeatures)
-    anOwner->executeFeatures() = false;
+  if (!theExecuteFeatures)
+    anOwner->setExecuteFeatures(false);
   aLoop->activateFlushes(isActive);
 
   if (theFlush) {
@@ -906,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
@@ -922,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
@@ -958,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();
 }
 
@@ -1079,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,
@@ -1093,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),
@@ -1300,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;
@@ -1313,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)
@@ -1350,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) {
@@ -1373,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));
   }
 
@@ -1388,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
   }
 
@@ -1479,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)
@@ -1561,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
@@ -1580,6 +1714,74 @@ bool Model_Objects::removeFromFolder(
   return true;
 }
 
+FolderPtr Model_Objects::findContainingFolder(const FeaturePtr& theFeature, int& theIndexInFolder)
+{
+  // search the label in the list of references
+  TDF_Label aFeaturesLab = featuresLabel();
+  Handle(TDataStd_ReferenceArray) aRefs;
+  if (!aFeaturesLab.FindAttribute(TDataStd_ReferenceArray::GetID(), aRefs))
+    return FolderPtr(); // no reference array (something is wrong)
+
+  std::shared_ptr<Model_Data> aData =
+      std::static_pointer_cast<Model_Data>(theFeature->data());
+  if (!aData || !aData->isValid())
+    return FolderPtr();
+  TDF_Label aLabelToFind = aData->label().Father();
+
+  theIndexInFolder = -1;
+  FolderPtr aFoundFolder;
+  TDF_Label aLastFeatureLabel;
+
+  for (int aRefIndex = aRefs->Lower(); aRefIndex <= aRefs->Upper(); ++aRefIndex) {
+    TDF_Label aCurLabel = aRefs->Value(aRefIndex);
+
+    if (aFoundFolder)
+      ++theIndexInFolder;
+
+    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
+      const ObjectPtr& aFolderObj = folder(aCurLabel);
+      if (aFolderObj.get()) {
+        aFoundFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(aFolderObj);
+        theIndexInFolder = -1;
+
+        AttributeReferencePtr aLastRef =
+            aFoundFolder->reference(ModelAPI_Folder::LAST_FEATURE_ID());
+        if (aLastRef->value()) {
+          aData = std::static_pointer_cast<Model_Data>(aLastRef->value()->data());
+          if (aData && aData->isValid())
+            aLastFeatureLabel = aData->label().Father();
+        } else // folder is empty
+          aFoundFolder = FolderPtr();
+      }
+    } else if (aLastFeatureLabel == aCurLabel) {
+      // folder is finished, clear all stored data
+      theIndexInFolder = -1;
+      aFoundFolder = FolderPtr();
+    }
+  }
+
+  // folder is not found
+  theIndexInFolder = -1;
+  return FolderPtr();
+}
+
 
 std::shared_ptr<ModelAPI_Feature> Model_Objects::feature(
     const std::shared_ptr<ModelAPI_Result>& theResult)
@@ -1739,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
 }
@@ -1759,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