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