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