]> SALOME platform Git repositories - modules/shaper.git/blob - src/ModuleBase/ModuleBase_OperationFeature.cpp
Salome HOME
Call method setStable inside of transaction
[modules/shaper.git] / src / ModuleBase / ModuleBase_OperationFeature.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 /*
4  * ModuleBase_OperationFeature.cpp
5  *
6  *  Created on: Apr 2, 2014
7  *      Author: sbh
8  */
9
10 #include "ModuleBase_OperationFeature.h"
11
12 #include "ModuleBase_OperationDescription.h"
13 #include "ModuleBase_ModelWidget.h"
14 #include "ModuleBase_ViewerPrs.h"
15 #include "ModuleBase_IPropertyPanel.h"
16 #include "ModuleBase_ISelection.h"
17 #include "ModuleBase_IViewer.h"
18
19 #include <ModelAPI_AttributeDouble.h>
20 #include <ModelAPI_Document.h>
21 #include <ModelAPI_Feature.h>
22 #include <ModelAPI_Data.h>
23 #include <ModelAPI_Document.h>
24 #include <ModelAPI_Events.h>
25 #include <ModelAPI_Result.h>
26 #include <ModelAPI_Object.h>
27 #include <ModelAPI_Validator.h>
28 #include <ModelAPI_Session.h>
29 #include <ModelAPI_Tools.h>
30
31 #include <GeomAPI_Pnt2d.h>
32
33 #include <Events_Loop.h>
34
35 #include <QTimer>
36
37 #ifdef _DEBUG
38 #include <QDebug>
39 #endif
40
41 ModuleBase_OperationFeature::ModuleBase_OperationFeature(const QString& theId, QObject* theParent)
42 : ModuleBase_Operation(theId, theParent),
43   myIsEditing(false)
44 {
45 }
46
47 ModuleBase_OperationFeature::~ModuleBase_OperationFeature()
48 {
49   clearPreselection();
50 }
51
52 FeaturePtr ModuleBase_OperationFeature::feature() const
53 {
54   return myFeature;
55 }
56
57 bool ModuleBase_OperationFeature::isValid() const
58 {
59   if (!myFeature || !myFeature->data()->isValid())
60     return true; // rename operation
61   if (myFeature->isAction())
62     return true;
63   //Get validators for the Id
64   SessionPtr aMgr = ModelAPI_Session::get();
65   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
66   bool aValid = aFactory->validate(myFeature);
67
68   // the feature exec state should be checked in order to do not apply features, which result can not
69   // be built. E.g. extrusion on sketch, where the "to" is a perpendicular plane to the sketch
70   bool isDone = ( myFeature->data()->execState() == ModelAPI_StateDone
71                || myFeature->data()->execState() == ModelAPI_StateMustBeUpdated );
72
73   return aValid && isDone;
74 }
75
76 void ModuleBase_OperationFeature::startOperation()
77 {
78   FeaturePtr aFeature = feature();
79   if (!aFeature.get() || !isEditOperation())
80     return;
81
82   if (aFeature.get() && isEditOperation())
83     aFeature->setStable(false);
84
85   myVisualizedObjects.clear();
86   // store hidden result features
87   std::list<ResultPtr> aResults = aFeature->results();
88   std::list<ResultPtr>::const_iterator aIt;
89   for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
90     ObjectPtr anObject = *aIt;
91     if (anObject.get() && !anObject->isDisplayed()) {
92       myVisualizedObjects.insert(*aIt);
93       anObject->setDisplayed(true);
94     }
95   }
96   if (!aFeature->isDisplayed()) {
97     myVisualizedObjects.insert(aFeature);
98     aFeature->setDisplayed(true);
99   }
100   Events_Loop::loop()->flush(Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY));
101 }
102
103 void ModuleBase_OperationFeature::stopOperation()
104 {
105   FeaturePtr aFeature = feature();
106   if (!aFeature.get() || !isEditOperation())
107     return;
108
109   // store hidden result features
110   std::list<ResultPtr> aResults = aFeature->results();
111   std::list<ResultPtr>::const_iterator aIt;
112   for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
113     ObjectPtr anObject = *aIt;
114     if (anObject.get() && myVisualizedObjects.find(anObject) != myVisualizedObjects.end()) {
115       anObject->setDisplayed(false);
116     }
117   }
118   if (myVisualizedObjects.find(aFeature) != myVisualizedObjects.end()) {
119     aFeature->setDisplayed(false);
120   }
121   if (myVisualizedObjects.size() > 0)
122     Events_Loop::loop()->flush(Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY));
123 }
124
125 void ModuleBase_OperationFeature::resumeOperation()
126 {
127   ModuleBase_Operation::resumeOperation();
128   //if (!myIsEditing)
129     setCurrentFeature(feature());
130   //SessionPtr aMgr = ModelAPI_Session::get();
131   //DocumentPtr aDoc = aMgr->activeDocument();
132   //aDoc->setCurrentFeature(feature(), false);
133 }
134
135 FeaturePtr ModuleBase_OperationFeature::createFeature(const bool theFlushMessage)
136 {
137   if (myParentFeature.get()) {
138     myFeature = myParentFeature->addFeature(getDescription()->operationId().toStdString());
139   } else {
140     std::shared_ptr<ModelAPI_Document> aDoc = ModelAPI_Session::get()->activeDocument();
141     myFeature = aDoc->addFeature(getDescription()->operationId().toStdString());
142   }
143   if (myFeature) {  // TODO: generate an error if feature was not created
144     setIsModified(true);
145     // Model update should call "execute" of a feature.
146     //myFeature->execute();
147     // Init default values
148     /*QList<ModuleBase_ModelWidget*> aWidgets = getDescription()->modelWidgets();
149      QList<ModuleBase_ModelWidget*>::const_iterator anIt = aWidgets.begin(), aLast = aWidgets.end();
150      for (; anIt != aLast; anIt++) {
151      (*anIt)->storeValue(aFeature);
152      }*/
153   }
154
155   if (theFlushMessage)
156     Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED));
157   return myFeature;
158 }
159
160 void ModuleBase_OperationFeature::setFeature(FeaturePtr theFeature)
161 {
162   myFeature = theFeature;
163   myIsEditing = true;
164 }
165
166 bool ModuleBase_OperationFeature::hasObject(ObjectPtr theObj) const
167 {
168   FeaturePtr aFeature = feature();
169   if (aFeature) {
170     if (aFeature == theObj)
171       return true;
172     std::list<ResultPtr> aResults = aFeature->results();
173     std::list<ResultPtr>::const_iterator aIt;
174     for (aIt = aResults.cbegin(); aIt != aResults.cend(); ++aIt) {
175       if (theObj == (*aIt))
176         return true;
177     }
178   }
179   return false;
180 }
181
182 bool ModuleBase_OperationFeature::isDisplayedOnStart(ObjectPtr theObject)
183 {
184   return myVisualizedObjects.find(theObject) != myVisualizedObjects.end();
185 }
186
187 void ModuleBase_OperationFeature::start()
188 {
189   setIsModified(false);
190   QString anId = getDescription()->operationId();
191   if (myIsEditing) {
192     anId = anId.append(EditSuffix());
193   }
194   ModelAPI_Session::get()->startOperation(anId.toStdString());
195
196   startOperation();
197
198   if (!myIsEditing) {
199     FeaturePtr aFeature = createFeature();
200     // if the feature is not created, there is no sense to start the operation
201     if (aFeature.get() == NULL) {
202       // it is necessary to abor the operation in the session and emit the aborted signal
203       // in order to update commands status in the workshop, to be exact the feature action
204       // to be unchecked
205       abort();
206       return;
207     }
208   }
209   /// Set current feature and remeber old current feature
210   if (myIsEditing) {
211     SessionPtr aMgr = ModelAPI_Session::get();
212     DocumentPtr aDoc = aMgr->activeDocument();
213     // the parameter of current feature should be false, we should use all feature, not only visible
214     // in order to correctly save the previous feature of the nested operation, where the
215     // features can be not visible in the tree. The problem case is Edit sketch entitity(line)
216     // in the Sketch, created in ExtrusionCut operation. The entity disappears by commit.
217     // When sketch entity operation started, the sketch should be cashed here as the current.
218     // Otherwise(the flag is true), the ExtrusionCut is cashed, when commit happens, the sketch
219     // is disabled, sketch entity is disabled as extrusion cut is created earliest then sketch.
220     // As a result the sketch disappears from the viewer. However after commit it is displayed back.
221     myPreviousCurrentFeature = aDoc->currentFeature(false);
222     aDoc->setCurrentFeature(feature(), false);
223   }
224
225   //Already called startOperation();
226   emit started();
227
228 }
229
230 void ModuleBase_OperationFeature::abort()
231 {
232   // the viewer update should be blocked in order to avoid the features blinking before they are
233   // hidden
234   std::shared_ptr<Events_Message> aMsg = std::shared_ptr<Events_Message>(
235       new Events_Message(Events_Loop::eventByName(EVENT_UPDATE_VIEWER_BLOCKED)));
236   Events_Loop::loop()->send(aMsg);
237
238   // the widgets of property panel should not process any events come from data mode
239   // after abort clicked. Some signal such as redisplay/create influence on content
240   // of the object browser and viewer context. Therefore it influence to the current
241   // selection and if the active widget listens it, the attribute value is errnoneous
242   // changed.
243   ModuleBase_IPropertyPanel* aPropertyPanel = propertyPanel();
244   if (aPropertyPanel)
245     aPropertyPanel->cleanContent();
246
247   myFeature->setStable(true);
248
249   SessionPtr aMgr = ModelAPI_Session::get();
250   if (myIsEditing) {
251     DocumentPtr aDoc = aMgr->activeDocument();
252     bool aIsOp = aMgr->isOperation();
253     if (!aIsOp)
254       aMgr->startOperation();
255     aDoc->setCurrentFeature(myPreviousCurrentFeature, false);//true);
256     if (!aIsOp)
257       aMgr->finishOperation();
258     myPreviousCurrentFeature = FeaturePtr();
259   }
260   abortOperation();
261
262   stopOperation();
263
264   aMgr->abortOperation();
265   emit stopped();
266   // the viewer update should be unblocked in order to avoid the features blinking before they are
267   // hidden
268   aMsg = std::shared_ptr<Events_Message>(
269                 new Events_Message(Events_Loop::eventByName(EVENT_UPDATE_VIEWER_UNBLOCKED)));
270
271   Events_Loop::loop()->send(aMsg);
272
273   emit aborted();
274 }
275
276 bool ModuleBase_OperationFeature::commit()
277 {
278   if (canBeCommitted()) {
279     // the widgets of property panel should not process any events come from data mode
280     // after commit clicked. Some signal such as redisplay/create influence on content
281     // of the object browser and viewer context. Therefore it influence to the current
282     // selection and if the active widget listens it, the attribute value is errnoneous
283     // changed.
284     ModuleBase_IPropertyPanel* aPropertyPanel = propertyPanel();
285     if (aPropertyPanel)
286       aPropertyPanel->cleanContent();
287     
288     myFeature->setStable(true);
289
290     SessionPtr aMgr = ModelAPI_Session::get();
291     /// Set current feature and remeber old current feature
292
293     if (myIsEditing) {
294       setCurrentFeature(myPreviousCurrentFeature);
295       /*DocumentPtr aDoc = aMgr->activeDocument();
296       bool aIsOp = aMgr->isOperation();
297       if (!aIsOp)
298         aMgr->startOperation();
299       aDoc->setCurrentFeature(myPreviousCurrentFeature, true);
300       if (!aIsOp)
301         aMgr->finishOperation();*/
302       myPreviousCurrentFeature = FeaturePtr();
303     }
304     else {
305       /*CompositeFeaturePtr aCompositeFeature = ModelAPI_Tools::compositeOwner(feature());
306       if (aCompositeFeature.get())
307         setCurrentFeature(aCompositeFeature);//myPreviousCurrentFeature);
308       //else
309       //  setCurrentFeature(feature());*/
310     }
311     commitOperation();
312     aMgr->finishOperation();
313
314     stopOperation();
315     emit stopped();
316     emit committed();
317
318     afterCommitOperation();
319     return true;
320   }
321   return false;
322 }
323
324 void ModuleBase_OperationFeature::setCurrentFeature(const FeaturePtr& theFeature)
325 {
326   SessionPtr aMgr = ModelAPI_Session::get();
327   DocumentPtr aDoc = aMgr->activeDocument();
328   bool aIsOp = aMgr->isOperation();
329   if (!aIsOp)
330     aMgr->startOperation();
331   aDoc->setCurrentFeature(theFeature, false);//true);
332   if (!aIsOp)
333     aMgr->finishOperation();
334 }
335
336 void ModuleBase_OperationFeature::activateByPreselection()
337 {
338   if (myPreSelection.empty())
339     return;
340
341   ModuleBase_ModelWidget* aFilledWgt = 0;
342   ModuleBase_IPropertyPanel* aPropertyPanel = propertyPanel();
343   if (aPropertyPanel) {
344     const QList<ModuleBase_ModelWidget*>& aWidgets = aPropertyPanel->modelWidgets();
345     if (!aWidgets.empty()) {
346       ModuleBase_ModelWidget* aWgt = 0;
347       QList<ModuleBase_ModelWidget*>::const_iterator aWIt;
348       bool isSet = false;
349       // 1. apply the selection to controls
350       for (aWIt = aWidgets.constBegin(); aWIt != aWidgets.constEnd(); ++aWIt) {
351         aWgt = (*aWIt);
352         if (!aWgt->canSetValue())
353           continue;
354         aPropertyPanel->setPreselectionWidget(aWgt);
355         if (!aWgt->setSelection(myPreSelection, true)) {
356           isSet = false;
357           break;
358         } else {
359           isSet = true;
360           aFilledWgt = aWgt;
361         }
362       }
363       aPropertyPanel->setPreselectionWidget(NULL);
364       // in order to redisplay object in the viewer, the update/redisplay signals should be flushed
365       // it is better to perform it not in setSelection of each widget, but do it here,
366       // after the preselection is processed
367       ModuleBase_ModelWidget::updateObject(myFeature);
368
369       // 3. a signal should be emitted before the next widget activation
370       // because, the activation of the next widget will give a focus to the widget. As a result
371       // the value of the widget is initialized. And commit may happens until the value is entered.
372       if (aFilledWgt)
373         emit activatedByPreselection();
374     }
375     // 4. activate the next obligatory widget
376     aPropertyPanel->activateNextWidget(aFilledWgt);
377   }
378
379   clearPreselection();
380 }
381
382 void ModuleBase_OperationFeature::setParentFeature(CompositeFeaturePtr theParent)
383 {
384   myParentFeature = theParent;
385 }
386
387 CompositeFeaturePtr ModuleBase_OperationFeature::parentFeature() const
388 {
389   return myParentFeature;
390 }
391
392 void ModuleBase_OperationFeature::initSelection(ModuleBase_ISelection* theSelection,
393                                          ModuleBase_IViewer* theViewer)
394 {
395   clearPreselection();
396
397   QList<ModuleBase_ViewerPrs> aPreSelected;
398   // Check that the selected result are not results of operation feature
399   FeaturePtr aFeature = feature();
400   if (aFeature) {
401     QList<ModuleBase_ViewerPrs> aSelected = theSelection->getSelected(ModuleBase_ISelection::AllControls);
402
403     std::list<ResultPtr> aResults = aFeature->results();
404     QObjectPtrList aResList;
405     std::list<ResultPtr>::const_iterator aIt;
406     for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt)
407       aResList.append(*aIt);
408
409     foreach (ModuleBase_ViewerPrs aPrs, aSelected) {
410       if ((!aResList.contains(aPrs.object())) && (aPrs.object() != aFeature))
411         aPreSelected.append(aPrs);
412     }
413   } else
414     aPreSelected = theSelection->getSelected(ModuleBase_ISelection::AllControls);
415
416   myPreSelection = aPreSelected;
417 }
418
419 void ModuleBase_OperationFeature::clearPreselection()
420 {
421   myPreSelection.clear();
422 }
423
424 void ModuleBase_OperationFeature::setPropertyPanel(ModuleBase_IPropertyPanel* theProp) 
425 {
426   ModuleBase_Operation::setPropertyPanel(theProp);
427
428   theProp->setEditingMode(isEditOperation());
429
430   if (theProp) {
431     const QList<ModuleBase_ModelWidget*>& aWidgets = theProp->modelWidgets();
432     QList<ModuleBase_ModelWidget*>::const_iterator aWIt;
433     for (aWIt = aWidgets.constBegin(); aWIt != aWidgets.constEnd(); ++aWIt) {
434       ModuleBase_ModelWidget* aWgt = (*aWIt);
435       connect(aWgt, SIGNAL(valuesChanged()), this, SLOT(onValuesChanged()));
436     }
437   }
438
439   // Do not activate widgets by default if the current operation is editing operation
440   // Because we don't know which widget is going to be edited. 
441   if (!isEditOperation()) {
442     // 4. activate the first obligatory widget
443     theProp->activateNextWidget(NULL);
444   }
445   else {
446     // set focus on Ok button in order to operation manager could process Enter press
447     if (theProp)
448       theProp->setFocusOnOkButton();
449   }
450 }