]> SALOME platform Git repositories - modules/shaper.git/blob - src/XGUI/XGUI_Workshop.cpp
Salome HOME
Fix compilation problems
[modules/shaper.git] / src / XGUI / XGUI_Workshop.cpp
1 #include "XGUI_Module.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_ObjectsBrowser.h"
14 #include "XGUI_Displayer.h"
15 #include "XGUI_OperationMgr.h"
16 #include "XGUI_SalomeConnector.h"
17 #include "XGUI_SalomeViewer.h"
18 #include "XGUI_ActionsMgr.h"
19 #include "XGUI_ErrorDialog.h"
20 #include "XGUI_ViewerProxy.h"
21 #include "XGUI_PropertyPanel.h"
22 #include "XGUI_ContextMenuMgr.h"
23
24 #include <Model_Events.h>
25 #include <ModelAPI_PluginManager.h>
26 #include <ModelAPI_Feature.h>
27 #include <ModelAPI_Data.h>
28 #include <ModelAPI_AttributeDocRef.h>
29 #include <ModelAPI_Object.h>
30
31 #include <Events_Loop.h>
32 #include <Events_Error.h>
33 #include <ModuleBase_Operation.h>
34 #include <ModuleBase_Operation.h>
35 #include <ModuleBase_OperationDescription.h>
36 #include <Config_Common.h>
37 #include <Config_FeatureMessage.h>
38 #include <Config_PointerMessage.h>
39 #include <Config_ModuleReader.h>
40
41 #include <QApplication>
42 #include <QFileDialog>
43 #include <QMessageBox>
44 #include <QMdiSubWindow>
45 #include <QPushButton>
46 #include <QDockWidget>
47 #include <QLayout>
48 #include <QTimer>
49
50 #ifdef _DEBUG
51 #include <QDebug>
52 #endif
53
54 #ifdef WIN32
55 #include <windows.h>
56 #else
57 #include <dlfcn.h>
58 #endif
59
60
61 QMap<QString, QString> XGUI_Workshop::myIcons;
62
63 QString XGUI_Workshop::featureIcon(const std::string& theId)
64 {
65   QString aId(theId.c_str());
66   if (myIcons.contains(aId))
67     return myIcons[aId];
68   return QString();
69 }
70
71 XGUI_Workshop::XGUI_Workshop(XGUI_SalomeConnector* theConnector)
72   : QObject(),
73   myCurrentFile(QString()),
74   myPartSetModule(NULL),
75   mySalomeConnector(theConnector),
76   myPropertyPanel(0),
77   myObjectBrowser(0),
78   myDisplayer(0)
79 {
80   myMainWindow = mySalomeConnector? 0 : new XGUI_MainWindow();
81
82   myDisplayer = new XGUI_Displayer(this);
83
84   mySelector = new XGUI_SelectionMgr(this);
85
86   myOperationMgr = new XGUI_OperationMgr(this);
87   myActionsMgr = new XGUI_ActionsMgr(this);
88   myErrorDlg = new XGUI_ErrorDialog(myMainWindow);
89   myContextMenuMgr = new XGUI_ContextMenuMgr(this);
90   connect(myContextMenuMgr, SIGNAL(actionTriggered(const QString&, bool)), 
91           this, SLOT(onContextMenuCommand(const QString&, bool)));
92
93   myViewerProxy = new XGUI_ViewerProxy(this);
94
95   connect(myOperationMgr, SIGNAL(operationStarted()),  this, SLOT(onOperationStarted()));
96   connect(myOperationMgr, SIGNAL(operationResumed()),  this, SLOT(onOperationStarted()));
97   connect(myOperationMgr, SIGNAL(operationStopped(ModuleBase_Operation*)),
98           this, SLOT(onOperationStopped(ModuleBase_Operation*)));
99   connect(this, SIGNAL(errorOccurred(const QString&)), myErrorDlg, SLOT(addError(const QString&)));
100 }
101
102 //******************************************************
103 XGUI_Workshop::~XGUI_Workshop(void)
104 {
105 }
106
107 //******************************************************
108 void XGUI_Workshop::startApplication()
109 {
110   initMenu();
111   //Initialize event listening
112   Events_Loop* aLoop = Events_Loop::loop();
113   aLoop->registerListener(this, Events_Error::errorID()); //!< Listening application errors.
114   //TODO(sbh): Implement static method to extract event id [SEID]
115   Events_ID aFeatureId = aLoop->eventByName(EVENT_FEATURE_LOADED);
116   aLoop->registerListener(this, aFeatureId);
117   Events_ID aPartSetId = aLoop->eventByName("PartSetModuleEvent");
118   aLoop->registerListener(this, aPartSetId);
119   Events_ID aFeatureUpdatedId = aLoop->eventByName(EVENT_FEATURE_UPDATED);
120   aLoop->registerListener(this, aFeatureUpdatedId);
121   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_CREATED));
122   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_FEATURE_DELETED));
123
124   activateModule();
125   if (myMainWindow) {
126     myMainWindow->show();
127     updateCommandStatus();
128   }
129   onNew();
130 }
131
132 //******************************************************
133 void XGUI_Workshop::initMenu()
134 {
135   myContextMenuMgr->createActions();
136
137   if (isSalomeMode()) {
138     // Create only Undo, Redo commands
139     QAction* aAction = salomeConnector()->addEditCommand("UNDO_CMD", 
140                                       tr("Undo"), tr("Undo last command"),
141                                       QIcon(":pictures/undo.png"), 
142                                       QKeySequence::Undo, false);
143     connect(aAction, SIGNAL(triggered(bool)), this, SLOT(onUndo()));
144     aAction = salomeConnector()->addEditCommand("REDO_CMD", 
145                                       tr("Redo"), tr("Redo last command"),
146                                       QIcon(":pictures/redo.png"), 
147                                       QKeySequence::Redo, false);
148     connect(aAction, SIGNAL(triggered(bool)), this, SLOT(onRedo()));
149     salomeConnector()->addEditMenuSeparator();
150     return;
151   }
152   XGUI_Workbench* aPage = myMainWindow->menuObject()->generalPage();
153
154   // File commands group
155   XGUI_MenuGroupPanel* aGroup = aPage->addGroup("Default");
156
157   XGUI_Command* aCommand;
158
159   aCommand = aGroup->addFeature("SAVE_CMD", tr("Save..."), tr("Save the document"),
160                                 QIcon(":pictures/save.png"), QKeySequence::Save);
161   aCommand->connectTo(this, SLOT(onSave()));
162   //aCommand->disable();
163
164   aCommand = aGroup->addFeature("UNDO_CMD", tr("Undo"), tr("Undo last command"),
165                                 QIcon(":pictures/undo.png"), QKeySequence::Undo);
166   aCommand->connectTo(this, SLOT(onUndo()));
167
168   aCommand = aGroup->addFeature("REDO_CMD", tr("Redo"), tr("Redo last command"),
169                                 QIcon(":pictures/redo.png"), QKeySequence::Redo);
170   aCommand->connectTo(this, SLOT(onRedo()));
171
172   aCommand = aGroup->addFeature("REBUILD_CMD", tr("Rebuild"), tr("Rebuild data objects"),
173                                 QIcon(":pictures/rebuild.png"));
174
175   aCommand = aGroup->addFeature("SAVEAS_CMD", tr("Save as..."), tr("Save the document into a file"),
176                                 QIcon(":pictures/save.png"));
177   aCommand->connectTo(this, SLOT(onSaveAs()));
178   //aCommand->disable();
179
180   aCommand = aGroup->addFeature("OPEN_CMD", tr("Open..."), tr("Open a new document"),
181                                 QIcon(":pictures/open.png"), QKeySequence::Open);
182   aCommand->connectTo(this, SLOT(onOpen()));
183
184   //aCommand = aGroup->addFeature("NEW_CMD", tr("New"), tr("Create a new document"),
185   //                              QIcon(":pictures/new.png"), QKeySequence::New);
186   //aCommand->connectTo(this, SLOT(onNew()));
187
188   aCommand = aGroup->addFeature("EXIT_CMD", tr("Exit"), tr("Exit application"),
189                                 QIcon(":pictures/close.png"), QKeySequence::Close);
190   aCommand->connectTo(this, SLOT(onExit()));
191 }
192
193 //******************************************************
194 XGUI_Workbench* XGUI_Workshop::addWorkbench(const QString& theName)
195 {
196   XGUI_MainMenu* aMenuBar = myMainWindow->menuObject();
197   return aMenuBar->addWorkbench(theName);
198 }
199
200 //******************************************************
201 void XGUI_Workshop::processEvent(const Events_Message* theMessage)
202 {
203   //A message to start feature creation received.
204   static Events_ID aFeatureLoadedId = Events_Loop::loop()->eventByName(EVENT_FEATURE_LOADED);
205   if (theMessage->eventID() == aFeatureLoadedId) {
206     const Config_FeatureMessage* aFeatureMsg = dynamic_cast<const Config_FeatureMessage*>(theMessage);
207     addFeature(aFeatureMsg);
208     return;
209   }
210   // Process creation of Part
211   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_CREATED)) {
212     const Model_FeatureUpdatedMessage* aUpdMsg = dynamic_cast<const Model_FeatureUpdatedMessage*>(theMessage);
213     std::set<FeaturePtr> aFeatures = aUpdMsg->features();
214
215     std::set<FeaturePtr>::const_iterator aIt;
216     bool aHasPart = false;
217     for (aIt = aFeatures.begin(); aIt != aFeatures.end(); ++aIt) {
218       FeaturePtr aFeature = (*aIt);
219       if (aFeature->getKind() == "Part") {
220         aHasPart = true;
221         break;
222       }
223     }
224     if (aHasPart) {
225       //The created part will be created in Object Browser later and we have to activate it
226       // only when it is created everywere
227       QTimer::singleShot(50, this, SLOT(activateLastPart()));
228     }
229   }
230
231   // Process deletion of a part
232   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_FEATURE_DELETED)) {
233     PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
234     if (aMgr->currentDocument() == aMgr->rootDocument())
235       activatePart(FeaturePtr()); // Activate PartSet
236   }
237
238   //Update property panel on corresponding message. If there is no current operation (no
239   //property panel), or received message has different feature to the current - do nothing.
240   static Events_ID aFeatureUpdatedId = Events_Loop::loop()->eventByName(EVENT_FEATURE_UPDATED);
241   if (theMessage->eventID() == aFeatureUpdatedId && myOperationMgr->hasOperation())
242   {
243     const Model_FeatureUpdatedMessage* anUpdateMsg =
244         dynamic_cast<const Model_FeatureUpdatedMessage*>(theMessage);
245     std::set<FeaturePtr> aFeatures = anUpdateMsg->features();
246
247     FeaturePtr aCurrentFeature = myOperationMgr->currentOperation()->feature();
248     std::set<FeaturePtr>::const_iterator aIt;
249     for (aIt = aFeatures.begin(); aIt != aFeatures.end(); ++aIt) {
250       FeaturePtr aNewFeature = (*aIt);
251       if(aNewFeature == aCurrentFeature) {
252         myPropertyPanel->updateContentWidget(aCurrentFeature);
253         break;
254       } 
255     }
256   }
257   //An operation passed by message. Start it, process and commit.
258   const Config_PointerMessage* aPartSetMsg = dynamic_cast<const Config_PointerMessage*>(theMessage);
259   if (aPartSetMsg) {
260     ModuleBase_Operation* anOperation =
261         (ModuleBase_Operation*)(aPartSetMsg->pointer());
262
263     if (myOperationMgr->startOperation(anOperation)) {
264       myPropertyPanel->updateContentWidget(anOperation->feature());
265       if (anOperation->getDescription()->xmlRepresentation().isEmpty()) {
266         anOperation->commit();
267         updateCommandStatus();
268       }
269     }
270     return;
271   }
272   //Show error dialog if error message received.
273   const Events_Error* anAppError = dynamic_cast<const Events_Error*>(theMessage);
274   if (anAppError) {
275     emit errorOccurred(QString::fromLatin1(anAppError->description()));
276     myErrorDlg->show();
277     myErrorDlg->raise();
278     myErrorDlg->activateWindow();
279   }
280
281 }
282
283 //******************************************************
284 void XGUI_Workshop::onOperationStarted()
285 {
286   ModuleBase_Operation* aOperation = myOperationMgr->currentOperation();
287
288   if(!aOperation->getDescription()->xmlRepresentation().isEmpty()) { //!< No need for property panel
289     connectWithOperation(aOperation);
290
291     showPropertyPanel();
292
293     ModuleBase_WidgetFactory aFactory = ModuleBase_WidgetFactory(aOperation);
294     QWidget* aContent = myPropertyPanel->contentWidget();
295     qDeleteAll(aContent->children());
296     aFactory.createWidget(aContent);
297     myPropertyPanel->setModelWidgets(aFactory.getModelWidgets());
298     myPropertyPanel->setWindowTitle(aOperation->getDescription()->description());
299   }
300 }
301
302 //******************************************************
303 void XGUI_Workshop::onOperationStopped(ModuleBase_Operation* theOperation)
304 {
305   ModuleBase_Operation* aOperation = myOperationMgr->currentOperation();
306
307   //!< No need for property panel
308   updateCommandStatus();
309   hidePropertyPanel();
310   if(myOperationMgr->operationsCount() > 1) {
311     myActionsMgr->updateAction(theOperation->getDescription()->operationId());
312     return;
313   }
314   if(!aOperation->getDescription()->xmlRepresentation().isEmpty()) { 
315     myActionsMgr->restoreCommandState();
316   }
317 }
318
319 /*
320  *
321  */
322 void XGUI_Workshop::addFeature(const Config_FeatureMessage* theMessage)
323 {
324   if (!theMessage) {
325 #ifdef _DEBUG
326     qDebug() << "XGUI_Workshop::addFeature: NULL message.";
327 #endif
328     return;
329   }
330   // Remember features icons
331   myIcons[QString::fromStdString(theMessage->id())] = QString::fromStdString(theMessage->icon());
332
333   //Find or create Workbench
334   QString aWchName = QString::fromStdString(theMessage->workbenchId());
335   QString aNestedFeatures = QString::fromStdString(theMessage->nestedFeatures());
336   bool isUsePropPanel = theMessage->isUseInput();
337   QString aId = QString::fromStdString(theMessage->id());
338   if (isSalomeMode()) {
339     QAction* aAction = salomeConnector()->addFeature(aWchName,
340                               aId,
341                               QString::fromStdString(theMessage->text()),
342                               QString::fromStdString(theMessage->tooltip()),
343                               QIcon(theMessage->icon().c_str()),
344                               QKeySequence(), isUsePropPanel);
345     myActionsMgr->addCommand(aAction);
346     salomeConnector()->setNestedActions(aId, aNestedFeatures.split(" "));
347     myPartSetModule->featureCreated(aAction);
348   } else {
349
350     XGUI_MainMenu* aMenuBar = myMainWindow->menuObject();
351     XGUI_Workbench* aPage = aMenuBar->findWorkbench(aWchName);
352     if (!aPage) {
353       aPage = addWorkbench(aWchName);
354     }
355     //Find or create Group
356     QString aGroupName = QString::fromStdString(theMessage->groupId());
357     XGUI_MenuGroupPanel* aGroup = aPage->findGroup(aGroupName);
358     if (!aGroup) {
359       aGroup = aPage->addGroup(aGroupName);
360     }
361     //Create feature...
362     XGUI_Command* aCommand = aGroup->addFeature(aId,
363                                                 QString::fromStdString(theMessage->text()),
364                                                 QString::fromStdString(theMessage->tooltip()),
365                                                 QIcon(theMessage->icon().c_str()),
366                                                 QKeySequence(), isUsePropPanel);
367     aCommand->setUnblockableCommands(aNestedFeatures.split(" "));
368     myActionsMgr->addCommand(aCommand);
369     myPartSetModule->featureCreated(aCommand);
370   }
371 }
372
373 /*
374  * Makes a signal/slot connections between Property Panel
375  * and given operation. The given operation becomes a
376  * current operation and previous operation if exists
377  */
378 void XGUI_Workshop::connectWithOperation(ModuleBase_Operation* theOperation)
379 {
380   QAction* aCommand = 0;
381   if (isSalomeMode()) {
382     aCommand = salomeConnector()->command(theOperation->getDescription()->operationId());
383   } else {
384     XGUI_MainMenu* aMenu = myMainWindow->menuObject();
385     aCommand = aMenu->feature(theOperation->getDescription()->operationId());
386   }
387   //Abort operation on uncheck the command
388   connect(aCommand, SIGNAL(triggered(bool)), theOperation, SLOT(setRunning(bool)));
389 }
390
391 /*
392  * Saves document with given name.
393  */
394 void XGUI_Workshop::saveDocument(QString theName)
395 {
396   QApplication::restoreOverrideCursor();
397   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
398   DocumentPtr aDoc = aMgr->rootDocument();
399   aDoc->save(theName.toLatin1().constData());
400   QApplication::restoreOverrideCursor();
401 }
402
403 //******************************************************
404 void XGUI_Workshop::onExit()
405 {
406   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
407   DocumentPtr aDoc = aMgr->rootDocument();
408   if(aDoc->isModified()) {
409     int anAnswer = QMessageBox::question(
410         myMainWindow, tr("Save current file"),
411         tr("The document is modified, save before exit?"),
412         QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel);
413     if(anAnswer == QMessageBox::Save) {
414       onSave();
415     } else if (anAnswer == QMessageBox::Cancel) {
416       return;
417     }
418   }
419   qApp->exit();
420 }
421
422 //******************************************************
423 void XGUI_Workshop::onNew()
424 {
425   QApplication::setOverrideCursor(Qt::WaitCursor);
426   if (objectBrowser() == 0) {
427     createDockWidgets();
428     mySelector->connectViewers();
429   }
430   myViewerProxy->connectToViewer();
431   showObjectBrowser();
432   if (!isSalomeMode()) {
433     myMainWindow->showPythonConsole();
434     QMdiSubWindow* aWnd = myMainWindow->viewer()->createView();
435     aWnd->showMaximized();
436     updateCommandStatus();
437   }
438   QApplication::restoreOverrideCursor();
439 }
440
441 //******************************************************
442 void XGUI_Workshop::onOpen()
443 {
444   //save current file before close if modified
445   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
446   DocumentPtr aDoc = aMgr->rootDocument();
447   if(aDoc->isModified()) {
448     //TODO(sbh): re-launch the app?
449     int anAnswer = QMessageBox::question(
450         myMainWindow, tr("Save current file"),
451         tr("The document is modified, save before opening another?"),
452         QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel);
453     if(anAnswer == QMessageBox::Save) {
454       onSave();
455     } else if (anAnswer == QMessageBox::Cancel) {
456       return;
457     }
458     aDoc->close();
459     myCurrentFile = "";
460   }
461
462   //show file dialog, check if readable and open
463   myCurrentFile = QFileDialog::getExistingDirectory(mainWindow());
464   if(myCurrentFile.isEmpty())
465     return;
466   QFileInfo aFileInfo(myCurrentFile);
467   if(!aFileInfo.exists() || !aFileInfo.isReadable()) {
468     QMessageBox::critical(myMainWindow, tr("Warning"), tr("Unable to open the file."));
469     myCurrentFile = "";
470     return;
471   }
472   QApplication::setOverrideCursor(Qt::WaitCursor);
473   aDoc->load(myCurrentFile.toLatin1().constData());
474   QApplication::restoreOverrideCursor();
475   updateCommandStatus();
476 }
477
478 //******************************************************
479 void XGUI_Workshop::onSave()
480 {
481   if(myCurrentFile.isEmpty()) {
482     onSaveAs();
483     return;
484   }
485   saveDocument(myCurrentFile);
486   updateCommandStatus();
487 }
488
489 //******************************************************
490 void XGUI_Workshop::onSaveAs()
491 {
492   QString aTemp = myCurrentFile;
493   myCurrentFile = QFileDialog::getSaveFileName(mainWindow());
494   if(myCurrentFile.isEmpty()) {
495     myCurrentFile = aTemp;
496     return;
497   }
498   QFileInfo aFileInfo(myCurrentFile);
499   if(aFileInfo.exists() && !aFileInfo.isWritable()) {
500     QMessageBox::critical(myMainWindow, tr("Warning"), tr("Unable to save the file."));
501     return;
502   }
503   onSave();
504 }
505
506 //******************************************************
507 void XGUI_Workshop::onUndo()
508 {
509   objectBrowser()->treeView()->setCurrentIndex(QModelIndex());
510   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
511   DocumentPtr aDoc = aMgr->rootDocument();
512   if (aDoc->isOperation())
513     operationMgr()->abortOperation();
514   aDoc->undo();
515   updateCommandStatus();
516 }
517
518 //******************************************************
519 void XGUI_Workshop::onRedo()
520 {
521   objectBrowser()->treeView()->setCurrentIndex(QModelIndex());
522   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
523   DocumentPtr aDoc = aMgr->rootDocument();
524   aDoc->redo();
525   updateCommandStatus();
526 }
527
528 //******************************************************
529 XGUI_Module* XGUI_Workshop::loadModule(const QString& theModule)
530 {
531   QString libName =
532       QString::fromStdString(library(theModule.toStdString()));
533   if (libName.isEmpty()) {
534     qWarning(
535     qPrintable( tr( "Information about module \"%1\" doesn't exist." ).arg( theModule ) ));
536     return 0;
537   }
538
539   QString err;
540   CREATE_FUNC crtInst = 0;
541
542 #ifdef WIN32
543   HINSTANCE modLib = ::LoadLibrary((LPTSTR) qPrintable(libName));
544   if (!modLib) {
545     LPVOID lpMsgBuf;
546     ::FormatMessage(
547         FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
548         0, ::GetLastError(), 0, (LPTSTR) & lpMsgBuf, 0, 0);
549     QString aMsg((char*) &lpMsgBuf);
550     err = QString("Failed to load  %1. %2").arg(libName).arg(aMsg);
551     ::LocalFree(lpMsgBuf);
552   } else {
553     crtInst = (CREATE_FUNC) ::GetProcAddress(modLib, CREATE_MODULE);
554     if (!crtInst) {
555       LPVOID lpMsgBuf;
556       ::FormatMessage(
557           FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
558               | FORMAT_MESSAGE_IGNORE_INSERTS,
559           0, ::GetLastError(), 0, (LPTSTR) & lpMsgBuf, 0, 0);
560       QString aMsg((char*) &lpMsgBuf);
561       err = QString("Failed to find  %1 function. %2").arg( CREATE_MODULE).arg(aMsg);
562       ::LocalFree(lpMsgBuf);
563     }
564   }
565 #else
566   void* modLib = dlopen( libName.toLatin1(), RTLD_LAZY | RTLD_GLOBAL );
567   if ( !modLib ) {
568     err = QString( "Can not load library %1. %2" ).arg( libName ).arg( dlerror() );
569   } else {
570     crtInst = (CREATE_FUNC)dlsym( modLib, CREATE_MODULE );
571     if ( !crtInst ) {
572       err = QString( "Failed to find function %1. %2" ).arg( CREATE_MODULE ).arg( dlerror() );
573     }
574   }
575 #endif
576
577   XGUI_Module* aModule = crtInst ? crtInst(this) : 0;
578
579   if (!err.isEmpty()) {
580     if (mainWindow()) {
581       QMessageBox::warning(mainWindow(), tr("Error"), err);
582     } else {
583       qWarning( qPrintable( err ));
584     }
585   }
586   return aModule;
587 }
588
589 //******************************************************
590 bool XGUI_Workshop::activateModule()
591 {
592   Config_ModuleReader aModuleReader;
593   QString moduleName = QString::fromStdString(aModuleReader.getModuleName());
594   myPartSetModule = loadModule(moduleName);
595   if (!myPartSetModule)
596     return false;
597   myPartSetModule->createFeatures();
598   return true;
599 }
600
601 //******************************************************
602 void XGUI_Workshop::updateCommandStatus()
603 {
604   QList<QAction*> aCommands;
605   if (isSalomeMode()) { // update commands in SALOME mode
606     aCommands = salomeConnector()->commandList();
607   } else {
608     XGUI_MainMenu* aMenuBar = myMainWindow->menuObject();
609     foreach (XGUI_Command* aCmd, aMenuBar->features())
610       aCommands.append(aCmd);
611   }
612   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
613   if (aMgr->hasRootDocument()) {
614     QAction* aUndoCmd;
615     QAction* aRedoCmd;
616     foreach(QAction* aCmd, aCommands) {
617       QString aId = aCmd->data().toString();
618       if (aId == "UNDO_CMD")
619         aUndoCmd = aCmd;
620       else if (aId == "REDO_CMD")
621         aRedoCmd = aCmd;
622       else // Enable all commands
623         aCmd->setEnabled(true);
624     }
625     DocumentPtr aDoc = aMgr->rootDocument();
626     aUndoCmd->setEnabled(aDoc->canUndo());
627     aRedoCmd->setEnabled(aDoc->canRedo());
628   } else {
629     foreach(QAction* aCmd, aCommands) {
630       QString aId = aCmd->data().toString();
631       if (aId == "NEW_CMD")
632         aCmd->setEnabled(true);
633       else if (aId == "EXIT_CMD")
634         aCmd->setEnabled(true);
635       else 
636         aCmd->setEnabled(false);
637     }
638   }
639 }
640
641 //******************************************************
642 QDockWidget* XGUI_Workshop::createObjectBrowser(QWidget* theParent)
643 {
644   QDockWidget* aObjDock = new QDockWidget(theParent);
645   aObjDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
646   aObjDock->setWindowTitle(tr("Object browser"));
647   myObjectBrowser = new XGUI_ObjectsBrowser(aObjDock);
648   connect(myObjectBrowser, SIGNAL(activePartChanged(FeaturePtr)), this, SLOT(changeCurrentDocument(FeaturePtr)));
649   aObjDock->setWidget(myObjectBrowser);
650
651   myContextMenuMgr->connectObjectBrowser();
652   return aObjDock;
653 }
654
655 //******************************************************
656 /*
657  * Creates dock widgets, places them in corresponding area
658  * and tabifies if necessary.
659  */
660 void XGUI_Workshop::createDockWidgets()
661 {
662   QMainWindow* aDesktop = isSalomeMode()? salomeConnector()->desktop() :
663                                           myMainWindow;
664   QDockWidget* aObjDock = createObjectBrowser(aDesktop);
665   aDesktop->addDockWidget(Qt::LeftDockWidgetArea, aObjDock);
666   myPropertyPanel = new XGUI_PropertyPanel(aDesktop);
667   aDesktop->addDockWidget(Qt::LeftDockWidgetArea, myPropertyPanel);
668   hidePropertyPanel(); //<! Invisible by default
669   hideObjectBrowser();
670   aDesktop->tabifyDockWidget(aObjDock, myPropertyPanel);
671
672   QPushButton* aOkBtn = myPropertyPanel->findChild<QPushButton*>(XGUI::PROP_PANEL_OK);
673   connect(aOkBtn, SIGNAL(clicked()), myOperationMgr, SLOT(onCommitOperation()));
674   QPushButton* aCancelBtn = myPropertyPanel->findChild<QPushButton*>(XGUI::PROP_PANEL_CANCEL);
675   connect(aCancelBtn, SIGNAL(clicked()), myOperationMgr, SLOT(onAbortOperation()));
676 }
677
678 //******************************************************
679 void XGUI_Workshop::showPropertyPanel()
680 {
681   QAction* aViewAct = myPropertyPanel->toggleViewAction();
682   //<! Restore ability to close panel from the window's menu
683   aViewAct->setEnabled(true);
684   myPropertyPanel->show();
685   myPropertyPanel->raise();
686 }
687
688 //******************************************************
689 void XGUI_Workshop::hidePropertyPanel()
690 {
691   QAction* aViewAct = myPropertyPanel->toggleViewAction();
692   //<! Do not allow to show empty property panel
693   aViewAct->setEnabled(false);
694   myPropertyPanel->hide();
695 }
696
697 //******************************************************
698 void XGUI_Workshop::showObjectBrowser()
699 {
700   myObjectBrowser->parentWidget()->show();
701 }
702
703 //******************************************************
704 void XGUI_Workshop::hideObjectBrowser()
705 {
706   myObjectBrowser->parentWidget()->hide();
707 }
708
709 //******************************************************
710 void XGUI_Workshop::onFeatureTriggered()
711 {
712   QAction* aCmd = dynamic_cast<QAction*>(sender());
713   if (aCmd) {
714     QString aId = salomeConnector()->commandId(aCmd);
715     if (!aId.isNull())
716       myPartSetModule->launchOperation(aId);
717   }
718 }
719
720 //******************************************************
721 void XGUI_Workshop::changeCurrentDocument(FeaturePtr thePart)
722 {
723   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
724   if (thePart) {
725     boost::shared_ptr<ModelAPI_AttributeDocRef> aDocRef = thePart->data()->docRef("PartDocument");
726     if (aDocRef)
727       aMgr->setCurrentDocument(aDocRef->value());
728   } else {
729     aMgr->setCurrentDocument(aMgr->rootDocument());
730   }
731 }
732
733 //******************************************************
734 void XGUI_Workshop::salomeViewerSelectionChanged()
735 {
736   emit salomeViewerSelection();
737 }
738
739
740 //**************************************************************
741 XGUI_SalomeViewer* XGUI_Workshop::salomeViewer() const 
742
743   return mySalomeConnector->viewer(); 
744 }
745
746 //**************************************************************
747 void XGUI_Workshop::onContextMenuCommand(const QString& theId, bool isChecked)
748 {
749   QFeatureList aFeatures = mySelector->selectedFeatures();
750   if ((theId == "ACTIVATE_PART_CMD") && (aFeatures.size() > 0))
751     activatePart(aFeatures.first());
752   else if (theId == "DEACTIVATE_PART_CMD") 
753     activatePart(FeaturePtr());
754   else if (theId == "DELETE_CMD")
755     deleteFeatures(aFeatures);
756 }
757
758 //**************************************************************
759 void XGUI_Workshop::activatePart(FeaturePtr theFeature)
760 {
761   changeCurrentDocument(theFeature);
762   myObjectBrowser->activatePart(theFeature);
763 }
764
765 //**************************************************************
766 void XGUI_Workshop::activateLastPart()
767 {
768   PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
769   DocumentPtr aDoc = aMgr->rootDocument();
770   FeaturePtr aLastPart = aDoc->feature(PARTS_GROUP, aDoc->size(PARTS_GROUP) - 1, true);
771   activatePart(aLastPart);
772 }
773
774 //**************************************************************
775 void XGUI_Workshop::deleteFeatures(QFeatureList theList)
776 {
777   QMainWindow* aDesktop = isSalomeMode()? salomeConnector()->desktop() : myMainWindow;
778   QMessageBox::StandardButton aRes = QMessageBox::warning(aDesktop, tr("Delete features"), 
779                                                           tr("Seleted features will be deleted. Continue?"), 
780                                                           QMessageBox::No | QMessageBox::Yes, QMessageBox::No);
781   if (aRes == QMessageBox::Yes) {
782     PluginManagerPtr aMgr = ModelAPI_PluginManager::get();
783     aMgr->rootDocument()->startOperation();
784     foreach (FeaturePtr aFeature, theList) {
785       if (aFeature->getKind() == "Part") {
786         DocumentPtr aDoc = aFeature->data()->docRef("PartDocument")->value();
787         if (aDoc == aMgr->currentDocument()) {
788           aDoc->close();
789         }
790       } //else
791         //aDoc = aFeature->document();
792       aMgr->rootDocument()->removeFeature(aFeature);
793     }
794     aMgr->rootDocument()->finishOperation();
795   }
796 }