Salome HOME
Issue #1659 New widget for supporting optional inputs : correction for enclosed cases.
[modules/shaper.git] / src / Model / Model_Update.cpp
old mode 100644 (file)
new mode 100755 (executable)
index 6e9f446..1f6c831
@@ -27,7 +27,7 @@
 #include <GeomDataAPI_Point2D.h>
 #include <Events_Loop.h>
 #include <Events_LongOp.h>
-#include <Events_Error.h>
+#include <Events_InfoMessage.h>
 #include <Config_PropManager.h>
 
 using namespace std;
@@ -52,41 +52,67 @@ Model_Update::Model_Update()
   aLoop->registerListener(this, kOpStartEvent);
   static const Events_ID kStabilityEvent = aLoop->eventByName(EVENT_STABILITY_CHANGED);
   aLoop->registerListener(this, kStabilityEvent);
+  static const Events_ID kPreviewBlockedEvent = aLoop->eventByName(EVENT_PREVIEW_BLOCKED);
+  aLoop->registerListener(this, kPreviewBlockedEvent);
+  static const Events_ID kPreviewRequestedEvent = aLoop->eventByName(EVENT_PREVIEW_REQUESTED);
+  aLoop->registerListener(this, kPreviewRequestedEvent);
+  static const Events_ID kReorderEvent = aLoop->eventByName(EVENT_ORDER_UPDATED);
+  aLoop->registerListener(this, kReorderEvent);
 
   //  Config_PropManager::findProp("Model update", "automatic_rebuild")->value() == "true";
   myIsParamUpdated = false;
   myIsFinish = false;
   myIsProcessed = false;
+  myIsPreviewBlocked = false;
 }
 
