1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
3 #include "PartSet_SketcherReetntrantMgr.h"
4 #include "PartSet_Module.h"
5 #include "PartSet_SketcherMgr.h"
6 #include "PartSet_WidgetPoint2d.h"
8 #include "ModelAPI_Session.h"
9 #include "ModelAPI_AttributeString.h"
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>
21 #include <SketchPlugin_Feature.h>
22 #include <SketchPlugin_Line.h>
23 #include <SketchPlugin_Arc.h>
24 #include <SketchPlugin_Circle.h>
26 #include <XGUI_Workshop.h>
27 #include <XGUI_ModuleConnector.h>
28 #include <XGUI_OperationMgr.h>
29 #include <XGUI_PropertyPanel.h>
31 #include <QToolButton>
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("")
43 PartSet_SketcherReetntrantMgr::~PartSet_SketcherReetntrantMgr()
47 ModuleBase_ModelWidget* PartSet_SketcherReetntrantMgr::internalActiveWidget() const
49 ModuleBase_ModelWidget* aWidget = 0;
53 ModuleBase_Operation* anOperation = myWorkshop->currentOperation();
55 ModuleBase_IPropertyPanel* aPanel = anOperation->propertyPanel();
56 ModuleBase_ModelWidget* anActiveWidget = aPanel->activeWidget();
57 if (myIsInternalEditOperation && (!anActiveWidget || !anActiveWidget->isViewerSelector()))
58 aWidget = myInternalActiveWidget;
63 bool PartSet_SketcherReetntrantMgr::isInternalEditActive() const
65 return myIsInternalEditOperation;
68 bool PartSet_SketcherReetntrantMgr::operationCommitted(ModuleBase_Operation* theOperation)
70 bool aProcessed = false;
74 aProcessed = myIsInternalEditOperation;
80 void PartSet_SketcherReetntrantMgr::operationStarted(ModuleBase_Operation* theOperation)
88 void PartSet_SketcherReetntrantMgr::operationAborted(ModuleBase_Operation* theOperation)
96 bool PartSet_SketcherReetntrantMgr::processMouseMoved(ModuleBase_IViewWindow* /* theWnd*/,
97 QMouseEvent* /* theEvent*/)
99 bool aProcessed = false;
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();
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);
127 bool PartSet_SketcherReetntrantMgr::processMousePressed(ModuleBase_IViewWindow* /* theWnd*/,
128 QMouseEvent* /* theEvent*/)
130 return isActiveMgr() && myIsInternalEditOperation;
133 bool PartSet_SketcherReetntrantMgr::processMouseReleased(ModuleBase_IViewWindow* theWnd,
134 QMouseEvent* theEvent)
136 bool aProcessed = false;
140 if (myIsInternalEditOperation) {
141 ModuleBase_Operation* anOperation = myWorkshop->currentOperation();
142 ModuleBase_IPropertyPanel* aPanel = anOperation->propertyPanel();
144 ModuleBase_ModelWidget* anActiveWidget = aPanel->activeWidget();
145 if (!anActiveWidget || !anActiveWidget->isViewerSelector()) {
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);
163 void PartSet_SketcherReetntrantMgr::onWidgetActivated()
167 if (!myIsInternalEditOperation)
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);
176 aWSelector->activateSelectionAndFilters(true);
180 void PartSet_SketcherReetntrantMgr::onNoMoreWidgets(const std::string& thePreviousAttributeID)
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;
194 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
195 (myWorkshop->currentOperation());
196 if (!myWorkshop->module()->getFeatureError(aFOperation->feature()).isEmpty())
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);
208 aFOperation->commit();
212 bool PartSet_SketcherReetntrantMgr::processEnter(const std::string& thePreviousAttributeID)
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())
224 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
225 (myWorkshop->currentOperation());
226 if (!myWorkshop->module()->getFeatureError(aFOperation->feature()).isEmpty())
229 bool isSketchSolverError = module()->sketchMgr()->sketchSolverError();
231 if (!isSketchSolverError) {
232 myRestartingMode = RM_EmptyFeatureUsed;
233 isDone = startInternalEdit(thePreviousAttributeID);
239 /*bool isTangentArc(ModuleBase_Operation* theOperation)
241 bool aTangentArc = false;
242 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
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();
256 void PartSet_SketcherReetntrantMgr::onVertexSelected()
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++) {
273 aFoundWidget = *anIt == anActiveWidget;
275 aFoundObligatory = (*anIt)->isObligatory();
277 if (!aFoundObligatory)
278 myRestartingMode = RM_Forbided;
282 void PartSet_SketcherReetntrantMgr::onBeforeStopped()
284 if (!isActiveMgr() || !myIsInternalEditOperation)
287 beforeStopInternalEdit();
290 bool PartSet_SketcherReetntrantMgr::canBeCommittedByPreselection()
292 return !isActiveMgr() || myRestartingMode == RM_None;
295 bool PartSet_SketcherReetntrantMgr::isActiveMgr() const
297 ModuleBase_Operation* aCurrentOperation = myWorkshop->currentOperation();
299 bool anActive = PartSet_SketcherMgr::isSketchOperation(aCurrentOperation);
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;
312 bool PartSet_SketcherReetntrantMgr::startInternalEdit(const std::string& thePreviousAttributeID)
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)
321 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
322 (myWorkshop->currentOperation());
324 if (aFOperation && PartSet_SketcherMgr::isNestedSketchOperation(aFOperation)) {
325 aFOperation->setEditOperation(false);
326 workshop()->operationMgr()->updateApplyOfOperations();
328 createInternalFeature();
330 myIsInternalEditOperation = true;
332 connect(aFOperation, SIGNAL(beforeCommitted()), this, SLOT(onBeforeStopped()));
333 connect(aFOperation, SIGNAL(beforeAborted()), this, SLOT(onBeforeStopped()));
335 // activate selection filters of the first widget in the viewer
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];
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();
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);
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);
367 anOkBtn->setFocus(Qt::TabFocusReason);
374 module()->sketchMgr()->clearClickedFlags();
379 void PartSet_SketcherReetntrantMgr::beforeStopInternalEdit()
381 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
382 (myWorkshop->currentOperation());
384 disconnect(aFOperation, SIGNAL(beforeCommitted()), this, SLOT(onBeforeStopped()));
385 disconnect(aFOperation, SIGNAL(beforeAborted()), this, SLOT(onBeforeStopped()));
388 deleteInternalFeature();
391 void PartSet_SketcherReetntrantMgr::restartOperation()
393 if (myIsInternalEditOperation) {
394 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(
395 myWorkshop->currentOperation());
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());
407 myIsFlagsBlocked = false;
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 = "";
421 bool PartSet_SketcherReetntrantMgr::copyReetntrantAttributes(const FeaturePtr& theSourceFeature,
422 const FeaturePtr& theNewFeature)
425 std::string aTypeAttributeId;
426 if (theSourceFeature->getKind() == SketchPlugin_Circle::ID()) {
427 aTypeAttributeId = SketchPlugin_Circle::CIRCLE_TYPE();
429 if (theSourceFeature->getKind() == SketchPlugin_Arc::ID()) {
430 aTypeAttributeId = SketchPlugin_Arc::ARC_TYPE();
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);
442 void PartSet_SketcherReetntrantMgr::createInternalFeature()
444 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
445 (myWorkshop->currentOperation());
447 if (aFOperation && PartSet_SketcherMgr::isNestedSketchOperation(aFOperation)) {
448 FeaturePtr anOperationFeature = aFOperation->feature();
450 CompositeFeaturePtr aSketch = module()->sketchMgr()->activeSketch();
451 myInternalFeature = aSketch->addFeature(anOperationFeature->getKind());
453 bool isFeatureChanged = copyReetntrantAttributes(anOperationFeature, myInternalFeature);
454 XGUI_PropertyPanel* aPropertyPanel = dynamic_cast<XGUI_PropertyPanel*>
455 (aFOperation->propertyPanel());
457 myInternalWidget = new QWidget(aPropertyPanel->contentWidget()->pageWidget());
458 myInternalWidget->setVisible(false);
460 ModuleBase_PageWidget* anInternalPage = new ModuleBase_PageWidget(myInternalWidget);
462 QString aXmlRepr = aFOperation->getDescription()->xmlRepresentation();
463 ModuleBase_WidgetFactory aFactory(aXmlRepr.toStdString(), myWorkshop);
465 aFactory.createWidget(anInternalPage);
466 QList<ModuleBase_ModelWidget*> aWidgets = aFactory.getModelWidgets();
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();
477 ModuleBase_ModelWidget* aFirstWidget = ModuleBase_IPropertyPanel::findFirstAcceptingValueWidget
480 myInternalActiveWidget = aFirstWidget;
484 void PartSet_SketcherReetntrantMgr::deleteInternalFeature()
486 if (myInternalActiveWidget) {
487 ModuleBase_WidgetSelector* aWSelector = dynamic_cast<ModuleBase_WidgetSelector*>(myInternalActiveWidget);
489 aWSelector->activateSelectionAndFilters(false);
490 myInternalActiveWidget = 0;
492 delete myInternalWidget;
493 myInternalWidget = 0;
495 QObjectPtrList anObjects;
496 anObjects.append(myInternalFeature);
497 workshop()->deleteFeatures(anObjects);
500 void PartSet_SketcherReetntrantMgr::resetFlags()
502 if (!myIsFlagsBlocked) {
503 myIsInternalEditOperation = false;
504 myRestartingMode = RM_None;
508 XGUI_Workshop* PartSet_SketcherReetntrantMgr::workshop() const
510 XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myWorkshop);
511 return aConnector->workshop();
514 PartSet_Module* PartSet_SketcherReetntrantMgr::module() const
516 return dynamic_cast<PartSet_Module*>(myWorkshop->module());