Salome HOME
Coincidence to rectangle macro feature: 1. Get point of sub-feature to set coincidenc...
[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     if (aFeature->isMacro()) {
199       // macro feature may refers to sub-features, which also should be deactivated when the operation
200       // is active, e.g. rectangle'lines.
201       FeaturePtr anObjectFeature = ModelAPI_Feature::feature(theObj);
202       std::list<AttributePtr> anAttributes = aFeature->data()->attributes(
203                                               ModelAPI_AttributeRefList::typeId());
204       std::list<AttributePtr>::const_iterator anIt = anAttributes.begin(), aLast = anAttributes.end();
205       bool aFoundObject = false;
206       for (; anIt != aLast && !aFoundObject; anIt++) {
207         std::shared_ptr<ModelAPI_AttributeRefList> aCurSelList =
208                                          std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(*anIt);
209         for (int i = 0, aNb = aCurSelList->size(); i < aNb && !aFoundObject; i++) {
210           ObjectPtr anObject = aCurSelList->object(i);
211           FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anObject);
212           if (aFeature.get()) {
213             aFoundObject = anObjectFeature == aFeature;
214           }
215         }
216       }
217       return aFoundObject;
218     }
219 #endif
220   }
221   return false;
222 }
223
224 bool ModuleBase_OperationFeature::isDisplayedOnStart(ObjectPtr theObject)
225 {
226   return myVisualizedObjects.find(theObject) != myVisualizedObjects.end();
227 }
228
229 bool ModuleBase_OperationFeature::start()
230 {
231   setIsModified(false);
232   QString anId = getDescription()->operationId();
233   if (myIsEditing) {
234     anId = anId.append(EditSuffix());
235   }
236   ModelAPI_Session::get()->startOperation(anId.toStdString());
237
238   emit beforeStarted();
239   startOperation();
240
241   if (!myIsEditing) {
242     FeaturePtr aFeature = createFeature();
243     // if the feature is not created, there is no sense to start the operation
244     if (aFeature.get() == NULL) {
245       // it is necessary to abor the operation in the session and emit the aborted signal
246       // in order to update commands status in the workshop, to be exact the feature action
247       // to be unchecked
248       abort();
249       return false;
250     }
251   }
252   //Already called startOperation();
253   emit started();
254   return true;
255 }
256
257 void ModuleBase_OperationFeature::abort()
258 {
259   emit beforeAborted();
260
261   // the viewer update should be blocked in order to avoid the features blinking before they are
262   // hidden
263   std::shared_ptr<Events_Message> aMsg = std::shared_ptr<Events_Message>(
264       new Events_Message(Events_Loop::eventByName(EVENT_UPDATE_VIEWER_BLOCKED)));
265   Events_Loop::loop()->send(aMsg);
266
267   // the widgets of property panel should not process any events come from data mode
268   // after abort 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   if (myFeature.get())
277     myFeature->setStable(true);
278
279   abortOperation();
280   stopOperation();
281
282   SessionPtr aMgr = ModelAPI_Session::get();
283   aMgr->abortOperation();
284   emit stopped();
285   // the viewer update should be unblocked in order to avoid the features blinking before they are
286   // hidden
287   aMsg = std::shared_ptr<Events_Message>(
288                 new Events_Message(Events_Loop::eventByName(EVENT_UPDATE_VIEWER_UNBLOCKED)));
289
290   Events_Loop::loop()->send(aMsg);
291
292   emit aborted();
293 }
294
295 bool ModuleBase_OperationFeature::commit()
296 {
297   ModuleBase_IPropertyPanel* aPanel = propertyPanel();
298   if (aPanel) {
299     ModuleBase_ModelWidget* anActiveWidget = aPanel->activeWidget();
300     if (anActiveWidget && anActiveWidget->getValueState() == ModuleBase_ModelWidget::ModifiedInPP) {
301       anActiveWidget->storeValue();
302     }
303   }
304   if (canBeCommitted()) {
305     emit beforeCommitted();
306     // the widgets of property panel should not process any events come from data mode
307     // after commit clicked. Some signal such as redisplay/create influence on content
308     // of the object browser and viewer context. Therefore it influence to the current
309     // selection and if the active widget listens it, the attribute value is errnoneous
310     // changed.
311     ModuleBase_IPropertyPanel* aPropertyPanel = propertyPanel();
312     if (aPropertyPanel)
313       aPropertyPanel->cleanContent();
314
315     myFeature->setStable(true);
316
317     SessionPtr aMgr = ModelAPI_Session::get();
318     /// Set current feature and remeber old current feature
319
320     commitOperation();
321     aMgr->finishOperation();
322
323     stopOperation();
324     emit stopped();
325     emit committed();
326
327     afterCommitOperation();
328     return true;
329   }
330   return false;
331 }
332
333 void ModuleBase_OperationFeature::activateByPreselection()
334 {
335   if (myPreSelection.empty())
336     return;
337
338   ModuleBase_ISelection::filterSelectionOnEqualPoints(myPreSelection);
339
340   ModuleBase_ModelWidget* aFilledWgt = 0;
341   ModuleBase_IPropertyPanel* aPropertyPanel = propertyPanel();
342   if (aPropertyPanel) {
343     const QList<ModuleBase_ModelWidget*>& aWidgets = aPropertyPanel->modelWidgets();
344     if (!aWidgets.empty()) {
345       ModuleBase_ModelWidget* aWgt = 0;
346       QList<ModuleBase_ModelWidget*>::const_iterator aWIt;
347       bool isSet = false;
348       // 1. apply the selection to controls
349       for (aWIt = aWidgets.constBegin(); aWIt != aWidgets.constEnd(); ++aWIt) {
350         aWgt = (*aWIt);
351         if (!aWgt->canSetValue())
352           continue;
353         aPropertyPanel->setPreselectionWidget(aWgt);
354         if (!aWgt->setSelection(myPreSelection, true)) {
355           isSet = false;
356           break;
357         } else {
358           isSet = true;
359           aFilledWgt = aWgt;
360         }
361       }
362       aPropertyPanel->setPreselectionWidget(NULL);
363       // in order to redisplay object in the viewer, the update/redisplay signals should be flushed
364       // it is better to perform it not in setSelection of each widget, but do it here,
365       // after the preselection is processed
366       ModuleBase_ModelWidget::updateObject(myFeature);
367
368       // 3. a signal should be emitted before the next widget activation
369       // because, the activation of the next widget will give a focus to the widget. As a result
370       // the value of the widget is initialized. And commit may happens until the value is entered.
371       if (aFilledWgt)
372         emit activatedByPreselection();
373     }
374     // 4. activate the next obligatory widget
375     aPropertyPanel->activateNextWidget(aFilledWgt);
376   }
377
378   clearPreselection();
379 }
380
381 void ModuleBase_OperationFeature::setParentFeature(CompositeFeaturePtr theParent)
382 {
383   myParentFeature = theParent;
384 }
385
386 CompositeFeaturePtr ModuleBase_OperationFeature::parentFeature() const
387 {
388   return myParentFeature;
389 }
390
391 void ModuleBase_OperationFeature::setPreviousCurrentFeature(const FeaturePtr& theFeature)
392 {
393   myPreviousCurrentFeature = theFeature;
394 }
395
396 FeaturePtr ModuleBase_OperationFeature::previousCurrentFeature()
397 {
398   return myPreviousCurrentFeature;
399 }
400
401 void ModuleBase_OperationFeature::initSelection(ModuleBase_ISelection* theSelection,
402                                          ModuleBase_IViewer* theViewer)
403 {
404   clearPreselection();
405
406   QList<ModuleBase_ViewerPrs> aPreSelected;
407   // Check that the selected result are not results of operation feature
408   FeaturePtr aFeature = feature();
409   if (aFeature) {
410     QList<ModuleBase_ViewerPrs> aSelected = theSelection->getSelected(ModuleBase_ISelection::AllControls);
411
412     std::list<ResultPtr> aResults = aFeature->results();
413     QObjectPtrList aResList;
414     std::list<ResultPtr>::const_iterator aIt;
415     for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt)
416       aResList.append(*aIt);
417
418     foreach (ModuleBase_ViewerPrs aPrs, aSelected) {
419       if ((!aResList.contains(aPrs.object())) && (aPrs.object() != aFeature))
420         aPreSelected.append(aPrs);
421     }
422   } else
423     aPreSelected = theSelection->getSelected(ModuleBase_ISelection::AllControls);
424
425   myPreSelection = aPreSelected;
426 }
427
428 void ModuleBase_OperationFeature::clearPreselection()
429 {
430   myPreSelection.clear();
431 }
432
433 void ModuleBase_OperationFeature::setPropertyPanel(ModuleBase_IPropertyPanel* theProp) 
434 {
435   ModuleBase_Operation::setPropertyPanel(theProp);
436
437   theProp->setEditingMode(isEditOperation());
438
439   if (theProp) {
440     const QList<ModuleBase_ModelWidget*>& aWidgets = theProp->modelWidgets();
441     QList<ModuleBase_ModelWidget*>::const_iterator aWIt;
442     for (aWIt = aWidgets.constBegin(); aWIt != aWidgets.constEnd(); ++aWIt) {
443       ModuleBase_ModelWidget* aWgt = (*aWIt);
444       connect(aWgt, SIGNAL(valuesChanged()), this, SLOT(onValuesChanged()));
445     }
446   }
447
448   // Do not activate widgets by default if the current operation is editing operation
449   // Because we don't know which widget is going to be edited. 
450   if (!isEditOperation()) {
451     // 4. activate the first obligatory widget
452     theProp->activateNextWidget(NULL);
453   }
454   else {
455     // set focus on Ok button in order to operation manager could process Enter press
456     if (theProp)
457       theProp->setFocusOnOkButton();
458   }
459 }