Salome HOME
Add copyright header according to request of CEA from 06.06.2017
[modules/shaper.git] / src / Model / Model_Update.cpp
1 // Copyright (C) 2014-2017  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
19 //
20
21 #include <Model_Update.h>
22 #include <Model_Document.h>
23 #include <Model_Data.h>
24 #include <Model_Objects.h>
25 #include <Model_AttributeSelection.h>
26 #include <ModelAPI_Feature.h>
27 #include <ModelAPI_Data.h>
28 #include <ModelAPI_Document.h>
29 #include <ModelAPI_Events.h>
30 #include <ModelAPI_AttributeReference.h>
31 #include <ModelAPI_AttributeRefList.h>
32 #include <ModelAPI_AttributeRefAttr.h>
33 #include <ModelAPI_AttributeSelection.h>
34 #include <ModelAPI_AttributeSelectionList.h>
35 #include <ModelAPI_Result.h>
36 #include <ModelAPI_ResultPart.h>
37 #include <ModelAPI_Validator.h>
38 #include <ModelAPI_CompositeFeature.h>
39 #include <ModelAPI_Session.h>
40 #include <ModelAPI_Tools.h>
41 #include <GeomAPI_Shape.h>
42 #include <GeomDataAPI_Point.h>
43 #include <GeomDataAPI_Point2D.h>
44 #include <Events_Loop.h>
45 #include <Events_LongOp.h>
46 #include <Events_InfoMessage.h>
47 #include <Config_PropManager.h>
48
49 Model_Update MY_UPDATER_INSTANCE;  /// the only one instance initialized on load of the library
50 //#define DEB_UPDATE
51
52 Model_Update::Model_Update()
53 {
54   Events_Loop* aLoop = Events_Loop::loop();
55   static const Events_ID kChangedEvent = aLoop->eventByName("PreferenceChanged");
56   aLoop->registerListener(this, kChangedEvent);
57   static const Events_ID kCreatedEvent = Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED);
58   aLoop->registerListener(this, kCreatedEvent);
59   static const Events_ID kUpdatedEvent = Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED);
60   aLoop->registerListener(this, kUpdatedEvent);
61   static const Events_ID kOpFinishEvent = aLoop->eventByName("FinishOperation");
62   aLoop->registerListener(this, kOpFinishEvent);
63   static const Events_ID kOpAbortEvent = aLoop->eventByName("AbortOperation");
64   aLoop->registerListener(this, kOpAbortEvent);
65   static const Events_ID kOpStartEvent = aLoop->eventByName("StartOperation");
66   aLoop->registerListener(this, kOpStartEvent);
67   static const Events_ID kStabilityEvent = aLoop->eventByName(EVENT_STABILITY_CHANGED);
68   aLoop->registerListener(this, kStabilityEvent);
69   static const Events_ID kPreviewBlockedEvent = aLoop->eventByName(EVENT_PREVIEW_BLOCKED);
70   aLoop->registerListener(this, kPreviewBlockedEvent);
71   static const Events_ID kPreviewRequestedEvent = aLoop->eventByName(EVENT_PREVIEW_REQUESTED);
72   aLoop->registerListener(this, kPreviewRequestedEvent);
73   static const Events_ID kReorderEvent = aLoop->eventByName(EVENT_ORDER_UPDATED);
74   aLoop->registerListener(this, kReorderEvent);
75   static const Events_ID kUpdatedSel = aLoop->eventByName(EVENT_UPDATE_SELECTION);
76   aLoop->registerListener(this, kUpdatedSel);
77
78   //  Config_PropManager::findProp("Model update", "automatic_rebuild")->value() == "true";
79   myIsParamUpdated = false;
80   myIsFinish = false;
81   myIsProcessed = false;
82   myIsPreviewBlocked = false;
83 }
84
85 bool Model_Update::addModified(FeaturePtr theFeature, FeaturePtr theReason) {
86
87   if (!theFeature->data()->isValid())
88     return false; // delete an extrusion created on the sketch
89
90   bool isNotExecuted = theFeature->isPersistentResult() &&
91     !std::dynamic_pointer_cast<Model_Document>((theFeature)->document())->executeFeatures();
92   if (isNotExecuted) {
93     if (!theReason.get()) // no reason => no construction reason
94       return false;
95     if (myNotPersistentRefs.find(theFeature) == myNotPersistentRefs.end()) {
96       myNotPersistentRefs[theFeature].insert(theReason);
97     } else {
98       std::set<std::shared_ptr<ModelAPI_Feature> > aNewSet;
99       aNewSet.insert(theReason);
100       myNotPersistentRefs[theFeature] = aNewSet;
101     }
102     return false;
103   }
104
105   // update arguments for "apply button" state change
106   if ((!theFeature->isPreviewNeeded() && !myIsFinish) || myIsPreviewBlocked) {
107     myProcessOnFinish.insert(theFeature);
108 #ifdef DEB_UPDATE
109       std::cout<<"*** Add process on finish "<<theFeature->name()<<std::endl;
110 #endif
111     // keeps the currently updated features to avoid infinitive cycling here: where feature on
112     // "updateArguments" sends "updated" (in selection attribute) and goes here again
113     static std::set<FeaturePtr> aCurrentlyUpdated;
114     if (aCurrentlyUpdated.find(theFeature) == aCurrentlyUpdated.end()) {
115       aCurrentlyUpdated.insert(theFeature);
116       updateArguments(theFeature);
117       aCurrentlyUpdated.erase(theFeature);
118     }
119     // make it without conditions otherwise the apply button may have a bad state
120     theFeature->data()->execState(ModelAPI_StateDone);
121     static ModelAPI_ValidatorsFactory* aFactory = ModelAPI_Session::get()->validators();
122      // need to be validated to update the "Apply" state if not previewed
123     aFactory->validate(theFeature);
124
125     // to redisplay split's arguments presentation, even result is not computed
126     if (!theFeature->isPreviewNeeded()) {
127       static Events_Loop* aLoop = Events_Loop::loop();
128       static const Events_ID kRedisplayEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
129       ModelAPI_EventCreator::get()->sendUpdated(theFeature, kRedisplayEvent);
130       aLoop->flush(kRedisplayEvent);
131     }
132
133     if (!myIsPreviewBlocked)
134       return true;
135   }
136   if (myModified.find(theFeature) != myModified.end()) {
137     if (theReason.get()) {
138 #ifdef DEB_UPDATE
139       //std::cout<<"*** Add already modified "
140       //   <<theFeature->name()<<" reason "<<theReason->name()<<std::endl;
141 #endif
142       myModified[theFeature].insert(theReason);
143     }
144     return true;
145   }
146   // do not add the disabled, but possibly the sub-elements are not disabled
147   bool aIsDisabled = theFeature->isDisabled();
148   if (!aIsDisabled) {
149     std::set<std::shared_ptr<ModelAPI_Feature> > aNewSet;
150     if (theFeature->data()->execState() == ModelAPI_StateMustBeUpdated ||
151         theFeature->data()->execState() == ModelAPI_StateInvalidArgument || // issue 1519
152         theFeature->data()->execState() == ModelAPI_StateExecFailed) {
153       // do not forget that in this case all were the reasons
154       aNewSet.insert(theFeature);
155     } else {
156       if (theReason.get())
157         aNewSet.insert(theReason);
158     }
159       myModified[theFeature] = aNewSet;
160 #ifdef DEB_UPDATE
161     if (theReason.get()) {
162       //std::cout<<"*** Add modified "<<theFeature->name()
163       //  <<" reason "<<theReason->name()<<std::endl;
164     } else {
165       //std::cout<<"*** Add modified "<<theFeature->name()<<std::endl;
166     }
167 #endif
168   } else { // will be updated during the finish of the operation, or when it becomes enabled
169     if (theFeature->data()->execState() == ModelAPI_StateDone ||
170         theFeature->data()->execState() == ModelAPI_StateExecFailed) // fix issue 1819
171       theFeature->data()->execState(ModelAPI_StateMustBeUpdated);
172     else
173       return true; // do not need iteration deeply if it is already marked as modified or so
174 #ifdef DEB_UPDATE
175     //std::cout<<"*** Set modified state "<<theFeature->name()<<std::endl;
176 #endif
177   }
178   // clear processed and fill modified recursively
179   const std::set<std::shared_ptr<ModelAPI_Attribute> >& aRefs = theFeature->data()->refsToMe();
180   std::set<std::shared_ptr<ModelAPI_Attribute> >::const_iterator aRefIter = aRefs.cbegin();
181   for(; aRefIter != aRefs.cend(); aRefIter++) {
182     if ((*aRefIter)->isArgument()) {
183       FeaturePtr aReferenced = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRefIter)->owner());
184       if (aReferenced.get()) {
185         addModified(aReferenced, theFeature);
186       }
187     }
188   }
189   // proccess also results
190   std::list<ResultPtr> allResults; // list of this feature and results
191   ModelAPI_Tools::allResults(theFeature, allResults);
192   std::list<ResultPtr>::iterator aRes = allResults.begin();
193   for(; aRes != allResults.end(); aRes++) {
194     const std::set<std::shared_ptr<ModelAPI_Attribute> >& aRefs = (*aRes)->data()->refsToMe();
195     std::set<std::shared_ptr<ModelAPI_Attribute> >::const_iterator aRefIter = aRefs.cbegin();
196     for(; aRefIter != aRefs.cend(); aRefIter++) {
197       if ((*aRefIter)->isArgument()) {
198         FeaturePtr aReferenced = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRefIter)->owner());
199         if (aReferenced.get()) {
200           addModified(aReferenced, theFeature);
201         }
202       }
203     }
204   }
205
206   // also add part feature that contains this feature to the modified
207   if (theFeature->document()->kind() != "PartSet") {
208     FeaturePtr aPart = ModelAPI_Tools::findPartFeature(
209       ModelAPI_Session::get()->moduleDocument(), theFeature->document());
210     if (aPart.get())
211       addModified(aPart, theFeature);
212   }
213   return true;
214 }
215
216 void Model_Update::processEvent(const std::shared_ptr<Events_Message>& theMessage)
217 {
218   static Events_Loop* aLoop = Events_Loop::loop();
219   static const Events_ID kCreatedEvent = aLoop->eventByName(EVENT_OBJECT_CREATED);
220   static const Events_ID kUpdatedEvent = aLoop->eventByName(EVENT_OBJECT_UPDATED);
221   static const Events_ID kOpFinishEvent = aLoop->eventByName("FinishOperation");
222   static const Events_ID kOpAbortEvent = aLoop->eventByName("AbortOperation");
223   static const Events_ID kOpStartEvent = aLoop->eventByName("StartOperation");
224   static const Events_ID kStabilityEvent = aLoop->eventByName(EVENT_STABILITY_CHANGED);
225   static const Events_ID kPreviewBlockedEvent = aLoop->eventByName(EVENT_PREVIEW_BLOCKED);
226   static const Events_ID kPreviewRequestedEvent = aLoop->eventByName(EVENT_PREVIEW_REQUESTED);
227   static const Events_ID kReorderEvent = aLoop->eventByName(EVENT_ORDER_UPDATED);
228   static const Events_ID kRedisplayEvent = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
229   static const Events_ID kUpdatedSel = aLoop->eventByName(EVENT_UPDATE_SELECTION);
230
231 #ifdef DEB_UPDATE
232   std::cout<<"****** Event "<<theMessage->eventID().eventText()<<std::endl;
233 #endif
234   if (theMessage->eventID() == kStabilityEvent) {
235     updateStability(theMessage->sender());
236     return;
237   }
238   if (theMessage->eventID() == kPreviewBlockedEvent) {
239     myIsPreviewBlocked = true;
240     return;
241   }
242   if (theMessage->eventID() == kPreviewRequestedEvent) {
243     if (myIsPreviewBlocked) {
244       myIsPreviewBlocked = false;
245       processFeatures();
246       myIsPreviewBlocked = true;
247     }
248     return;
249   }
250   if (theMessage->eventID() == kUpdatedSel) {
251     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aMsg =
252         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
253     updateSelection(aMsg->objects());
254   }
255   // creation is added to "update" to avoid recomputation twice:
256   // on create and immediately after on update
257   if (theMessage->eventID() == kCreatedEvent) {
258     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aMsg =
259         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
260     const std::set<ObjectPtr>& anObjs = aMsg->objects();
261     std::set<ObjectPtr>::const_iterator anObjIter = anObjs.cbegin();
262     for(; anObjIter != anObjs.cend(); anObjIter++) {
263       if (std::dynamic_pointer_cast<Model_Document>((*anObjIter)->document())->executeFeatures()) {
264         if ((*anObjIter)->groupName() == ModelAPI_Feature::group()) {
265           // results creation means enabling, not update
266           ModelAPI_EventCreator::get()->sendUpdated(*anObjIter, kUpdatedEvent);
267         } else {
268           ModelAPI_EventCreator::get()->sendUpdated(*anObjIter, kRedisplayEvent);
269         }
270       }
271     }
272     return;
273   }
274   if (theMessage->eventID() == kUpdatedEvent) {
275     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aMsg =
276         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
277     const std::set<ObjectPtr>& anObjs = aMsg->objects();
278     std::set<ObjectPtr>::const_iterator anObjIter = anObjs.cbegin();
279     bool aSomeModified = false; // check that features not changed: only redisplay is needed
280     for(; anObjIter != anObjs.cend(); anObjIter++) {
281       if (!(*anObjIter)->data()->isValid())
282         continue;
283 #ifdef DEB_UPDATE
284       std::cout<<">>> in event updated "<<(*anObjIter)->groupName()<<
285         " "<<(*anObjIter)->data()->name()<<std::endl;
286 #endif
287       if ((*anObjIter)->groupName() == ModelAPI_ResultParameter::group()) {
288         myIsParamUpdated = true;
289       }
290       // on undo/redo, abort do not update persisten features
291       FeaturePtr anUpdated = std::dynamic_pointer_cast<ModelAPI_Feature>(*anObjIter);
292       if (anUpdated.get()) {
293         if (addModified(anUpdated, FeaturePtr()))
294           aSomeModified = true;
295       } else {
296         // process the updated result as update of features that refers to this result
297         const std::set<std::shared_ptr<ModelAPI_Attribute> >&
298           aRefs = (*anObjIter)->data()->refsToMe();
299         std::set<std::shared_ptr<ModelAPI_Attribute> >::const_iterator aRefIter = aRefs.cbegin();
300         for(; aRefIter != aRefs.cend(); aRefIter++) {
301           if (!(*aRefIter)->owner()->data()->isValid())
302             continue;
303           FeaturePtr anUpdated = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRefIter)->owner());
304           if (anUpdated.get()) {
305             if (addModified(anUpdated, FeaturePtr()))
306               aSomeModified = true;
307           }
308         }
309       }
310     }
311     // this event is for solver update, not here, do not react immediately
312     if (aSomeModified) {
313         processFeatures();
314     }
315   } else if (theMessage->eventID() == kOpFinishEvent || theMessage->eventID() == kOpAbortEvent ||
316       theMessage->eventID() == kOpStartEvent) {
317     myIsPreviewBlocked = false;
318
319     if (theMessage->eventID() == kOpFinishEvent) {
320       myIsFinish = true;
321       // add features that wait for finish as modified
322       std::set<std::shared_ptr<ModelAPI_Feature> >::iterator aFeature = myProcessOnFinish.begin();
323       for(; aFeature != myProcessOnFinish.end(); aFeature++)
324         if ((*aFeature)->data()->isValid()) // there may be already removed wait for features
325           addModified(*aFeature, FeaturePtr());
326       myIsFinish = false;
327     }
328     // processed features must be only on finish, so clear anyway (to avoid reimport on load)
329     myProcessOnFinish.clear();
330
331     // #2156: current must be sketch, left after the macro execution
332     DocumentPtr anActiveDoc = ModelAPI_Session::get()->activeDocument();
333     FeaturePtr aCurrent;
334     if (anActiveDoc.get())
335       aCurrent = anActiveDoc->currentFeature(false);
336
337     if (!(theMessage->eventID() == kOpStartEvent)) {
338       processFeatures(false);
339     }
340
341     if (anActiveDoc.get() && aCurrent.get() && aCurrent->data()->isValid()) {
342       if (anActiveDoc->currentFeature(false) != aCurrent)
343         anActiveDoc->setCurrentFeature(aCurrent, false); // #2156 make the current feature back
344     }
345
346
347     // remove all macros before clearing all created
348     std::set<FeaturePtr>::iterator anUpdatedIter = myWaitForFinish.begin();
349     while(anUpdatedIter != myWaitForFinish.end()) {
350       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anUpdatedIter);
351       if (aFeature.get()) {
352         // remove macro on finish
353         if (aFeature->isMacro()) {
354           aFeature->document()->removeFeature(aFeature);
355           myWaitForFinish.erase(aFeature);
356         }
357         // to avoid the map update problems on "remove"
358         if (myWaitForFinish.find(aFeature) == myWaitForFinish.end()) {
359           anUpdatedIter = myWaitForFinish.begin();
360         } else {
361           anUpdatedIter++;
362         }
363       } else {
364         anUpdatedIter++;
365       }
366     }
367     // the redisplay signal should be flushed in order
368     // to erase the feature presentation in the viewer
369     // if should be done after removeFeature() of document,
370     // by this reason, upper processFeatures() do not perform this flush
371     Events_Loop::loop()->flush(kRedisplayEvent);
372
373     // in the end of transaction everything is updated, so clear the old objects
374     myIsParamUpdated = false;
375     myWaitForFinish.clear();
376   } else if (theMessage->eventID() == kReorderEvent) {
377     std::shared_ptr<ModelAPI_OrderUpdatedMessage> aMsg =
378       std::dynamic_pointer_cast<ModelAPI_OrderUpdatedMessage>(theMessage);
379     if (aMsg->reordered().get())
380       addModified(aMsg->reordered(), aMsg->reordered()); // to update all attributes
381   }
382 }
383
384 void Model_Update::processFeatures(const bool theFlushRedisplay)
385 {
386    // perform update of everything if it is not performed right now or any preview is blocked
387   if (!myIsProcessed && !myIsPreviewBlocked) {
388     myIsProcessed = true;
389     #ifdef DEB_UPDATE
390       std::cout<<"****** Start processing"<<std::endl;
391     #endif
392
393     while(!myModified.empty()) {
394       processFeature(myModified.begin()->first);
395     }
396     myIsProcessed = false;
397
398     // to update the object browser if something is updated/created during executions
399     static Events_Loop* aLoop = Events_Loop::loop();
400     static const Events_ID kCreatedEvent= aLoop->eventByName(EVENT_OBJECT_CREATED);
401     aLoop->flush(kCreatedEvent);
402     static const Events_ID kUpdatedEvent = aLoop->eventByName(EVENT_OBJECT_UPDATED);
403     aLoop->flush(kUpdatedEvent);
404
405     // flush to update display
406     if (theFlushRedisplay) {
407       static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
408       aLoop->flush(EVENT_DISP);
409     }
410     #ifdef DEB_UPDATE
411       std::cout<<"****** End processing"<<std::endl;
412     #endif
413     myProcessed.clear();
414   }
415 }
416
417 // collects all the feautres this feature depends on: reasons
418 static void allReasons(FeaturePtr theFeature, std::set<FeaturePtr>& theReasons) {
419   std::list<std::pair<std::string, std::list<std::shared_ptr<ModelAPI_Object> > > > aDeps;
420   theFeature->data()->referencesToObjects(aDeps);
421   std::list<std::pair<std::string, std::list<std::shared_ptr<ModelAPI_Object> > > >::iterator
422     anAttrsIter = aDeps.begin();
423   for(; anAttrsIter != aDeps.end(); anAttrsIter++) {
424     if (theFeature->attribute(anAttrsIter->first)->isArgument()) {
425       std::list<std::shared_ptr<ModelAPI_Object> >::iterator aDepIter = anAttrsIter->second.begin();
426       for(; aDepIter != anAttrsIter->second.end(); aDepIter++) {
427         FeaturePtr aDepFeat = std::dynamic_pointer_cast<ModelAPI_Feature>(*aDepIter);
428         if (!aDepFeat.get()) { // so, it depends on the result and process the feature owner of it
429           ResultPtr aDepRes = std::dynamic_pointer_cast<ModelAPI_Result>(*aDepIter);
430           if (aDepRes.get()) {
431             aDepFeat = (*aDepIter)->document()->feature(aDepRes);
432           }
433         }
434         if (aDepFeat.get() && aDepFeat->data()->isValid()) {
435           theReasons.insert(aDepFeat);
436         }
437       }
438     }
439   }
440   if (theFeature->getKind() == "Part") {
441     // part is not depended on its subs directly, but subs must be iterated anyway
442     CompositeFeaturePtr aPart = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theFeature);
443     int aNum = aPart->numberOfSubs();
444     for(int a = 0; a < aNum; a++) {
445       FeaturePtr aSub = aPart->subFeature(a);
446       if (aSub.get() && aSub->data()->isValid()) {
447         theReasons.insert(aSub);
448       }
449     }
450   }
451 }
452
453 bool Model_Update::processFeature(FeaturePtr theFeature)
454 {
455   static ModelAPI_ValidatorsFactory* aFactory = ModelAPI_Session::get()->validators();
456
457   if (!theFeature->data()->isValid()) { // deleted feature, just remove from all containers
458     if (myModified.find(theFeature) != myModified.end())
459       myModified.erase(theFeature);
460     return false;
461   }
462
463   if (theFeature->isPersistentResult()) {
464     if (!std::dynamic_pointer_cast<Model_Document>((theFeature)->document())->executeFeatures())
465       return false;
466   }
467
468   if (myProcessed.find(theFeature) == myProcessed.end()) {
469     myProcessed[theFeature] = 0;
470   } else {
471     int aCount = myProcessed[theFeature];
472     if (aCount > 100) {
473       // too many repetition of processing (in VS it may crash on 330 with stack overflow)
474       Events_InfoMessage("Model_Update",
475         "Feature '%1' is updated in infinitive loop").arg(theFeature->data()->name()).send();
476       // to stop iteration
477       myModified.clear();
478       return false;
479     }
480     myProcessed[theFeature] = aCount + 1;
481   }
482
483   // check this feature is not yet checked or processed
484   bool aIsModified = myModified.find(theFeature) != myModified.end();
485   if (!aIsModified && myIsFinish) { // get info about the modification for features without preview
486     if (theFeature->data()->execState() == ModelAPI_StateMustBeUpdated) {
487       aIsModified = true;
488       std::set<std::shared_ptr<ModelAPI_Feature> > aNewSet;
489       // contains itself, so, we don't know which was the reason and the reason is any
490       aNewSet.insert(theFeature);
491       myModified[theFeature] = aNewSet;
492     }
493   }
494
495 #ifdef DEB_UPDATE
496     std::cout<<"* Process feature "<<theFeature->name()<<std::endl;
497 #endif
498
499   // update the sketch plane before the sketch sub-elements are recomputed
500   // (otherwise sketch will update plane, modify subs, after executed, but with old subs edges)
501   if (aIsModified && theFeature->getKind() == "Sketch") {
502 #ifdef DEB_UPDATE
503     std::cout<<"****** Update sketch args "<<theFeature->name()<<std::endl;
504 #endif
505     AttributeSelectionPtr anExtSel = theFeature->selection("External");
506     if (anExtSel.get()) {
507       ResultPtr aContext = anExtSel->context();
508       if (aContext.get() && aContext->document().get()) {
509         FeaturePtr anExtBase = aContext->document()->feature(aContext);
510         if (anExtBase.get()) {
511           processFeature(anExtBase);
512         }
513       }
514     }
515     std::shared_ptr<GeomAPI_Shape> aShapeBefore = anExtSel->value();
516     if (!aShapeBefore.get() && anExtSel->context()) aShapeBefore = anExtSel->context()->shape();
517     updateArguments(theFeature);
518     std::shared_ptr<GeomAPI_Shape> aShapeAfter = anExtSel->value();
519     if (!aShapeAfter.get() && anExtSel->context()) aShapeAfter = anExtSel->context()->shape();
520     // if selected plane is changed, try to re-take external references of all subs of the sketch
521     if (aShapeBefore.get() && !aShapeBefore->isEqual(aShapeAfter)) {
522       std::set<FeaturePtr> aWholeR;
523       allReasons(theFeature, aWholeR);
524       std::set<FeaturePtr>::iterator aRIter = aWholeR.begin();
525       for(; aRIter != aWholeR.end(); aRIter++) {
526         if ((*aRIter)->data()->selection("External"))
527           (*aRIter)->attributeChanged("External");
528       }
529     }
530   }
531
532   if (!aIsModified) { // no modification is needed
533     return false;
534   }
535
536   // evaluate parameter before the sub-elements update:
537   // it updates dependencies on evaluation (#1085)
538   if (theFeature->getKind() == "Parameter") {
539     theFeature->execute();
540   }
541
542   bool isReferencedInvalid = false;
543   // check all features this feature depended on (recursive call of updateFeature)
544   std::set<FeaturePtr>& aReasons = myModified[theFeature];
545   bool allSubsUsed = aReasons.find(theFeature) != aReasons.end();
546   if (allSubsUsed) {
547     // add all subs in aReasons and temporary remove "theFeature" to avoid processing itself
548     allReasons(theFeature, aReasons);
549     aReasons.erase(theFeature);
550   }
551   // take reasons one by one (they may be added during the feature process
552   // (circle by the radius of sketch)
553   std::set<FeaturePtr> aProcessedReasons;
554   while(!aReasons.empty()) {
555     FeaturePtr aReason = *(aReasons.begin());
556 #ifdef DEB_UPDATE
557     //cout<<theFeature->name()<<" process next reason "<<aReason->name()<<endl;
558 #endif
559     if (aReason != theFeature && (aReason)->data()->isValid()) {
560       if (processFeature(aReason))
561         aIsModified = true;
562       if (aReason->data()->execState() == ModelAPI_StateInvalidArgument)
563         isReferencedInvalid = true;
564     }
565     // searching for the next not used reason
566     aProcessedReasons.insert(aReason);
567     aReasons.erase(aReason);
568   }
569   // restore the modified reasons: they will be used in the update of arguments
570   if (allSubsUsed) { // restore theFeature in this set
571     aProcessedReasons.insert(theFeature);
572   }
573   myModified[theFeature] = aProcessedReasons;
574
575   // do not execute the composite that contains the current
576   bool isPostponedMain = false;
577   CompositeFeaturePtr aCompos = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theFeature);
578   if (theFeature->getKind() == "ExtrusionSketch" && aCompos.get()) {
579     CompositeFeaturePtr aCurrentOwner =
580       ModelAPI_Tools::compositeOwner(theFeature->document()->currentFeature(false));
581     isPostponedMain = aCurrentOwner.get() && aCompos->isSub(aCurrentOwner);
582   }
583
584 #ifdef DEB_UPDATE
585   std::cout<<"Update args "<<theFeature->name()<<std::endl;
586 #endif
587   // TestImport.py : after arguments are updated, theFeature may be removed
588   if (!theFeature->data()->isValid())
589     return false;
590   // Update selection and parameters attributes first, before sub-features analysis (sketch plane).
591   updateArguments(theFeature);
592
593   // add this feature to the processed right now to be able remove it from this list on
594   // update signal during this feature execution
595   myModified.erase(theFeature);
596   if (myNotPersistentRefs.find(theFeature) != myNotPersistentRefs.end())
597     myNotPersistentRefs.erase(theFeature);
598   if (theFeature->data()->execState() == ModelAPI_StateMustBeUpdated)
599     theFeature->data()->execState(ModelAPI_StateDone);
600
601   // this checking must be after the composite feature sub-elements processing:
602   // composite feature status may depend on it's subelements
603   if ((theFeature->data()->execState() == ModelAPI_StateInvalidArgument || isReferencedInvalid) &&
604     theFeature->getKind() != "Part") {
605       // don't disable Part because it will make disabled all the features
606       // (performance and problems with the current feature)
607   #ifdef DEB_UPDATE
608     std::cout<<"Invalid args "<<theFeature->name()<<std::endl;
609   #endif
610     theFeature->eraseResults(false);
611     redisplayWithResults(theFeature, ModelAPI_StateInvalidArgument); // result also must be updated
612     return true; // so, feature is modified (results are erased)
613   }
614
615   // execute feature if it must be updated
616   ModelAPI_ExecState aState = theFeature->data()->execState();
617   if (aFactory->validate(theFeature)) {
618     if (!isPostponedMain) {
619       executeFeature(theFeature);
620     }
621   } else {
622     #ifdef DEB_UPDATE
623       std::cout<<"Feature is not valid, erase results "<<theFeature->name()<<std::endl;
624     #endif
625     theFeature->eraseResults(false);
626     redisplayWithResults(theFeature, ModelAPI_StateInvalidArgument); // result also must be updated
627   }
628   return true;
629 }
630
631 void Model_Update::redisplayWithResults(FeaturePtr theFeature, const ModelAPI_ExecState theState)
632 {
633   // make updated and redisplay all results
634   static Events_ID EVENT_DISP = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
635
636   std::list<ResultPtr> allResults;
637   ModelAPI_Tools::allResults(theFeature, allResults);
638   std::list<ResultPtr>::iterator aRIter = allResults.begin();
639   for (; aRIter != allResults.cend(); aRIter++) {
640     std::shared_ptr<ModelAPI_Result> aRes = *aRIter;
641     if (!aRes->isDisabled()) {
642       // update state only for enabled results
643       // (Placement Result Part may make the original Part Result as invalid)
644       aRes->data()->execState(theState);
645     }
646     if (theFeature->data()->updateID() > aRes->data()->updateID()) {
647       aRes->data()->setUpdateID(theFeature->data()->updateID());
648     }
649     ModelAPI_EventCreator::get()->sendUpdated(aRes, EVENT_DISP);
650   }
651   // to redisplay "presentable" feature (for ex. distance constraint)
652   ModelAPI_EventCreator::get()->sendUpdated(theFeature, EVENT_DISP);
653   theFeature->data()->execState(theState);
654 }
655
656 /// Updates the state by the referenced object: if something bad with it, set state for this one
657 ModelAPI_ExecState stateByReference(ObjectPtr theTarget, const ModelAPI_ExecState theCurrent)
658 {
659   if (theTarget) {
660     ModelAPI_ExecState aRefState = theTarget->data()->execState();
661     if (aRefState == ModelAPI_StateMustBeUpdated) {
662       if (theCurrent == ModelAPI_StateDone)
663         return ModelAPI_StateMustBeUpdated;
664     } else if (aRefState != ModelAPI_StateDone) {
665       return ModelAPI_StateInvalidArgument;
666     }
667   }
668   return theCurrent;
669 }
670
671 void Model_Update::updateArguments(FeaturePtr theFeature) {
672   // perform this method also for disabled features: to make "not done" state for
673   // features referenced to the active and modified features
674   static ModelAPI_ValidatorsFactory* aFactory = ModelAPI_Session::get()->validators();
675
676   ModelAPI_ExecState aState = theFeature->data()->execState();
677   if (aState == ModelAPI_StateExecFailed) { // try again failed feature: issue 577
678     aState = ModelAPI_StateMustBeUpdated;
679   }
680   if (aState == ModelAPI_StateInvalidArgument) // a chance to be corrected
681     aState = ModelAPI_StateMustBeUpdated;
682   // check the parameters state
683   // Integer
684   {
685     std::list<AttributePtr> anAttrinbutes =
686       theFeature->data()->attributes(ModelAPI_AttributeInteger::typeId());
687     std::list<AttributePtr>::iterator anIter = anAttrinbutes.begin();
688     for(; anIter != anAttrinbutes.end(); anIter++) {
689       AttributeIntegerPtr anAttribute =
690         std::dynamic_pointer_cast<ModelAPI_AttributeInteger>(*anIter);
691       if (anAttribute.get() && !anAttribute->text().empty()) {
692         if (myIsParamUpdated) {
693           ModelAPI_AttributeEvalMessage::send(anAttribute, this);
694         }
695         if (anAttribute->expressionInvalid()) {
696           aState = ModelAPI_StateInvalidArgument;
697         }
698       }
699     }
700   }
701   // Double
702   {
703     std::list<AttributePtr> aDoubles =
704       theFeature->data()->attributes(ModelAPI_AttributeDouble::typeId());
705     std::list<AttributePtr>::iterator aDoubleIter = aDoubles.begin();
706     for(; aDoubleIter != aDoubles.end(); aDoubleIter++) {
707       AttributeDoublePtr aDouble =
708         std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(*aDoubleIter);
709       if (aDouble.get() && !aDouble->text().empty()) {
710         if (myIsParamUpdated) {
711           ModelAPI_AttributeEvalMessage::send(aDouble, this);
712         }
713         if (aDouble->expressionInvalid()) {
714           aState = ModelAPI_StateInvalidArgument;
715         }
716       }
717     }
718   }
719   // Point
720   {
721     std::list<AttributePtr> anAttributes =
722       theFeature->data()->attributes(GeomDataAPI_Point::typeId());
723     std::list<AttributePtr>::iterator anIter = anAttributes.begin();
724     for(; anIter != anAttributes.end(); anIter++) {
725       AttributePointPtr aPointAttribute =
726         std::dynamic_pointer_cast<GeomDataAPI_Point>(*anIter);
727       if (aPointAttribute.get() && (!aPointAttribute->textX().empty() ||
728           !aPointAttribute->textY().empty() || !aPointAttribute->textZ().empty())) {
729         if (myIsParamUpdated) {
730           ModelAPI_AttributeEvalMessage::send(aPointAttribute, this);
731         }
732         if ((!aPointAttribute->textX().empty() && aPointAttribute->expressionInvalid(0)) ||
733           (!aPointAttribute->textY().empty() && aPointAttribute->expressionInvalid(1)) ||
734           (!aPointAttribute->textZ().empty() && aPointAttribute->expressionInvalid(2)))
735           aState = ModelAPI_StateInvalidArgument;
736       }
737     }
738   }
739   // Point2D
740   {
741     std::list<AttributePtr> anAttributes =
742       theFeature->data()->attributes(GeomDataAPI_Point2D::typeId());
743     std::list<AttributePtr>::iterator anIter = anAttributes.begin();
744     for(; anIter != anAttributes.end(); anIter++) {
745       AttributePoint2DPtr aPoint2DAttribute =
746         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(*anIter);
747       if (aPoint2DAttribute.get()) {
748         if (myIsParamUpdated && (!aPoint2DAttribute->textX().empty() ||
749             !aPoint2DAttribute->textY().empty())) {
750           ModelAPI_AttributeEvalMessage::send(aPoint2DAttribute, this);
751         }
752         if ((!aPoint2DAttribute->textX().empty() && aPoint2DAttribute->expressionInvalid(0)) ||
753           (!aPoint2DAttribute->textY().empty() && aPoint2DAttribute->expressionInvalid(1)))
754           aState = ModelAPI_StateInvalidArgument;
755       }
756     }
757   }
758   // update the selection attributes if any
759   std::list<AttributePtr> aRefs =
760     theFeature->data()->attributes(ModelAPI_AttributeSelection::typeId());
761   std::list<AttributePtr>::iterator aRefsIter = aRefs.begin();
762   for (; aRefsIter != aRefs.end(); aRefsIter++) {
763     std::shared_ptr<ModelAPI_AttributeSelection> aSel =
764       std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(*aRefsIter);
765     ObjectPtr aContext = aSel->context();
766     // update argument only if the referenced object is ready to use
767     if (aContext.get() && !aContext->isDisabled()) {
768       if (isReason(theFeature, aContext)) {
769         if (!aSel->update()) { // this must be done on execution since it may be long operation
770           bool isObligatory = !aFactory->isNotObligatory(
771             theFeature->getKind(), theFeature->data()->id(aSel)) &&
772             aFactory->isCase(theFeature, theFeature->data()->id(aSel));
773           if (isObligatory)
774             aState = ModelAPI_StateInvalidArgument;
775         }
776       }
777     } else if (aContext.get()) {
778       // here it may be not obligatory, but if the reference is wrong, it should not be correct
779       bool isObligatory = aFactory->isCase(theFeature, theFeature->data()->id(aSel));
780       if (isObligatory)
781         aState = ModelAPI_StateInvalidArgument;
782     }
783   }
784   // update the selection list attributes if any
785   aRefs = theFeature->data()->attributes(ModelAPI_AttributeSelectionList::typeId());
786   for (aRefsIter = aRefs.begin(); aRefsIter != aRefs.end(); aRefsIter++) {
787     std::shared_ptr<ModelAPI_AttributeSelectionList> aSel =
788       std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(*aRefsIter);
789     for(int a = aSel->size() - 1; a >= 0; a--) {
790       std::shared_ptr<ModelAPI_AttributeSelection> aSelAttr =
791         std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(aSel->value(a));
792       if (aSelAttr) {
793         ObjectPtr aContext = aSelAttr->context();
794         // update argument only if the referenced object is ready to use
795         if (aContext.get() && !aContext->isDisabled()) {
796           if (isReason(theFeature, aContext)) {
797             if (!aSelAttr->update()) {
798               bool isObligatory = !aFactory->isNotObligatory(
799                 theFeature->getKind(), theFeature->data()->id(aSel)) &&
800                 aFactory->isCase(theFeature, theFeature->data()->id(aSel));
801               if (isObligatory)
802                 aState = ModelAPI_StateInvalidArgument;
803             }
804           }
805         } else if (aContext.get()) {
806           // here it may be not obligatory, but if the reference is wrong, it should not be correct
807           bool isObligatory = aFactory->isCase(theFeature, theFeature->data()->id(aSel));
808           if (isObligatory)
809             aState = ModelAPI_StateInvalidArgument;
810         }
811       }
812     }
813   }
814
815   if (aState != ModelAPI_StateDone)
816     theFeature->data()->execState(aState);
817 }
818
819 bool Model_Update::isReason(std::shared_ptr<ModelAPI_Feature>& theFeature,
820      std::shared_ptr<ModelAPI_Object> theReason)
821 {
822   std::map<std::shared_ptr<ModelAPI_Feature>, std::set<std::shared_ptr<ModelAPI_Feature> > >
823     ::iterator aReasonsIt = myModified.find(theFeature);
824   if (aReasonsIt != myModified.end()) {
825     if (aReasonsIt->second.find(theFeature) != aReasonsIt->second.end())
826       return true; // any is reason if it contains itself
827     FeaturePtr aReasFeat = std::dynamic_pointer_cast<ModelAPI_Feature>(theReason);
828     if (!aReasFeat.get()) { // try to get feature of this result
829       ResultPtr aReasRes = std::dynamic_pointer_cast<ModelAPI_Result>(theReason);
830       if (aReasRes.get())
831         aReasFeat = theReason->document()->feature(aReasRes);
832     }
833     if (aReasonsIt->second.find(aReasFeat) != aReasonsIt->second.end())
834       return true;
835   }
836   // another try: postponed modification by not-persistences
837   std::map<std::shared_ptr<ModelAPI_Feature>, std::set<std::shared_ptr<ModelAPI_Feature> > >
838     ::iterator aNotPersist = myNotPersistentRefs.find(theFeature);
839   if (aNotPersist != myNotPersistentRefs.end()) {
840     FeaturePtr aReasFeat = std::dynamic_pointer_cast<ModelAPI_Feature>(theReason);
841     if (!aReasFeat.get()) { // try to get feature of this result
842       ResultPtr aReasRes = std::dynamic_pointer_cast<ModelAPI_Result>(theReason);
843       if (aReasRes.get())
844         aReasFeat = theReason->document()->feature(aReasRes);
845     }
846     if (aNotPersist->second.find(aReasFeat) != aNotPersist->second.end())
847       return true;
848   }
849
850   // this case only for not-previewed items update state, nothing is changed in args for it
851   return false;
852 }
853
854 void Model_Update::executeFeature(FeaturePtr theFeature)
855 {
856 #ifdef DEB_UPDATE
857   std::cout<<"Execute Feature "<<theFeature->name()<<std::endl;
858 #endif
859   // execute in try-catch to avoid internal problems of the feature
860   ModelAPI_ExecState aState = ModelAPI_StateDone;
861   theFeature->data()->execState(ModelAPI_StateDone);
862   try {
863     theFeature->execute();
864     if (theFeature->data()->execState() != ModelAPI_StateDone) {
865       aState = ModelAPI_StateExecFailed;
866     } else {
867       aState = ModelAPI_StateDone;
868     }
869   } catch(...) {
870     aState = ModelAPI_StateExecFailed;
871     Events_InfoMessage("Model_Update",
872       "Feature %1 has failed during the execution").arg(theFeature->getKind()).send();
873   }
874   // The macro feature has to be deleted in any case even its execution is failed
875   myWaitForFinish.insert(theFeature);
876   if (aState != ModelAPI_StateDone) {
877     theFeature->eraseResults(false);
878   }
879   theFeature->data()->setUpdateID(ModelAPI_Session::get()->transactionID());
880   redisplayWithResults(theFeature, aState);
881 }
882
883 void Model_Update::updateStability(void* theSender)
884 {
885   static ModelAPI_ValidatorsFactory* aFactory = ModelAPI_Session::get()->validators();
886   if (theSender) {
887     bool added = false; // object may be was crated
888     ModelAPI_Object* aSender = static_cast<ModelAPI_Object*>(theSender);
889     if (aSender && aSender->document()) {
890       FeaturePtr aFeatureSender =
891         std::dynamic_pointer_cast<ModelAPI_Feature>(aSender->data()->owner());
892       if (aFeatureSender.get()) {
893         Model_Objects* aDocObjects =
894           std::dynamic_pointer_cast<Model_Document>(aSender->document())->objects();
895         if (aDocObjects) {
896           //aDocObjects->synchronizeBackRefs();
897           // remove or add all concealment refs from this feature
898           std::list<std::pair<std::string, std::list<ObjectPtr> > > aRefs;
899           aSender->data()->referencesToObjects(aRefs);
900           std::list<std::pair<std::string, std::list<ObjectPtr> > >::iterator
901             aRefIt = aRefs.begin();
902           for(; aRefIt != aRefs.end(); aRefIt++) {
903             if (!aFactory->isConcealed(aFeatureSender->getKind(), aRefIt->first))
904               // take into account only concealed references
905               // (do not remove the sketch constraint and the edge on constraint edit)
906               continue;
907             std::list<ObjectPtr>& aRefFeaturesList = aRefIt->second;
908             std::list<ObjectPtr>::iterator aReferenced = aRefFeaturesList.begin();
909             for(; aReferenced != aRefFeaturesList.end(); aReferenced++) {
910                // stability is only on results: feature to feature reference mean nested
911               // features, that will remove nesting references
912               if (aReferenced->get() && (*aReferenced)->data()->isValid() &&
913                 (*aReferenced)->groupName() != ModelAPI_Feature::group()) {
914                 std::shared_ptr<Model_Data> aData =
915                   std::dynamic_pointer_cast<Model_Data>((*aReferenced)->data());
916                 if (aFeatureSender->isStable()) {
917                   aData->addBackReference(aFeatureSender, aRefIt->first);
918                 } else {
919                   aData->removeBackReference(aFeatureSender, aRefIt->first);
920                   added = true; // remove of concealment may be caused creation of some result
921                 }
922               }
923             }
924           }
925         }
926       }
927     }
928     if (added) {
929       static Events_Loop* aLoop = Events_Loop::loop();
930       static Events_ID kEventCreated = aLoop->eventByName(EVENT_OBJECT_CREATED);
931       aLoop->flush(kEventCreated);
932     }
933   }
934 }
935
936 void Model_Update::updateSelection(const std::set<std::shared_ptr<ModelAPI_Object> >& theObjects)
937 {
938   std::set<std::shared_ptr<ModelAPI_Object> >::iterator anObj = theObjects.begin();
939   for(; anObj != theObjects.end(); anObj++) {
940     std::list<AttributePtr> aRefs =
941       (*anObj)->data()->attributes(ModelAPI_AttributeSelection::typeId());
942     std::list<AttributePtr>::iterator aRefsIter = aRefs.begin();
943     for (; aRefsIter != aRefs.end(); aRefsIter++) {
944       std::shared_ptr<Model_AttributeSelection> aSel =
945         std::dynamic_pointer_cast<Model_AttributeSelection>(*aRefsIter);
946       aSel->updateInHistory();
947     }
948     // update the selection list attributes if any
949     aRefs = (*anObj)->data()->attributes(ModelAPI_AttributeSelectionList::typeId());
950     for (aRefsIter = aRefs.begin(); aRefsIter != aRefs.end(); aRefsIter++) {
951       std::shared_ptr<ModelAPI_AttributeSelectionList> aSel =
952         std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(*aRefsIter);
953       for(int a = aSel->size() - 1; a >= 0; a--) {
954         std::shared_ptr<Model_AttributeSelection> aSelAttr =
955           std::dynamic_pointer_cast<Model_AttributeSelection>(aSel->value(a));
956         if (aSelAttr.get())
957           aSelAttr->updateInHistory();
958       }
959     }
960   }
961 }