-void Model_Update::addModified(FeaturePtr theFeature, FeaturePtr theReason) {
-  if (!theFeature->isPreviewNeeded() && !myIsFinish) {
+bool Model_Update::addModified(FeaturePtr theFeature, FeaturePtr theReason) {
+
+  if (!theFeature->data()->isValid())
+    return false; // delete an extrusion created on the sketch
+
+  if (theFeature->isPersistentResult()) {
+    if (!std::dynamic_pointer_cast<Model_Document>((theFeature)->document())->executeFeatures())
+      return false;
+  }
+
+  // update arguments for "apply button" state change
+  if ((!theFeature->isPreviewNeeded() && !myIsFinish) || myIsPreviewBlocked) {
     myProcessOnFinish.insert(theFeature);
 #ifdef DEB_UPDATE
       std::cout<<"*** Add process on finish "<<theFeature->name()<<std::endl;
 #endif
-    updateArguments(theFeature);
-    if (theFeature->data()->execState() == ModelAPI_StateMustBeUpdated) {
-      theFeature->data()->execState(ModelAPI_StateDone);
-      static ModelAPI_ValidatorsFactory* aFactory = ModelAPI_Session::get()->validators();
-      aFactory->validate(theFeature); // need to be validated to update the "Apply" state if not previewed
+    // keeps the currently updated features to avoid infinitive cycling here: where feature on
+    // "updateArguments" sends "updated" (in selection attribute) and goes here again
+    static std::set<FeaturePtr> aCurrentlyUpdated;
+    if (aCurrentlyUpdated.find(theFeature) == aCurrentlyUpdated.end()) {
+      aCurrentlyUpdated.insert(theFeature);
+      updateArguments(theFeature);
+      aCurrentlyUpdated.erase(theFeature);
     }
-    return;
+    // make it without conditions otherwise the apply button may have a bad state
+    theFeature->data()->execState(ModelAPI_StateDone);
+    static ModelAPI_ValidatorsFactory* aFactory = ModelAPI_Session::get()->validators();
+    aFactory->validate(theFeature); // need to be validated to update the "Apply" state if not previewed
+
+    if (!myIsPreviewBlocked)
+      return true;
   }
   if (myModified.find(theFeature) != myModified.end()) {
     if (theReason.get()) {
 #ifdef DEB_UPDATE
-      std::cout<<"*** Add already modified "<<theFeature->name()<<std::endl;
+      std::cout<<"*** Add already modified "<<theFeature->name()<<" reason "<<theReason->name()<<std::endl;
 #endif
       myModified[theFeature].insert(theReason);
     }
-    return; // already is marked as modified, so, nothing to do, it will be processed
+    return true;
   }
   // do not add the disabled, but possibly the sub-elements are not disabled
   bool aIsDisabled = theFeature->isDisabled();
   if (!aIsDisabled) {
     std::set<std::shared_ptr<ModelAPI_Feature> > aNewSet;
-    if (theFeature->data()->execState() == ModelAPI_StateMustBeUpdated) {
+    if (theFeature->data()->execState() == ModelAPI_StateMustBeUpdated ||
+        theFeature->data()->execState() == ModelAPI_StateInvalidArgument) { // issue 1519
       // do not forget that in this case all were the reasons
       aNewSet.insert(theFeature);
     } else {
@@ -95,13 +121,16 @@ void Model_Update::addModified(FeaturePtr theFeature, FeaturePtr theReason) {
     }
     myModified[theFeature] = aNewSet;
 #ifdef DEB_UPDATE
-    std::cout<<"*** Add modified "<<theFeature->name()<<std::endl;
+    if (theReason.get())
+      std::cout<<"*** Add modified "<<theFeature->name()<<" reason "<<theReason->name()<<std::endl;
+    else 
+      std::cout<<"*** Add modified "<<theFeature->name()<<std::endl;
 #endif
   } else { // will be updated during the finish of the operation, or when it becomes enabled
     if (theFeature->data()->execState() == ModelAPI_StateDone)
       theFeature->data()->execState(ModelAPI_StateMustBeUpdated);
     else 
-      return; // do not need iteration deeply if it is already marked as modified or so
+      return true; // do not need iteration deeply if it is already marked as modified or so
 #ifdef DEB_UPDATE
     std::cout<<"*** Set modified state "<<theFeature->name()<<std::endl;
 #endif
@@ -110,9 +139,11 @@ void Model_Update::addModified(FeaturePtr theFeature, FeaturePtr theReason) {
   const std::set<std::shared_ptr<ModelAPI_Attribute> >& aRefs = theFeature->data()->refsToMe();
   std::set<std::shared_ptr<ModelAPI_Attribute> >::const_iterator aRefIter = aRefs.cbegin();
   for(; aRefIter != aRefs.cend(); aRefIter++) {
-    FeaturePtr aReferenced = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRefIter)->owner());
-    if (aReferenced.get()) {
-      addModified(aReferenced, theFeature);
+    if ((*aRefIter)->isArgument()) {
+      FeaturePtr aReferenced = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRefIter)->owner());
+      if (aReferenced.get()) {
+        addModified(aReferenced, theFeature);
+      }
     }
   }
   // proccess also results
@@ -123,9 +154,11 @@ void Model_Update::addModified(FeaturePtr theFeature, FeaturePtr theReason) {
     const std::set<std::shared_ptr<ModelAPI_Attribute> >& aRefs = (*aRes)->data()->refsToMe();
     std::set<std::shared_ptr<ModelAPI_Attribute> >::const_iterator aRefIter = aRefs.cbegin();
     for(; aRefIter != aRefs.cend(); aRefIter++) {
-      FeaturePtr aReferenced = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRefIter)->owner());
-      if (aReferenced.get()) {
-        addModified(aReferenced, theFeature);
+      if ((*aRefIter)->isArgument()) {
+        FeaturePtr aReferenced = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRefIter)->owner());
+        if (aReferenced.get()) {
+          addModified(aReferenced, theFeature);
+        }
       }
     }
   }
@@ -137,7 +170,7 @@ void Model_Update::addModified(FeaturePtr theFeature, FeaturePtr theReason) {
     if (aPart.get())
       addModified(aPart, theFeature);
   }
-  return;
+  return true;
 }
 
 void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessage)
