]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Debug of the parametric model updates
authormpv <mikhail.ponikarov@opencascade.com>
Thu, 6 Nov 2014 07:25:00 +0000 (10:25 +0300)
committermpv <mikhail.ponikarov@opencascade.com>
Thu, 6 Nov 2014 07:25:00 +0000 (10:25 +0300)
13 files changed:
src/Events/Events_Loop.cpp
src/Events/Events_Loop.h
src/GeomData/GeomData_Dir.cpp
src/GeomData/GeomData_Point.cpp
src/GeomData/GeomData_Point2D.cpp
src/Model/Model_Document.cpp
src/Model/Model_Events.cpp
src/Model/Model_Session.cpp
src/Model/Model_Update.cpp
src/Model/Model_Update.h
src/PartSet/PartSet_Listener.cpp
src/SketchPlugin/SketchPlugin_Line.cpp
src/SketchPlugin/SketchPlugin_Sketch.cpp

index b13472aee107dc9ade02cf43d3e3686597af9580..eadae7283f44541edb18b0a29c53404ef304d891 100644 (file)
@@ -43,7 +43,7 @@ void Events_Loop::send(const boost::shared_ptr<Events_Message>& theMessage, bool
     myImmediateListeners[theMessage->eventID().eventText()]->processEvent(theMessage);
   }
   // if it is grouped message, just accumulate it
