]> SALOME platform Git repositories - modules/shaper.git/blob - src/XGUI/XGUI_Workshop.cpp
Salome HOME
Merge branch 'master' of newgeom:newgeom.git
[modules/shaper.git] / src / XGUI / XGUI_Workshop.cpp
1 #include "ModuleBase_IModule.h"
2 #include "XGUI_Constants.h"
3 #include "XGUI_Command.h"
4 #include "XGUI_MainMenu.h"
5 #include "XGUI_MainWindow.h"
6 #include "XGUI_MenuGroupPanel.h"
7 #include "XGUI_Tools.h"
8 #include "XGUI_Workbench.h"
9 #include "XGUI_Workshop.h"
10 #include "XGUI_Viewer.h"
11 #include "ModuleBase_WidgetFactory.h"
12 #include "XGUI_SelectionMgr.h"
13 #include "XGUI_Selection.h"
14 #include "XGUI_ObjectsBrowser.h"
15 #include "XGUI_Displayer.h"
16 #include "XGUI_OperationMgr.h"
17 #include "XGUI_SalomeConnector.h"
18 #include "XGUI_SalomeViewer.h"
19 #include "XGUI_ActionsMgr.h"
20 #include "XGUI_ErrorDialog.h"
21 #include "XGUI_ViewerProxy.h"
22 #include "XGUI_PropertyPanel.h"
23 #include "XGUI_ContextMenuMgr.h"
24 #include "XGUI_ModuleConnector.h"
25
26 #include <ModelAPI_Events.h>
27 #include <ModelAPI_PluginManager.h>
28 #include <ModelAPI_Feature.h>
29 #include <ModelAPI_Data.h>
30 #include <ModelAPI_AttributeDocRef.h>
31 #include <ModelAPI_Object.h>
32 #include <ModelAPI_Validator.h>
33 #include <ModelAPI_ResultConstruction.h>
34 #include <ModelAPI_ResultBody.h>
35
36 #include <PartSetPlugin_Part.h>
37
38 #include <Events_Loop.h>
39 #include <Events_Error.h>
40 #include <Events_LongOp.h>
41
42 #include <ModuleBase_Operation.h>
43 #include <ModuleBase_Operation.h>
44 #include <ModuleBase_OperationDescription.h>
45 #include <ModuleBase_SelectionValidator.h>
46 #include <ModuleBase_ResultValidators.h>
47
48 #include <Config_Common.h>
49 #include <Config_FeatureMessage.h>
50 #include <Config_PointerMessage.h>
51 #include <Config_ModuleReader.h>
52
53 #include <SUIT_ResourceMgr.h>
54
55 #include <QApplication>
56 #include <QFileDialog>
57 #include <QMessageBox>
58 #include <QMdiSubWindow>
59 #include <QPushButton>
60 #include <QDockWidget>
61 #include <QLayout>
62 #include <QTimer>
63
64 #ifdef _DEBUG
65 #include <QDebug>
66 #endif
67
68 #ifdef WIN32
69 #include <windows.h>
70 #else
71 #include <dlfcn.h>
72 #endif
73
74 SUIT_ResourceMgr* XGUI_Workshop::myResourceMgr = 0;
75
76 QMap<QString, QString> XGUI_Workshop::myIcons;
77
78 QString XGUI_Workshop::featureIcon(const std::string& theId)
79 {
80   QString aId(theId.c_str());
81   if (myIcons.contains(aId))
82     return myIcons[aId];
83   return QString();
84 }
85
86 XGUI_Workshop::XGUI_Workshop(XGUI_SalomeConnector* theConnector)
87   : QObject(),
88   myCurrentDir(QString()),
89   myModule(NULL),
90   mySalomeConnector(theConnector),
91   myPropertyPanel(0),
92   myObjectBrowser(0),
93   myDisplayer(0)
94 {
95   if (!myResourceMgr) {
96     myResourceMgr = new SUIT_ResourceMgr("NewGeom");
97     myResourceMgr->setCurrentFormat("xml");
98   }
99   myMainWindow = mySalomeConnector? 0 : new XGUI_MainWindow();
100
101   myDisplayer = new XGUI_Displayer(this);
102
103   mySelector = new XGUI_SelectionMgr(this);
104   //connect(mySelector, SIGNAL(selectionChanged()), this, SLOT(updateModuleCommands()));
105
106   myOperationMgr = new XGUI_OperationMgr(this);
107   myActionsMgr = new XGUI_ActionsMgr(this);
108   myErrorDlg = new XGUI_ErrorDialog(myMainWindow);
109   myContextMenuMgr = new XGUI_ContextMenuMgr(this);
110   connect(myContextMenuMgr, SIGNAL(actionTriggered(const QString&, bool)), 
111           this, SLOT(onContextMenuCommand(const QString&, bool)));
112
113   myViewerProxy = new XGUI_ViewerProxy(this);
114   connect(myViewerProxy, SIGNAL(selectionChanged()), this, SLOT(updateCommandsOnViewSelection()));
115   
116   myModuleConnector = new XGUI_ModuleConnector(this);
117
118   connect(myOperationMgr, SIGNAL(operationStarted()), SLOT(onOperationStarted()));
119   connect(myOperationMgr, SIGNAL(operationResumed()), SLOT(onOperationStarted()));
120   connect(myOperationMgr, SIGNAL(operationStopped(ModuleBase_Operation*)), SLOT(onOperationStopped(ModuleBase_Operation*)));
121   connect(myMainWindow, SIGNAL(exitKeySequence()), SLOT(onExit()));
122   connect(myOperationMgr, SIGNAL(operationStarted()), myActionsMgr, SLOT(update()));
123   connect(myOperationMgr, SIGNAL(operationStopped(ModuleBase_Operation*)), myActionsMgr, SLOT(update()));
124   connect(this, SIGNAL(errorOccurred(const QString&)), myErrorDlg, SLOT(addError(const QString&)));
125 }
126
127 //******************************************************
128 XGUI_Workshop::~XGUI_Workshop(void)
129 {
130 }
131
132 //******************************************************
133 void XGUI_Workshop::startApplication()
134 {
135   initMenu();
136   //Initialize event listening
137   Events_Loop* aLoop = Events_Loop::loop();
138   aLoop->registerListener(this, Events_Error::errorID()); //!< Listening application errors.
139   //TODO(sbh): Implement static method to extract event id [SEID]
140   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_LOADED));
141   // TODO Is it good to use non standard event within workshop?
142   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OPERATION_LAUNCHED));
143   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
144   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
145   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
146   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
147   aLoop->registerListener(this, Events_Loop::eventByName("LongOperation"));
148
149   registerValidators();
150   activateModule();
151   if (myMainWindow) {
152     myMainWindow->show();
153     updateCommandStatus();
154   }
155   onNew();
156 }
157
158 //******************************************************
159 void XGUI_Workshop::initMenu()
160 {
161   myContextMenuMgr->createActions();
162
163   if (isSalomeMode()) {
164     // Create only Undo, Redo commands
165     QAction* aAction = salomeConnector()->addEditCommand("UNDO_CMD", 
166                                       tr("Undo"), tr("Undo last command"),
167                                       QIcon(":pictures/undo.png"), 
168                                       QKeySequence::Undo, false);
169     connect(aAction, SIGNAL(triggered(bool)), this, SLOT(onUndo()));
170     aAction = salomeConnector()->addEditCommand("REDO_CMD", 
171                                       tr("Redo"), tr("Redo last command"),
172                                       QIcon(":pictures/redo.png"), 
173                                       QKeySequence::Redo, false);
174     connect(aAction, SIGNAL(triggered(bool)), this, SLOT(onRedo()));
175     salomeConnector()->addEditMenuSeparator();
176     return;
177   }
178   // File commands group
179   XGUI_MenuGroupPanel* aGroup = myMainWindow->menuObject()->generalPage();
180
181   XGUI_Command* aCommand;
182
183   aCommand = aGroup->addFeature("SAVE_CMD", tr("Save..."), tr("Save the document"),
184                                 QIcon(":pictures/save.png"), QKeySequence::Save);
185   aCommand->connectTo(this, SLOT(onSave()));
186   //aCommand->disable();
187
188   aCommand = aGroup->addFeature("UNDO_CMD", tr("Undo"), tr("Undo last command"),
189                                 QIcon(":pictures/undo.png"), QKeySequence::Undo);
190   aCommand->connectTo(this, SLOT(onUndo()));
191
192   aCommand = aGroup->addFeature("REDO_CMD", tr("Redo"), tr("Redo last command"),
193                                 QIcon(":pictures/redo.png"), QKeySequence::Redo);
194   aCommand->connectTo(this, SLOT(onRedo()));
195
196   aCommand = aGroup->addFeature("REBUILD_CMD", tr("Rebuild"), tr("Rebuild data objects"),
197                                 QIcon(":pictures/rebuild.png"));
198
199   aCommand = aGroup->addFeature("SAVEAS_CMD", tr("Save as..."), tr("Save the document into a file"),
200                                 QIcon(":pictures/save.png"));
201   aCommand->connectTo(this, SLOT(onSaveAs()));
202   //aCommand->disable();
203
204   aCommand = aGroup->addFeature("OPEN_CMD", tr("Open..."), tr("Open a new document"),
205                                 QIcon(":pictures/open.png"), QKeySequence::Open);
206   aCommand->connectTo(this, SLOT(onOpen()));
207
208   //aCommand = aGroup->addFeature("NEW_CMD", tr("New"), tr("Create a new document"),
209   //                              QIcon(":pictures/new.png"), QKeySequence::New);
210   //aCommand->connectTo(this, SLOT(onNew()));
211
212   aCommand = aGroup->addFeature("EXIT_CMD", tr("Exit"), tr("Exit application"),
213                                 QIcon(":pictures/close.png"), QKeySequence::Close);
214   aCommand->connectTo(this, SLOT(onExit()));
215   //FIXME: SBH's test action. Can be used for some GUI tests.
216 //  #ifdef _DEBUG
217 //    aCommand = aGroup->addFeature("TEST_CMD", "Test!", "Private debug button",
218 //                                  QIcon(":pictures/close.png"), QKeySequence(), true);
219 //    aCommand->connectTo(myMainWindow, SLOT(dockPythonConsole()));
220 //  #endif
221 }
222
223 //******************************************************
224 XGUI_Workbench* XGUI_Workshop::addWorkbench(const QString& theName)
225 {
226   XGUI_MainMenu* aMenuBar = myMainWindow->menuObject();
227   return aMenuBar->addWorkbench(theName);
228 }
229
230 //******************************************************
231 void XGUI_Workshop::processEvent(const Events_Message* theMessage)
232 {
233   //A message to start feature creation received.
234   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_LOADED)) {
235     const Config_FeatureMessage* aFeatureMsg = dynamic_cast<const Config_FeatureMessage*>(theMessage);
236     if(!aFeatureMsg->isInternal()) {
237       addFeature(aFeatureMsg);
238     }
239     return;
240   }
241
242   // Process creation of Part
243   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)) {
244     const ModelAPI_ObjectUpdatedMessage* aUpdMsg = dynamic_cast<const ModelAPI_ObjectUpdatedMessage*>(theMessage);
245     onFeatureCreatedMsg(aUpdMsg);
246     return;
247   }
248
249   // Redisplay feature
250   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY)) {
251     const ModelAPI_ObjectUpdatedMessage* aUpdMsg = dynamic_cast<const ModelAPI_ObjectUpdatedMessage*>(theMessage);
252     onFeatureRedisplayMsg(aUpdMsg);
253     return;
254   }
255
256   //Update property panel on corresponding message. If there is no current operation (no
257   //property panel), or received message has different feature to the current - do nothing.
258   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)) {
259     const ModelAPI_ObjectUpdatedMessage* anUpdateMsg =
260         dynamic_cast<const ModelAPI_ObjectUpdatedMessage*>(theMessage);
261     onFeatureUpdatedMsg(anUpdateMsg);
262     return;
263   }
264
265   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
266     const ModelAPI_ObjectDeletedMessage* aDelMsg =
267         dynamic_cast<const ModelAPI_ObjectDeletedMessage*>(theMessage);
268     onObjectDeletedMsg(aDelMsg);
269     return;
270   }
271
272   if (theMessage->eventID() == Events_LongOp::eventID()) {
273     if (Events_LongOp::isPerformed())
274       QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
275       //QTimer::singleShot(10, this, SLOT(onStartWaiting()));
276     else 
277       QApplication::restoreOverrideCursor();
278     return;
279   }
280
281   //An operation passed by message. Start it, process and commit.
282   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OPERATION_LAUNCHED)) {
283     const Config_PointerMessage* aPartSetMsg = dynamic_cast<const Config_PointerMessage*>(theMessage);
284     //myPropertyPanel->cleanContent();
285     ModuleBase_Operation* anOperation = (ModuleBase_Operation*)aPartSetMsg->pointer();
286
287     if (myOperationMgr->startOperation(anOperation)) {
288       myPropertyPanel->updateContentWidget(anOperation->feature());
289       if (!anOperation->getDescription()->hasXmlRepresentation()) {
290         if (anOperation->commit())
291           updateCommandStatus();
292       }
293     }
294     return;
295   }
296   //Show error dialog if error message received.
297   const Events_Error* anAppError = dynamic_cast<const Events_Error*>(theMessage);
298   if (anAppError) {
299     emit errorOccurred(QString::fromLatin1(anAppError->description()));
300   }
301 }
302
303 //******************************************************
304 void XGUI_Workshop::onStartWaiting()
305 {
306   if (Events_LongOp::isPerformed()) {
307     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
308   }
309 }
310
311 //******************************************************
312 void XGUI_Workshop::onFeatureUpdatedMsg(const ModelAPI_ObjectUpdatedMessage* theMsg)
313 {
314   std::set<ObjectPtr> aFeatures = theMsg->objects();
315   if (myOperationMgr->hasOperation())
316   {
317     FeaturePtr aCurrentFeature = myOperationMgr->currentOperation()->feature();
318     std::set<ObjectPtr>::const_iterator aIt;
319     for (aIt = aFeatures.begin(); aIt != aFeatures.end(); ++aIt) {
320       ObjectPtr aNewFeature = (*aIt);
321       if(aNewFeature == aCurrentFeature) {
322         myPropertyPanel->updateContentWidget(aCurrentFeature);
323         break;
324       } 
325     }
326   }
327 }
328
329 //******************************************************
330 void XGUI_Workshop::onFeatureRedisplayMsg(const ModelAPI_ObjectUpdatedMessage* theMsg)
331 {
332   std::set<ObjectPtr> aObjects = theMsg->objects();
333   std::set<ObjectPtr>::const_iterator aIt;
334   for (aIt = aObjects.begin(); aIt != aObjects.end(); ++aIt) {
335     ObjectPtr aObj = (*aIt);
336     if (!aObj->data() || !aObj->data()->isValid())
337       myDisplayer->erase(aObj, false);
338     else {
339       if (myDisplayer->isVisible(aObj)) // TODO VSV: Correction sketch drawing
340         myDisplayer->display(aObj, false); // In order to update presentation
341       else {
342         if(myOperationMgr->hasOperation()) {
343           ModuleBase_Operation* aOperation = myOperationMgr->currentOperation();
344           if (aOperation->hasObject(aObj)) { // Display only current operation results
345             myDisplayer->display(aObj, false);        
346           }
347         }
348       }
349     }
350   }
351   myDisplayer->updateViewer();
352 }
353
354 //******************************************************
355 void XGUI_Workshop::onFeatureCreatedMsg(const ModelAPI_ObjectUpdatedMessage* theMsg)
356 {
357   std::set<ObjectPtr> aObjects = theMsg->objects();
358
359   std::set<ObjectPtr>::const_iterator aIt;
360   bool aHasPart = false;
361   bool isDisplayed = false;
362   for (aIt = aObjects.begin(); aIt != aObjects.end(); ++aIt) {
363     ResultPartPtr aPart = boost::dynamic_pointer_cast<ModelAPI_ResultPart>(*aIt);
364     if (aPart) {
365       aHasPart = true;
366     // If a feature is created from the aplication's python console  
367     // it doesn't stored in the operation mgr and doesn't displayed
368     } else if(myOperationMgr->hasOperation()) {
369       ModuleBase_Operation* aOperation = myOperationMgr->currentOperation();
370       if (aOperation->hasObject(*aIt)) { // Display only current operation results
371         myDisplayer->display(*aIt, false);
372         isDisplayed = true;
373       }
374     }
375   }
376   if (isDisplayed)
377     myDisplayer->updateViewer();
378   if (aHasPart) {
379     //The created part will be created in Object Browser later and we have to activate it
380     // only when it is created everywere
381     QTimer::singleShot(50, this, SLOT(activateLastPart()));
382   }
383 }
384
385 //******************************************************
386 void XGUI_Workshop::onObjectDeletedMsg(const ModelAPI_ObjectDeletedMessage* theMsg)
387 {
388   //std::set<ObjectPtr> aFeatures = theMsg->objects();
389 }
390  
391 //******************************************************
392 void XGUI_Workshop::onOperationStarted()
393 {
394   ModuleBase_Operation* aOperation = myOperationMgr->currentOperation();
395
396   if(aOperation->getDescription()->hasXmlRepresentation()) { //!< No need for property panel
397     connectWithOperation(aOperation);
398
399     showPropertyPanel();
400     QString aXmlRepr = aOperation->getDescription()->xmlRepresentation();
401     ModuleBase_WidgetFactory aFactory = ModuleBase_WidgetFactory(aXmlRepr.toStdString(), myModuleConnector);
402
403     myPropertyPanel->cleanContent();
404     aFactory.createWidget(myPropertyPanel->contentWidget());
405     
406     QList<ModuleBase_ModelWidget*> aWidgets = aFactory.getModelWidgets();
407     QList<ModuleBase_ModelWidget*>::const_iterator anIt = aWidgets.begin(), aLast = aWidgets.end();
408     ModuleBase_ModelWidget* aWidget;
409     for (; anIt != aLast; anIt++) {
410       aWidget = *anIt;
411       aWidget->setFeature(aOperation->feature());
412       //QObject::connect(aWidget, SIGNAL(valuesChanged()),  aOperation, SLOT(storeCustomValue()));
413       QObject::connect(aWidget, SIGNAL(valuesChanged()),
414                        this, SLOT(onWidgetValuesChanged()));
415       // Init default values
416       if (!aOperation->isEditOperation() && aWidget->hasDefaultValue()) {
417         //aWidget->storeValue(aOperation->feature());
418         
419         aWidget->storeValue();
420       }
421     }
422
423     myPropertyPanel->setModelWidgets(aWidgets);
424     myPropertyPanel->setWindowTitle(aOperation->getDescription()->description());
425   }
426   updateCommandStatus();
427 }
428
429 //******************************************************
430 void XGUI_Workshop::onOperationStopped(ModuleBase_Operation* theOperation)
431 {
432   //!< No need for property panel
433   updateCommandStatus();
434   hidePropertyPanel();
435   myPropertyPanel->cleanContent();
436 }
437
438 /*
439  *
440  */
441 void XGUI_Workshop::addFeature(const Config_FeatureMessage* theMessage)
442 {
443   if (!theMessage) {
444 #ifdef _DEBUG
445     qDebug() << "XGUI_Workshop::addFeature: NULL message.";
446 #endif
447     return;
448   }
449   // Remember features icons
450   myIcons[QString::fromStdString(theMessage->id())] = QString::fromStdString(theMessage->icon());
451
452   //Find or create Workbench
453   QString aWchName = QString::fromStdString(theMessage->workbenchId());
454   QString aNestedFeatures = QString::fromStdString(theMessage->nestedFeatures());
455   bool isUsePropPanel = theMessage->isUseInput();
456   QString aFeatureId = QString::fromStdString(theMessage->id());
457   if (isSalomeMode()) {
458     QAction* aAction = salomeConnector()->addFeature(aWchName,
459                               aFeatureId,
460                               QString::fromStdString(theMessage->text()),
461                               QString::fromStdString(theMessage->tooltip()),
462                               QIcon(theMessage->icon().c_str()),
463                               QKeySequence(), isUsePropPanel);
464     salomeConnector()->setNestedActions(aFeatureId, aNestedFeatures.split(" "));
465     myActionsMgr->addCommand(aAction);
466     myModule->featureCreated(aAction);
467   } else {
468
469     XGUI_MainMenu* aMenuBar = myMainWindow->menuObject();
470     XGUI_Workbench* aPage = aMenuBar->findWorkbench(aWchName);
471     if (!aPage) {
472       aPage = addWorkbench(aWchName);
473     }
474     //Find or create Group
475     QString aGroupName = QString::fromStdString(theMessage->groupId());
476     XGUI_MenuGroupPanel* aGroup = aPage->findGroup(aGroupName);
477     if (!aGroup) {
478       aGroup = aPage->addGroup(aGroupName);
479     }
480     // Check if hotkey sequence is already defined:
481     QKeySequence aHotKey = myActionsMgr->registerShortcut(
482         QString::fromStdString(theMessage->keysequence()));
483     // Create feature...
484     XGUI_Command* aCommand = aGroup->addFeature(aFeatureId,
485                                                 QString::fromStdString(theMessage->text()),
486                                                 QString::fromStdString(theMessage->tooltip()),
487                                                 QIcon(theMessage->icon().c_str()),
488                                                 aHotKey, isUsePropPanel);
489     aCommand->setNestedCommands(aNestedFeatures.split(" ", QString::SkipEmptyParts));
490     myActionsMgr->addCommand(aCommand);
491     myModule->featureCreated(aCommand);
492   }
493 }
494
495 /*
496  * Makes a signal/slot connections between Property Panel
497  * and given operation. The given operation becomes a
498  * current operation and previous operation if exists
499  */
500 void XGUI_Workshop::connectWithOperation(ModuleBase_Operation* theOperation)
501 {
502   QAction* aCommand = 0;
503   if (isSalomeMode()) {
504     aCommand = salomeConnector()->command(theOperation->getDescription()->operationId());
505   } else {
506     XGUI_MainMenu* aMenu = myMainWindow->menuObject();
507     aCommand = aMenu->feature(theOperation->getDescription()->operationId());
508   }
509   //Abort operation on uncheck the command
510   if (aCommand)
511     connect(aCommand, SIGNAL(triggered(bool)), theOperation, SLOT(setRunning(bool)));
512 }
513
514 /*
515  * Saves document with given name.
516  */
517 void XGUI_Workshop::saveDocument(QString theName)
518 {
519   QApplication::restoreOverrideCursor();
520   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
521   DocumentPtr aDoc = aMgr->rootDocument();
522   aDoc->save(theName.toLatin1().constData());
523   QApplication::restoreOverrideCursor();
524 }
525
526 //******************************************************
527 void XGUI_Workshop::onExit()
528 {
529   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
530   DocumentPtr aDoc = aMgr->rootDocument();
531   if(aDoc->isModified()) {
532     int anAnswer = QMessageBox::question(
533         myMainWindow, tr("Save current file"),
534         tr("The document is modified, save before exit?"),
535         QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel);
536     if(anAnswer == QMessageBox::Save) {
537       bool saved = onSave();
538       if(!saved) {
539         return;
540       }
541     } else if (anAnswer == QMessageBox::Cancel) {
542       return;
543     }
544   }
545   qApp->exit();
546 }
547
548 //******************************************************
549 void XGUI_Workshop::onNew()
550 {
551   QApplication::setOverrideCursor(Qt::WaitCursor);
552   if (objectBrowser() == 0) {
553     createDockWidgets();
554     mySelector->connectViewers();
555   }
556   myViewerProxy->connectToViewer();
557   showObjectBrowser();
558   if (!isSalomeMode()) {
559     myMainWindow->showPythonConsole();
560     QMdiSubWindow* aWnd = myMainWindow->viewer()->createView();
561     aWnd->showMaximized();
562     updateCommandStatus();
563   }
564   myContextMenuMgr->connectViewer();
565   QApplication::restoreOverrideCursor();
566 }
567
568 //******************************************************
569 void XGUI_Workshop::onOpen()
570 {
571   //save current file before close if modified
572   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
573   DocumentPtr aDoc = aMgr->rootDocument();
574   if(aDoc->isModified()) {
575     //TODO(sbh): re-launch the app?
576     int anAnswer = QMessageBox::question(
577         myMainWindow, tr("Save current file"),
578         tr("The document is modified, save before opening another?"),
579         QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel);
580     if(anAnswer == QMessageBox::Save) {
581       onSave();
582     } else if (anAnswer == QMessageBox::Cancel) {
583       return;
584     }
585     aDoc->close();
586     myCurrentDir = "";
587   }
588
589   //show file dialog, check if readable and open
590   myCurrentDir = QFileDialog::getExistingDirectory(mainWindow());
591   if(myCurrentDir.isEmpty())
592     return;
593   QFileInfo aFileInfo(myCurrentDir);
594   if(!aFileInfo.exists() || !aFileInfo.isReadable()) {
595     QMessageBox::critical(myMainWindow, tr("Warning"), tr("Unable to open the file."));
596     myCurrentDir = "";
597     return;
598   }
599   QApplication::setOverrideCursor(Qt::WaitCursor);
600   aDoc->load(myCurrentDir.toLatin1().constData());
601   myObjectBrowser->rebuildDataTree();
602   displayAllResults();
603   updateCommandStatus();
604   QApplication::restoreOverrideCursor();
605 }
606
607 //******************************************************
608 bool XGUI_Workshop::onSave()
609 {
610   if(myCurrentDir.isEmpty()) {
611     return onSaveAs();
612   }
613   saveDocument(myCurrentDir);
614   updateCommandStatus();
615   return true;
616 }
617
618 //******************************************************
619 bool XGUI_Workshop::onSaveAs()
620 {
621   QFileDialog dialog(mainWindow());
622   dialog.setWindowTitle(tr("Select directory to save files..."));
623   dialog.setFileMode(QFileDialog::Directory);
624   dialog.setFilter(tr("Folders (*)"));
625   dialog.setOptions(QFileDialog::HideNameFilterDetails | QFileDialog::ShowDirsOnly);
626   dialog.setViewMode(QFileDialog::Detail);
627
628   if(!dialog.exec()) {
629     return false;
630   }
631   QString aTempDir = dialog.selectedFiles().first();
632   QDir aDir(aTempDir);
633   if(aDir.exists() && !aDir.entryInfoList(QDir::NoDotAndDotDot|QDir::AllEntries).isEmpty()) {
634     int answer = QMessageBox::question(myMainWindow,
635                                        //: Title of the dialog which asks user if he wants to save study in existing non-empty folder
636                                        tr("Save"),
637                                        tr("The folder already contains some files, save anyway?"),
638                                        QMessageBox::Save|QMessageBox::Cancel);
639     if(answer == QMessageBox::Cancel) {
640       return false;
641     }
642   }
643   myCurrentDir = aTempDir;
644   return onSave();
645 }
646
647 //******************************************************
648 void XGUI_Workshop::onUndo()
649 {
650   objectBrowser()->treeView()->setCurrentIndex(QModelIndex());
651   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
652   DocumentPtr aDoc = aMgr->rootDocument();
653   if (aDoc->isOperation())
654     operationMgr()->abortOperation();
655   aDoc->undo();
656   updateCommandStatus();
657 }
658
659 //******************************************************
660 void XGUI_Workshop::onRedo()
661 {
662   objectBrowser()->treeView()->setCurrentIndex(QModelIndex());
663   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
664   DocumentPtr aDoc = aMgr->rootDocument();
665   if (aDoc->isOperation())
666     operationMgr()->abortOperation();
667   aDoc->redo();
668   updateCommandStatus();
669 }
670
671 //******************************************************
672 ModuleBase_IModule* XGUI_Workshop::loadModule(const QString& theModule)
673 {
674   QString libName =
675       QString::fromStdString(library(theModule.toStdString()));
676   if (libName.isEmpty()) {
677     qWarning(
678     qPrintable( tr( "Information about module \"%1\" doesn't exist." ).arg( theModule ) ));
679     return 0;
680   }
681
682   QString err;
683   CREATE_FUNC crtInst = 0;
684
685 #ifdef WIN32
686   HINSTANCE modLib = ::LoadLibrary((LPTSTR) qPrintable(libName));
687   if (!modLib) {
688     LPVOID lpMsgBuf;
689     ::FormatMessage(
690         FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
691         0, ::GetLastError(), 0, (LPTSTR) & lpMsgBuf, 0, 0);
692     QString aMsg((char*) &lpMsgBuf);
693     err = QString("Failed to load  %1. %2").arg(libName).arg(aMsg);
694     ::LocalFree(lpMsgBuf);
695   } else {
696     crtInst = (CREATE_FUNC) ::GetProcAddress(modLib, CREATE_MODULE);
697     if (!crtInst) {
698       LPVOID lpMsgBuf;
699       ::FormatMessage(
700           FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
701               | FORMAT_MESSAGE_IGNORE_INSERTS,
702           0, ::GetLastError(), 0, (LPTSTR) & lpMsgBuf, 0, 0);
703       QString aMsg((char*) &lpMsgBuf);
704       err = QString("Failed to find  %1 function. %2").arg( CREATE_MODULE).arg(aMsg);
705       ::LocalFree(lpMsgBuf);
706     }
707   }
708 #else
709   void* modLib = dlopen( libName.toLatin1(), RTLD_LAZY | RTLD_GLOBAL );
710   if ( !modLib ) {
711     err = QString( "Can not load library %1. %2" ).arg( libName ).arg( dlerror() );
712   } else {
713     crtInst = (CREATE_FUNC)dlsym( modLib, CREATE_MODULE );
714     if ( !crtInst ) {
715       err = QString( "Failed to find function %1. %2" ).arg( CREATE_MODULE ).arg( dlerror() );
716     }
717   }
718 #endif
719
720   ModuleBase_IModule* aModule = crtInst ? crtInst(this) : 0;
721
722   if (!err.isEmpty()) {
723     if (mainWindow()) {
724       QMessageBox::warning(mainWindow(), tr("Error"), err);
725     } else {
726       qWarning( qPrintable( err ));
727     }
728   }
729   return aModule;
730 }
731
732 //******************************************************
733 bool XGUI_Workshop::activateModule()
734 {
735   Config_ModuleReader aModuleReader;
736   QString moduleName = QString::fromStdString(aModuleReader.getModuleName());
737   myModule = loadModule(moduleName);
738   if (!myModule)
739     return false;
740   myModule->createFeatures();
741   myActionsMgr->update();
742   return true;
743 }
744
745 //******************************************************
746 void XGUI_Workshop::updateCommandStatus()
747 {
748   QList<QAction*> aCommands;
749   if (isSalomeMode()) { // update commands in SALOME mode
750     aCommands = salomeConnector()->commandList();
751   } else {
752     XGUI_MainMenu* aMenuBar = myMainWindow->menuObject();
753     foreach (XGUI_Command* aCmd, aMenuBar->features())
754       aCommands.append(aCmd);
755   }
756   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
757   if (aMgr->hasRootDocument()) {
758     QAction* aUndoCmd;
759     QAction* aRedoCmd;
760     foreach(QAction* aCmd, aCommands) {
761       QString aId = aCmd->data().toString();
762       if (aId == "UNDO_CMD")
763         aUndoCmd = aCmd;
764       else if (aId == "REDO_CMD")
765         aRedoCmd = aCmd;
766       else // Enable all commands
767         aCmd->setEnabled(true);
768     }
769     DocumentPtr aDoc = aMgr->rootDocument();
770     aUndoCmd->setEnabled(aDoc->canUndo());
771     aRedoCmd->setEnabled(aDoc->canRedo());
772   } else {
773     foreach(QAction* aCmd, aCommands) {
774       QString aId = aCmd->data().toString();
775       if (aId == "NEW_CMD")
776         aCmd->setEnabled(true);
777       else if (aId == "EXIT_CMD")
778         aCmd->setEnabled(true);
779       else 
780         aCmd->setEnabled(false);
781     }
782   }
783   myActionsMgr->update();
784 }
785
786 //******************************************************
787 QList<QAction*> XGUI_Workshop::getModuleCommands() const
788 {
789   QList<QAction*> aCommands;
790   if (isSalomeMode()) { // update commands in SALOME mode
791     aCommands = salomeConnector()->commandList();
792   } else {
793     XGUI_MainMenu* aMenuBar = myMainWindow->menuObject();
794     foreach (XGUI_Workbench* aWb, aMenuBar->workbenches()) {
795       foreach(XGUI_Command* aCmd, aWb->features())
796         aCommands.append(aCmd);
797     }
798   }
799   return aCommands;
800 }
801
802 //******************************************************
803 QDockWidget* XGUI_Workshop::createObjectBrowser(QWidget* theParent)
804 {
805   QDockWidget* aObjDock = new QDockWidget(theParent);
806   aObjDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
807   aObjDock->setWindowTitle(tr("Object browser"));
808   aObjDock->setStyleSheet("::title { position: relative; padding-left: 5px; text-align: left center }");
809   myObjectBrowser = new XGUI_ObjectsBrowser(aObjDock);
810   connect(myObjectBrowser, SIGNAL(activePartChanged(ObjectPtr)), this, SLOT(changeCurrentDocument(ObjectPtr)));
811   aObjDock->setWidget(myObjectBrowser);
812
813   myContextMenuMgr->connectObjectBrowser();
814   return aObjDock;
815 }
816
817 //******************************************************
818 /*
819  * Creates dock widgets, places them in corresponding area
820  * and tabifies if necessary.
821  */
822 void XGUI_Workshop::createDockWidgets()
823 {
824   QMainWindow* aDesktop = isSalomeMode()? salomeConnector()->desktop() :
825                                           myMainWindow;
826   QDockWidget* aObjDock = createObjectBrowser(aDesktop);
827   aDesktop->addDockWidget(Qt::LeftDockWidgetArea, aObjDock);
828   myPropertyPanel = new XGUI_PropertyPanel(aDesktop);
829   aDesktop->addDockWidget(Qt::LeftDockWidgetArea, myPropertyPanel);
830   hidePropertyPanel(); //<! Invisible by default
831   hideObjectBrowser();
832   aDesktop->tabifyDockWidget(aObjDock, myPropertyPanel);
833
834   QPushButton* aOkBtn = myPropertyPanel->findChild<QPushButton*>(XGUI::PROP_PANEL_OK);
835   connect(aOkBtn, SIGNAL(clicked()), myOperationMgr, SLOT(onCommitOperation()));
836   QPushButton* aCancelBtn = myPropertyPanel->findChild<QPushButton*>(XGUI::PROP_PANEL_CANCEL);
837   connect(aCancelBtn, SIGNAL(clicked()), myOperationMgr, SLOT(onAbortOperation()));
838
839   connect(myPropertyPanel, SIGNAL(keyReleased(const std::string&, QKeyEvent*)),
840           myOperationMgr, SLOT(onKeyReleased(const std::string&, QKeyEvent*)));
841
842   connect(myPropertyPanel, SIGNAL(widgetActivated(ModuleBase_ModelWidget*)),
843           myOperationMgr, SLOT(onWidgetActivated(ModuleBase_ModelWidget*)));
844   connect(myOperationMgr, SIGNAL(activateNextWidget(ModuleBase_ModelWidget*)),
845           myPropertyPanel, SLOT(onActivateNextWidget(ModuleBase_ModelWidget*)));
846 }
847
848 //******************************************************
849 void XGUI_Workshop::showPropertyPanel()
850 {
851   QAction* aViewAct = myPropertyPanel->toggleViewAction();
852   //<! Restore ability to close panel from the window's menu
853   aViewAct->setEnabled(true);
854   myPropertyPanel->show();
855   myPropertyPanel->raise();
856 }
857
858 //******************************************************
859 void XGUI_Workshop::hidePropertyPanel()
860 {
861   QAction* aViewAct = myPropertyPanel->toggleViewAction();
862   //<! Do not allow to show empty property panel
863   aViewAct->setEnabled(false);
864   myPropertyPanel->hide();
865 }
866
867 //******************************************************
868 void XGUI_Workshop::showObjectBrowser()
869 {
870   myObjectBrowser->parentWidget()->show();
871 }
872
873 //******************************************************
874 void XGUI_Workshop::hideObjectBrowser()
875 {
876   myObjectBrowser->parentWidget()->hide();
877 }
878
879 //******************************************************
880 void XGUI_Workshop::onFeatureTriggered()
881 {
882   QAction* aCmd = dynamic_cast<QAction*>(sender());
883   if (aCmd) {
884     QString aId = salomeConnector()->commandId(aCmd);
885     if (!aId.isNull())
886       myModule->launchOperation(aId);
887   }
888 }
889
890 //******************************************************
891 void XGUI_Workshop::changeCurrentDocument(ObjectPtr theObj)
892 {
893   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
894   if (theObj) {
895     ResultPartPtr aPart = boost::dynamic_pointer_cast<ModelAPI_ResultPart>(theObj);
896     if (aPart) {
897       DocumentPtr aPartDoc = aPart->partDoc();
898       if (aPartDoc) {
899         aMgr->setCurrentDocument(aPartDoc);
900         return;
901       }
902     }
903   } 
904   aMgr->setCurrentDocument(aMgr->rootDocument());
905 }
906
907 //******************************************************
908 void XGUI_Workshop::salomeViewerSelectionChanged()
909 {
910   emit salomeViewerSelection();
911 }
912
913
914 //**************************************************************
915 XGUI_SalomeViewer* XGUI_Workshop::salomeViewer() const 
916
917   return mySalomeConnector->viewer(); 
918 }
919
920 //**************************************************************
921 void XGUI_Workshop::onContextMenuCommand(const QString& theId, bool isChecked)
922 {
923   QList<ObjectPtr> aObjects = mySelector->selection()->selectedObjects();
924   if ((theId == "ACTIVATE_PART_CMD") && (aObjects.size() > 0)) {
925     ResultPartPtr aPart = boost::dynamic_pointer_cast<ModelAPI_ResultPart>(aObjects.first());
926     activatePart(aPart);
927   } else if (theId == "DEACTIVATE_PART_CMD") 
928     activatePart(ResultPartPtr());
929   else if (theId == "DELETE_CMD")
930     deleteObjects(aObjects);
931   else if (theId == "SHOW_CMD")
932     showObjects(aObjects, true);
933   else if (theId == "HIDE_CMD")
934     showObjects(aObjects, false);
935 }
936
937 //**************************************************************
938 void XGUI_Workshop::onWidgetValuesChanged()
939 {
940   ModuleBase_Operation* anOperation = myOperationMgr->currentOperation();
941   FeaturePtr aFeature = anOperation->feature();
942
943   ModuleBase_ModelWidget* aSenderWidget = dynamic_cast<ModuleBase_ModelWidget*>(sender());
944   //if (aCustom)
945   //  aCustom->storeValue(aFeature);
946
947   const QList<ModuleBase_ModelWidget*>& aWidgets = myPropertyPanel->modelWidgets();
948   QList<ModuleBase_ModelWidget*>::const_iterator anIt = aWidgets.begin(), aLast = aWidgets.end();
949   for (; anIt != aLast; anIt++) {
950     ModuleBase_ModelWidget* aCustom = *anIt;
951     if (aCustom && (/*!aCustom->isInitialized(aFeature) ||*/ aCustom == aSenderWidget)) {
952       //aCustom->storeValue(aFeature);
953       aCustom->storeValue();
954     }
955   }
956 }
957
958 //**************************************************************
959 void XGUI_Workshop::activatePart(ResultPartPtr theFeature)
960 {
961   changeCurrentDocument(theFeature);
962   myObjectBrowser->activatePart(theFeature);
963 }
964
965 //**************************************************************
966 void XGUI_Workshop::activateLastPart()
967 {
968   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
969   DocumentPtr aDoc = aMgr->rootDocument();
970   std::string aGrpName = ModelAPI_ResultPart::group();
971   ObjectPtr aLastPart = aDoc->object(aGrpName, aDoc->size(aGrpName) - 1);
972   ResultPartPtr aPart = boost::dynamic_pointer_cast<ModelAPI_ResultPart>(aLastPart);
973   if (aPart)
974     activatePart(aPart);
975 }
976
977 //**************************************************************
978 void XGUI_Workshop::deleteObjects(const QList<ObjectPtr>& theList)
979 {
980   QMainWindow* aDesktop = isSalomeMode()? salomeConnector()->desktop() : myMainWindow;
981   QMessageBox::StandardButton aRes = QMessageBox::warning(aDesktop, tr("Delete features"), 
982                                                           tr("Seleted features will be deleted. Continue?"), 
983                                                           QMessageBox::No | QMessageBox::Yes, QMessageBox::No);
984   // ToDo: definbe deleting method
985   if (aRes == QMessageBox::Yes) {
986     PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
987     aMgr->rootDocument()->startOperation();
988     foreach (ObjectPtr aObj, theList) {
989       ResultPartPtr aPart = boost::dynamic_pointer_cast<ModelAPI_ResultPart>(aObj);
990       if (aPart) {
991         DocumentPtr aDoc = aPart->document();
992         if (aDoc == aMgr->currentDocument()) {
993           aDoc->close();
994         }
995         //aMgr->rootDocument()->removeFeature(aPart->owner());
996       } else {
997         FeaturePtr aFeature = boost::dynamic_pointer_cast<ModelAPI_Feature>(aObj);
998         if (aFeature)
999           aObj->document()->removeFeature(aFeature);
1000       }
1001     }
1002     myDisplayer->updateViewer();
1003     aMgr->rootDocument()->finishOperation();
1004   }
1005 }
1006
1007 //**************************************************************
1008 void XGUI_Workshop::showObjects(const QList<ObjectPtr>& theList, bool isVisible)
1009 {
1010   foreach (ObjectPtr aObj, theList) {
1011     ResultPtr aRes = boost::dynamic_pointer_cast<ModelAPI_Result>(aObj);
1012     if (aRes) {
1013       if (isVisible) {
1014         myDisplayer->display(aRes, false);
1015       } else {
1016         myDisplayer->erase(aRes, false);
1017       }
1018     }
1019   }
1020   myDisplayer->updateViewer();
1021 }
1022
1023 //**************************************************************
1024 void XGUI_Workshop::updateCommandsOnViewSelection()
1025 {
1026   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
1027   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
1028   XGUI_Selection* aSelection = mySelector->selection();
1029   if (aSelection->getSelected().size() == 0)
1030     return;
1031
1032   QList<QAction*> aActions = getModuleCommands();
1033   foreach(QAction* aAction, aActions) {
1034     QString aId = aAction->data().toString();
1035     std::list<ModelAPI_Validator*> aValidators;
1036     aFactory->validators(aId.toStdString(), aValidators);
1037     std::list<ModelAPI_Validator*>::iterator aValidator = aValidators.begin();
1038     for(; aValidator != aValidators.end(); aValidator++) {
1039       if (*aValidator) {
1040         const ModuleBase_SelectionValidator* aSelValidator = 
1041           dynamic_cast<const ModuleBase_SelectionValidator*>(*aValidator);
1042         if (aSelValidator) {
1043           aAction->setEnabled(aSelValidator->isValid(aSelection));
1044         }
1045       }
1046     }
1047   }
1048 }
1049
1050
1051 //**************************************************************
1052 void XGUI_Workshop::registerValidators() const
1053 {
1054   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
1055   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
1056 }
1057
1058
1059 //**************************************************************
1060 void XGUI_Workshop::displayAllResults()
1061 {
1062   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
1063   DocumentPtr aRootDoc = aMgr->rootDocument();
1064   displayDocumentResults(aRootDoc);
1065   for (int i = 0; i < aRootDoc->size(ModelAPI_ResultPart::group()); i++) {
1066     ObjectPtr aObject = aRootDoc->object(ModelAPI_ResultPart::group(), i);
1067     ResultPartPtr aPart = boost::dynamic_pointer_cast<ModelAPI_ResultPart>(aObject);
1068     displayDocumentResults(aPart->partDoc());
1069   }
1070   myDisplayer->updateViewer();
1071 }
1072
1073 //**************************************************************
1074 void XGUI_Workshop::displayDocumentResults(DocumentPtr theDoc)
1075 {
1076   displayGroupResults(theDoc, ModelAPI_ResultConstruction::group());
1077   displayGroupResults(theDoc, ModelAPI_ResultBody::group());
1078 }
1079
1080 //**************************************************************
1081 void XGUI_Workshop::displayGroupResults(DocumentPtr theDoc, std::string theGroup)
1082 {
1083   for (int i = 0; i < theDoc->size(theGroup); i++)
1084     myDisplayer->display(theDoc->object(theGroup, i), false);
1085 }