@@ -149,6 +182,10 @@ void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessag
   static const Events_ID kOpAbortEvent = aLoop->eventByName("AbortOperation");
   static const Events_ID kOpStartEvent = aLoop->eventByName("StartOperation");
   static const Events_ID kStabilityEvent = aLoop->eventByName(EVENT_STABILITY_CHANGED);
+  static const Events_ID kPreviewBlockedEvent = aLoop->eventByName(EVENT_PREVIEW_BLOCKED);
+  static const Events_ID kPreviewRequestedEvent = aLoop->eventByName(EVENT_PREVIEW_REQUESTED);
+  static const Events_ID kReorderEvent = aLoop->eventByName(EVENT_ORDER_UPDATED);
+
 #ifdef DEB_UPDATE
   std::cout<<"****** Event "<<theMessage->eventID().eventText()<<std::endl;
 #endif
@@ -156,6 +193,18 @@ void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessag
     updateStability(theMessage->sender());
     return;
   }
+  if (theMessage->eventID() == kPreviewBlockedEvent) {
+    myIsPreviewBlocked = true;
+    return;
+  }
+  if (theMessage->eventID() == kPreviewRequestedEvent) {
+    if (myIsPreviewBlocked) {
+      myIsPreviewBlocked = false;
+      processFeatures();
+      myIsPreviewBlocked = true;
+    }
+    return;
+  }
   // creation is added to "update" to avoid recomputation twice: on create and immediately after on update
   if (theMessage->eventID() == kCreatedEvent) {
     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aMsg =
@@ -178,20 +227,16 @@ void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessag
       if (!(*anObjIter)->data()->isValid())
         continue;
 #ifdef DEB_UPDATE
-      std::cout<<">>> in event updated "<<(*anObjIter)->data()->name()<<std::endl;
+      std::cout<<">>> in event updated "<<(*anObjIter)->groupName()<<" "<<(*anObjIter)->data()->name()<<std::endl;
 #endif
       if ((*anObjIter)->groupName() == ModelAPI_ResultParameter::group()) {
         myIsParamUpdated = true;
       }
       // on undo/redo, abort do not update persisten features
-      bool anUpdateOnlyNotPersistent = 
-        !std::dynamic_pointer_cast<Model_Document>((*anObjIter)->document())->executeFeatures();
       FeaturePtr anUpdated = std::dynamic_pointer_cast<ModelAPI_Feature>(*anObjIter);
       if (anUpdated.get()) {
-        if (!anUpdateOnlyNotPersistent || !anUpdated->isPersistentResult()) {
-          addModified(anUpdated, FeaturePtr());
+        if (addModified(anUpdated, FeaturePtr()))
           aSomeModified = true;
-        }
       } else { // process the updated result as update of features that refers to this result
         const std::set<std::shared_ptr<ModelAPI_Attribute> >& aRefs = (*anObjIter)->data()->refsToMe();
         std::set<std::shared_ptr<ModelAPI_Attribute> >::const_iterator aRefIter = aRefs.cbegin();
@@ -199,9 +244,9 @@ void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessag
           if (!(*aRefIter)->owner()->data()->isValid())
             continue;
           FeaturePtr anUpdated = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRefIter)->owner());
-          if (anUpdated.get() && (!anUpdateOnlyNotPersistent || !anUpdated->isPersistentResult())) {
-            addModified(anUpdated, FeaturePtr());
-            aSomeModified = true;
+          if (anUpdated.get()) {
+            if (addModified(anUpdated, FeaturePtr()))
+              aSomeModified = true;
           }
         }
       }
