]> SALOME platform Git repositories - modules/shaper.git/blob - src/XGUI/XGUI_WorkshopListener.cpp
Salome HOME
Compsolid - debug of visibility in the viewer.
[modules/shaper.git] / src / XGUI / XGUI_WorkshopListener.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
2
3 #include "XGUI_WorkshopListener.h"
4 #include "XGUI_Workshop.h"
5 #include "XGUI_Displayer.h"
6 #include "XGUI_OperationMgr.h"
7 #include "XGUI_SalomeConnector.h"
8 #include "XGUI_ActionsMgr.h"
9 #include "XGUI_PropertyPanel.h"
10 #include "XGUI_ModuleConnector.h"
11 #include "XGUI_QtEvents.h"
12
13 #include <AppElements_Workbench.h>
14 #include <AppElements_Command.h>
15 #include <AppElements_MainMenu.h>
16 #include <AppElements_MainWindow.h>
17 #include <AppElements_MenuGroupPanel.h>
18 #include <AppElements_Button.h>
19
20 #include <ModuleBase_IModule.h>
21
22 #include <ModelAPI_Object.h>
23 #include <ModelAPI_Events.h>
24 #include <ModelAPI_Session.h>
25 #include <ModelAPI_Result.h>
26 #include <ModelAPI_Feature.h>
27 #include <ModelAPI_Data.h>
28 #include <ModelAPI_ResultBody.h>
29 #include <ModelAPI_ResultCompSolid.h>
30 #include <ModelAPI_Tools.h>
31
32 #include <Events_Loop.h>
33 #include <Events_Error.h>
34 #include <Events_LongOp.h>
35
36 #include <ModuleBase_IWorkshop.h>
37
38 #include <ModuleBase_Operation.h>
39 #include <ModuleBase_OperationDescription.h>
40 #include <ModuleBase_Tools.h>
41 #include <ModuleBase_IViewer.h>
42 #include <ModuleBase_FilterFactory.h>
43
44 #include <Config_FeatureMessage.h>
45 #include <Config_PointerMessage.h>
46 #include <Config_SelectionFilterMessage.h>
47
48 #include <QApplication>
49 #include <QMainWindow>
50 #include <QThread>
51 #include <QAction>
52
53 #ifdef _DEBUG
54 #include <QDebug>
55 #include <iostream>
56 #endif
57
58 //#define DEBUG_FEATURE_CREATED
59 //#define DEBUG_FEATURE_REDISPLAY
60 //#define DEBUG_RESULT_COMPSOLID
61
62 XGUI_WorkshopListener::XGUI_WorkshopListener(ModuleBase_IWorkshop* theWorkshop)
63   : myWorkshop(theWorkshop),
64     myUpdatePrefs(false)
65 {
66 }
67
68 //******************************************************
69 XGUI_WorkshopListener::~XGUI_WorkshopListener(void)
70 {
71 }
72
73 //******************************************************
74 void XGUI_WorkshopListener::initializeEventListening()
75 {
76   //Initialize event listening
77   Events_Loop* aLoop = Events_Loop::loop();
78   aLoop->registerListener(this, Events_Error::errorID());  //!< Listening application errors.
79   aLoop->registerListener(this, Events_Loop::eventByName(Config_FeatureMessage::GUI_EVENT()));
80   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OPERATION_LAUNCHED));
81   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
82   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
83   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
84   aLoop->registerListener(this, Events_LongOp::eventID());
85   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_PLUGIN_LOADED));
86   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_SELFILTER_LOADED));
87
88   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_UPDATE_VIEWER_BLOCKED));
89   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_UPDATE_VIEWER_UNBLOCKED));
90 }
91
92 //******************************************************
93 void XGUI_WorkshopListener::processEvent(const std::shared_ptr<Events_Message>& theMessage)
94 {
95   if (QApplication::instance()->thread() != QThread::currentThread()) {
96     #ifdef _DEBUG
97     std::cout << "XGUI_Workshop::processEvent: " << "Working in another thread." << std::endl;
98     #endif
99     SessionPtr aMgr = ModelAPI_Session::get();
100     PostponeMessageQtEvent* aPostponeEvent = new PostponeMessageQtEvent(theMessage);
101     QApplication::postEvent(this, aPostponeEvent);
102     return;
103   }
104
105   //A message to start feature creation received.
106   if (theMessage->eventID() == Events_Loop::loop()->eventByName(Config_FeatureMessage::GUI_EVENT())) {
107     std::shared_ptr<Config_FeatureMessage> aFeatureMsg =
108        std::dynamic_pointer_cast<Config_FeatureMessage>(theMessage);
109     if (!aFeatureMsg->isInternal()) {
110       addFeature(aFeatureMsg);
111     }
112   }
113   // Process creation of Part
114   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)) {
115     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
116         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
117     onFeatureCreatedMsg(aUpdMsg);
118     if (myUpdatePrefs) {
119       XGUI_SalomeConnector* aSalomeConnector = workshop()->salomeConnector();
120       if (aSalomeConnector)
121         aSalomeConnector->createPreferences();
122       myUpdatePrefs = false;
123     }
124   }
125   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_PLUGIN_LOADED)) {
126     myUpdatePrefs = true;
127   }
128   // Redisplay feature
129   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY)) {
130     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
131         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
132     onFeatureRedisplayMsg(aUpdMsg);
133   }
134   //Update property panel on corresponding message. If there is no current operation (no
135   //property panel), or received message has different feature to the current - do nothing.
136   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)) {
137     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> anUpdateMsg =
138         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
139     onFeatureUpdatedMsg(anUpdateMsg);
140   } else if (theMessage->eventID() == Events_LongOp::eventID()) {
141     if (Events_LongOp::isPerformed()) {
142       QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
143     } else {
144       QApplication::restoreOverrideCursor();
145     }
146   }
147   //An operation passed by message. Start it, process and commit.
148   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OPERATION_LAUNCHED)) {
149     std::shared_ptr<Config_PointerMessage> aPartSetMsg =
150         std::dynamic_pointer_cast<Config_PointerMessage>(theMessage);
151     //myPropertyPanel->cleanContent();
152     ModuleBase_Operation* anOperation = (ModuleBase_Operation*) aPartSetMsg->pointer();
153     XGUI_OperationMgr* anOperationMgr = workshop()->operationMgr();
154
155     if (anOperationMgr->startOperation(anOperation)) {
156       workshop()->propertyPanel()->updateContentWidget(anOperation->feature());
157       if (!anOperation->getDescription()->hasXmlRepresentation()) {
158         if (anOperation->commit())
159           workshop()->updateCommandStatus();
160       }
161     }
162   } 
163   else if (theMessage->eventID() == Events_Loop::eventByName(EVENT_SELFILTER_LOADED)) {
164     std::shared_ptr<Config_SelectionFilterMessage> aMsg = 
165       std::dynamic_pointer_cast<Config_SelectionFilterMessage>(theMessage);
166     if (aMsg) {
167       ModuleBase_FilterFactory* aFactory = myWorkshop->selectionFilters();
168       if (!aMsg->attributeId().empty()) {
169         aFactory->assignFilter(aMsg->selectionFilterId(), aMsg->featureId(), aMsg->attributeId(),
170                                aMsg->parameters());
171       }
172     }
173   } else if (theMessage->eventID() == Events_Loop::eventByName(EVENT_UPDATE_VIEWER_BLOCKED)) {
174     // the viewer's update context will not happens until viewer updated is emitted
175       workshop()->displayer()->enableUpdateViewer(false);
176   } else if (theMessage->eventID() == Events_Loop::eventByName(EVENT_UPDATE_VIEWER_UNBLOCKED)) {
177     // the viewer's update context is unblocked, the viewer's update works
178     XGUI_Displayer* aDisplayer = workshop()->displayer();
179     aDisplayer->enableUpdateViewer(true);
180     aDisplayer->updateViewer();
181   } else {
182     //Show error dialog if error message received.
183     std::shared_ptr<Events_Error> anAppError = std::dynamic_pointer_cast<Events_Error>(theMessage);
184     if (anAppError) {
185       emit errorOccurred(QString::fromLatin1(anAppError->description()));
186     }
187     return;
188   }
189   if (!workshop()->isSalomeMode()) {
190     SessionPtr aMgr = ModelAPI_Session::get();
191     AppElements_MainWindow* aMainWindow = workshop()->mainWindow();
192     if (aMgr->isModified() != aMainWindow->isModifiedState())
193       aMainWindow->setModifiedState(aMgr->isModified());
194   }
195 }
196
197 //******************************************************
198 void XGUI_WorkshopListener::onFeatureUpdatedMsg(const std::shared_ptr<ModelAPI_ObjectUpdatedMessage>& theMsg)
199 {
200   std::set<ObjectPtr> aFeatures = theMsg->objects();
201   XGUI_OperationMgr* anOperationMgr = workshop()->operationMgr();
202   if (anOperationMgr->hasOperation()) {
203     FeaturePtr aCurrentFeature = anOperationMgr->currentOperation()->feature();
204     std::set<ObjectPtr>::const_iterator aIt;
205     for (aIt = aFeatures.begin(); aIt != aFeatures.end(); ++aIt) {
206       ObjectPtr aNewFeature = (*aIt);
207       if (aNewFeature == aCurrentFeature) {
208         workshop()->propertyPanel()->updateContentWidget(aCurrentFeature);
209         break;
210       }
211     }
212   }
213   anOperationMgr->onValidateOperation();
214   //if (myObjectBrowser)
215   //  myObjectBrowser->processEvent(theMsg);
216 }
217
218 //******************************************************
219 void XGUI_WorkshopListener::onFeatureRedisplayMsg(const std::shared_ptr<ModelAPI_ObjectUpdatedMessage>& theMsg)
220 {
221   std::set<ObjectPtr> aObjects = theMsg->objects();
222   std::set<ObjectPtr>::const_iterator aIt;
223
224 #ifdef DEBUG_FEATURE_REDISPLAY
225   QStringList anInfo;
226   for (aIt = aObjects.begin(); aIt != aObjects.end(); ++aIt) {
227     anInfo.append(ModuleBase_Tools::objectInfo((*aIt)));
228   }
229   QString anInfoStr = anInfo.join(";\t");
230   qDebug(QString("onFeatureRedisplayMsg: %1, %2").arg(aObjects.size()).arg(anInfoStr).toStdString().c_str());
231 #endif
232
233   XGUI_Workshop* aWorkshop = workshop();
234   XGUI_Displayer* aDisplayer = aWorkshop->displayer();
235   for (aIt = aObjects.begin(); aIt != aObjects.end(); ++aIt) {
236     ObjectPtr aObj = (*aIt);
237
238     // Hide the object if it is invalid or concealed one
239     bool aHide = !aObj->data() || !aObj->data()->isValid() || 
240       aObj->isDisabled() || (!aObj->isDisplayed());
241     if (!aHide) { // check that this is not hidden result
242       ResultPtr aRes = std::dynamic_pointer_cast<ModelAPI_Result>(aObj);
243       aHide = aRes && aRes->isConcealed();
244     }
245 #ifdef DEBUG_RESULT_COMPSOLID
246     ResultPtr aRes = std::dynamic_pointer_cast<ModelAPI_Result>(aObj);
247     if (aRes.get()) {
248       ResultCompSolidPtr aCompSolidRes = std::dynamic_pointer_cast<ModelAPI_ResultCompSolid>(aRes);
249       if (aCompSolidRes.get()) {
250           qDebug(QString("COMPSOLID, numberOfSubs = %1").arg(aCompSolidRes->numberOfSubs()).toStdString().c_str());
251       }
252       if (ModelAPI_Tools::compSolidOwner(aRes))
253         qDebug("COMPSOLID sub-object");
254     }
255 #endif
256     if (aHide) {
257       aDisplayer->erase(aObj, false);
258       #ifdef DEBUG_FEATURE_REDISPLAY
259         // Redisplay the visible object or the object of the current operation
260         bool isVisibleObject = aDisplayer->isVisible(aObj);
261
262         QString anObjInfo = ModuleBase_Tools::objectInfo((aObj));
263         //qDebug(QString("visible=%1 : erase  = %2").arg(isVisibleObject).arg(anObjInfo).toStdString().c_str());
264       #endif
265     }
266     else {
267       // Redisplay the visible object or the object of the current operation
268       bool isVisibleObject = aDisplayer->isVisible(aObj);
269       #ifdef DEBUG_FEATURE_REDISPLAY
270         QString anObjInfo = ModuleBase_Tools::objectInfo((aObj));
271         //qDebug(QString("visible=%1 : display= %2").arg(isVisibleObject).arg(anObjInfo).toStdString().c_str());
272         /*FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObj);
273         if (aFeature.get()) {
274           std::string aKind = aFeature->getKind();
275           if (aKind == "SketchMultiRotation")
276             bool aValue = true;
277         }*/
278       #endif
279
280       if (isVisibleObject)  { // redisplay visible object
281         //displayObject(aObj);  // In order to update presentation
282         // in order to avoid the check whether the object can be redisplayed, the exact method
283         // of redisplay is called. This modification is made in order to have the line is updated
284         // by creation of a horizontal constraint on the line by preselection
285         if (ModelAPI_Tools::hasSubResults(std::dynamic_pointer_cast<ModelAPI_Result>(aObj))) {
286           aDisplayer->erase(aObj, false);
287         }
288         else {
289           aDisplayer->redisplay(aObj, false);
290           // Deactivate object of current operation from selection
291           aWorkshop->deactivateActiveObject(aObj, false);
292         }
293       } else { // display object if the current operation has it
294         if (displayObject(aObj)) {
295           // Deactivate object of current operation from selection
296           aWorkshop->deactivateActiveObject(aObj, false);
297         }
298       }
299     }
300   }
301   aDisplayer->updateViewer();
302 }
303 //******************************************************
304 void XGUI_WorkshopListener::onFeatureCreatedMsg(const std::shared_ptr<ModelAPI_ObjectUpdatedMessage>& theMsg)
305 {
306   std::set<ObjectPtr> aObjects = theMsg->objects();
307   std::set<ObjectPtr>::const_iterator aIt;
308 #ifdef DEBUG_FEATURE_CREATED
309   QStringList anInfo;
310   for (aIt = aObjects.begin(); aIt != aObjects.end(); ++aIt) {
311     anInfo.append(ModuleBase_Tools::objectInfo((*aIt)));
312   }
313   QString anInfoStr = anInfo.join(";\t");
314   qDebug(QString("onFeatureCreatedMsg: %1, %2").arg(aObjects.size()).arg(anInfoStr).toStdString().c_str());
315 #endif
316
317   //bool aHasPart = false;
318   bool isDisplayed = false;
319   for (aIt = aObjects.begin(); aIt != aObjects.end(); ++aIt) {
320     ObjectPtr anObject = *aIt;
321
322 #ifdef DEBUG_RESULT_COMPSOLID
323     ResultPtr aRes = std::dynamic_pointer_cast<ModelAPI_Result>(anObject);
324     if (aRes.get()) {
325       ResultCompSolidPtr aCompSolidRes = std::dynamic_pointer_cast<ModelAPI_ResultCompSolid>(aRes);
326       if (aCompSolidRes.get()) {
327           qDebug(QString("COMPSOLID, numberOfSubs = %1").arg(aCompSolidRes->numberOfSubs()).toStdString().c_str());
328       }
329       if (ModelAPI_Tools::compSolidOwner(aRes))
330         qDebug("COMPSOLID sub-object");
331     }
332 #endif
333     // the validity of the data should be checked here in order to avoid display of the objects,
334     // which were created, then deleted, but flush for the creation event happens after that
335     // we should not display disabled objects
336     bool aHide = !anObject->data()->isValid() || 
337                  anObject->isDisabled() ||
338                  !anObject->isDisplayed();
339     if (!aHide) {
340       // setDisplayed has to be called in order to synchronize internal state of the object 
341       // with list of displayed objects
342       if (myWorkshop->module()->canDisplayObject(anObject)) {
343         anObject->setDisplayed(true);
344         isDisplayed = displayObject(*aIt);
345       } else 
346         anObject->setDisplayed(false);
347     }
348   }
349   //if (myObjectBrowser)
350   //  myObjectBrowser->processEvent(theMsg);
351   if (isDisplayed)
352     workshop()->displayer()->updateViewer();
353   //if (aHasPart) { // TODO: Avoid activate last part on loading of document
354   //  activateLastPart();
355   //}
356 }
357
358 bool XGUI_WorkshopListener::event(QEvent * theEvent)
359 {
360   PostponeMessageQtEvent* aPostponedEv = dynamic_cast<PostponeMessageQtEvent*>(theEvent);
361   if (aPostponedEv) {
362     std::shared_ptr<Events_Message> aEventPtr = aPostponedEv->postponedMessage();
363     processEvent(aEventPtr);
364     return true;
365   }
366   return false;
367 }
368
369 void XGUI_WorkshopListener::addFeature(const std::shared_ptr<Config_FeatureMessage>& theMessage)
370 {
371   if (!theMessage) {
372 #ifdef _DEBUG
373     qDebug() << "XGUI_WorkshopListener::addFeature: NULL message.";
374 #endif
375     return;
376   }
377   ActionInfo aFeatureInfo;
378   aFeatureInfo.initFrom(theMessage);
379
380   XGUI_Workshop* aWorkshop = workshop();
381
382   QString aWchName = QString::fromStdString(theMessage->workbenchId());
383   QStringList aNestedFeatures =
384       QString::fromStdString(theMessage->nestedFeatures()).split(" ", QString::SkipEmptyParts);
385   QString aDocKind = QString::fromStdString(theMessage->documentKind());
386   QList<QAction*> aNestedActList;
387   bool isColumnButton = !aNestedFeatures.isEmpty();
388   if (isColumnButton) {
389     QString aNestedActions = QString::fromStdString(theMessage->actionsWhenNested());
390     XGUI_OperationMgr* anOperationMgr = aWorkshop->operationMgr();
391     XGUI_ActionsMgr* anActionsMgr = aWorkshop->actionsMgr();
392     if (aNestedActions.contains("accept")) {
393       QAction* anAction = anActionsMgr->operationStateAction(XGUI_ActionsMgr::AcceptAll, NULL);
394       connect(anAction, SIGNAL(triggered()), anOperationMgr, SLOT(commitAllOperations()));
395       aNestedActList << anAction;
396     }
397     if (aNestedActions.contains("abort")) {
398       QAction* anAction = anActionsMgr->operationStateAction(XGUI_ActionsMgr::AbortAll, NULL);
399       connect(anAction, SIGNAL(triggered()), anOperationMgr, SLOT(abortAllOperations()));
400       aNestedActList << anAction;
401     }
402   }
403
404   if (aWorkshop->isSalomeMode()) {
405     XGUI_SalomeConnector* aSalomeConnector = aWorkshop->salomeConnector();
406     QAction* aAction;
407     if (isColumnButton) {
408       aAction = aSalomeConnector->addNestedFeature(aWchName, aFeatureInfo, aNestedActList);
409     } else {
410       //Issue #650: in the SALOME mode the tooltip should be same as text
411       aFeatureInfo.toolTip = aFeatureInfo.text;
412       aAction = aSalomeConnector->addFeature(aWchName, aFeatureInfo);
413     }
414     aSalomeConnector->setNestedActions(aFeatureInfo.id, aNestedFeatures);
415     aSalomeConnector->setDocumentKind(aFeatureInfo.id, aDocKind);
416
417     aWorkshop->actionsMgr()->addCommand(aAction);
418     aWorkshop->module()->actionCreated(aAction);
419   } else {
420     //Find or create Workbench
421     AppElements_MainMenu* aMenuBar = aWorkshop->mainWindow()->menuObject();
422     AppElements_Workbench* aPage = aMenuBar->findWorkbench(aWchName);
423     if (!aPage) {
424       aPage = aWorkshop->addWorkbench(aWchName);
425     }
426     //Find or create Group
427     QString aGroupName = QString::fromStdString(theMessage->groupId());
428     AppElements_MenuGroupPanel* aGroup = aPage->findGroup(aGroupName);
429     if (!aGroup) {
430       aGroup = aPage->addGroup(aGroupName);
431     }
432     // Check if hotkey sequence is already defined:
433     XGUI_ActionsMgr* anActionsMgr = aWorkshop->actionsMgr();
434     QKeySequence aHotKey = anActionsMgr->registerShortcut(aFeatureInfo.shortcut);
435     if(aHotKey != aFeatureInfo.shortcut) {
436       aFeatureInfo.shortcut = aHotKey;
437     }
438     // Create feature...
439     AppElements_Command* aCommand = aGroup->addFeature(aFeatureInfo,
440                                                        aDocKind,
441                                                        aNestedFeatures);
442     // Enrich created button with accept/abort buttons if necessary
443     AppElements_Button* aButton = aCommand->button();
444     if (aButton->isColumnButton()) {
445       aButton->setAdditionalButtons(aNestedActList);
446     }
447     aWorkshop->actionsMgr()->addCommand(aCommand);
448     aWorkshop->module()->actionCreated(aCommand);
449   }
450 }
451
452
453 //**************************************************************
454 bool XGUI_WorkshopListener::displayObject(ObjectPtr theObj)
455 {
456 #ifdef DEBUG_RESULT_COMPSOLID
457   ResultPtr aRes = std::dynamic_pointer_cast<ModelAPI_Result>(theObj);
458   if (aRes.get() && (ModelAPI_Tools::hasSubResults(aRes) || ModelAPI_Tools::compSolidOwner(aRes))) {
459     ResultCompSolidPtr aCompSolidRes = std::dynamic_pointer_cast<ModelAPI_ResultCompSolid>(aRes);
460     if (aCompSolidRes.get()) {
461       qDebug("COMPSOLID: displayObject");
462     }
463   }
464 #endif
465
466   XGUI_Workshop* aWorkshop = workshop();
467   // do not display the object if it has sub objects. They should be displayed separately.
468   if (!aWorkshop->module()->canDisplayObject(theObj) ||
469       ModelAPI_Tools::hasSubResults(std::dynamic_pointer_cast<ModelAPI_Result>(theObj)))
470     return false;
471
472   XGUI_Displayer* aDisplayer = aWorkshop->displayer();
473   ResultBodyPtr aBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(theObj);
474   if (aBody.get() != NULL) {
475     int aNb = aDisplayer->objectsCount();
476     aDisplayer->display(theObj, false);
477     if (aNb == 0)
478       myWorkshop->viewer()->fitAll();
479   } else 
480     aDisplayer->display(theObj, false);
481
482   return true;
483 }
484
485 XGUI_Workshop* XGUI_WorkshopListener::workshop() const
486 {
487   XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myWorkshop);
488   return aConnector->workshop();
489 }