1 // Copyright (C) 2014-2017 CEA/DEN, EDF R&D
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
21 #include "XGUI_WorkshopListener.h"
22 #include "XGUI_Workshop.h"
23 #include "XGUI_Displayer.h"
24 #include "XGUI_ErrorMgr.h"
25 #include "XGUI_OperationMgr.h"
26 #include "XGUI_SalomeConnector.h"
27 #include "XGUI_ActionsMgr.h"
28 #include "XGUI_PropertyPanel.h"
29 #include "XGUI_ModuleConnector.h"
30 #include "XGUI_QtEvents.h"
31 #include "XGUI_SelectionMgr.h"
34 #include <AppElements_MainWindow.h>
37 #include <ModuleBase_IModule.h>
38 #include <ModuleBase_Events.h>
40 #include <ModelAPI_Object.h>
41 #include <ModelAPI_Events.h>
42 #include <ModelAPI_Session.h>
43 #include <ModelAPI_Result.h>
44 #include <ModelAPI_Feature.h>
45 #include <ModelAPI_Data.h>
46 #include <ModelAPI_ResultBody.h>
47 #include <ModelAPI_ResultGroup.h>
48 #include <ModelAPI_ResultCompSolid.h>
49 #include <ModelAPI_Tools.h>
51 #include <Events_Loop.h>
52 #include <Events_LongOp.h>
54 #include <ModuleBase_IWorkshop.h>
56 #include <ModuleBase_Operation.h>
57 #include <ModuleBase_OperationDescription.h>
58 #include <ModuleBase_OperationFeature.h>
59 #include <ModuleBase_Tools.h>
60 #include <ModuleBase_IViewer.h>
61 #include <ModuleBase_WidgetSelector.h>
63 #include <Config_FeatureMessage.h>
64 #include <Config_PointerMessage.h>
65 #include <Config_Keywords.h>
66 #include <Events_InfoMessage.h>
68 #include <QApplication>
69 #include <QMainWindow>
78 //#define DEBUG_FEATURE_CREATED
79 //#define DEBUG_FEATURE_REDISPLAY
80 //#define DEBUG_FEATURE_UPDATED
81 //#define DEBUG_RESULT_COMPSOLID
83 #ifdef DEBUG_FEATURE_REDISPLAY
84 const std::string DebugFeatureKind = "";//"Extrusion";
87 XGUI_WorkshopListener::XGUI_WorkshopListener(ModuleBase_IWorkshop* theWorkshop)
88 : myWorkshop(theWorkshop),
91 XGUI_OperationMgr* anOperationMgr = workshop()->operationMgr();
94 //******************************************************
95 XGUI_WorkshopListener::~XGUI_WorkshopListener(void)
99 //******************************************************
100 void XGUI_WorkshopListener::initializeEventListening()
102 //Initialize event listening
103 Events_Loop* aLoop = Events_Loop::loop();
104 aLoop->registerListener(this, Events_InfoMessage::errorID()); //!< Listening application errors.
105 aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
106 aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
107 aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
108 aLoop->registerListener(this, Events_LongOp::eventID());
109 aLoop->registerListener(this, Events_Loop::eventByName(EVENT_PLUGIN_LOADED));
111 aLoop->registerListener(this, Events_Loop::eventByName(EVENT_UPDATE_VIEWER_BLOCKED));
112 aLoop->registerListener(this, Events_Loop::eventByName(EVENT_UPDATE_VIEWER_UNBLOCKED));
113 aLoop->registerListener(this, Events_Loop::eventByName(EVENT_EMPTY_AIS_PRESENTATION));
114 aLoop->registerListener(this, Events_Loop::eventByName(EVENT_UPDATE_BY_WIDGET_SELECTION));
117 //******************************************************
118 void XGUI_WorkshopListener::processEvent(const std::shared_ptr<Events_Message>& theMessage)
120 if (QApplication::instance()->thread() != QThread::currentThread()) {
122 std::cout << "XGUI_Workshop::processEvent: " << "Working in another thread." << std::endl;
124 SessionPtr aMgr = ModelAPI_Session::get();
125 PostponeMessageQtEvent* aPostponeEvent = new PostponeMessageQtEvent(theMessage);
126 QApplication::postEvent(this, aPostponeEvent);
130 // Process creation of Part
131 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)) {
132 std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
133 std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
134 onFeatureCreatedMsg(aUpdMsg);
136 XGUI_SalomeConnector* aSalomeConnector = workshop()->salomeConnector();
137 if (aSalomeConnector)
138 aSalomeConnector->createPreferences();
139 myUpdatePrefs = false;
142 else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_PLUGIN_LOADED)) {
143 myUpdatePrefs = true;
146 else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY)) {
147 std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
148 std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
149 onFeatureRedisplayMsg(aUpdMsg);
150 } else if (theMessage->eventID() == Events_Loop::eventByName(EVENT_EMPTY_AIS_PRESENTATION)) {
151 std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
152 std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
153 onFeatureEmptyPresentationMsg(aUpdMsg);
154 } else if (theMessage->eventID() == Events_Loop::eventByName(EVENT_UPDATE_BY_WIDGET_SELECTION)) {
155 ModuleBase_ModelWidget* aWidget = workshop()->propertyPanel()->activeWidget();
157 ModuleBase_WidgetSelector* aWidgetSelector =
158 dynamic_cast<ModuleBase_WidgetSelector*>(aWidget);
160 workshop()->selector()->setSelected(aWidgetSelector->getAttributeSelection());
164 //Update property panel on corresponding message. If there is no current operation (no
165 //property panel), or received message has different feature to the current - do nothing.
166 else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)) {
167 std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
168 std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
169 onFeatureUpdatedMsg(anUpdateMsg);
170 } else if (theMessage->eventID() == Events_LongOp::eventID()) {
171 if (Events_LongOp::isPerformed()) {
172 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
174 QApplication::restoreOverrideCursor();
176 } else if (theMessage->eventID() == Events_Loop::eventByName(EVENT_UPDATE_VIEWER_BLOCKED)) {
177 // the viewer's update context will not happens until viewer updated is emitted
178 workshop()->displayer()->enableUpdateViewer(false);
179 } else if (theMessage->eventID() == Events_Loop::eventByName(EVENT_UPDATE_VIEWER_UNBLOCKED)) {
180 // the viewer's update context is unblocked, the viewer's update works
181 XGUI_Displayer* aDisplayer = workshop()->displayer();
182 aDisplayer->enableUpdateViewer(true);
184 //Show error dialog if error message received.
185 std::shared_ptr<Events_InfoMessage> anIngfoMsg =
186 std::dynamic_pointer_cast<Events_InfoMessage>(theMessage);
188 emit errorOccurred(anIngfoMsg);
193 SessionPtr aMgr = ModelAPI_Session::get();
194 AppElements_MainWindow* aMainWindow = workshop()->mainWindow();
195 if (aMgr->isModified() != aMainWindow->isModifiedState())
196 aMainWindow->setModifiedState(aMgr->isModified());
200 //******************************************************
201 void XGUI_WorkshopListener::onFeatureUpdatedMsg(
202 const std::shared_ptr<ModelAPI_ObjectUpdatedMessage>& theMsg)
204 #ifdef DEBUG_FEATURE_UPDATED
205 std::set<ObjectPtr> anObjects = theMsg->objects();
206 std::set<ObjectPtr>::const_iterator aIt;
208 for (aIt = anObjects.begin(); aIt != anObjects.end(); ++aIt) {
209 anInfo.append(ModuleBase_Tools::objectInfo((*aIt)));
211 QString anInfoStr = anInfo.join(";\t");
212 qDebug(QString("onFeatureUpdatedMsg: %1, %2")
213 .arg(anObjects.size()).arg(anInfoStr).toStdString().c_str());
215 std::set<ObjectPtr> aFeatures = theMsg->objects();
216 XGUI_OperationMgr* anOperationMgr = workshop()->operationMgr();
217 if (anOperationMgr->hasOperation()) {
218 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
219 (anOperationMgr->currentOperation());
221 FeaturePtr aCurrentFeature = aFOperation->feature();
222 std::set<ObjectPtr>::const_iterator aIt;
223 for (aIt = aFeatures.begin(); aIt != aFeatures.end(); ++aIt) {
224 ObjectPtr aNewFeature = (*aIt);
225 if (aNewFeature == aCurrentFeature) {
226 workshop()->propertyPanel()->updateContentWidget(aCurrentFeature);
232 //anOperationMgr->onValidateOperation();
234 //if (myObjectBrowser)
235 // myObjectBrowser->processEvent(theMsg);
238 //******************************************************
239 void XGUI_WorkshopListener::
240 onFeatureRedisplayMsg(const std::shared_ptr<ModelAPI_ObjectUpdatedMessage>& theMsg)
242 std::set<ObjectPtr> anObjects = theMsg->objects();
243 std::set<ObjectPtr>::const_iterator aIt;
245 #ifdef DEBUG_FEATURE_REDISPLAY
247 for (aIt = anObjects.begin(); aIt != anObjects.end(); ++aIt) {
248 anInfo.append(ModuleBase_Tools::objectInfo((*aIt)));
250 QString anInfoStr = anInfo.join(";\t");
251 qDebug(QString("onFeatureRedisplayMsg: %1, %2")
252 .arg(anObjects.size()).arg(anInfoStr).toStdString().c_str());
255 XGUI_Workshop* aWorkshop = workshop();
256 XGUI_Displayer* aDisplayer = aWorkshop->displayer();
257 //bool aFirstVisualizedBody = false;
258 bool aDoFitAll = false;
259 int aNbOfShownObjects = workshop()->displayer()->objectsCount();
260 bool aRedisplayed = false;
261 //std::list<ObjectPtr> aHiddenObjects;
262 for (aIt = anObjects.begin(); aIt != anObjects.end(); ++aIt) {
263 ObjectPtr aObj = (*aIt);
265 // Hide the object if it is invalid or concealed one
266 bool aHide = !aObj->data() || !aObj->data()->isValid() ||
267 aObj->isDisabled() || (!aObj->isDisplayed());
268 if (!aHide) { // check that this is not hidden result
269 ResultPtr aRes = std::dynamic_pointer_cast<ModelAPI_Result>(aObj);
270 aHide = aRes && aRes->isConcealed();
272 // Hide the presentation with an empty shape. But isDisplayed state of the object should not
273 // be changed to the object becomes visible when the shape becomes not empty
274 if (!aHide && aRes.get())
275 aHide = !aRes->shape().get() || aRes->shape()->isNull();
278 #ifdef DEBUG_RESULT_COMPSOLID
279 ResultPtr aRes = std::dynamic_pointer_cast<ModelAPI_Result>(aObj);
281 ResultCompSolidPtr aCompSolidRes = std::dynamic_pointer_cast<ModelAPI_ResultCompSolid>(aRes);
282 if (aCompSolidRes.get()) {
283 qDebug(QString("COMPSOLID, numberOfSubs = %1")
284 .arg(aCompSolidRes->numberOfSubs()).toStdString().c_str());
286 if (ModelAPI_Tools::compSolidOwner(aRes))
287 qDebug("COMPSOLID sub-object");
290 #ifdef DEBUG_FEATURE_REDISPLAY
291 QString anObjInfo = ModuleBase_Tools::objectInfo((aObj));
292 FeaturePtr aFeature = ModelAPI_Feature::feature(aObj);
293 if (aFeature.get()) {
294 std::string aKind = aFeature->getKind();
295 if (aKind == DebugFeatureKind || DebugFeatureKind.empty()) {
296 qDebug(QString("visible=%1, hide=%2 : display= %2").arg(aDisplayer->isVisible(aObj))
297 .arg(aHide).arg(anObjInfo).toStdString().c_str());
302 //we should provide objects which are hidden in the viewer, e.g. sketch always should
303 // visualizes all sub-features, if some features are to be hidden, sould be proposed may
304 // be to removed #1223
305 // aHiddenObjects.push_back(aObj);
306 aRedisplayed = aDisplayer->erase(aObj, false) || aRedisplayed;
307 #ifdef DEBUG_FEATURE_REDISPLAY
308 // Redisplay the visible object or the object of the current operation
309 bool isVisibleObject = aDisplayer->isVisible(aObj);
311 QString anObjInfo = ModuleBase_Tools::objectInfo((aObj));
315 // Redisplay the visible object or the object of the current operation
316 bool isVisibleObject = aDisplayer->isVisible(aObj);
317 #ifdef DEBUG_FEATURE_REDISPLAY
318 QString anObjInfo = ModuleBase_Tools::objectInfo((aObj));
321 if (isVisibleObject) { // redisplay visible object
322 //displayObject(aObj); // In order to update presentation
323 // in order to avoid the check whether the object can be redisplayed, the exact method
324 // of redisplay is called. This modification is made in order to have the line is updated
325 // by creation of a horizontal constraint on the line by preselection
326 if (ModelAPI_Tools::hasSubResults(std::dynamic_pointer_cast<ModelAPI_Result>(aObj))) {
327 aRedisplayed = aDisplayer->erase(aObj, false) || aRedisplayed;
330 aRedisplayed = aDisplayer->redisplay(aObj, false) || aRedisplayed;
331 // Deactivate object of current operation from selection
332 aWorkshop->deactivateActiveObject(aObj, false);
334 } else { // display object if the current operation has it
335 if (displayObject(aObj)) {
336 aDoFitAll = aDoFitAll || neededFitAll(aObj, aNbOfShownObjects);
339 // Deactivate object of current operation from selection
340 aWorkshop->deactivateActiveObject(aObj, false);
345 // this processing should be moved in another place in order to do not cause problems in
346 // flush messages chain
347 //if (aHiddenObjects.size() > 0)
348 // myWorkshop->module()->processHiddenObject(aHiddenObjects);
350 bool isCustomized = customizeCurrentObject(anObjects, aRedisplayed);
351 if (aRedisplayed || isCustomized) {
352 Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_EMPTY_AIS_PRESENTATION));
354 //VSV FitAll updated viewer by itself
356 myWorkshop->viewer()->fitAll();
358 aDisplayer->updateViewer();
362 //******************************************************
363 void XGUI_WorkshopListener::
364 onFeatureCreatedMsg(const std::shared_ptr<ModelAPI_ObjectUpdatedMessage>& theMsg)
366 std::set<ObjectPtr> anObjects = theMsg->objects();
367 std::set<ObjectPtr>::const_iterator aIt;
368 #ifdef DEBUG_FEATURE_CREATED
370 for (aIt = anObjects.begin(); aIt != anObjects.end(); ++aIt) {
371 anInfo.append(ModuleBase_Tools::objectInfo((*aIt)));
373 QString anInfoStr = anInfo.join(";\t");
374 qDebug(QString("onFeatureCreatedMsg: %1, %2")
375 .arg(anObjects.size()).arg(anInfoStr).toStdString().c_str());
378 bool aDoFitAll = false;
379 int aNbOfShownObjects = workshop()->displayer()->objectsCount();
381 //bool aHasPart = false;
382 bool aDisplayed = false;
383 for (aIt = anObjects.begin(); aIt != anObjects.end(); ++aIt) {
384 ObjectPtr anObject = *aIt;
386 #ifdef DEBUG_RESULT_COMPSOLID
387 ResultPtr aRes = std::dynamic_pointer_cast<ModelAPI_Result>(anObject);
389 ResultCompSolidPtr aCompSolidRes = std::dynamic_pointer_cast<ModelAPI_ResultCompSolid>(aRes);
390 if (aCompSolidRes.get()) {
391 qDebug(QString("COMPSOLID, numberOfSubs = %1")
392 .arg(aCompSolidRes->numberOfSubs()).toStdString().c_str());
394 if (ModelAPI_Tools::compSolidOwner(aRes))
395 qDebug("COMPSOLID sub-object");
398 // the validity of the data should be checked here in order to avoid display of the objects,
399 // which were created, then deleted, but flush for the creation event happens after that
400 // we should not display disabled objects
401 bool aHide = !anObject->data()->isValid() ||
402 anObject->isDisabled() ||
403 !anObject->isDisplayed();
404 if (!aHide) { // check that this is not hidden result
405 ResultPtr aRes = std::dynamic_pointer_cast<ModelAPI_Result>(anObject);
406 bool isConcealed = aRes && aRes->isConcealed();
407 aHide = aRes && aRes->isConcealed();
408 // Hide the presentation with an empty shape. But isDisplayed state of the object should not
409 // be changed to the object becomes visible when the shape becomes not empty
410 if (!aHide && aRes.get())
411 aHide = !aRes->shape().get() || aRes->shape()->isNull();
414 // setDisplayed has to be called in order to synchronize internal state of the object
415 // with list of displayed objects
416 if (myWorkshop->module()->canDisplayObject(anObject)) {
417 anObject->setDisplayed(true);
418 aDisplayed = displayObject(anObject);
420 aDoFitAll = aDoFitAll || neededFitAll(anObject, aNbOfShownObjects);
422 anObject->setDisplayed(false);
426 bool isCustomized = customizeCurrentObject(anObjects, aDisplayed);
428 //if (myObjectBrowser)
429 // myObjectBrowser->processEvent(theMsg);
431 Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_EMPTY_AIS_PRESENTATION));
432 //VSV FitAll updated viewer by itself
434 myWorkshop->viewer()->fitAll();
436 workshop()->displayer()->updateViewer();
438 //if (aHasPart) { // TODO: Avoid activate last part on loading of document
439 // activateLastPart();
443 //******************************************************
444 void XGUI_WorkshopListener::onFeatureEmptyPresentationMsg(
445 const std::shared_ptr<ModelAPI_ObjectUpdatedMessage>& theMsg)
447 std::set<ObjectPtr> anObjects = theMsg->objects();
448 std::set<ObjectPtr>::const_iterator aIt;
449 #ifdef DEBUG_FEATURE_CREATED
451 for (aIt = anObjects.begin(); aIt != anObjects.end(); ++aIt) {
452 anInfo.append(ModuleBase_Tools::objectInfo((*aIt)));
454 QString anInfoStr = anInfo.join(";\t");
455 qDebug(QString("onFeatureEmptyPresentationMsg: %1, %2")
456 .arg(anObjects.size()).arg(anInfoStr).toStdString().c_str());
459 XGUI_Workshop* aWorkshop = workshop();
460 XGUI_Displayer* aDisplayer = aWorkshop->displayer();
462 bool aRedisplayed = false;
463 for (aIt = anObjects.begin(); aIt != anObjects.end(); ++aIt) {
464 ObjectPtr anObject = *aIt;
465 aRedisplayed = aDisplayer->erase(anObject, false) || aRedisplayed;
469 aDisplayer->updateViewer();
472 bool XGUI_WorkshopListener::event(QEvent * theEvent)
474 PostponeMessageQtEvent* aPostponedEv = dynamic_cast<PostponeMessageQtEvent*>(theEvent);
476 std::shared_ptr<Events_Message> aEventPtr = aPostponedEv->postponedMessage();
477 processEvent(aEventPtr);
483 //**************************************************************
484 bool XGUI_WorkshopListener::displayObject(ObjectPtr theObj)
486 #ifdef DEBUG_RESULT_COMPSOLID
487 ResultPtr aRes = std::dynamic_pointer_cast<ModelAPI_Result>(theObj);
488 if (aRes.get() && (ModelAPI_Tools::hasSubResults(aRes) || ModelAPI_Tools::compSolidOwner(aRes))) {
489 ResultCompSolidPtr aCompSolidRes = std::dynamic_pointer_cast<ModelAPI_ResultCompSolid>(aRes);
490 if (aCompSolidRes.get()) {
491 qDebug("COMPSOLID: displayObject");
496 bool aDisplayed = false;
497 XGUI_Workshop* aWorkshop = workshop();
498 // do not display the object if it has sub objects. They should be displayed separately.
499 if (!aWorkshop->module()->canDisplayObject(theObj) ||
500 ModelAPI_Tools::hasSubResults(std::dynamic_pointer_cast<ModelAPI_Result>(theObj)))
503 XGUI_Displayer* aDisplayer = aWorkshop->displayer();
504 int aNb = aDisplayer->objectsCount();
505 return aDisplayer->display(theObj, false);
508 //**************************************************************
509 bool XGUI_WorkshopListener::neededFitAll(ObjectPtr theObj, const int theNbOfShownObjects)
511 bool aFirstVisualizedBody = false;
513 if (theNbOfShownObjects == 0) {
514 ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObj);
516 std::string aResultGroupName = aResult->groupName();
517 if (aResultGroupName == ModelAPI_ResultBody::group() ||
518 aResultGroupName == ModelAPI_ResultGroup::group()) {
519 std::shared_ptr<GeomAPI_Shape> aShapePtr = ModelAPI_Tools::shape(aResult);
520 aFirstVisualizedBody = aShapePtr.get() != NULL;
524 return aFirstVisualizedBody;
527 bool XGUI_WorkshopListener::customizeCurrentObject(const std::set<ObjectPtr>& theObjects,
528 bool theForceRedisplay)
530 XGUI_OperationMgr* anOperationMgr = workshop()->operationMgr();
531 FeaturePtr aCurrentFeature;
532 if (anOperationMgr->hasOperation()) {
533 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
534 (anOperationMgr->currentOperation());
536 aCurrentFeature = aFOperation->feature();
540 bool aCustomized = false;
541 if (aCurrentFeature.get()) {
542 // the customize presentation should be redisplayed if force redislayed is true or
543 // if a list of message objects contains the operation feature for case when
544 // the feature is hidden, but arguments of the feature are modified
545 // e.g. extrusion is hidden(h=0) but sketch is chosen
546 if (theForceRedisplay || theObjects.find(aCurrentFeature) != theObjects.end()) {
547 aCustomized = myWorkshop->module()->customizeObject(aCurrentFeature,
548 ModuleBase_IModule::CustomizeArguments, false) || aCustomized;
549 aCustomized = myWorkshop->module()->customizeObject(aCurrentFeature,
550 ModuleBase_IModule::CustomizeResults, false) || aCustomized;
551 aCustomized = myWorkshop->module()->customizeObject(aCurrentFeature,
552 ModuleBase_IModule::CustomizeHighlightedObjects, false) || aCustomized;
558 XGUI_Workshop* XGUI_WorkshopListener::workshop() const
560 XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myWorkshop);
561 return aConnector->workshop();