@@ -212,6 +257,7 @@ void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessag
     }
   } else if (theMessage->eventID() == kOpFinishEvent || theMessage->eventID() == kOpAbortEvent ||
       theMessage->eventID() == kOpStartEvent) {
+    myIsPreviewBlocked = false;
 
     if (theMessage->eventID() == kOpFinishEvent) {
       myIsFinish = true;
@@ -225,7 +271,7 @@ void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessag
     myProcessOnFinish.clear(); // processed features must be only on finish, so clear anyway (to avoid reimport on load)
 
     if (!(theMessage->eventID() == kOpStartEvent)) {
-      processFeatures();
+      processFeatures(false);
     }
     // remove all macros before clearing all created
     std::set<FeaturePtr>::iterator anUpdatedIter = myWaitForFinish.begin();
@@ -247,15 +293,25 @@ void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessag
         anUpdatedIter++;
       }
     }
+    // the redisplay signal should be flushed in order to erase the feature presentation in the viewer
+    // if should be done after removeFeature() of document,
+    // by this reason, upper processFeatures() do not perform this flush
+    Events_Loop::loop()->flush(Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY));
+
     // in the end of transaction everything is updated, so clear the old objects
     myIsParamUpdated = false;
     myWaitForFinish.clear();
+  } else if (theMessage->eventID() == kReorderEvent) {
+    std::shared_ptr<ModelAPI_OrderUpdatedMessage> aMsg = 
+      std::dynamic_pointer_cast<ModelAPI_OrderUpdatedMessage>(theMessage);
+    addModified(aMsg->reordered(), aMsg->reordered()); // to update all attributes
   }
 }
 
-void Model_Update::processFeatures()
+void Model_Update::processFeatures(const bool theFlushRedisplay)
 {
-  if (!myIsProcessed) { // perform update of everything if it is not performed right now
+   // perform update of everything if it is not performed right now or any preview is blocked
+  if (!myIsProcessed && !myIsPreviewBlocked) {
     myIsProcessed = true;
     #ifdef DEB_UPDATE
       std::cout<<"****** Start processing"<<std::endl;
@@ -266,19 +322,57 @@ void Model_Update::processFeatures()
     }
     myIsProcessed = false;
 
-    // flush updates just before "myModification" increment: to distinguish
-    // updates by "execute" produced by this updater and other updates, coming outside,
-    // which are really important for "processEvent" of this updater
+    // to update the object browser if something is updated/created during executions
     static Events_Loop* aLoop = Events_Loop::loop();
+    static const Events_ID kCreatedEvent= aLoop->eventByName(EVENT_OBJECT_CREATED);
+    aLoop->flush(kCreatedEvent);
     static const Events_ID kUpdatedEvent = aLoop->eventByName(EVENT_OBJECT_UPDATED);
     aLoop->flush(kUpdatedEvent);
 
     // flush to update display
-    static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
-    aLoop->flush(EVENT_DISP);
+    if (theFlushRedisplay) {
+      static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
+      aLoop->flush(EVENT_DISP);
+    }
     #ifdef DEB_UPDATE
       std::cout<<"****** End processing"<<std::endl;
     #endif
+    myProcessed.clear();
+  }
+}
+
+// collects all the feautres this feature depends on: reasons
+static void allReasons(FeaturePtr theFeature, std::set<FeaturePtr>& theReasons) {
+  std::list<std::pair<std::string, std::list<std::shared_ptr<ModelAPI_Object> > > > aDeps;
+  theFeature->data()->referencesToObjects(aDeps);
+  std::list<std::pair<std::string, std::list<std::shared_ptr<ModelAPI_Object> > > >::iterator
+    anAttrsIter = aDeps.begin();
+  for(; anAttrsIter != aDeps.end(); anAttrsIter++) {
+    if (theFeature->attribute(anAttrsIter->first)->isArgument()) {
+      std::list<std::shared_ptr<ModelAPI_Object> >::iterator aDepIter = anAttrsIter->second.begin();
+      for(; aDepIter != anAttrsIter->second.end(); aDepIter++) {
+        FeaturePtr aDepFeat = std::dynamic_pointer_cast<ModelAPI_Feature>(*aDepIter);
+        if (!aDepFeat.get()) { // so, it depends on the result and process the feature owner of it
+          ResultPtr aDepRes = std::dynamic_pointer_cast<ModelAPI_Result>(*aDepIter);
+          if (aDepRes.get()) {
+            aDepFeat = (*aDepIter)->document()->feature(aDepRes);
+          }
+        }
+        if (aDepFeat.get() && aDepFeat->data()->isValid()) {
+          theReasons.insert(aDepFeat);
+        }
+      }
+    }
+  }
+  if (theFeature->getKind() == "Part") { // part is not depended on its subs directly, but subs must be iterated anyway
+    CompositeFeaturePtr aPart = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theFeature);
+    int aNum = aPart->numberOfSubs();
+    for(int a = 0; a < aNum; a++) {
+      FeaturePtr aSub = aPart->subFeature(a);
+      if (aSub.get() && aSub->data()->isValid()) {
+        theReasons.insert(aSub);
+      }
+    }
   }
 }
 
