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