-  if (isGroup) {
+  if (isGroup && myFlushed.find(theMessage->eventID().myID) == myFlushed.end()) {
     boost::shared_ptr<Events_MessageGroup> aGroup = 
       boost::dynamic_pointer_cast<Events_MessageGroup>(theMessage);
     if (aGroup) {
@@ -118,9 +118,11 @@ void Events_Loop::flush(const Events_ID& theID)
   std::map<char*, boost::shared_ptr<Events_Message>>::iterator aMyGroup =
     myGroups.find(theID.eventText());
   if (aMyGroup != myGroups.end()) {  // really sends
+    myFlushed.insert(theID.myID);
     boost::shared_ptr<Events_Message> aGroup = aMyGroup->second;
     myGroups.erase(aMyGroup);
     send(aGroup, false);
+    myFlushed.erase(myFlushed.find(theID.myID));
   }
 }
 
@@ -128,3 +130,20 @@ void Events_Loop::activateFlushes(const bool theActivate)
 {
   myFlushActive = theActivate;
 }
+
+void Events_Loop::clear(const Events_ID& theID)
+{
+  std::map<char*, boost::shared_ptr<Events_Message>>::iterator aMyGroup =
+    myGroups.find(theID.eventText());
+  if (aMyGroup != myGroups.end()) {  // really sends
+    myGroups.erase(aMyGroup);
+  }
+}
+
+void Events_Loop::autoFlush(const Events_ID& theID, const bool theAuto)
+{
+  if (theAuto)
+    myFlushed.insert(theID.myID);
+  else
+    myFlushed.erase(myFlushed.find(theID.myID));
+}
index b02c978c89109fd63c58a546f9bba937ae6559ae..b8a628ff7d73a9050e6ab527f5b620d4661ea2ca 100644 (file)
@@ -9,6 +9,7 @@
 #include <Events_Listener.h>
 
 #include <map>
+#include <set>
 #include <list>
 
 class Events_MessageGroup;
@@ -34,6 +35,9 @@ class Events_Loop
   /// map from event ID to groupped messages (accumulated on flush)
   std::map<char*, boost::shared_ptr<Events_Message> > myGroups;
 
+  ///< set of messages that are flushed right now, so they are not grouped
+  std::set<char*> myFlushed;
+
   /// to process flushes or not
   bool myFlushActive;
 
@@ -62,6 +66,12 @@ class Events_Loop
   //! Allows to disable flushes: needed in synchronization of document mechanism 
   //! (to synchronize all and only then flush create, update, etc in correct order)
   EVENTS_EXPORT void activateFlushes(const bool theActivate);
+
+  //! Clears all collected messages
+  EVENTS_EXPORT void clear(const Events_ID& theID);
+
+  //! Enables flush without grouping for the given message
+  EVENTS_EXPORT void autoFlush(const Events_ID& theID, const bool theAuto = true);
 };
 
 #endif
index b7c2b1c30223a41bfe0a7e81cdee09b76dad6b52..ddf20ebbd1ae25609a5e1033326e81fab10c2a6e 100644 (file)
@@ -24,7 +24,6 @@ void GeomData_Dir::setValue(const double theX, const double theY, const double t
 void GeomData_Dir::setValue(const boost::shared_ptr<GeomAPI_Dir>& theDir)
 {
   setValue(theDir->x(), theDir->y(), theDir->z());
-  owner()->data()->sendAttributeUpdated(this);
 }
 
 double GeomData_Dir::x() const
index 023b12d9628c4ae466b4ce77ac8c266b77aed051..61c92e12bbed4730cae31908d8eb7e781168efd0 100644 (file)
@@ -23,7 +23,6 @@ void GeomData_Point::setValue(const double theX, const double theY, const double
 void GeomData_Point::setValue(const boost::shared_ptr<GeomAPI_Pnt>& thePoint)
 {
   setValue(thePoint->x(), thePoint->y(), thePoint->z());
-  owner()->data()->sendAttributeUpdated(this);
 }
 
 double GeomData_Point::x() const
index 08913436c7f757ec2261079c299c16abc9c49e55..924f80f8ca32bafd745151d4505d4dfd3033ab91 100644 (file)
@@ -21,7 +21,6 @@ void GeomData_Point2D::setValue(const double theX, const double theY)
 void GeomData_Point2D::setValue(const boost::shared_ptr<GeomAPI_Pnt2d>& thePoint)
 {
   setValue(thePoint->x(), thePoint->y());
-  owner()->data()->sendAttributeUpdated(this);
 }
 
 double GeomData_Point2D::x() const
index 59cb68ddbcc6a96864852f215a529f244260e688..4ad70e8470680dc3df6074cfc0455f3a43a55ef8 100644 (file)
@@ -257,11 +257,6 @@ bool Model_Document::compactNested()
 
 void Model_Document::finishOperation()
 {
-  // finish for all subs first: to avoid nested finishing and "isOperation" calls problems inside
-  std::set<std::string>::iterator aSubIter = mySubs.begin();
-  for (; aSubIter != mySubs.end(); aSubIter++)
-    subDoc(*aSubIter)->finishOperation();
-
   // just to be sure that everybody knows that changes were performed
   if (!myDoc->HasOpenCommand() && myNestedNum != -1)
     boost::static_pointer_cast<Model_Session>(Model_Session::get())
@@ -273,10 +268,28 @@ void Model_Document::finishOperation()
   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TOHIDE));
   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
+  // this must be here just after everything is finished but before real transaction stop
+  // to avoid messages about modifications outside of the transaction
+  // and to rebuild everything after all updates and creates
+  if (Model_Session::get()->moduleDocument().get() == this) { // once for root document
+    Events_Loop::loop()->autoFlush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
+    static boost::shared_ptr<Events_Message> aFinishMsg
+      (new Events_Message(Events_Loop::eventByName("FinishOperation")));
+    Events_Loop::loop()->send(aFinishMsg);
+    Events_Loop::loop()->autoFlush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED), false);
+  }
+  // to avoid "updated" message appearance by updater
+  //aLoop->clear(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
+
   if (!myDoc->HasOpenCommand() && myNestedNum != -1)
     boost::static_pointer_cast<Model_Session>(Model_Session::get())
         ->setCheckTransactions(true);  // for nested transaction commit
 