@@ -292,6 +386,23 @@ bool Model_Update::processFeature(FeaturePtr theFeature)
     return false;
   }
 
+  if (theFeature->isPersistentResult()) {
+    if (!std::dynamic_pointer_cast<Model_Document>((theFeature)->document())->executeFeatures())
+      return false;
+  }
+
+  if (myProcessed.find(theFeature) == myProcessed.end()) {
+    myProcessed[theFeature] = 0;
+  } else {
+    int aCount = myProcessed[theFeature];
+    if (aCount > 100) { // too many repetition of processing (in VS it may crash on 330 with stack overflow)
+      Events_InfoMessage("Model_Update",
+        "Feature '%1' is updated in infinitive loop").arg(theFeature->data()->name()).send();
+      return false;
+    }
+    myProcessed[theFeature] = aCount + 1;
+  }
+
   // check this feature is not yet checked or processed
   bool aIsModified = myModified.find(theFeature) != myModified.end();
   if (!aIsModified && myIsFinish) { // get info about the modification for features without preview
@@ -330,48 +441,41 @@ bool Model_Update::processFeature(FeaturePtr theFeature)
     return false;
   }
 
+  // evaluate parameter before the sub-elements update: it updates dependencies on evaluation (#1085)
+  if (theFeature->getKind() == "Parameter") {
+    theFeature->execute();
+  }
+
+  bool isReferencedInvalid = false;
   // check all features this feature depended on (recursive call of updateFeature)
