Salome HOME
15cf843d3072df5989d5dd1d08db2db2f0d81a1e
[modules/shaper.git] / src / PartSet / PartSet_SketcherReetntrantMgr.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 #include "PartSet_SketcherReetntrantMgr.h"
4 #include "PartSet_Module.h"
5 #include "PartSet_SketcherMgr.h"
6 #include "PartSet_WidgetPoint2d.h"
7
8 #include "ModelAPI_Session.h"
9
10 #include <ModuleBase_IPropertyPanel.h>
11 #include <ModuleBase_OperationFeature.h>
12 #include <ModuleBase_ModelWidget.h>
13 #include <ModuleBase_ViewerPrs.h>
14 #include <ModuleBase_WidgetSelector.h>
15 #include <ModuleBase_PageWidget.h>
16 #include <ModuleBase_PageBase.h>
17 #include <ModuleBase_WidgetFactory.h>
18 #include <ModuleBase_OperationDescription.h>
19
20 #include <SketchPlugin_Feature.h>
21 #include <SketchPlugin_Line.h>
22
23 #include <XGUI_Workshop.h>
24 #include <XGUI_ModuleConnector.h>
25 #include <XGUI_OperationMgr.h>
26 #include <XGUI_PropertyPanel.h>
27
28 #include <QToolButton>
29
30 PartSet_SketcherReetntrantMgr::PartSet_SketcherReetntrantMgr(ModuleBase_IWorkshop* theWorkshop)
31 : QObject(theWorkshop),
32   myWorkshop(theWorkshop),
33   myRestartingMode(RM_None),
34   myIsFlagsBlocked(false),
35   myIsInternalEditOperation(false),
36   myNoMoreWidgetsAttribute("")
37 {
38 }
39
40 PartSet_SketcherReetntrantMgr::~PartSet_SketcherReetntrantMgr()
41 {
42 }
43
44 ModuleBase_ModelWidget* PartSet_SketcherReetntrantMgr::internalActiveWidget() const
45 {
46   ModuleBase_ModelWidget* aWidget = 0;
47   if (!isActiveMgr())
48     return aWidget;
49
50   ModuleBase_Operation* aOperation = myWorkshop->currentOperation();
51   if (aOperation) {
52     ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
53     ModuleBase_ModelWidget* anActiveWidget = aPanel->activeWidget();
54     if (myIsInternalEditOperation && (!anActiveWidget || !anActiveWidget->isViewerSelector()))
55       aWidget = myInternalActiveWidget;
56   }
57   return aWidget;
58 }
59
60 bool PartSet_SketcherReetntrantMgr::isInternalEditActive() const
61 {
62   return myIsInternalEditOperation;
63 }
64
65 bool PartSet_SketcherReetntrantMgr::operationCommitted(ModuleBase_Operation* theOperation)
66 {
67   bool aProcessed = false;
68   if (!isActiveMgr())
69     return aProcessed;
70
71   aProcessed = myIsInternalEditOperation;
72   resetFlags();
73
74   return aProcessed;
75 }
76
77 void PartSet_SketcherReetntrantMgr::operationStarted(ModuleBase_Operation* theOperation)
78 {
79   if (!isActiveMgr())
80     return;
81
82   resetFlags();
83 }
84
85 void PartSet_SketcherReetntrantMgr::operationAborted(ModuleBase_Operation* theOperation)
86 {
87   if (!isActiveMgr())
88     return;
89
90   resetFlags();
91 }
92
93 bool PartSet_SketcherReetntrantMgr::processMouseMoved(ModuleBase_IViewWindow* /* theWnd*/,
94                                                       QMouseEvent* /* theEvent*/)
95 {
96   bool aProcessed = false;
97   if (!isActiveMgr())
98     return aProcessed;
99
100   if  (myIsInternalEditOperation) {
101     PartSet_WidgetPoint2D* aPoint2DWdg = dynamic_cast<PartSet_WidgetPoint2D*>(module()->activeWidget());
102     if (aPoint2DWdg && aPoint2DWdg->canBeActivatedByMove()) {
103       ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
104                                                          (myWorkshop->currentOperation());
105       FeaturePtr aLastFeature = myRestartingMode == RM_LastFeatureUsed ? aFOperation->feature() : FeaturePtr();
106       restartOperation();
107       aProcessed = true;
108
109       if (aLastFeature) {
110         ModuleBase_IPropertyPanel* aPanel = myWorkshop->currentOperation()->propertyPanel();
111         PartSet_WidgetPoint2D* aPoint2DWdg = dynamic_cast<PartSet_WidgetPoint2D*>(aPanel->activeWidget());
112         if (aPoint2DWdg && aPoint2DWdg->canBeActivatedByMove()) {
113           QList<ModuleBase_ViewerPrs> aSelection;
114           aSelection.append(ModuleBase_ViewerPrs(aLastFeature, TopoDS_Shape(), NULL));
115           if (aPoint2DWdg->setSelection(aSelection, true))
116             aPanel->activateNextWidget(aPoint2DWdg);
117         }
118       }
119     }
120   }
121   return aProcessed;
122 }
123
124 bool PartSet_SketcherReetntrantMgr::processMousePressed(ModuleBase_IViewWindow* /* theWnd*/,
125                                                         QMouseEvent* /* theEvent*/)
126 {
127   return isActiveMgr() && myIsInternalEditOperation;
128 }
129
130 bool PartSet_SketcherReetntrantMgr::processMouseReleased(ModuleBase_IViewWindow* theWnd,
131                                                          QMouseEvent* theEvent)
132 {
133   bool aProcessed = false;
134   if (!isActiveMgr())
135     return aProcessed;
136
137   if (myIsInternalEditOperation) {
138     ModuleBase_Operation* anOperation = myWorkshop->currentOperation();
139     ModuleBase_IPropertyPanel* aPanel = anOperation->propertyPanel();
140
141     ModuleBase_ModelWidget* anActiveWidget = aPanel->activeWidget();
142     if (!anActiveWidget || !anActiveWidget->isViewerSelector()) {
143       restartOperation();
144       aProcessed = true;
145
146       // fill the first widget by the mouse event point
147       // if the active widget is not the first, it means that the restarted operation is filled by
148       // the current preselection.
149       PartSet_WidgetPoint2D* aPoint2DWdg = dynamic_cast<PartSet_WidgetPoint2D*>(module()->activeWidget());
150       ModuleBase_ModelWidget* aFirstWidget = aPanel->findFirstAcceptingValueWidget();
151       if (aPoint2DWdg && aPoint2DWdg == aFirstWidget) {
152         aPoint2DWdg->onMouseRelease(theWnd, theEvent);
153       }
154     }
155   }
156
157   return aProcessed;
158 }
159
160 void PartSet_SketcherReetntrantMgr::onWidgetActivated()
161 {
162   if (!isActiveMgr())
163     return;
164   if (!myIsInternalEditOperation)
165     return;
166
167   PartSet_Module* aModule = module();
168   ModuleBase_ModelWidget* aFirstWidget = aModule->activeWidget();
169   ModuleBase_IPropertyPanel* aPanel = aModule->currentOperation()->propertyPanel();
170   if (aFirstWidget != aPanel->activeWidget()) {
171     ModuleBase_WidgetSelector* aWSelector = dynamic_cast<ModuleBase_WidgetSelector*>(aFirstWidget);
172     if (aWSelector)
173       aWSelector->activateSelectionAndFilters(true);
174   }
175 }
176
177 void PartSet_SketcherReetntrantMgr::onNoMoreWidgets(const std::string& thePreviousAttributeID)
178 {
179   if (!isActiveMgr())
180     return;
181
182   // we should avoid processing of the signal about no more widgets attributes and 
183   // do this after the restart operaion is finished if it was called
184   // onNoMoreWidgets depends on myIsFlagsBlocked and fill myNoMoreWidgetsAttribute
185   // if it should be called after restart
186   if (myIsFlagsBlocked) {
187     myNoMoreWidgetsAttribute = thePreviousAttributeID;
188     return;
189   }
190
191   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
192                                                        (myWorkshop->currentOperation());
193   if (!myWorkshop->module()->getFeatureError(aFOperation->feature()).isEmpty())
194     return;
195
196   if (aFOperation && PartSet_SketcherMgr::isNestedSketchOperation(aFOperation)) {
197     bool isStarted = false;
198     if (!module()->sketchMgr()->sketchSolverError()) {
199       if (myRestartingMode != RM_Forbided) {
200         myRestartingMode = RM_LastFeatureUsed;
201         isStarted = startInternalEdit(thePreviousAttributeID);
202       }
203     }
204     if (!isStarted)
205       aFOperation->commit();
206   }
207 }
208
209 bool PartSet_SketcherReetntrantMgr::processEnter(const std::string& thePreviousAttributeID)
210 {
211   bool isDone = false;
212
213   if (!isActiveMgr())
214     return isDone;
215
216   // empty previous attribute means that the Apply/Ok button has focus and the enter
217   // should not lead to start edition mode of the previous operation
218   if (thePreviousAttributeID.empty())
219     return isDone;
220
221   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
222                                                        (myWorkshop->currentOperation());
223   if (!myWorkshop->module()->getFeatureError(aFOperation->feature()).isEmpty())
224     return isDone;
225
226   bool isSketchSolverError = module()->sketchMgr()->sketchSolverError();
227
228   if (!isSketchSolverError) {
229     myRestartingMode = RM_EmptyFeatureUsed;
230     isDone = startInternalEdit(thePreviousAttributeID);
231   }
232
233   return isDone;
234 }
235
236 void PartSet_SketcherReetntrantMgr::onVertexSelected()
237 {
238   if (!isActiveMgr())
239     return;
240
241   ModuleBase_Operation* aOperation = myWorkshop->currentOperation();
242   if (aOperation->id().toStdString() == SketchPlugin_Line::ID()) {
243     /// If last line finished on vertex the lines creation sequence has to be break
244     ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
245     ModuleBase_ModelWidget* anActiveWidget = aPanel->activeWidget();
246     const QList<ModuleBase_ModelWidget*>& aWidgets = aPanel->modelWidgets();
247     QList<ModuleBase_ModelWidget*>::const_iterator anIt = aWidgets.begin(), aLast = aWidgets.end();
248     bool aFoundWidget = false;
249     bool aFoundObligatory = false;
250     for (; anIt != aLast && !aFoundObligatory; anIt++) {
251       if (!aFoundWidget)
252         aFoundWidget = *anIt == anActiveWidget;
253       else
254         aFoundObligatory = (*anIt)->isObligatory();
255     }
256     if (!aFoundObligatory)
257       myRestartingMode = RM_Forbided;
258   }
259 }
260
261 void PartSet_SketcherReetntrantMgr::onBeforeStopped()
262 {
263   if (!isActiveMgr() || !myIsInternalEditOperation)
264     return;
265
266   beforeStopInternalEdit();
267 }
268
269 bool PartSet_SketcherReetntrantMgr::canBeCommittedByPreselection()
270 {
271   return !isActiveMgr() || myRestartingMode == RM_None;
272 }
273
274 bool PartSet_SketcherReetntrantMgr::isActiveMgr() const
275 {
276   ModuleBase_Operation* aCurrentOperation = myWorkshop->currentOperation();
277
278   bool anActive = PartSet_SketcherMgr::isSketchOperation(aCurrentOperation);
279   if (!anActive) {
280     anActive = PartSet_SketcherMgr::isNestedSketchOperation(aCurrentOperation);
281     if (anActive) { // the manager is not active when the current operation is a usual Edit
282       ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
283                                                        (myWorkshop->currentOperation());
284       if (aFOperation->isEditOperation())
285         anActive = myIsInternalEditOperation;
286     }
287   }
288   return anActive;
289 }
290
291 bool PartSet_SketcherReetntrantMgr::startInternalEdit(const std::string& thePreviousAttributeID)
292 {
293   bool isDone = false;
294   /// this is workaround for ModuleBase_WidgetEditor, used in SALOME mode. Sometimes key enter
295   /// event comes two times, so we should not start another internal edit operation
296   /// the Apply button becomes disabled becase the second additional internal feature is created
297   if (myIsInternalEditOperation)
298     return true;
299
300   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
301                                                      (myWorkshop->currentOperation());
302
303   if (aFOperation && PartSet_SketcherMgr::isNestedSketchOperation(aFOperation)) {
304     aFOperation->setEditOperation(false);
305     workshop()->operationMgr()->updateApplyOfOperations();
306
307     createInternalFeature();
308
309     myIsInternalEditOperation = true;
310     isDone = true;
311     connect(aFOperation, SIGNAL(beforeCommitted()), this, SLOT(onBeforeStopped()));
312     connect(aFOperation, SIGNAL(beforeAborted()), this, SLOT(onBeforeStopped()));
313
314     // activate selection filters of the first widget in the viewer
315     onWidgetActivated();
316
317     // activate the last active widget in the Property Panel
318     if (!thePreviousAttributeID.empty()) {
319       ModuleBase_Operation* anEditOperation = module()->currentOperation();
320       if (anEditOperation) {
321         ModuleBase_IPropertyPanel* aPanel = aFOperation->propertyPanel();
322         ModuleBase_ModelWidget* aPreviousAttributeWidget = 0;
323         QList<ModuleBase_ModelWidget*> aWidgets = aPanel->modelWidgets();
324         for (int i = 0, aNb = aWidgets.size(); i < aNb && !aPreviousAttributeWidget; i++) {
325           if (aWidgets[i]->attributeID() == thePreviousAttributeID)
326             aPreviousAttributeWidget = aWidgets[i];
327         }
328         // If the current widget is a selector, do nothing, it processes the mouse press
329         if (aPreviousAttributeWidget) {
330           if (!aPreviousAttributeWidget->isViewerSelector()) {
331             aPreviousAttributeWidget->focusTo();
332             aPreviousAttributeWidget->selectContent();
333           }
334           else {
335             // in case of shape multi selector, the widget does not lose focus by filling
336             // like it is in shape selector. So, if enter is pressed, the multi shape selector
337             // control should be deactivated. The focus is moved to Apply button and there
338             // should not be active control visualized in property panel
339             if (aPreviousAttributeWidget == aPanel->activeWidget()) {
340               aPanel->activateWidget(NULL, false);
341             }
342             // if there is no the next widget to be automatically activated, the Ok button in property
343             // panel should accept the focus(example is parallel constraint on sketch lines)
344             QToolButton* anOkBtn = aPanel->findChild<QToolButton*>(PROP_PANEL_OK);
345             if (anOkBtn)
346               anOkBtn->setFocus(Qt::TabFocusReason);
347           }
348         }
349       }
350     }
351   }
352   if (isDone)
353     module()->sketchMgr()->clearClickedFlags();
354
355   return isDone;
356 }
357
358 void PartSet_SketcherReetntrantMgr::beforeStopInternalEdit()
359 {
360   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
361                                                       (myWorkshop->currentOperation());
362   if (aFOperation) {
363     disconnect(aFOperation, SIGNAL(beforeCommitted()), this, SLOT(onBeforeStopped()));
364     disconnect(aFOperation, SIGNAL(beforeAborted()), this, SLOT(onBeforeStopped()));
365   }
366
367   deleteInternalFeature();
368 }
369
370 void PartSet_SketcherReetntrantMgr::restartOperation()
371 {
372   if (myIsInternalEditOperation) {
373     ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(
374                                                                   myWorkshop->currentOperation());
375     if (aFOperation) {
376       myNoMoreWidgetsAttribute = "";
377       myIsFlagsBlocked = true;
378       aFOperation->commit();
379       module()->launchOperation(aFOperation->id());
380       myIsFlagsBlocked = false;
381       resetFlags();
382       // we should avoid processing of the signal about no more widgets attributes and 
383       // do this after the restart operaion is finished if it was called
384       // onNoMoreWidgets depends on myIsFlagsBlocked and fill myNoMoreWidgetsAttribute
385       // if it should be called after restart
386       if (!myNoMoreWidgetsAttribute.empty()) {
387         onNoMoreWidgets(myNoMoreWidgetsAttribute);
388         myNoMoreWidgetsAttribute = "";
389       }
390     }
391   }
392 }
393
394 void PartSet_SketcherReetntrantMgr::createInternalFeature()
395 {
396   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
397                                                      (myWorkshop->currentOperation());
398
399   if (aFOperation && PartSet_SketcherMgr::isNestedSketchOperation(aFOperation)) {
400     FeaturePtr anOperationFeature = aFOperation->feature();
401
402     CompositeFeaturePtr aSketch = module()->sketchMgr()->activeSketch();
403     myInternalFeature = aSketch->addFeature(anOperationFeature->getKind());
404     XGUI_PropertyPanel* aPropertyPanel = dynamic_cast<XGUI_PropertyPanel*>
405                                                   (aFOperation->propertyPanel());
406
407     myInternalWidget = new QWidget(aPropertyPanel->contentWidget()->pageWidget());
408     myInternalWidget->setVisible(false);
409
410     ModuleBase_PageWidget* anInternalPage = new ModuleBase_PageWidget(myInternalWidget);
411
412     QString aXmlRepr = aFOperation->getDescription()->xmlRepresentation();
413     ModuleBase_WidgetFactory aFactory(aXmlRepr.toStdString(), myWorkshop);
414
415     aFactory.createWidget(anInternalPage);
416     QList<ModuleBase_ModelWidget*> aWidgets = aFactory.getModelWidgets();
417
418     foreach (ModuleBase_ModelWidget* aWidget, aWidgets) {
419       bool isStoreValue = !aFOperation->isEditOperation() &&
420                           !aWidget->getDefaultValue().empty() &&
421                           !aWidget->isComputedDefault();
422       aWidget->setFeature(myInternalFeature, isStoreValue);
423     }
424     ModuleBase_ModelWidget* aFirstWidget = ModuleBase_IPropertyPanel::findFirstAcceptingValueWidget
425                                                                                         (aWidgets);
426     if (aFirstWidget)
427       myInternalActiveWidget = aFirstWidget;
428   }
429 }
430
431 void PartSet_SketcherReetntrantMgr::deleteInternalFeature()
432 {
433   if (myInternalActiveWidget) {
434     ModuleBase_WidgetSelector* aWSelector = dynamic_cast<ModuleBase_WidgetSelector*>(myInternalActiveWidget);
435     if (aWSelector)
436       aWSelector->activateSelectionAndFilters(false);
437     myInternalActiveWidget = 0;
438   }
439   delete myInternalWidget;
440   myInternalWidget = 0;
441
442   QObjectPtrList anObjects;
443   anObjects.append(myInternalFeature);
444   workshop()->deleteFeatures(anObjects);
445 }
446
447 void PartSet_SketcherReetntrantMgr::resetFlags()
448 {
449   if (!myIsFlagsBlocked) {
450     myIsInternalEditOperation = false;
451     myRestartingMode = RM_None;
452   }
453 }
454
455 XGUI_Workshop* PartSet_SketcherReetntrantMgr::workshop() const
456 {
457   XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myWorkshop);
458   return aConnector->workshop();
459 }
460
461 PartSet_Module* PartSet_SketcherReetntrantMgr::module() const
462 {
463   return dynamic_cast<PartSet_Module*>(myWorkshop->module());
464 }
465