Salome HOME
Add copyright header according to request of CEA from 06.06.2017
[modules/shaper.git] / src / ModuleBase / ModuleBase_OperationFeature.cpp
1 // Copyright (C) 2014-2017  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
18 //
19
20 /*
21  * ModuleBase_OperationFeature.cpp
22  *
23  *  Created on: Apr 2, 2014
24  *      Author: sbh
25  */
26
27 #include "ModuleBase_OperationFeature.h"
28
29 #include "ModuleBase_OperationDescription.h"
30 #include "ModuleBase_ModelWidget.h"
31 #include "ModuleBase_ViewerPrs.h"
32 #include "ModuleBase_IPropertyPanel.h"
33 #include "ModuleBase_ISelection.h"
34 #include "ModuleBase_IViewer.h"
35 #include "ModuleBase_Tools.h"
36
37 #include <ModelAPI_AttributeDouble.h>
38 #include <ModelAPI_Document.h>
39 #include <ModelAPI_Feature.h>
40 #include <ModelAPI_Data.h>
41 #include <ModelAPI_Document.h>
42 #include <ModelAPI_Events.h>
43 #include <ModelAPI_Result.h>
44 #include <ModelAPI_Object.h>
45 #include <ModelAPI_Validator.h>
46 #include <ModelAPI_Session.h>
47 #include <ModelAPI_Tools.h>
48
49 #include <GeomAPI_Pnt2d.h>
50
51 #include <Events_Loop.h>
52
53 #include <QTimer>
54
55 // the define to check the activated object as a sub-feature by argument of
56 // the operation feature. E.g. rectangle feature(operation), line(in argument) to be not activated
57 #define DEBUG_DO_NOT_ACTIVATE_SUB_FEATURE
58 #ifdef DEBUG_DO_NOT_ACTIVATE_SUB_FEATURE
59 #include <ModelAPI_AttributeRefList.h>
60 #endif
61
62 //#define DEBUG_OPERATION_START
63
64 #ifdef _DEBUG
65 #include <QDebug>
66 #endif
67
68 ModuleBase_OperationFeature::ModuleBase_OperationFeature(const QString& theId, QObject* theParent)
69 : ModuleBase_Operation(theId, theParent),
70   myIsEditing(false)
71 {
72 }
73
74 ModuleBase_OperationFeature::~ModuleBase_OperationFeature()
75 {
76   clearPreselection();
77 }
78
79 void ModuleBase_OperationFeature::setEditOperation(const bool& isEditState
80                                                    /*const bool theRestartTransaction*/)
81 {
82   bool isCurrentEditState = isEditOperation();
83   if (isCurrentEditState == isEditState)
84     return;
85
86   /*
87   // this case is obsolete as it was not approved for reentrant sketch operation
88   // it was implemented when isEditState did not exist and only edit operation can be set
89   if (theRestartTransaction) {
90     // finsh previous create operation
91     emit beforeCommitted();
92     SessionPtr aMgr = ModelAPI_Session::get();
93     ModelAPI_Session::get()->finishOperation();
94
95     // start new edit operation
96     myIsEditing = true;
97     QString anId = getDescription()->operationId();
98     if (myIsEditing) {
99       anId = anId.append(EditSuffix());
100     }
101     ModelAPI_Session::get()->startOperation(anId.toStdString());
102     emit beforeStarted();
103   } else*/
104   myIsEditing = isEditState;
105
106   propertyPanel()->setEditingMode(isEditOperation());
107 }
108
109 FeaturePtr ModuleBase_OperationFeature::feature() const
110 {
111   return myFeature;
112 }
113
114 bool ModuleBase_OperationFeature::isValid() const
115 {
116   if (!myFeature || !myFeature->data()->isValid())
117     return true; // rename operation
118   if (myFeature->isAction())
119     return true;
120
121   std::string anError = ModelAPI_Tools::getFeatureError(myFeature);
122   //ModuleBase_Tools::translate(myFeature->getKind(), anError);
123   return anError.empty();
124 }
125
126 void ModuleBase_OperationFeature::startOperation()
127 {
128   FeaturePtr aFeature = feature();
129   if (!aFeature.get() || !isEditOperation())
130     return;
131
132   if (aFeature.get() && isEditOperation())
133     aFeature->setStable(false);
134
135   myVisualizedObjects.clear();
136   // store hidden result features
137   std::list<ResultPtr> aResults;
138   ModelAPI_Tools::allResults(aFeature, aResults);
139   std::list<ResultPtr>::const_iterator aIt;
140   for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
141     ObjectPtr anObject = *aIt;
142     if (anObject.get() && !anObject->isDisplayed()) {
143       myVisualizedObjects.insert(*aIt);
144       anObject->setDisplayed(true);
145     }
146   }
147   if (!aFeature->isDisplayed()) {
148     myVisualizedObjects.insert(aFeature);
149     aFeature->setDisplayed(true);
150   }
151   Events_Loop::loop()->flush(Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY));
152 }
153
154 void ModuleBase_OperationFeature::stopOperation()
155 {
156   FeaturePtr aFeature = feature();
157   if (!aFeature.get() || !isEditOperation())
158     return;
159
160   // store hidden result features
161   std::list<ResultPtr> aResults;
162   ModelAPI_Tools::allResults(aFeature, aResults);
163   std::list<ResultPtr>::const_iterator aIt;
164   for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt) {
165     ObjectPtr anObject = *aIt;
166     if (anObject.get() && myVisualizedObjects.find(anObject) != myVisualizedObjects.end()) {
167       anObject->setDisplayed(false);
168     }
169   }
170   if (myVisualizedObjects.find(aFeature) != myVisualizedObjects.end()) {
171     aFeature->setDisplayed(false);
172   }
173   if (myVisualizedObjects.size() > 0)
174     Events_Loop::loop()->flush(Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY));
175 }
176
177 FeaturePtr ModuleBase_OperationFeature::createFeature(const bool theFlushMessage)
178 {
179   if (myParentFeature.get()) {
180     myFeature = myParentFeature->addFeature(getDescription()->operationId().toStdString());
181   } else {
182     std::shared_ptr<ModelAPI_Document> aDoc = ModelAPI_Session::get()->activeDocument();
183     myFeature = aDoc->addFeature(getDescription()->operationId().toStdString());
184   }
185   if (myFeature) {  // TODO: generate an error if feature was not created
186     setIsModified(true);
187     // Model update should call "execute" of a feature.
188     //myFeature->execute();
189     // Init default values
190     /*QList<ModuleBase_ModelWidget*> aWidgets = getDescription()->modelWidgets();
191      QList<ModuleBase_ModelWidget*>::const_iterator anIt = aWidgets.begin(), aLast = aWidgets.end();
192      for (; anIt != aLast; anIt++) {
193      (*anIt)->storeValue(aFeature);
194      }*/
195   }
196
197   if (theFlushMessage) {
198     Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED));
199     Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
200   }
201   return myFeature;
202 }
203
204 void ModuleBase_OperationFeature::setFeature(FeaturePtr theFeature)
205 {
206   myFeature = theFeature;
207   myIsEditing = true;
208 }
209
210 bool ModuleBase_OperationFeature::hasObject(ObjectPtr theObj) const
211 {
212   FeaturePtr aFeature = feature();
213   if (aFeature) {
214     if (aFeature == theObj)
215       return true;
216     std::list<ResultPtr> aResults;
217     ModelAPI_Tools::allResults(aFeature, aResults);
218     std::list<ResultPtr>::const_iterator aIt;
219     for (aIt = aResults.cbegin(); aIt != aResults.cend(); ++aIt) {
220       ResultPtr aResult = *aIt;
221       if (theObj == aResult)
222          return true;
223     }
224 #ifdef DEBUG_DO_NOT_ACTIVATE_SUB_FEATURE
225     if (aFeature->isMacro()) {
226       // macro feature may refers to sub-features,
227       // which also should be deactivated when the operation
228       // is active, e.g. rectangle'lines.
229       FeaturePtr anObjectFeature = ModelAPI_Feature::feature(theObj);
230       std::list<AttributePtr> anAttributes = aFeature->data()->attributes(
231                                               ModelAPI_AttributeRefList::typeId());
232       std::list<AttributePtr>::const_iterator
233         anIt = anAttributes.begin(), aLast = anAttributes.end();
234       bool aFoundObject = false;
235       for (; anIt != aLast && !aFoundObject; anIt++) {
236         std::shared_ptr<ModelAPI_AttributeRefList> aCurSelList =
237                                std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(*anIt);
238         for (int i = 0, aNb = aCurSelList->size(); i < aNb && !aFoundObject; i++) {
239           ObjectPtr anObject = aCurSelList->object(i);
240           FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anObject);
241           if (aFeature.get()) {
242             aFoundObject = anObjectFeature == aFeature;
243           }
244         }
245       }
246       return aFoundObject;
247     }
248 #endif
249   }
250   return false;
251 }
252
253 bool ModuleBase_OperationFeature::isDisplayedOnStart(ObjectPtr theObject)
254 {
255   return myVisualizedObjects.find(theObject) != myVisualizedObjects.end();
256 }
257
258 bool ModuleBase_OperationFeature::start()
259 {
260 #ifdef DEBUG_OPERATION_START
261   qDebug("ModuleBase_OperationFeature::start -- begin");
262 #endif
263   setIsModified(false);
264   QString anId = getDescription()->operationId();
265   if (myIsEditing) {
266     anId = anId.append(EditSuffix());
267   }
268   ModelAPI_Session::get()->startOperation(anId.toStdString());
269
270   emit beforeStarted();
271   startOperation();
272
273   if (!myIsEditing) {
274     FeaturePtr aFeature = createFeature();
275     // if the feature is not created, there is no sense to start the operation
276     if (aFeature.get() == NULL) {
277       // it is necessary to abor the operation in the session and emit the aborted signal
278       // in order to update commands status in the workshop, to be exact the feature action
279       // to be unchecked
280       abort();
281 #ifdef DEBUG_OPERATION_START
282   qDebug("ModuleBase_OperationFeature::start -- end : IMPOSSIBLE to start");
283 #endif
284       return false;
285     }
286   }
287   //Already called startOperation();
288   emit started();
289 #ifdef DEBUG_OPERATION_START
290   qDebug("ModuleBase_OperationFeature::start -- end");
291 #endif
292   return true;
293 }
294
295 void ModuleBase_OperationFeature::abort()
296 {
297 #ifdef DEBUG_OPERATION_START
298   qDebug("ModuleBase_OperationFeature::abort -- begin");
299 #endif
300
301   emit beforeAborted();
302
303   // the viewer update should be blocked in order to avoid the features blinking before they are
304   // hidden
305   std::shared_ptr<Events_Message> aMsg = std::shared_ptr<Events_Message>(
306       new Events_Message(Events_Loop::eventByName(EVENT_UPDATE_VIEWER_BLOCKED)));
307   Events_Loop::loop()->send(aMsg);
308
309   // the widgets of property panel should not process any events come from data mode
310   // after abort clicked. Some signal such as redisplay/create influence on content
311   // of the object browser and viewer context. Therefore it influence to the current
312   // selection and if the active widget listens it, the attribute value is errnoneous
313   // changed.
314   ModuleBase_IPropertyPanel* aPropertyPanel = propertyPanel();
315   if (aPropertyPanel)
316     aPropertyPanel->cleanContent();
317
318   if (myFeature.get())
319     myFeature->setStable(true);
320
321   abortOperation();
322   stopOperation();
323
324   SessionPtr aMgr = ModelAPI_Session::get();
325   aMgr->abortOperation();
326   emit stopped();
327   // the viewer update should be unblocked in order to avoid the features blinking before they are
328   // hidden
329   aMsg = std::shared_ptr<Events_Message>(
330                 new Events_Message(Events_Loop::eventByName(EVENT_UPDATE_VIEWER_UNBLOCKED)));
331
332   Events_Loop::loop()->send(aMsg);
333
334   emit aborted();
335 #ifdef DEBUG_OPERATION_START
336   qDebug("ModuleBase_OperationFeature::abort -- end");
337 #endif
338 }
339
340 bool ModuleBase_OperationFeature::commit()
341 {
342 #ifdef DEBUG_OPERATION_START
343   qDebug("ModuleBase_OperationFeature::commit -- begin");
344 #endif
345   ModuleBase_IPropertyPanel* aPanel = propertyPanel();
346   if (aPanel) {
347     ModuleBase_ModelWidget* anActiveWidget = aPanel->activeWidget();
348     if (anActiveWidget && anActiveWidget->getValueState() == ModuleBase_ModelWidget::ModifiedInPP) {
349       anActiveWidget->storeValue();
350     }
351   }
352   if (canBeCommitted()) {
353     emit beforeCommitted();
354     // the widgets of property panel should not process any events come from data mode
355     // after commit clicked. Some signal such as redisplay/create influence on content
356     // of the object browser and viewer context. Therefore it influence to the current
357     // selection and if the active widget listens it, the attribute value is errnoneous
358     // changed.
359     ModuleBase_IPropertyPanel* aPropertyPanel = propertyPanel();
360     if (aPropertyPanel)
361       aPropertyPanel->cleanContent();
362
363     myFeature->setStable(true);
364
365     SessionPtr aMgr = ModelAPI_Session::get();
366     /// Set current feature and remeber old current feature
367
368     commitOperation();
369     aMgr->finishOperation();
370
371     stopOperation();
372     emit stopped();
373     emit committed();
374
375     afterCommitOperation();
376 #ifdef DEBUG_OPERATION_START
377   qDebug("ModuleBase_OperationFeature::commit -- end : IMPOSSIBLE to commit");
378 #endif
379     return true;
380   }
381 #ifdef DEBUG_OPERATION_START
382   qDebug("ModuleBase_OperationFeature::commit -- end");
383 #endif
384   return false;
385 }
386
387 ModuleBase_ModelWidget* ModuleBase_OperationFeature::activateByPreselection(
388                                               const std::string& theGreedAttributeId)
389 {
390   ModuleBase_ModelWidget* aWidget = 0;
391   if (myPreSelection.empty())
392     return aWidget;
393
394   ModuleBase_IPropertyPanel* aPropertyPanel = propertyPanel();
395   ModuleBase_ModelWidget* aFilledWgt = 0;
396   if (aPropertyPanel) {
397     const QList<ModuleBase_ModelWidget*>& aWidgets = aPropertyPanel->modelWidgets();
398     QList<ModuleBase_ModelWidget*>::const_iterator aWIt;
399     ModuleBase_ModelWidget* aWgt = 0;
400     if (!aWidgets.empty()) {
401       // equal vertices should not be used here
402       ModuleBase_ISelection::filterSelectionOnEqualPoints(myPreSelection);
403
404       if (!theGreedAttributeId.empty()) {
405         // set preselection to greed widget
406         for (aWIt = aWidgets.constBegin(); aWIt != aWidgets.constEnd(); ++aWIt) {
407           aWgt = (*aWIt);
408           if (aWgt->attributeID() == theGreedAttributeId) {
409             aPropertyPanel->setPreselectionWidget(aWgt);
410             if (aWgt->setSelection(myPreSelection, true)) {
411               aPropertyPanel->setPreselectionWidget(NULL);
412               aFilledWgt = aWgt;
413               break;
414             }
415             else { // do not process invalid for greed widget selection
416               break;
417             }
418           }
419         }
420       }
421       else {
422         bool isSet = false;
423         // 1. apply the selection to controls
424         for (aWIt = aWidgets.constBegin(); aWIt != aWidgets.constEnd(); ++aWIt) {
425           aWgt = (*aWIt);
426           if (!aWgt->canAcceptFocus())
427             continue;
428           aPropertyPanel->setPreselectionWidget(aWgt);
429           if (myPreSelection.empty() || !aWgt->setSelection(myPreSelection, true)) {
430             isSet = false;
431             break;
432           } else {
433             isSet = true;
434             aFilledWgt = aWgt;
435           }
436         }
437       }
438       aPropertyPanel->setPreselectionWidget(NULL);
439       // in order to redisplay object in the viewer, the update/redisplay signals should be flushed
440       // it is better to perform it not in setSelection of each widget, but do it here,
441       // after the preselection is processed
442       ModuleBase_Tools::flushUpdated(myFeature);
443     }
444   }
445   clearPreselection();
446
447   return aFilledWgt;
448 }
449
450 void ModuleBase_OperationFeature::setParentFeature(CompositeFeaturePtr theParent)
451 {
452   myParentFeature = theParent;
453 }
454
455 CompositeFeaturePtr ModuleBase_OperationFeature::parentFeature() const
456 {
457   return myParentFeature;
458 }
459
460 void ModuleBase_OperationFeature::setPreviousCurrentFeature(const FeaturePtr& theFeature)
461 {
462   myPreviousCurrentFeature = theFeature;
463 }
464
465 FeaturePtr ModuleBase_OperationFeature::previousCurrentFeature()
466 {
467   return myPreviousCurrentFeature;
468 }
469
470 void ModuleBase_OperationFeature::initSelection(
471   const QList<ModuleBase_ViewerPrsPtr>& thePreSelected)
472 {
473   QObjectPtrList aCurrentFeatureResults;
474
475   // Check that the selected result are not results of operation feature
476   FeaturePtr aFeature = feature();
477   if (aFeature) {
478     std::list<ResultPtr> aResults;
479     ModelAPI_Tools::allResults(aFeature, aResults);
480     std::list<ResultPtr>::const_iterator aIt;
481     for (aIt = aResults.begin(); aIt != aResults.end(); ++aIt)
482       aCurrentFeatureResults.append(*aIt);
483   }
484
485   if (aCurrentFeatureResults.empty()) /// filtering of selection is not necessary
486     setPreselection(thePreSelected);
487   else { // create preselection list without results of current feature
488     QList<ModuleBase_ViewerPrsPtr> aPreSelected;
489     foreach (ModuleBase_ViewerPrsPtr aPrs, thePreSelected) {
490       if ((!aCurrentFeatureResults.contains(aPrs->object())) && (aPrs->object() != aFeature))
491         aPreSelected.append(aPrs);
492     }
493     setPreselection(aPreSelected);
494   }
495 }
496
497 void ModuleBase_OperationFeature::setPreselection(const QList<ModuleBase_ViewerPrsPtr>& theValues)
498 {
499   clearPreselection();
500   myPreSelection = theValues;
501 }
502
503 void ModuleBase_OperationFeature::clearPreselection()
504 {
505   myPreSelection.clear();
506 }
507
508 void ModuleBase_OperationFeature::setPropertyPanel(ModuleBase_IPropertyPanel* theProp)
509 {
510   ModuleBase_Operation::setPropertyPanel(theProp);
511
512   theProp->setEditingMode(isEditOperation());
513
514   if (theProp) {
515     const QList<ModuleBase_ModelWidget*>& aWidgets = theProp->modelWidgets();
516     QList<ModuleBase_ModelWidget*>::const_iterator aWIt;
517     for (aWIt = aWidgets.constBegin(); aWIt != aWidgets.constEnd(); ++aWIt) {
518       ModuleBase_ModelWidget* aWgt = (*aWIt);
519       connect(aWgt, SIGNAL(valuesChanged()), this, SLOT(onValuesChanged()));
520       connect(aWgt, SIGNAL(valueStateChanged(int)), this, SLOT(onValueStateChanged(int)));
521     }
522   }
523
524   // Do not activate widgets by default if the current operation is editing operation
525   // Because we don't know which widget is going to be edited.
526   if (!isEditOperation()) {
527     // 4. activate the first obligatory widget
528     theProp->activateNextWidget(NULL);
529   }
530   else {
531     // set focus on Ok button in order to operation manager could process Enter press
532     if (theProp)
533       theProp->setFocusOnOkButton();
534   }
535 }