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