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