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