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