+  // finish for all subs first: to avoid nested finishing and "isOperation" calls problems inside
+  std::set<std::string>::iterator aSubIter = mySubs.begin();
+  for (; aSubIter != mySubs.end(); aSubIter++)
+    subDoc(*aSubIter)->finishOperation();
+
   if (myNestedNum != -1)  // this nested transaction is owervritten
     myNestedNum++;
   if (!myDoc->HasOpenCommand()) {
index 421c493e27f8ee577ba7ce00695ae54b87be8f31..850b68d5daa12d2ae45bb23879a4cce0c6580340 100644 (file)
@@ -35,8 +35,9 @@ Model_ObjectUpdatedMessage::Model_ObjectUpdatedMessage(const ObjectPtr& theObjec
                                                        const Events_ID& theEvent)
     : ModelAPI_ObjectUpdatedMessage(theEvent, 0)
 {
-  if (theObject)
+  if (theObject) {
     myObjects.insert(theObject);
+  }
 }
 
 const std::set<ObjectPtr>& Model_ObjectUpdatedMessage::objects() const
index 37b020eeeec6be69126e17a0eefcf386887059f8..13b8a9a50665b05f3e5319b14142e0dcba851590 100644 (file)
@@ -41,14 +41,14 @@ bool Model_Session::save(const char* theFileName, std::list<std::string>& theRes
 void Model_Session::startOperation()
 {
   ROOT_DOC->startOperation();
+  static boost::shared_ptr<Events_Message> aStartedMsg
+    (new Events_Message(Events_Loop::eventByName("StartOperation")));
+  Events_Loop::loop()->send(aStartedMsg);
 }
 
 void Model_Session::finishOperation()
 {
   ROOT_DOC->finishOperation();
-  static boost::shared_ptr<Events_Message> aFinishMsg
-    (new Events_Message(Events_Loop::eventByName("FinishOperation")));
-  Events_Loop::loop()->send(aFinishMsg);
 }
 
 void Model_Session::abortOperation()
index 80f44ccf4e87c8861f44c12c8699b4158d63560c..7209c8aeffe0d05ca30e402ddab216210b3bb5f1 100644 (file)
@@ -42,6 +42,8 @@ Model_Update::Model_Update()
   aLoop->registerListener(this, kOpFinishEvent);
   static const Events_ID kOpAbortEvent = aLoop->eventByName("AbortOperation");
   aLoop->registerListener(this, kOpAbortEvent);
+  static const Events_ID kOpStartEvent = aLoop->eventByName("StartOperation");
+  aLoop->registerListener(this, kOpStartEvent);
 
   Config_PropManager::registerProp("Model update", "automatic_rebuild", "Rebuild automatically",
                                    Config_Prop::Bool, "false");
@@ -57,6 +59,7 @@ void Model_Update::processEvent(const boost::shared_ptr<Events_Message>& theMess
   static const Events_ID kUpdatedEvent = aLoop->eventByName(EVENT_OBJECT_UPDATED);
   static const Events_ID kOpFinishEvent = aLoop->eventByName("FinishOperation");
   static const Events_ID kOpAbortEvent = aLoop->eventByName("AbortOperation");
+  static const Events_ID kOpStartEvent = aLoop->eventByName("StartOperation");
   bool isAutomaticChanged = false;
   if (theMessage->eventID() == kChangedEvent) { // automatic and manual rebuild flag is changed
     isAutomatic = 
@@ -69,16 +72,19 @@ void Model_Update::processEvent(const boost::shared_ptr<Events_Message>& theMess
   } else if (theMessage->eventID() == kCreatedEvent || theMessage->eventID() == kUpdatedEvent) {
     boost::shared_ptr<ModelAPI_ObjectUpdatedMessage> aMsg =
         boost::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
-    if (theMessage->eventID() == kCreatedEvent) {
-      myJustCreatedOrUpdated.clear();
-    }
     const std::set<ObjectPtr>& anObjs = aMsg->objects();
     std::set<ObjectPtr>::const_iterator anObjIter = anObjs.cbegin();
-    for(; anObjIter != anObjs.cend(); anObjIter++)
+    for(; anObjIter != anObjs.cend(); anObjIter++) {
       myJustCreatedOrUpdated.insert(*anObjIter);
-  } else if (theMessage->eventID() == kOpFinishEvent || theMessage->eventID() == kOpAbortEvent) {
+    }
+  } else if (theMessage->eventID() == kOpStartEvent) {
     myJustCreatedOrUpdated.clear();
-    return;
+    return; // we don't need the update only on operation start (caused problems in PartSet_Listener::processEvent)
+  } else if (theMessage->eventID() == kOpFinishEvent || theMessage->eventID() == kOpAbortEvent) {
+    if (isAutomatic == false) { // Apply button now works as "Rebuild"
+      isAutomaticChanged = true;
+      isAutomatic = true;
+    }
   }
 
   if (isExecuted)
@@ -86,7 +92,7 @@ void Model_Update::processEvent(const boost::shared_ptr<Events_Message>& theMess
 
   //Events_LongOp::start(this);
   isExecuted = true;
-  list<boost::shared_ptr<ModelAPI_Document> > aDocs;
+  std::list<boost::shared_ptr<ModelAPI_Document> > aDocs;
   boost::shared_ptr<ModelAPI_ObjectUpdatedMessage> aMsg =
       boost::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
   if (aMsg) myInitial = aMsg->objects();
@@ -103,8 +109,12 @@ void Model_Update::processEvent(const boost::shared_ptr<Events_Message>& theMess
     aDocs.push_back((*aFIter)->document());
   }
   // iterate all features of features-documents to update them (including hidden)
+  std::set<boost::shared_ptr<ModelAPI_Document> > alreadyUsed;
   list<boost::shared_ptr<ModelAPI_Document> >::iterator aDIter = aDocs.begin();
   for (; aDIter != aDocs.end(); aDIter++) {
+    if (alreadyUsed.find(*aDIter) != alreadyUsed.end())
+      continue;
+    alreadyUsed.insert(*aDIter);
     int aNbFeatures = (*aDIter)->size(ModelAPI_Feature::group(), true);
     for (int aFIndex = 0; aFIndex < aNbFeatures; aFIndex++) {
       FeaturePtr aFeature = boost::dynamic_pointer_cast<ModelAPI_Feature>(
@@ -114,18 +124,35 @@ void Model_Update::processEvent(const boost::shared_ptr<Events_Message>& theMess
     }
   }
   myUpdated.clear();
-  if (theMessage->eventID() == kUpdatedEvent) {
-    // flush updates without execution now (updates are caused by this process)
-    aLoop->flush(kUpdatedEvent);
-  }
   // flush to update display
   static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
   aLoop->flush(EVENT_DISP);
   //Events_LongOp::end(this);
   if (isAutomaticChanged) isAutomatic = false;
+
+  if (theMessage->eventID() == kOpFinishEvent || theMessage->eventID() == kOpAbortEvent) {
+    myJustCreatedOrUpdated.clear();
+  }
+
   isExecuted = false;
 }
 
+void Model_Update::redisplayWithResults(FeaturePtr theFeature) {
+  // maske updated and redisplay all results
+  static Events_ID EVENT_DISP = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
+  const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
+  std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
+  for (; aRIter != aResults.cend(); aRIter++) {
+    boost::shared_ptr<ModelAPI_Result> aRes = *aRIter;
+    aRes->data()->mustBeUpdated(false);
+    myUpdated[aRes] = true;
+    ModelAPI_EventCreator::get()->sendUpdated(aRes, EVENT_DISP);
+  }
+  // to redisplay "presentable" feature (for ex. distance constraint)
+  ModelAPI_EventCreator::get()->sendUpdated(theFeature, EVENT_DISP);
+  theFeature->data()->mustBeUpdated(false);
+}
+
 bool Model_Update::updateFeature(FeaturePtr theFeature)
 {
   // check it is already processed
@@ -163,7 +190,6 @@ bool Model_Update::updateFeature(FeaturePtr theFeature)
 
     // execute feature if it must be updated
     if (aMustbeUpdated) {
-
       if (boost::dynamic_pointer_cast<Model_Document>(theFeature->document())->executeFeatures() ||
           !theFeature->isPersistentResult()) {
         ModelAPI_ValidatorsFactory* aFactory = ModelAPI_Session::get()->validators();
@@ -185,7 +211,7 @@ bool Model_Update::updateFeature(FeaturePtr theFeature)
               boost::shared_ptr<ModelAPI_AttributeSelectionList> aSel =
                 boost::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(*aRefsIter);
               for(int a = aSel->size() - 1; a >= 0; a--) {
-                aSel->value(a)->update();
+                  aSel->value(a)->update();
               }
             }
             // execute in try-catch to avoid internal problems of the feature
@@ -196,14 +222,8 @@ bool Model_Update::updateFeature(FeaturePtr theFeature)
                 "Feature " + theFeature->getKind() + " has failed during the execution");
               theFeature->eraseResults();
             }
-            theFeature->data()->mustBeUpdated(false);
-            const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
-            std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
-            for (; aRIter != aResults.cend(); aRIter++) {
-              boost::shared_ptr<ModelAPI_Result> aRes = *aRIter;
-              aRes->data()->mustBeUpdated(false);
-            }
-          } else {
+            redisplayWithResults(theFeature);
+          } else { // must be updatet, but not updated yet
             theFeature->data()->mustBeUpdated(true);
             const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
             std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
@@ -211,23 +231,14 @@ bool Model_Update::updateFeature(FeaturePtr theFeature)
               boost::shared_ptr<ModelAPI_Result> aRes = *aRIter;
               aRes->data()->mustBeUpdated(true);
             }
-            aMustbeUpdated = false;
           }
         } else {
           theFeature->eraseResults();
+          redisplayWithResults(theFeature); // result also must be updated
         }
+      } else { // for automatically updated features (on abort, etc) it is necessary to redisplay anyway
+        redisplayWithResults(theFeature);
       }
-      // redisplay all results
-      static Events_ID EVENT_DISP = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
-      const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
-      std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
-      for (; aRIter != aResults.cend(); aRIter++) {
-        boost::shared_ptr<ModelAPI_Result> aRes = *aRIter;
-        myUpdated[aRes] = true;
-        ModelAPI_EventCreator::get()->sendUpdated(aRes, EVENT_DISP);
-      }
-      // to redisplay "presentable" feature (for ex. distance constraint)
-      ModelAPI_EventCreator::get()->sendUpdated(theFeature, EVENT_DISP);
     } else {  // returns also true is results were updated: for sketch that refers to sub-features but results of sub-features were changed
       const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
       std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
index f73378580918531e8c17f3e28da3cbce8eb18fda..a29d67d8df8992286a268f6a05ee7e740236984a 100644 (file)
@@ -47,6 +47,8 @@ class Model_Update : public Events_Listener
   /// Recoursively checks and updates the object (result or feature) if needed (calls updateFeature)
   /// Returns true if object was updated.
   bool updateObject(boost::shared_ptr<ModelAPI_Object> theObject);
+  /// Sends the redisplay events for feature and results, updates the updated status
+  void redisplayWithResults(boost::shared_ptr<ModelAPI_Feature> theFeature);
 };
 
 #endif
index 24c78b60faf03b8beb774960c21bfedaa1be99d0..8d34fb04157a77afd5d17c64e0607444d1048e9b 100644 (file)
@@ -77,7 +77,7 @@ void PartSet_Listener::processEvent(const boost::shared_ptr<Events_Message>& the
       FeaturePtr aFeature = aCreationOp->feature();
       const std::list<ResultPtr>& aResults = aFeature->results();
       boost::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
-          boost::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
+        boost::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
 
       std::set<ObjectPtr> aFeatures = aUpdMsg->objects();
       std::set<ObjectPtr>::const_iterator aObjIt, aNoObj = aFeatures.cend();
index 2d85a43b3873b99f1d938c29296da7230f0572c0..95b6de896acd8644ace08a86a29f8a48e17f047b 100644 (file)
@@ -100,7 +100,8 @@ bool SketchPlugin_Line::isFixed() {
 void SketchPlugin_Line::attributeChanged() {
   static bool myIsUpdated = false; // to avoid infinitive cycle on attrubtes change
   boost::shared_ptr<GeomAPI_Shape> aSelection = data()->selection(EXTERNAL_ID())->value();
-  if (aSelection && !myIsUpdated) { // update arguments due to the selection value
+   // update arguments due to the selection value
+  if (aSelection && !aSelection->isNull() && !myIsUpdated) {
     myIsUpdated = true;
     boost::shared_ptr<GeomAPI_Edge> anEdge( new GeomAPI_Edge(aSelection));
     boost::shared_ptr<GeomDataAPI_Point2D> aStartAttr = 
index b5f101595aa27b8786bd6391b71bb4964ace0b25..2b45cb65a45458a6bbb61f182e10b2fee5c92a31 100644 (file)
@@ -270,11 +270,12 @@ void SketchPlugin_Sketch::erase()
 }
 
 void SketchPlugin_Sketch::attributeChanged() {
-  static bool myIsUpdated = false; // to avoid infinitive cycle on attrubtes change
+  static bool kIsUpdated = false; // to avoid infinitive cycle on attrubtes change
+  static bool kIsAttrChanged = false;
   boost::shared_ptr<GeomAPI_Shape> aSelection = 
     data()->selection(SketchPlugin_Feature::EXTERNAL_ID())->value();
-  if (aSelection && !myIsUpdated) { // update arguments due to the selection value
-    myIsUpdated = true;
+  if (aSelection && !kIsUpdated) { // update arguments due to the selection value
+    kIsUpdated = true;
     // update the sketch plane
     boost::shared_ptr<GeomAPI_Pln> aPlane = GeomAlgoAPI_FaceBuilder::plane(aSelection);
     if (aPlane) {
@@ -295,8 +296,9 @@ void SketchPlugin_Sketch::attributeChanged() {
       boost::shared_ptr<GeomAPI_Dir> aYDir(new GeomAPI_Dir(aNormDir->cross(aTempDir)));
       boost::shared_ptr<GeomAPI_Dir> aXDir(new GeomAPI_Dir(aYDir->cross(aNormDir)));
 
-      boost::shared_ptr<GeomDataAPI_Point> anOrigin = boost::dynamic_pointer_cast<GeomDataAPI_Point>(
-        data()->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
+      kIsAttrChanged = false; // track the attributes were really changed during the plane update
+      boost::shared_ptr<GeomDataAPI_Point> anOrigin = boost::dynamic_pointer_cast
+        <GeomDataAPI_Point>(data()->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
       anOrigin->setValue(anOrigPnt);
       boost::shared_ptr<GeomDataAPI_Dir> aNormal = boost::dynamic_pointer_cast<GeomDataAPI_Dir>(
         data()->attribute(SketchPlugin_Sketch::NORM_ID()));
@@ -308,7 +310,23 @@ void SketchPlugin_Sketch::attributeChanged() {
         data()->attribute(SketchPlugin_Sketch::DIRY_ID()));
       aDirY->setValue(aYDir);
       boost::shared_ptr<GeomAPI_Dir> aDir = aPlane->direction();
+
+      if (kIsAttrChanged) {
+        // the plane was changed, so reexecute sub-elements to update shapes (located in new plane)
+        ModelAPI_ValidatorsFactory* aFactory = ModelAPI_Session::get()->validators();
+        list<ObjectPtr> aSubs = data()->reflist(SketchPlugin_Sketch::FEATURES_ID())->list();
+        for(list<ObjectPtr>::iterator aSubIt = aSubs.begin(); aSubIt != aSubs.end(); aSubIt++) {
+          boost::shared_ptr<SketchPlugin_Feature> aFeature = 
+            boost::dynamic_pointer_cast<SketchPlugin_Feature>(*aSubIt);
+          if (aFeature && aFactory->validate(aFeature)) {
+            aFeature->execute();
+          }
+        }
+        kIsAttrChanged = false;
+      }
     }
-    myIsUpdated = false;
+    kIsUpdated = false;
+  } else if (kIsUpdated) { // other attributes are updated during the selection comupation
+    kIsAttrChanged = true;
   }
 }