-  std::set<std::shared_ptr<ModelAPI_Feature> >& aReasons = myModified[theFeature];
-  if (aReasons.find(theFeature) == aReasons.end()) {
-    std::set<std::shared_ptr<ModelAPI_Feature> >::iterator aReasonIter = aReasons.begin();
-    for(; aReasonIter != aReasons.end(); aReasonIter++) {
-      if (*aReasonIter != theFeature && processFeature(*aReasonIter)) {
+  std::set<FeaturePtr>& aReasons = myModified[theFeature];
+  bool allSubsUsed = aReasons.find(theFeature) != aReasons.end();
+  if (allSubsUsed) { // add all subs in aReasons and temporary remove "theFeature" to avoid processing itself
+    allReasons(theFeature, aReasons);
+    aReasons.erase(theFeature);
+  }
+  // take reasons one by one (they may be added during the feature process (circle by the radius of sketch)
+  std::set<FeaturePtr> aProcessedReasons;
+  while(!aReasons.empty()) {
+    FeaturePtr aReason = *(aReasons.begin());
+#ifdef DEB_UPDATE
+    //cout<<theFeature->name()<<" process next reason "<<aReason->name()<<endl;
+#endif
+    if (aReason != theFeature && (aReason)->data()->isValid()) {
+      if (processFeature(aReason))
         aIsModified = true;
-      }
-    }
-  } else { // check all features this feature depended on because here which one was modified is unknown
-    std::list<std::pair<std::string, std::list<std::shared_ptr<ModelAPI_Object> > > > aDeps;
-    theFeature->data()->referencesToObjects(aDeps);
-    std::list<std::pair<std::string, std::list<std::shared_ptr<ModelAPI_Object> > > >::iterator
-      anAttrsIter = aDeps.begin();
-    for(; anAttrsIter != aDeps.end(); anAttrsIter++) {
-      std::list<std::shared_ptr<ModelAPI_Object> >::iterator aDepIter = anAttrsIter->second.begin();
-      for(; aDepIter != anAttrsIter->second.end(); aDepIter++) {
-        FeaturePtr aDepFeat = std::dynamic_pointer_cast<ModelAPI_Feature>(*aDepIter);
-        if (!aDepFeat.get()) { // so, it depends on the result and process the feature owner of it
-          ResultPtr aDepRes = std::dynamic_pointer_cast<ModelAPI_Result>(*aDepIter);
-          if (aDepRes.get()) {
-            aDepFeat = (*aDepIter)->document()->feature(aDepRes);
-          }
-        }
-        if (aDepFeat.get()) {
-          if (processFeature(aDepFeat))
-            aIsModified = true;
-        }
-      }
-    }
-    if (theFeature->getKind() == "Part") { // part is not depended on its subs directly, but subs must be iterated anyway
-      CompositeFeaturePtr aPart = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theFeature);
-      int aNum = aPart->numberOfSubs();
-      for(int a = 0; a < aNum; a++) {
-        FeaturePtr aSub = aPart->subFeature(a);
-        if (aSub.get()) {
-          if (processFeature(aSub))
-            aIsModified = true;
-        }
-      }
+      if (aReason->data()->execState() == ModelAPI_StateInvalidArgument)
+        isReferencedInvalid = true;
     }
+    // searching for the next not used reason
+    aProcessedReasons.insert(aReason);
+    aReasons.erase(aReason);
   }
+  // restore the modified reasons: they will be used in the update of arguments
+  if (allSubsUsed) { // restore theFeature in this set
+    aProcessedReasons.insert(theFeature);
+  }
+  myModified[theFeature] = aProcessedReasons;
 
   // do not execute the composite that contains the current
   bool isPostponedMain = false;
@@ -382,9 +486,12 @@ bool Model_Update::processFeature(FeaturePtr theFeature)
     isPostponedMain = aCurrentOwner.get() && aCompos->isSub(aCurrentOwner);
   }
 
-  #ifdef DEB_UPDATE
-    std::cout<<"Update args "<<theFeature->name()<<std::endl;
-  #endif
+#ifdef DEB_UPDATE
+  std::cout<<"Update args "<<theFeature->name()<<std::endl;
+#endif
+  // TestImport.py : after arguments are updated, theFeature may be removed
+  if (!theFeature->data()->isValid())
+    return false;
   // Update selection and parameters attributes first, before sub-features analysis (sketch plane).
   updateArguments(theFeature);
 
@@ -396,43 +503,28 @@ bool Model_Update::processFeature(FeaturePtr theFeature)
 
   // this checking must be after the composite feature sub-elements processing:
   // composite feature status may depend on it's subelements
-  if (theFeature->data()->execState() == ModelAPI_StateInvalidArgument) {
+  if ((theFeature->data()->execState() == ModelAPI_StateInvalidArgument || isReferencedInvalid) && 
+    theFeature->getKind() != "Part") { // don't disable Part because it will make disabled all the features (performance and problems with the current feature)
+  #ifdef DEB_UPDATE
+    std::cout<<"Invalid args "<<theFeature->name()<<std::endl;
+  #endif
     theFeature->eraseResults();
     redisplayWithResults(theFeature, ModelAPI_StateInvalidArgument); // result also must be updated
     return true; // so, feature is modified (results are erased)
   }
 
-  // On abort, undo or redo execute is not needed: results in document are updated automatically
-  // But redisplay is needed: results are updated, must be also updated in the viewer.
-  if (!std::dynamic_pointer_cast<Model_Document>(theFeature->document())->executeFeatures()) {
-    if (!theFeature->isPersistentResult()) { // not persistent must be re-executed on abort, etc.
-      ModelAPI_ExecState aState = theFeature->data()->execState();
-      if (aFactory->validate(theFeature)) {
-        executeFeature(theFeature);
-      } else {
-        theFeature->eraseResults();
-        redisplayWithResults(theFeature, ModelAPI_StateInvalidArgument); // result also must be updated
-      }
-    } else {
-      redisplayWithResults(theFeature, ModelAPI_StateNothing);
-      if (theFeature->data()->execState() == ModelAPI_StateMustBeUpdated) { // it is done (in the tree)
-        theFeature->data()->execState(ModelAPI_StateDone);
-      }
+  // execute feature if it must be updated
+  ModelAPI_ExecState aState = theFeature->data()->execState();
+  if (aFactory->validate(theFeature)) {
+    if (!isPostponedMain) {
+      executeFeature(theFeature);
     }
   } else {
-    // execute feature if it must be updated
-    ModelAPI_ExecState aState = theFeature->data()->execState();
-    if (aFactory->validate(theFeature)) {
-      if (!isPostponedMain) {
-        executeFeature(theFeature);
-      }
-    } else {
-      #ifdef DEB_UPDATE
-        std::cout<<"Feature is not valid, erase results "<<theFeature->name()<<std::endl;
-      #endif
-      theFeature->eraseResults();
-      redisplayWithResults(theFeature, ModelAPI_StateInvalidArgument); // result also must be updated
-    }
+    #ifdef DEB_UPDATE
+      std::cout<<"Feature is not valid, erase results "<<theFeature->name()<<std::endl;
+    #endif
+    theFeature->eraseResults();
+    redisplayWithResults(theFeature, ModelAPI_StateInvalidArgument); // result also must be updated
   }
   return true;
 }
@@ -532,7 +624,8 @@ void Model_Update::updateArguments(FeaturePtr theFeature) {
     for(; anIter != anAttributes.end(); anIter++) {
       AttributePointPtr aPointAttribute =
         std::dynamic_pointer_cast<GeomDataAPI_Point>(*anIter);
-      if (aPointAttribute.get()) {
+      if (aPointAttribute.get() && (!aPointAttribute->textX().empty() || 
+          !aPointAttribute->textY().empty() || !aPointAttribute->textZ().empty())) {
         if (myIsParamUpdated) {
           ModelAPI_AttributeEvalMessage::send(aPointAttribute, this);
         }
@@ -552,7 +645,8 @@ void Model_Update::updateArguments(FeaturePtr theFeature) {
       AttributePoint2DPtr aPoint2DAttribute =
         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(*anIter);
       if (aPoint2DAttribute.get()) {
-        if (myIsParamUpdated) {
+        if (myIsParamUpdated && (!aPoint2DAttribute->textX().empty() || 
+            !aPoint2DAttribute->textY().empty())) {
           ModelAPI_AttributeEvalMessage::send(aPoint2DAttribute, this);
         }
         if ((!aPoint2DAttribute->textX().empty() && aPoint2DAttribute->expressionInvalid(0)) ||
@@ -570,14 +664,21 @@ void Model_Update::updateArguments(FeaturePtr theFeature) {
       std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(*aRefsIter);
     ObjectPtr aContext = aSel->context();
     // update argument only if the referenced object is ready to use
-    if (aContext.get() && !aContext->isDisabled() && isReason(theFeature, aContext)) {
-      if (!aSel->update()) { // this must be done on execution since it may be long operation
-        bool isObligatory = !aFactory->isNotObligatory(
-          theFeature->getKind(), theFeature->data()->id(aSel)) &&
-          aFactory->isCase(theFeature, theFeature->data()->id(aSel));
-        if (isObligatory)
-          aState = ModelAPI_StateInvalidArgument;
+    if (aContext.get() && !aContext->isDisabled()) {
+      if (isReason(theFeature, aContext)) {
+        if (!aSel->update()) { // this must be done on execution since it may be long operation
+          bool isObligatory = !aFactory->isNotObligatory(
+            theFeature->getKind(), theFeature->data()->id(aSel)) &&
+            aFactory->isCase(theFeature, theFeature->data()->id(aSel));
+          if (isObligatory)
+            aState = ModelAPI_StateInvalidArgument;
+        }
       }
+    } else if (aContext.get()) {
+      // here it may be not obligatory, but if the reference is wrong, it should not be correct
+      bool isObligatory = aFactory->isCase(theFeature, theFeature->data()->id(aSel));
+      if (isObligatory)
+        aState = ModelAPI_StateInvalidArgument;
     }
   }
   // update the selection list attributes if any
@@ -591,14 +692,21 @@ void Model_Update::updateArguments(FeaturePtr theFeature) {
       if (aSelAttr) {
         ObjectPtr aContext = aSelAttr->context();
         // update argument only if the referenced object is ready to use
-        if (aContext.get() && !aContext->isDisabled() && isReason(theFeature, aContext)) {
-          if (!aSelAttr->update()) {
-            bool isObligatory = !aFactory->isNotObligatory(
-              theFeature->getKind(), theFeature->data()->id(aSel)) &&
-              aFactory->isCase(theFeature, theFeature->data()->id(aSel));
-            if (isObligatory)
-              aState = ModelAPI_StateInvalidArgument;
+        if (aContext.get() && !aContext->isDisabled()) {
+          if (isReason(theFeature, aContext)) {
+            if (!aSelAttr->update()) {
+              bool isObligatory = !aFactory->isNotObligatory(
+                theFeature->getKind(), theFeature->data()->id(aSel)) &&
+                aFactory->isCase(theFeature, theFeature->data()->id(aSel));
+              if (isObligatory)
+                aState = ModelAPI_StateInvalidArgument;
+            }
           }
+        } else if (aContext.get()) {
+          // here it may be not obligatory, but if the reference is wrong, it should not be correct
+          bool isObligatory = aFactory->isCase(theFeature, theFeature->data()->id(aSel));
+          if (isObligatory)
+            aState = ModelAPI_StateInvalidArgument;
         }
       }
     }
@@ -644,8 +752,8 @@ void Model_Update::executeFeature(FeaturePtr theFeature)
     }
   } catch(...) {
     aState = ModelAPI_StateExecFailed;
-    Events_Error::send(
-      "Feature " + theFeature->getKind() + " has failed during the execution");
+    Events_InfoMessage("Model_Update",
+      "Feature %1 has failed during the execution").arg(theFeature->getKind()).send();
   }
   // The macro feature has to be deleted in any case even its execution is failed 
   myWaitForFinish.insert(theFeature);
@@ -658,6 +766,7 @@ void Model_Update::executeFeature(FeaturePtr theFeature)
 
 void Model_Update::updateStability(void* theSender)
 {
+  static ModelAPI_ValidatorsFactory* aFactory = ModelAPI_Session::get()->validators();
   if (theSender) {
     bool added = false; // object may be was crated
     ModelAPI_Object* aSender = static_cast<ModelAPI_Object*>(theSender);
@@ -674,6 +783,8 @@ void Model_Update::updateStability(void* theSender)
           aSender->data()->referencesToObjects(aRefs);
           std::list<std::pair<std::string, std::list<ObjectPtr> > >::iterator aRefIt = aRefs.begin();
           for(; aRefIt != aRefs.end(); aRefIt++) {
+            if (!aFactory->isConcealed(aFeatureSender->getKind(), aRefIt->first))
+              continue; // take into account only concealed references (do not remove the sketch constraint and the edge on constraint edit)
             std::list<ObjectPtr>& aRefFeaturesList = aRefIt->second;
             std::list<ObjectPtr>::iterator aReferenced = aRefFeaturesList.begin();
             for(; aReferenced != aRefFeaturesList.end(); aReferenced++) {