Salome HOME
GUI launcher: systematize run procedure
[modules/gui.git] / src / SalomeApp / SalomeApp_Application.cxx
1 // Copyright (C) 2007-2019  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File:      SalomeApp_Application.cxx
24 // Created:   10/22/2004 3:23:45 PM
25 // Author:    Sergey LITONIN
26
27 #ifdef WIN32
28   // E.A. : On windows with python 2.6, there is a conflict
29   // E.A. : between pymath.h and Standard_math.h which define
30   // E.A. : some same symbols : acosh, asinh, ...
31   #include <Standard_math.hxx>
32   #ifndef DISABLE_PYCONSOLE
33     #include <pymath.h>
34   #endif
35 #endif
36
37 #ifndef DISABLE_PYCONSOLE
38   #include "SalomeApp_PyInterp.h"
39   #include "SalomeApp_NoteBook.h"
40   #include "LightApp_PyEditor.h"
41   #include "PyConsole_Console.h"
42 #endif
43 #include "SalomeApp_Application.h"
44 #include "SalomeApp_Study.h"
45 #include "SalomeApp_DataModel.h"
46 #include "SalomeApp_DataObject.h"
47 #include "SalomeApp_VisualState.h"
48 #include "SalomeApp_StudyPropertiesDlg.h"
49 #include "SalomeApp_LoadStudiesDlg.h"
50 #include "SalomeApp_ExitDlg.h"
51
52 #include <LightApp_Application.h>
53 #include <LightApp_FileValidator.h>
54 #include <LightApp_Module.h>
55 #include <LightApp_Preferences.h>
56 #include <LightApp_SelectionMgr.h>
57 #include <LightApp_NameDlg.h>
58 #include <LightApp_DataOwner.h>
59
60 #include <CAM_Module.h>
61
62 #include <SUIT_Tools.h>
63 #include <SUIT_Session.h>
64 #include <SUIT_Desktop.h>
65 #include <SUIT_DataBrowser.h>
66 #include <SUIT_FileDlg.h>
67 #include <SUIT_MessageBox.h>
68 #include <SUIT_ResourceMgr.h>
69 #include <SUIT_TreeModel.h>
70 #include <SUIT_ViewWindow.h>
71 #include <SUIT_ViewManager.h>
72 #include <SUIT_ViewModel.h>
73 #include <SUIT_OverrideCursor.h>
74
75 #include <QtxTreeView.h>
76
77 #include <SALOME_EventFilter.h>
78
79 // temporary commented
80 //#include <OB_ListItem.h>
81
82
83 #include <Utils_ORB_INIT.hxx>
84 #include <Utils_SINGLETON.hxx>
85 #include <SALOME_LifeCycleCORBA.hxx>
86
87 #include <QApplication>
88 #include <QWidget>
89 #include <QAction>
90 #include <QRegExp>
91 #include <QCheckBox>
92 #include <QPushButton>
93 #include <QLabel>
94 #include <QListWidget>
95 #include <QGridLayout>
96 #include <QMenu>
97 #include <QtDebug>
98
99 #include <SALOMEDSClient_ClientFactory.hxx>
100 #include <Basics_Utils.hxx>
101
102 #include <SALOME_ListIO.hxx>
103 #include <SALOME_Prs.h>
104
105
106 #include <ToolsGUI_CatalogGeneratorDlg.h>
107 #include <ToolsGUI_RegWidget.h>
108
109 #include <vector>
110
111 #include <SALOMEDS_Tool.hxx>
112
113 /*!Internal class that updates object browser item properties */
114 // temporary commented
115 /*class SalomeApp_Updater : public OB_Updater
116 {
117 public:
118   SalomeApp_Updater() : OB_Updater(){};
119   virtual ~SalomeApp_Updater(){};
120   virtual void update( SUIT_DataObject* theObj, OB_ListItem* theItem );
121 };
122
123 void SalomeApp_Updater::update( SUIT_DataObject* theObj, OB_ListItem* theItem )
124 {
125   if( !theObj || !theItem )
126     return;
127
128   SalomeApp_DataObject* SAObj = dynamic_cast<SalomeApp_DataObject*>( theObj );
129   if( !SAObj )
130     return;
131
132   _PTR(SObject) SObj = SAObj->object();
133   if( !SObj )
134     return;
135   _PTR( GenericAttribute ) anAttr;
136
137   // Selectable
138   if ( SObj->FindAttribute( anAttr, "AttributeSelectable" ) )
139   {
140     _PTR(AttributeSelectable) aAttrSel = anAttr;
141     theItem->setSelectable( aAttrSel->IsSelectable() );
142   }
143   // Expandable
144   if ( SObj->FindAttribute(anAttr, "AttributeExpandable") )
145   {
146     _PTR(AttributeExpandable) aAttrExpand = anAttr;
147     theItem->setExpandable( aAttrExpand->IsExpandable() );
148   }
149   // Opened
150   //this attribute is not supported in the version of SALOME 3.x
151   //if ( SObj->FindAttribute(anAttr, "AttributeOpened") )
152   //{
153   //  _PTR(AttributeOpened) aAttrOpen = anAttr;
154   //  theItem->setOpen( aAttrOpen->IsOpened() );
155   //}
156 }*/
157
158 namespace
159 {
160   /*!
161     \brief Flag locker.
162   */
163   class MessageLocker
164   {
165   public:
166     //! Constructor. Sets passed boolean flag to \c true.
167     MessageLocker( bool& Lock ) : myPrevState( Lock ), myLock( Lock ) { myLock = true; }
168     //! Destructor. Clear external boolean flag passed as parameter to the constructor to \c false.
169     ~MessageLocker() { myLock = myPrevState; }
170   private:
171     bool  myPrevState;
172     bool& myLock; //! External 'Lock state' boolean flag
173   };
174 }
175
176 /*!Create new instance of SalomeApp_Application.*/
177 extern "C" SALOMEAPP_EXPORT SUIT_Application* createApplication()
178 {
179   return new SalomeApp_Application();
180 }
181
182 /*!Constructor.*/
183 SalomeApp_Application::SalomeApp_Application()
184   : LightApp_Application(),
185     myIsCloseFromExit( false ),
186     myToIgnoreMessages( false )
187 {
188 }
189
190 /*!Destructor.
191  *\li Destroy event filter.
192  */
193 SalomeApp_Application::~SalomeApp_Application()
194 {
195   // Do not destroy. It's a singleton !
196   //SALOME_EventFilter::Destroy();
197 }
198
199 QStringList __getArgsList(QString argsString)
200 {
201   // Special process if some items of 'args:' list are themselves lists
202   // Note that an item can be a list, but not a list of lists...
203   // So we can have something like this:
204   // myscript.py args:['file1','file2'],val1,"done",[1,2,3],[True,False],"ok"
205   // With such a call, argsString variable contains the string representing "[file1,file2]", "val1", "done", "[1,2,3]", "[True,False]", "ok"
206   // We have to split argsString to obtain: [[file1,file2],val1,done,[1,2,3],[True,False],ok]
207   argsString.replace("\\\"", "'"); // replace escaped double quotes by simple quotes
208   bool containsList = (QRegExp("(\\[[^\\]]*\\])").indexIn(argsString) >= 0);
209   if (containsList) {
210     QStringList sl = argsString.split("\"", QString::SkipEmptyParts);
211     sl.removeAll(", ");
212     return sl;
213   }
214   else
215     return argsString.split(",", QString::SkipEmptyParts);
216 }
217
218 /*!Start application.*/
219 void SalomeApp_Application::start()
220 {
221   // process the command line options before start: to createActions in accordance to the options
222   static bool isFirst = true;
223   if ( isFirst ) {
224     isFirst = false;
225
226     QString hdffile;
227     QStringList pyfiles;
228
229     QStringList args = QApplication::arguments();
230     for (int i = 1; i < args.count(); i++) {
231       QRegExp rxs ("--study-hdf=(.+)");
232       if ( rxs.indexIn( args[i] ) >= 0 && rxs.capturedTexts().count() > 1 ) {
233         QString file = rxs.capturedTexts()[1];
234         QFileInfo fi ( file );
235         QString extension = fi.suffix().toLower();
236         if ( extension == "hdf" && fi.exists() )
237           hdffile = fi.absoluteFilePath();
238       }
239       else {
240         QRegExp rxp ("--pyscript=\\[(.+)\\]");
241         if ( rxp.indexIn( args[i] ) >= 0 && rxp.capturedTexts().count() > 1 ) {
242           // pyscript
243           QStringList dictList = rxp.capturedTexts()[1].split("},", QString::SkipEmptyParts);
244           for (int k = 0; k < dictList.count(); ++k) {
245             QRegExp rxd ("[\\s]*\\{?([^\\{\\}]+)\\}?[\\s]*");
246             if ( rxd.indexIn( dictList[k] ) >= 0 && rxd.capturedTexts().count() > 1 ) {
247               for (int m = 1; m < rxd.capturedTexts().count(); ++m) {
248                 pyfiles += rxd.capturedTexts()[m];
249               }
250             }
251           }
252         }
253       }
254     }
255     // Here pyfiles elements are: "script_name": [list_of_"arg"s]
256     // For example: "/absolute/path/to/my_script.py": ["1", "2"]
257
258     LightApp_Application::start();
259     SALOME_EventFilter::Init();
260
261     setProperty("open_study_from_command_line", true);
262     if ( !hdffile.isEmpty() ) // open hdf file given as parameter
263       onOpenDoc( hdffile );
264     setProperty("open_study_from_command_line", QVariant());
265
266 #ifndef DISABLE_PYCONSOLE
267     // import/execute python scripts
268     if ( pyfiles.count() > 0 && activeStudy() ) {
269       SalomeApp_Study* appStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
270       PyConsole_Console* pyConsole = pythonConsole();
271       if ( appStudy && pyConsole ) {
272         if ( !getStudy()->GetProperties()->IsLocked() ) {
273           // pyfiles[j] is a dictionary: {"/absolute/path/to/script.py": [script_args]}
274           // Path is absolute, script has .py extension
275           for (int j = 0; j < pyfiles.count(); j++ ) {
276             // Extract scripts and their arguments, if any
277             QRegExp rxp ("\"(.+)\":[\\s]*\\[(.*)\\]");
278             if ( rxp.indexIn( pyfiles[j] ) >= 0 && rxp.capturedTexts().count() == 3 ) {
279               QString script = rxp.capturedTexts()[1];
280               QStringList args;
281               QStringList argList = __getArgsList(rxp.capturedTexts()[2]);
282               for (int k = 0; k < argList.count(); k++ ) {
283                 QString arg = argList[k].trimmed();
284                 arg.remove( QRegExp("^[\"]") );
285                 arg.remove( QRegExp("[\"]$") );
286                 args << QString("\"%1\"").arg(arg);
287               }
288               if (args.count() == 1)
289                 args << "";
290
291               script.remove( QRegExp("^python.*[\\s]+") );
292               QString command = QString( "exec(open(\"%1\", \"rb\").read(), args=(%2))" ).arg(script).arg(args.join(","));
293               pyConsole->exec(command);
294             }
295           } // end for loop on pyfiles QStringList
296         }
297       }
298     }
299 #endif
300   } else {
301     LightApp_Application::start();
302     SALOME_EventFilter::Init();
303   }
304 }
305
306 /*!Create actions:*/
307 void SalomeApp_Application::createActions()
308 {
309   LightApp_Application::createActions();
310
311   SUIT_Desktop* desk = desktop();
312
313   //! Save GUI state
314   // "Save GUI State" command is moved to VISU module
315   //  createAction( SaveGUIStateId, tr( "TOT_DESK_FILE_SAVE_GUI_STATE" ), QIcon(),
316   //            tr( "MEN_DESK_FILE_SAVE_GUI_STATE" ), tr( "PRP_DESK_FILE_SAVE_GUI_STATE" ),
317   //            0, desk, false, this, SLOT( onSaveGUIState() ) );
318
319   //! Dump study
320   createAction( DumpStudyId, tr( "TOT_DESK_FILE_DUMP_STUDY" ), QIcon(),
321                 tr( "MEN_DESK_FILE_DUMP_STUDY" ), tr( "PRP_DESK_FILE_DUMP_STUDY" ),
322                 Qt::CTRL+Qt::Key_D, desk, false, this, SLOT( onDumpStudy() ) );
323
324   //! Load script
325   createAction( LoadScriptId, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), QIcon(),
326                 tr( "MEN_DESK_FILE_LOAD_SCRIPT" ), tr( "PRP_DESK_FILE_LOAD_SCRIPT" ),
327                 Qt::CTRL+Qt::Key_T, desk, false, this, SLOT( onLoadScript() ) );
328
329   //! Properties
330   createAction( PropertiesId, tr( "TOT_DESK_PROPERTIES" ), QIcon(),
331                 tr( "MEN_DESK_PROPERTIES" ), tr( "PRP_DESK_PROPERTIES" ),
332                 0, desk, false, this, SLOT( onProperties() ) );
333
334   //! Catalog Generator
335   createAction( CatalogGenId, tr( "TOT_DESK_CATALOG_GENERATOR" ),  QIcon(),
336                 tr( "MEN_DESK_CATALOG_GENERATOR" ), tr( "PRP_DESK_CATALOG_GENERATOR" ),
337                 Qt::ALT+Qt::SHIFT+Qt::Key_G, desk, false, this, SLOT( onCatalogGen() ) );
338
339   //! Registry Display
340   createAction( RegDisplayId, tr( "TOT_DESK_REGISTRY_DISPLAY" ),  QIcon(),
341                 tr( "MEN_DESK_REGISTRY_DISPLAY" ), tr( "PRP_DESK_REGISTRY_DISPLAY" ),
342                 /*Qt::SHIFT+Qt::Key_D*/0, desk, false, this, SLOT( onRegDisplay() ) );
343
344   createAction( ConnectId, tr( "TOT_DESK_CONNECT_STUDY" ), QIcon(),
345                 tr( "MEN_DESK_CONNECT" ), tr( "PRP_DESK_CONNECT" ),
346                 Qt::CTRL+Qt::Key_L, desk, false, this, SLOT( onLoadDoc() ) );
347   //no need at this action for mono-study application because study is always exists
348   action( ConnectId )->setVisible( false );
349
350   createAction( DisconnectId, tr( "TOT_DESK_DISCONNECT_STUDY" ), QIcon(),
351                 tr( "MEN_DESK_DISCONNECT" ), tr( "PRP_DESK_DISCONNECT" ),
352                 Qt::CTRL+Qt::Key_U, desk, false, this, SLOT( onUnloadDoc() ) );
353   //no need at this action for mono-study application because study is always exists
354   action( DisconnectId )->setVisible( false );
355
356
357   int fileMenu = createMenu( tr( "MEN_DESK_FILE" ), -1 );
358
359   // "Save GUI State" command is renamed to "Save VISU State" and
360   // creation of menu item is moved to VISU
361   //  createMenu( SaveGUIStateId, fileMenu, 10, -1 );
362
363   createMenu( ConnectId,    fileMenu, 5 );
364   createMenu( DisconnectId, fileMenu, 5 );
365   createMenu( separator(),  fileMenu, -1, 5 );
366
367   createMenu( DumpStudyId, fileMenu, 10, -1 );
368   createMenu( LoadScriptId, fileMenu, 10, -1 );
369   createMenu( separator(), fileMenu, -1, 10, -1 );
370   createMenu( PropertiesId, fileMenu, 10, -1 );
371   createMenu( separator(), fileMenu, -1, 10, -1 );
372
373   int toolsMenu = createMenu( tr( "MEN_DESK_TOOLS" ), -1, MenuToolsId, 50 );
374   createMenu( CatalogGenId, toolsMenu, 10, -1 );
375   createMenu( RegDisplayId, toolsMenu, 10, -1 );
376   createMenu( separator(), toolsMenu, -1, 15, -1 );
377
378   createExtraActions();
379
380 #ifndef DISABLE_PYCONSOLE
381 #ifndef DISABLE_SALOMEOBJECT
382   // import Python module that manages SALOME plugins
383   {
384     PyLockWrapper lck; // acquire GIL
385     PyObjWrapper pluginsmanager = PyImport_ImportModule((char*)"salome_pluginsmanager");
386     PyObjWrapper res = PyObject_CallMethod( pluginsmanager, (char*)"initialize", (char*)"isss",0,"salome",tr("MEN_DESK_TOOLS").toUtf8().data(),tr("MEN_DESK_PLUGINS").toUtf8().data());
387     if ( !res )
388       PyErr_Print();
389   }
390   // end of SALOME plugins loading
391 #endif
392 #endif
393
394 }
395
396 /*!
397   \brief Close application.
398 */
399 void SalomeApp_Application::onExit()
400 {
401   //MessageLocker ml( myToIgnoreMessages );
402
403   bool killServers = false;
404   bool result = true;
405
406   if ( exitConfirmation() ) {
407     SalomeApp_ExitDlg dlg( desktop() );
408     result = dlg.exec() == QDialog::Accepted;
409     killServers = dlg.isServersShutdown();
410   }
411
412   if ( result ) {
413     if ( !killServers ) myIsCloseFromExit = true;
414     SUIT_Session::session()->closeSession( SUIT_Session::ASK, killServers );
415     if ( SUIT_Session::session()->applications().count() > 0 ) myIsCloseFromExit = false;
416   }
417 }
418
419 /*!SLOT. Create a document.*/
420 void SalomeApp_Application::onNewDoc()
421 {
422   MessageLocker ml( myToIgnoreMessages );
423
424   LightApp_Application::onNewDoc();
425 }
426
427 /*!SLOT. Load document.*/
428 void SalomeApp_Application::onLoadDoc()
429 {
430   MessageLocker ml( myToIgnoreMessages );
431
432   QString studyName;
433
434   // rnv: According to the single-study approach on the server side
435   //      can be only one study. So if it is exists connect to them,
436   //      overwise show warning message: "No active study on the server"
437
438   /*
439   SUIT_Session* aSession = SUIT_Session::session();
440   QList<SUIT_Application*> aAppList = aSession->applications();
441
442   QStringList unloadedStudies;
443
444   for ( unsigned int ind = 0; ind < List.size(); ind++ ) {
445      studyName = List[ind].c_str();
446      // Add to list only unloaded studies
447      bool isAlreadyOpen = false;
448      QListIterator<SUIT_Application*> it( aAppList );
449      while ( it.hasNext() && !isAlreadyOpen ) {
450        SUIT_Application* aApp = it.next();
451        if( !aApp || !aApp->activeStudy() )
452          continue;
453        if ( aApp->activeStudy()->studyName() == studyName )
454          isAlreadyOpen = true;
455      }
456
457      if ( !isAlreadyOpen )
458        unloadedStudies << studyName;
459   }
460   studyName = SalomeApp_LoadStudiesDlg::selectStudy( desktop(), unloadedStudies );
461   if ( studyName.isEmpty() )
462     return;
463   */
464
465   if(!activeStudy()) {
466     SUIT_MessageBox::warning( desktop(),
467                               QObject::tr("WRN_WARNING"),
468                               QObject::tr("WRN_NO_STUDY_ON SERV") );
469     return;
470   }
471
472   studyName = activeStudy()->studyName();
473
474 #ifndef WIN32
475   // this code replaces marker of windows drive and path become invalid therefore
476   // defines placed there
477   studyName.replace( QRegExp(":"), "/" );
478 #endif
479
480   if ( onLoadDoc( studyName ) ) {
481     updateWindows();
482     updateViewManagers();
483     updateObjectBrowser( true );
484   }
485 }
486
487 /*!SLOT. Unload document.*/
488 void SalomeApp_Application::onUnloadDoc( bool ask )
489 {
490   if ( ask ) {
491     activeStudy()->abortAllOperations();
492     if ( activeStudy()->isModified() ) {
493       QString docName = activeStudy()->studyName().trimmed();
494       int answer = SUIT_MessageBox::question( desktop(), tr( "DISCONNECT_CAPTION" ),
495                                             tr( "DISCONNECT_DESCRIPTION" ),
496                                             tr( "DISCONNECT_SAVE" ),
497                                             tr( "DISCONNECT_WO_SAVE" ),
498                                             tr( "APPCLOSE_CANCEL" ), 0 );
499       if ( answer == 0 ) { // save before unload
500         if ( activeStudy()->isSaved() )
501           onSaveDoc();
502         else if ( !onSaveAsDoc() )
503           return;
504       }
505       else if ( answer == 2 ) // Cancel
506         return;
507     }
508   }
509   closeActiveDoc( false );
510 }
511
512 /*!SLOT. Create new study and load script*/
513 void SalomeApp_Application::onNewWithScript()
514 {
515   QStringList filtersList;
516   filtersList.append(tr("PYTHON_FILES_FILTER"));
517   filtersList.append(tr("ALL_FILES_FILTER"));
518
519   QString anInitialPath = "";
520   if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
521     anInitialPath = QDir::currentPath();
522
523   QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true );
524
525   if ( !aFile.isEmpty() )
526   {
527     onNewDoc();
528
529     QString command = QString("exec(open(\"%1\", \"rb\").read())").arg(aFile);
530
531 #ifndef DISABLE_PYCONSOLE
532     PyConsole_Console* pyConsole = pythonConsole();
533
534     if ( pyConsole )
535       pyConsole->exec( command );
536 #endif
537   }
538 }
539
540
541 /*!SLOT. Load document with \a aName.*/
542 bool SalomeApp_Application::onLoadDoc( const QString& aName )
543 {
544   if ( !LightApp_Application::closeDoc() )
545     return false;
546
547   bool res = true;
548   if ( !activeStudy() ) {
549     // if no study - load in current desktop
550     res = useStudy( aName );
551   }
552   else {
553     // if study exists - load in new desktop. Check: is the same file is loaded?
554     SUIT_Session* aSession = SUIT_Session::session();
555     QList<SUIT_Application*> aAppList = aSession->applications();
556     bool isAlreadyOpen = false;
557     SalomeApp_Application* aApp = 0;
558     for ( QList<SUIT_Application*>::iterator it = aAppList.begin();
559           it != aAppList.end() && !isAlreadyOpen; ++it ) {
560       aApp = dynamic_cast<SalomeApp_Application*>( *it );
561       if ( aApp && aApp->activeStudy()->studyName() == aName )
562         isAlreadyOpen = true;
563     }
564     if ( !isAlreadyOpen ) {
565       aApp = dynamic_cast<SalomeApp_Application*>( startApplication( 0, 0 ) );
566       if ( aApp )
567         res = aApp->useStudy( aName );
568     }
569     else {
570       aApp->desktop()->activateWindow();
571     }
572   }
573
574   return res;
575 }
576
577 /*!SLOT. Parse message for desktop.*/
578 void SalomeApp_Application::onDesktopMessage( const QString& message )
579 {
580   if ( myToIgnoreMessages )
581     return; // a message from SALOMEDS is caused by GUI action
582
583   MessageLocker ml( myToIgnoreMessages );
584
585   if (message.indexOf("studyCreated") == 0) {
586     if (!activeStudy()) {
587       onNewDoc();
588       updateCommandsStatus();
589     }
590   }
591   if (message.indexOf("studyCleared") == 0) {
592     // Disconnect GUI from active study, because it was closed on DS side.
593     if (activeStudy()) {
594       closeActiveDoc( false );
595       // Disable 'Connect' action
596       QAction* a = action( ConnectId );
597       if ( a )
598         a->setEnabled( false );
599     }
600   }
601   else if ( message.toLower() == "connect_to_study" ) {
602     if ( activeStudy() )
603       useStudy( activeStudy()->studyName() );
604   }
605   if (message.indexOf("studyNameChanged") == 0) {
606     updateDesktopTitle();
607   }
608   LightApp_Application::onDesktopMessage( message );
609 }
610
611 /*!On module activation action.*/
612 void SalomeApp_Application::onModuleActivation( const QString& modName )
613 {
614   if (!activeStudy() && !modName.isEmpty())
615     getStudy()->Init();
616
617   LightApp_Application::onModuleActivation( modName );
618 }
619
620 /*!SLOT. Copy objects to study maneger from selection maneger..*/
621 void SalomeApp_Application::onCopy()
622 {
623   LightApp_Application::onCopy();
624
625   SALOME_ListIO list;
626   LightApp_SelectionMgr* mgr = selectionMgr();
627   mgr->selectedObjects(list);
628
629   SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
630   if(study == NULL) return;
631
632   _PTR(Study) stdDS = getStudy();
633   if(!stdDS) return;
634
635   SALOME_ListIteratorOfListIO it( list );
636   if(it.More())
637     {
638       _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry());
639       if( so )
640       {
641         try {
642           stdDS->Copy(so);
643           onSelectionChanged();
644         }
645         catch(...) {
646         }
647       }
648     }
649 }
650
651 /*!SLOT. Paste objects to study maneger from selection manager.*/
652 void SalomeApp_Application::onPaste()
653 {
654   LightApp_Application::onPaste();
655
656   SALOME_ListIO list;
657   LightApp_SelectionMgr* mgr = selectionMgr();
658   mgr->selectedObjects(list);
659
660   SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
661   if(study == NULL) return;
662
663   _PTR(Study) stdDS = getStudy();
664   if(!stdDS) return;
665
666   if ( stdDS->GetProperties()->IsLocked() ) {
667     SUIT_MessageBox::warning( desktop(),
668                               QObject::tr("WRN_WARNING"),
669                               QObject::tr("WRN_STUDY_LOCKED") );
670     return;
671   }
672
673   SALOME_ListIteratorOfListIO it( list );
674   if(it.More())
675     {
676       _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry());
677       if( so )
678       {
679         try {
680           stdDS->Paste(so);
681           updateObjectBrowser( true );
682           updateActions(); //SRN: BugID IPAL9377, case 3
683         }
684         catch(...) {
685         }
686       }
687     }
688 }
689
690 /*!Check the application on closing.
691  * \retval true if possible, else false
692  */
693 bool SalomeApp_Application::isPossibleToClose( bool& closePermanently )
694 {
695   return LightApp_Application::isPossibleToClose( closePermanently );
696 }
697
698 /*! Check if the study is locked */
699 void SalomeApp_Application::onCloseDoc( bool ask )
700 {
701   if(getStudy()->IsStudyLocked()) {
702     if ( SUIT_MessageBox::question( desktop(),
703                                     QObject::tr( "WRN_WARNING" ),
704                                     QObject::tr( "CLOSE_LOCKED_STUDY" ),
705                                     SUIT_MessageBox::Yes | SUIT_MessageBox::No,
706                                     SUIT_MessageBox::No) == SUIT_MessageBox::No ) return;
707
708   }
709   MessageLocker ml( myToIgnoreMessages );
710
711   LightApp_Application::onCloseDoc( ask );
712
713   // reinitialize study to have empty data
714   //getStudy()->Init();
715 }
716
717 /*!SLOT. Reload document from the file.*/
718 bool SalomeApp_Application::onReopenDoc()
719 {
720   MessageLocker ml( myToIgnoreMessages );
721
722   return LightApp_Application::onReopenDoc();
723 }
724
725
726 /*!SLOT. Load document.*/
727 void SalomeApp_Application::onOpenDoc()
728 {
729   MessageLocker ml( myToIgnoreMessages );
730
731   LightApp_Application::onOpenDoc();
732 }
733
734 /*!SLOT. Load document.*/
735 bool SalomeApp_Application::onOpenDoc(const QString& name)
736 {
737   MessageLocker ml( myToIgnoreMessages );
738
739   return LightApp_Application::onOpenDoc(name);
740 }
741
742 /*!Sets enable or disable some actions on selection changed.*/
743 void SalomeApp_Application::onSelectionChanged()
744 {
745    SALOME_ListIO list;
746    LightApp_SelectionMgr* mgr = selectionMgr();
747    mgr->selectedObjects(list);
748
749    bool canCopy  = false;
750    bool canPaste = false;
751
752    LightApp_Module* m = dynamic_cast<LightApp_Module*>( activeModule() );
753
754    if ( m ) {
755      canCopy  = m->canCopy();
756      canPaste = m->canPaste();
757    }
758
759    SALOME_ListIteratorOfListIO it ( list );
760
761    if (it.More() && list.Extent() == 1) {
762      _PTR(SObject) so = getStudy()->FindObjectID(it.Value()->getEntry());
763
764      if ( so ) {
765        canCopy  = canCopy  || getStudy()->CanCopy(so);
766        canPaste = canPaste || getStudy()->CanPaste(so);
767      }
768    }
769
770    action(EditCopyId)->setEnabled(canCopy);
771    action(EditPasteId)->setEnabled(canPaste);
772 }
773
774 /*!Delete references.*/
775 void SalomeApp_Application::onDeleteInvalidReferences()
776 {
777   SALOME_ListIO aList;
778   LightApp_SelectionMgr* mgr = selectionMgr();
779   mgr->selectedObjects( aList, QString(), false );
780
781   if( aList.IsEmpty() )
782     return;
783
784   _PTR(Study) aStudyDS = getStudy();
785   _PTR(StudyBuilder) aStudyBuilder = aStudyDS->NewBuilder();
786   _PTR(SObject) anObj;
787
788   for( SALOME_ListIteratorOfListIO it( aList ); it.More(); it.Next() )
789     if ( it.Value()->hasEntry() )
790     {
791       _PTR(SObject) aSObject = aStudyDS->FindObjectID( it.Value()->getEntry() ), aRefObj = aSObject;
792       while( aRefObj && aRefObj->ReferencedObject( anObj ) )
793         aRefObj = anObj;
794
795       if( aRefObj && aRefObj!=aSObject && QString( aRefObj->GetName().c_str() ).isEmpty() )
796          aStudyBuilder->RemoveReference( aSObject );
797     }
798   updateObjectBrowser();
799 }
800
801 /*!Private SLOT. */
802 void SalomeApp_Application::onOpenWith()
803 {
804   QApplication::setOverrideCursor( Qt::WaitCursor );
805   SALOME_ListIO aList;
806   LightApp_SelectionMgr* mgr = selectionMgr();
807   mgr->selectedObjects(aList);
808   if (aList.Extent() != 1)
809     {
810       QApplication::restoreOverrideCursor();
811       return;
812     }
813   Handle(SALOME_InteractiveObject) aIObj = aList.First();
814   QString aModuleName(aIObj->getComponentDataType());
815   QString aModuleTitle = moduleTitle(aModuleName);
816   activateModule(aModuleTitle);
817   QApplication::restoreOverrideCursor();
818 }
819
820 /*!
821   Creates new study
822 */
823 SUIT_Study* SalomeApp_Application::createNewStudy()
824 {
825   SalomeApp_Study* aStudy = new SalomeApp_Study( this );
826
827   // Set up processing of major study-related events
828   connect( aStudy, SIGNAL( created( SUIT_Study* ) ), this, SLOT( onStudyCreated( SUIT_Study* ) ) );
829   connect( aStudy, SIGNAL( opened ( SUIT_Study* ) ), this, SLOT( onStudyOpened ( SUIT_Study* ) ) );
830   connect( aStudy, SIGNAL( saved  ( SUIT_Study* ) ), this, SLOT( onStudySaved  ( SUIT_Study* ) ) );
831   connect( aStudy, SIGNAL( closed ( SUIT_Study* ) ), this, SLOT( onStudyClosed ( SUIT_Study* ) ) );
832
833 #ifndef DISABLE_PYCONSOLE
834   //to receive signal in application that NoteBook's variable was modified
835   connect( aStudy, SIGNAL(notebookVarUpdated(QString)),
836            this, SIGNAL(notebookVarUpdated(QString)) );
837 #endif
838
839   getStudy()->Init();
840
841   return aStudy;
842 }
843
844 /*!
845   Enable/Disable menu items and toolbar buttons. Rebuild menu
846 */
847 void SalomeApp_Application::updateCommandsStatus()
848 {
849   LightApp_Application::updateCommandsStatus();
850
851   // Dump study menu
852   QAction* a = action( DumpStudyId );
853   if ( a )
854     a->setEnabled( activeStudy() );
855
856 #ifndef DISABLE_PYCONSOLE
857   // Load script menu
858   a = action( LoadScriptId );
859   if( a )
860     a->setEnabled( pythonConsole() );
861 #endif
862
863   // Properties menu
864   a = action( PropertiesId );
865   if( a )
866     a->setEnabled( activeStudy() );
867
868   // Save GUI state menu
869   a = action( SaveGUIStateId );
870   if( a )
871     a->setEnabled( activeStudy() );
872
873   // Connect study menu
874   a = action( ConnectId );
875   if( a )
876     a->setEnabled( !activeStudy() );
877
878   // Disconnect study menu
879   a = action( DisconnectId );
880   if( a )
881     a->setEnabled( activeStudy() );
882
883   // update state of Copy/Paste menu items
884   onSelectionChanged();
885 }
886
887 /*!
888   \class DumpStudyFileDlg
889   Private class used in Dump Study operation.  Consists 2 check boxes:
890   "Publish in study" and "Save GUI parameters"
891 */
892 class DumpStudyFileDlg : public SUIT_FileDlg
893 {
894 public:
895   DumpStudyFileDlg( QWidget* parent ) : SUIT_FileDlg( parent, false, true, true )
896   {
897     QGridLayout* grid = ::qobject_cast<QGridLayout*>( layout() );
898     if ( grid )
899     {
900       QWidget *hB = new QWidget( this );
901       myPublishChk = new QCheckBox( tr("PUBLISH_IN_STUDY") );
902       myMultiFileChk = new QCheckBox( tr("MULTI_FILE_DUMP") );
903       mySaveGUIChk = new QCheckBox( tr("SAVE_GUI_STATE") );
904
905       QHBoxLayout *layout = new QHBoxLayout;
906       layout->addWidget(myPublishChk);
907       layout->addWidget(myMultiFileChk);
908       layout->addWidget(mySaveGUIChk);
909       hB->setLayout(layout);
910
911       QPushButton* pb = new QPushButton(this);
912
913       int row = grid->rowCount();
914       grid->addWidget( new QLabel("", this), row, 0 );
915       grid->addWidget( hB, row, 1, 1, 3 );
916       grid->addWidget( pb, row, 5 );
917
918       pb->hide();
919     }
920   }
921   QCheckBox* myPublishChk;
922   QCheckBox* myMultiFileChk;
923   QCheckBox* mySaveGUIChk;
924 };
925
926 /*!Private SLOT. On dump study.*/
927 void SalomeApp_Application::onDumpStudy( )
928 {
929   SalomeApp_Study* appStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
930   if ( !appStudy ) return;
931
932   QStringList aFilters;
933   aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
934
935   bool anIsPublish = true;
936   bool anIsMultiFile = false;
937   bool anIsSaveGUI = true;
938
939   if ( SUIT_ResourceMgr* aResourceMgr = resourceMgr() ) {
940     anIsPublish   = aResourceMgr->booleanValue( "Study", "pydump_publish", anIsPublish );
941     anIsMultiFile = aResourceMgr->booleanValue( "Study", "multi_file_dump", anIsMultiFile );
942     anIsSaveGUI   = aResourceMgr->booleanValue( "Study", "pydump_save_gui", anIsSaveGUI );
943   }
944
945   DumpStudyFileDlg fd( desktop() );
946   fd.setValidator( new LightApp_PyFileValidator( &fd ) );
947   fd.setWindowTitle( tr( "TOT_DESK_FILE_DUMP_STUDY" ) );
948   fd.setNameFilters( aFilters );
949   fd.myPublishChk->setChecked( anIsPublish );
950   fd.myMultiFileChk->setChecked( anIsMultiFile );
951   fd.mySaveGUIChk->setChecked( anIsSaveGUI );
952   if ( fd.exec() == QDialog::Accepted )
953   {
954     QString aFileName = fd.selectedFile();
955
956     bool toPublish = fd.myPublishChk->isChecked();
957     bool isMultiFile = fd.myMultiFileChk->isChecked();
958     bool toSaveGUI = fd.mySaveGUIChk->isChecked();
959
960     if ( !aFileName.isEmpty() ) {
961       QFileInfo aFileInfo(aFileName);
962       if( aFileInfo.isDir() ) // IPAL19257
963         return;
964
965       // Issue 21377 - dump study implementation moved to SalomeApp_Study class
966       bool res;
967       {
968         SUIT_OverrideCursor wc;
969         res = appStudy->dump( aFileName, toPublish, isMultiFile, toSaveGUI );
970       }
971       if ( !res )
972         SUIT_MessageBox::warning( desktop(),
973                                   QObject::tr("WRN_WARNING"),
974                                   tr("WRN_DUMP_STUDY_FAILED") );
975     }
976   }
977 }
978
979 /*!Private SLOT. On load script.*/
980 void SalomeApp_Application::onLoadScript( )
981 {
982   if ( getStudy()->GetProperties()->IsLocked() ) {
983     SUIT_MessageBox::warning( desktop(),
984                               QObject::tr("WRN_WARNING"),
985                               QObject::tr("WRN_STUDY_LOCKED") );
986     return;
987   }
988
989   QStringList filtersList;
990   filtersList.append(tr("PYTHON_FILES_FILTER"));
991   filtersList.append(tr("ALL_FILES_FILTER"));
992
993   QString anInitialPath = "";
994   if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
995     anInitialPath = QDir::currentPath();
996
997   QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true );
998
999   if ( !aFile.isEmpty() )
1000   {
1001
1002     QString command = QString("exec(open(\"%1\", \"rb\").read())").arg(aFile);
1003
1004 #ifndef DISABLE_PYCONSOLE
1005     PyConsole_Console* pyConsole = pythonConsole();
1006
1007     if ( pyConsole )
1008       pyConsole->exec( command );
1009 #endif
1010   }
1011 }
1012
1013 /*!Private SLOT. On save GUI state.*/
1014 void SalomeApp_Application::onSaveGUIState()
1015 {
1016   SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1017   if ( study ) {
1018     SalomeApp_VisualState( this ).storeState();
1019     updateSavePointDataObjects( study );
1020     updateObjectBrowser();
1021   }
1022   updateActions();
1023 }
1024
1025 /*!Public SLOT. Performs some actions when dockable windows are triggered.*/
1026 void SalomeApp_Application::onDockWindowVisibilityChanged( bool theIsVisible )
1027 {
1028   LightApp_Application::onDockWindowVisibilityChanged( theIsVisible );
1029   QAction* send = ::qobject_cast<QAction*>( sender() );
1030   if ( !send )
1031     return;
1032   QString aWinName = send->data().toString();
1033   if ( theIsVisible && aWinName == "objectBrowser" )
1034     objectBrowserColumnsVisibility();
1035 }
1036
1037 /*!Create window.*/
1038 QWidget* SalomeApp_Application::createWindow( const int flag )
1039 {
1040   QWidget* wid = 0;
1041 #ifndef DISABLE_PYCONSOLE
1042   if ( flag != WT_PyConsole ) wid = LightApp_Application::createWindow(flag);
1043 #else
1044   wid = LightApp_Application::createWindow(flag);
1045 #endif
1046
1047   SUIT_ResourceMgr* resMgr = resourceMgr();
1048
1049   if ( flag == WT_ObjectBrowser )
1050   {
1051     SUIT_DataBrowser* ob = qobject_cast<SUIT_DataBrowser*>( wid );
1052     if ( ob ) {
1053       // temporary commented
1054       //ob->setUpdater( new SalomeApp_Updater() );
1055
1056 #ifdef WITH_SALOMEDS_OBSERVER
1057       //do not activate the automatic update of Qt tree through signal/slot
1058       ob->setAutoUpdate(false);
1059       //activate update of modified objects only
1060       ob->setUpdateModified(true);
1061 #endif
1062
1063       connect( ob, SIGNAL( doubleClicked( SUIT_DataObject* ) ), this, SLOT( onDblClick( SUIT_DataObject* ) ) );
1064
1065       QString
1066         ValueCol = QObject::tr( "VALUE_COLUMN" ),
1067         IORCol = QObject::tr( "IOR_COLUMN" ),
1068         RefCol = QObject::tr( "REFENTRY_COLUMN" ),
1069         EntryCol = QObject::tr( "ENTRY_COLUMN" );
1070
1071       SUIT_AbstractModel* treeModel = dynamic_cast<SUIT_AbstractModel*>( ob->model() );
1072       treeModel->registerColumn( 0, EntryCol, SalomeApp_DataObject::EntryId );
1073       treeModel->registerColumn( 0, ValueCol, SalomeApp_DataObject::ValueId );
1074       treeModel->registerColumn( 0, IORCol, SalomeApp_DataObject::IORId );
1075       treeModel->registerColumn( 0, RefCol, SalomeApp_DataObject::RefEntryId );
1076       treeModel->setAppropriate( EntryCol, Qtx::Toggled );
1077       treeModel->setAppropriate( ValueCol, Qtx::Toggled );
1078       treeModel->setAppropriate( IORCol, Qtx::Toggled );
1079       treeModel->setAppropriate( RefCol, Qtx::Toggled );
1080
1081       bool autoSize      = resMgr->booleanValue( "ObjectBrowser", "auto_size", false );
1082       bool autoSizeFirst = resMgr->booleanValue( "ObjectBrowser", "auto_size_first", true );
1083       bool resizeOnExpandItem = resMgr->booleanValue( "ObjectBrowser", "resize_on_expand_item", true );
1084
1085       ob->setAutoSizeFirstColumn(autoSizeFirst);
1086       ob->setAutoSizeColumns(autoSize);
1087       ob->setResizeOnExpandItem(resizeOnExpandItem);
1088       ob->setProperty( "shortcut", QKeySequence( "Alt+Shift+O" ) );
1089
1090       for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1091       {
1092         bool shown = resourceMgr()->booleanValue( "ObjectBrowser", QString( "visibility_column_id_%1" ).arg( i-1 ), true );
1093         ob->treeView()->setColumnHidden( i, !shown );
1094       }
1095
1096       // temporary commented
1097       /*
1098       for ( int i = SalomeApp_DataObject::ValueIdx; i <= SalomeApp_DataObject::RefEntryIdx; i++ )
1099       {
1100       ob->addColumn( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i ) ), i );
1101       ob->setColumnShown( i, resMgr->booleanValue( "ObjectBrowser",
1102                                                     QString().sprintf( "visibility_column_%d", i ), true ) );
1103       }
1104       */
1105
1106       // temporary commented
1107       /*
1108         ob->setWidthMode( autoSize ? QListView::Maximum : QListView::Manual );
1109         ob->listView()->setColumnWidthMode( 0, autoSizeFirst ? QListView::Maximum : QListView::Manual );
1110         ob->resize( desktop()->width()/3, ob->height() );
1111       */
1112     }
1113   }
1114 #ifndef DISABLE_PYCONSOLE
1115   else if ( flag == WT_PyConsole )
1116   {
1117     PyConsole_Console* pyCons = new PyConsole_Console( desktop(), new LightApp_PyEditor( getPyInterp() ) );
1118     pyCons->setObjectName( "pythonConsole" );
1119     pyCons->setWindowTitle( tr( "PYTHON_CONSOLE" ) );
1120     pyCons->setFont(resourceMgr()->fontValue( "PyConsole", "font" ));
1121     pyCons->setIsShowBanner(resourceMgr()->booleanValue( "PyConsole", "show_banner", true ));
1122     pyCons->setAutoCompletion( resMgr->booleanValue( "PyConsole", "auto_completion", true ) );
1123     pyCons->setProperty( "shortcut", QKeySequence( "Alt+Shift+P" ) );
1124     wid = pyCons;
1125   }
1126   else if ( flag == WT_NoteBook )
1127   {
1128     setNoteBook( new SalomeApp_NoteBook( desktop() ) );
1129     //to receive signal in NoteBook that it's variable was modified
1130     connect( this, SIGNAL( notebookVarUpdated( QString ) ),
1131              getNoteBook(), SLOT( onVarUpdate( QString ) ) );
1132
1133     wid = getNoteBook();
1134     wid->setObjectName( "noteBook" );
1135   }
1136 #endif
1137   return wid;
1138 }
1139
1140 /*!Create preferences.*/
1141 void SalomeApp_Application::createPreferences( LightApp_Preferences* pref )
1142 {
1143   LightApp_Application::createPreferences(pref);
1144
1145   if ( !pref )
1146     return;
1147
1148   int salomeCat = pref->addPreference( tr( "PREF_CATEGORY_SALOME" ) );
1149   int obTab = pref->addPreference( tr( "PREF_TAB_OBJBROWSER" ), salomeCat );
1150   int defCols = pref->addPreference( tr( "PREF_GROUP_DEF_COLUMNS" ), obTab );
1151   for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1152   {
1153     pref->addPreference( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i-SalomeApp_DataObject::EntryId ).toLatin1() ), defCols,
1154                          LightApp_Preferences::Bool, "ObjectBrowser", QString().sprintf( "visibility_column_id_%d", i-1 ) );
1155   }
1156   pref->setItemProperty( "orientation", Qt::Vertical, defCols );
1157
1158   // adding preference to LightApp_Application handled preferences..  a bit of hacking with resources..
1159   int genTab = pref->addPreference( LightApp_Application::tr( "PREF_TAB_GENERAL" ), salomeCat );
1160   int studyGroup = pref->addPreference( LightApp_Application::tr( "PREF_GROUP_STUDY" ), genTab );
1161   pref->addPreference( tr( "PREF_STORE_VISUAL_STATE" ), studyGroup, LightApp_Preferences::Bool, "Study", "store_visual_state" );
1162   pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1163   pref->addPreference( tr( "PREF_PYDUMP_PUBLISH" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_publish" );
1164   pref->addPreference( tr( "PREF_PYDUMP_MULTI_FILE" ), studyGroup, LightApp_Preferences::Bool, "Study", "multi_file_dump" );
1165   pref->addPreference( tr( "PREF_PYDUMP_SAVE_GUI" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_save_gui" );
1166   pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1167   pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1168 }
1169
1170 /*!Update desktop title.*/
1171 void SalomeApp_Application::updateDesktopTitle() {
1172   QString aTitle = applicationName();
1173   QString aVer = applicationVersion();
1174   if ( !aVer.isEmpty() )
1175     aTitle += QString( " " ) + aVer;
1176
1177   if ( activeStudy() )
1178   {
1179     QString sName = SUIT_Tools::file( activeStudy()->studyName().trimmed(), false );
1180     if ( !sName.isEmpty() ) {
1181       if ( getStudy()->GetProperties()->IsLocked() ) {
1182         aTitle += QString( " - [%1 (%2)]").arg( sName ).arg( tr( "STUDY_LOCKED" ) );
1183       } else {
1184         aTitle += QString( " - [%1]" ).arg( sName );
1185       }
1186     }
1187   }
1188
1189   desktop()->setWindowTitle( aTitle );
1190 }
1191
1192 int SalomeApp_Application::closeChoice( const QString& docName )
1193 {
1194   QStringList buttons;
1195   QMap<int, int> choices;
1196   int idx = 0;
1197   buttons << tr ("APPCLOSE_SAVE");                // Save & Clear
1198   choices.insert( idx++, CloseSave );             // ...
1199   buttons << tr ("APPCLOSE_CLOSE");               // Clear w/o saving
1200   choices.insert( idx++, CloseDiscard );          // ...
1201   if ( myIsCloseFromExit ) {
1202     buttons << tr ("APPCLOSE_UNLOAD_SAVE");       // Save & Disconnect
1203     choices.insert( idx++, CloseDisconnectSave );     // ...
1204     buttons << tr ("APPCLOSE_UNLOAD");            // Disconnect
1205     choices.insert( idx++, CloseDisconnect );         // ...
1206   }
1207   buttons << tr ("APPCLOSE_CANCEL");              // Cancel
1208   choices.insert( idx++, CloseCancel );           // ...
1209
1210   if( !activeStudy()->isModified() )
1211     return CloseCancel;
1212   int answer = SUIT_MessageBox::question( desktop(), tr( "APPCLOSE_CAPTION" ),
1213                                           tr( "APPCLOSE_DESCRIPTION" ), buttons, 0 );
1214   return choices[answer];
1215 }
1216
1217 bool SalomeApp_Application::closeAction( const int choice, bool& closePermanently )
1218 {
1219   bool res = true;
1220   switch( choice )
1221   {
1222   case CloseSave:
1223     if ( activeStudy()->isSaved() )
1224       onSaveDoc();
1225     else if ( !onSaveAsDoc() )
1226       res = false;
1227     break;
1228   case CloseDiscard:
1229     break;
1230   case CloseDisconnectSave:
1231     if ( activeStudy()->isSaved() )
1232       onSaveDoc();
1233     else if ( !onSaveAsDoc() )
1234       res = false;
1235   case CloseDisconnect:
1236     closeActiveDoc( false );
1237     closePermanently = false;
1238     break;
1239   case CloseCancel:
1240   default:
1241     res = false;
1242   }
1243   return res;
1244 }
1245
1246 int SalomeApp_Application::openChoice( const QString& aName )
1247 {
1248   int choice = LightApp_Application::openChoice( aName );
1249
1250   if ( QFileInfo( aName ).exists() ) {
1251     if ( choice == OpenNew ) { // The document isn't already open.
1252       bool exist = false;
1253       if ( aName == getStudy()->Name().c_str() )
1254         exist = true;
1255       // The document already exists in the study.
1256       // Do you want to reload it?
1257       if ( exist ) {
1258         int answer = SUIT_MessageBox::question( desktop(), tr( "WRN_WARNING" ), tr( "QUE_DOC_ALREADYEXIST" ).arg( aName ),
1259                                                 SUIT_MessageBox::Yes | SUIT_MessageBox::No, SUIT_MessageBox::No );
1260         if ( answer == SUIT_MessageBox::Yes )
1261           choice = OpenRefresh;
1262         else
1263           choice = OpenCancel;
1264       }
1265     }
1266   } else { // file is not exist on disk
1267     SUIT_MessageBox::warning( desktop(),
1268                               QObject::tr("WRN_WARNING"),
1269                               QObject::tr("WRN_FILE_NOT_EXIST").arg(aName.toUtf8().data()));
1270     return false;
1271   }
1272
1273   return choice;
1274 }
1275
1276 bool SalomeApp_Application::openAction( const int aChoice, const QString& aName )
1277 {
1278   bool res = false;
1279   int choice = aChoice;
1280   switch ( choice )
1281   {
1282   case OpenRefresh:
1283     {
1284       choice = OpenNew;
1285     }
1286   default:
1287     res = LightApp_Application::openAction( choice, aName );
1288     break;
1289   }
1290
1291   return res;
1292 }
1293
1294 /*!
1295   \brief Get map of the operations which can be performed
1296   on the module activation.
1297
1298   The method should return the map of the kind \c {<id>:<name>}
1299   where \c <id> is an integer identifier of the operation and
1300   \c <name> is a title for the button to be added to the
1301   dialog box. After user selects the required operation by the
1302   clicking the corresponding button in the dialog box, its identifier
1303   is passed to the moduleActionSelected() method to process
1304   the made choice.
1305
1306   \return map of the operations
1307   \sa moduleActionSelected()
1308 */
1309 QMap<int, QString> SalomeApp_Application::activateModuleActions() const
1310 {
1311   QMap<int, QString> opmap = LightApp_Application::activateModuleActions();
1312
1313   opmap.insert( LoadStudyId,     tr( "ACTIVATE_MODULE_OP_LOAD" ) );
1314
1315   opmap.insert( NewAndScriptId,  tr( "ACTIVATE_MODULE_OP_SCRIPT" ) );
1316   return opmap;
1317 }
1318
1319 /*!
1320   \brief Called when the used selectes required operation chosen
1321   from "Activate module" dialog box.
1322
1323   Performs the required operation according to the user choice.
1324
1325   \param id operation identifier
1326   \sa activateModuleActions()
1327 */
1328 void SalomeApp_Application::moduleActionSelected( const int id )
1329 {
1330   switch ( id ) {
1331   case LoadStudyId:
1332     onLoadDoc();
1333     break;
1334   case NewAndScriptId:
1335     onNewWithScript();
1336     break;
1337   default:
1338     LightApp_Application::moduleActionSelected( id );
1339     break;
1340   }
1341 }
1342
1343 /*!Gets CORBA::ORB_var*/
1344 CORBA::ORB_var SalomeApp_Application::orb()
1345 {
1346   static CORBA::ORB_var _orb;
1347
1348   if ( CORBA::is_nil( _orb ) ) {
1349     Qtx::CmdLineArgs args;
1350     ORB_INIT& init = *SINGLETON_<ORB_INIT>::Instance();
1351     _orb = init( args.argc(), args.argv() );
1352   }
1353
1354   return _orb;
1355 }
1356
1357 /*!Create and return SALOMEDS_Study.*/
1358 _PTR(Study) SalomeApp_Application::getStudy()
1359 {
1360   static _PTR(Study) _study;
1361   if(!_study) {
1362     CORBA::Object_var aSObject = namingService()->Resolve("/Study");
1363     SALOMEDS::Study_var aStudy = SALOMEDS::Study::_narrow(aSObject);
1364     _study = ClientFactory::Study(aStudy);
1365   }
1366   return _study;
1367 }
1368
1369 /*!Create and return SALOME_NamingService.*/
1370 SALOME_NamingService* SalomeApp_Application::namingService()
1371 {
1372   static SALOME_NamingService _ns(orb());
1373   return &_ns;
1374 }
1375
1376 /*!Create and return SALOME_LifeCycleCORBA.*/
1377 SALOME_LifeCycleCORBA* SalomeApp_Application::lcc()
1378 {
1379   static SALOME_LifeCycleCORBA _lcc( namingService() );
1380   return &_lcc;
1381 }
1382
1383 /*!Private SLOT. On preferences.*/
1384 void SalomeApp_Application::onProperties()
1385 {
1386   SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1387   if( !study )
1388     return;
1389
1390   _PTR(StudyBuilder) SB = study->studyDS()->NewBuilder();
1391   SB->NewCommand();
1392
1393   SalomeApp_StudyPropertiesDlg aDlg( desktop() );
1394   int res = aDlg.exec();
1395   if( res==QDialog::Accepted && aDlg.isChanged() )
1396     SB->CommitCommand();
1397   else
1398     SB->AbortCommand();
1399
1400   //study->updateCaptions();
1401   updateDesktopTitle();
1402   updateActions();
1403 }
1404
1405 /*!Insert items in popup, which necessary for current application*/
1406 void SalomeApp_Application::contextMenuPopup( const QString& type, QMenu* thePopup, QString& title )
1407 {
1408   LightApp_SelectionMgr* mgr = selectionMgr();
1409   bool cacheIsOn = mgr->isSelectionCacheEnabled();
1410   mgr->setSelectionCacheEnabled( true );
1411
1412   LightApp_Application::contextMenuPopup( type, thePopup, title );
1413
1414   // temporary commented
1415   /*OB_Browser* ob = objectBrowser();
1416   if ( !ob || type != ob->popupClientType() )
1417     return;*/
1418
1419   // Get selected objects
1420   SALOME_ListIO aList;
1421   mgr->selectedObjects( aList, QString(), false );
1422
1423   // add GUI state commands: restore, rename
1424   if ( aList.Extent() == 1 && aList.First()->hasEntry() &&
1425        QString( aList.First()->getEntry() ).startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) {
1426     thePopup->addSeparator();
1427     thePopup->addAction( tr( "MEN_RESTORE_VS" ), this, SLOT( onRestoreGUIState() ) );
1428     thePopup->addAction( tr( "MEN_RENAME_VS" ),  objectBrowser(),
1429                          SLOT( onStartEditing() ), objectBrowser()->shortcutKey(SUIT_DataBrowser::RenameShortcut) );
1430     thePopup->addAction( tr( "MEN_DELETE_VS" ),  this, SLOT( onDeleteGUIState() ) );
1431   }
1432
1433   // "Delete reference" item should appear only for invalid references
1434
1435   // isInvalidRefs will be true, if at least one of selected objects is invalid reference
1436   bool isInvalidRefs = false;
1437
1438   _PTR(SObject) anObj;
1439   for( SALOME_ListIteratorOfListIO it( aList ); it.More() && !isInvalidRefs; it.Next() )
1440   {
1441     if( it.Value()->hasEntry() )
1442     {
1443       _PTR(SObject) aSObject = getStudy()->FindObjectID( it.Value()->getEntry() ), aRefObj = aSObject;
1444       while( aRefObj && aRefObj->ReferencedObject( anObj ) )
1445         aRefObj = anObj;
1446
1447       if( aRefObj && aRefObj!=aSObject && QString( aRefObj->GetName().c_str() ).isEmpty() )
1448         isInvalidRefs = true;
1449     }
1450   }
1451
1452   // Add "Delete reference" item to popup
1453   if ( isInvalidRefs )
1454   {
1455     thePopup->addSeparator();
1456     thePopup->addAction( tr( "MEN_DELETE_INVALID_REFERENCE" ), this, SLOT( onDeleteInvalidReferences() ) );
1457     return;
1458   }
1459
1460   // "Activate module" item should appear only if it's necessary
1461   if ( aList.Extent() == 1 ) {
1462     aList.Clear();
1463     mgr->selectedObjects( aList );
1464
1465     Handle(SALOME_InteractiveObject) aIObj = aList.First();
1466
1467     // add extra popup menu (defined in XML)
1468     if ( myExtActions.size() > 0 ) {
1469       // Use only first selected object
1470       _PTR(SObject) aSO = getStudy()->FindObjectID( aIObj->getEntry() );
1471       if ( aSO ) {
1472         _PTR( GenericAttribute ) anAttr;
1473         std::string auid = "AttributeUserID";
1474         auid += Kernel_Utils::GetGUID(Kernel_Utils::ObjectdID);
1475         if ( aSO->FindAttribute( anAttr, auid ) ) {
1476           _PTR(AttributeUserID) aAttrID = anAttr;
1477           QString aId = aAttrID->Value().c_str();
1478           if ( myExtActions.contains( aId ) ) {
1479             thePopup->addAction(myExtActions[aId]);
1480           }
1481         }
1482       }
1483     }
1484
1485     // check if item is a "GUI state" item (also a first level object)
1486     QString entry( aIObj->getEntry() );
1487     if ( !entry.startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) {
1488       QString aModuleName( aIObj->getComponentDataType() );
1489       QString aModuleTitle = moduleTitle( aModuleName );
1490       CAM_Module* currentModule = activeModule();
1491       if ( ( !currentModule || currentModule->moduleName() != aModuleTitle ) && !aModuleTitle.isEmpty() )
1492         thePopup->addAction( tr( "MEN_OPENWITH" ).arg( aModuleTitle ), this, SLOT( onOpenWith() ) );
1493     }
1494   }
1495
1496   mgr->setSelectionCacheEnabled( cacheIsOn );
1497 }
1498
1499 /*!Update obect browser:
1500  1.if 'updateModels' true, update existing data models;
1501  2. update "non-existing" (not loaded yet) data models;
1502  3. update object browser if it exists */
1503 void SalomeApp_Application::updateObjectBrowser( const bool updateModels )
1504 {
1505   // update "non-existing" (not loaded yet) data models
1506   SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
1507   if ( study )
1508   {
1509     for ( _PTR(SComponentIterator) it ( getStudy()->NewComponentIterator() ); it->More(); it->Next() )
1510     {
1511       _PTR(SComponent) aComponent ( it->Value() );
1512
1513 #ifndef WITH_SALOMEDS_OBSERVER
1514       // with GUI observers this check is not needed anymore
1515       if ( aComponent->ComponentDataType() == study->getVisualComponentName().toLatin1().constData() )
1516         continue; // skip the magic "Interface Applicative" component
1517 #endif
1518       if ( !objectBrowser() )
1519         getWindow( WT_ObjectBrowser );
1520       const bool isAutoUpdate = objectBrowser()->autoUpdate();
1521       objectBrowser()->setAutoUpdate( false );
1522       SalomeApp_DataModel::synchronize( aComponent, study );
1523       objectBrowser()->setAutoUpdate( isAutoUpdate );
1524     }
1525   }
1526
1527   // create data objects that correspond to GUI state save points
1528   if ( study ) updateSavePointDataObjects( study );
1529
1530   // update existing data models (already loaded SComponents)
1531   LightApp_Application::updateObjectBrowser( updateModels );
1532 }
1533
1534 /*!Display Catalog Genenerator dialog */
1535 void SalomeApp_Application::onCatalogGen()
1536 {
1537   ToolsGUI_CatalogGeneratorDlg aDlg( desktop() );
1538   aDlg.exec();
1539 }
1540
1541 /*!Display Registry Display dialog */
1542 void SalomeApp_Application::onRegDisplay()
1543 {
1544   CORBA::ORB_var anOrb = orb();
1545   ToolsGUI_RegWidget* regWnd = ToolsGUI_RegWidget::GetRegWidget( anOrb, desktop() );
1546   regWnd->show();
1547   regWnd->raise();
1548   regWnd->activateWindow();
1549 }
1550
1551 /*!find original object by double click on item */
1552 void SalomeApp_Application::onDblClick( SUIT_DataObject* theObj )
1553 {
1554   // Issue 21379: References are supported at LightApp_DataObject level
1555   LightApp_DataObject* obj = dynamic_cast<LightApp_DataObject*>( theObj );
1556
1557   if( obj && obj->isReference() )
1558   {
1559     QString entry = obj->refEntry();
1560
1561     SUIT_DataOwnerPtrList aList;
1562     aList.append( new LightApp_DataOwner( entry ) );
1563     selectionMgr()->setSelected( aList, false );
1564
1565     SUIT_DataBrowser* ob = objectBrowser();
1566
1567     QModelIndexList aSelectedIndexes = ob->selectedIndexes();
1568     if ( !aSelectedIndexes.isEmpty() )
1569       ob->treeView()->scrollTo( aSelectedIndexes.first() );
1570   }
1571   emit objectDoubleClicked( theObj );
1572 }
1573
1574 /*!
1575   Creates new view manager
1576   \param type - type of view manager
1577 */
1578 SUIT_ViewManager* SalomeApp_Application::newViewManager(const QString& type)
1579 {
1580   return createViewManager(type);
1581 }
1582
1583
1584 /*!Global utility function, returns selected GUI Save point object's ID */
1585 int getSelectedSavePoint( const LightApp_SelectionMgr* selMgr )
1586 {
1587   SALOME_ListIO aList;
1588   selMgr->selectedObjects( aList );
1589   if( aList.Extent() > 0 ) {
1590     Handle(SALOME_InteractiveObject) aIObj = aList.First();
1591     QString entry( aIObj->getEntry() );
1592     QString startStr = QObject::tr( "SAVE_POINT_DEF_NAME" );
1593     if ( !entry.startsWith( startStr ) ) // it's a "GUI state" object
1594       return -1;
1595     bool ok; // conversion to integer is ok?
1596     int savePoint = entry.right( entry.length() - startStr.length() ).toInt( &ok );
1597     return ok ? savePoint : -1;
1598   }
1599   return -1;
1600 }
1601
1602 /*!Called on Restore GUI State popup command*/
1603 void SalomeApp_Application::onRestoreGUIState()
1604 {
1605   int savePoint = ::getSelectedSavePoint( selectionMgr() );
1606   if ( savePoint == -1 )
1607     return;
1608   SalomeApp_VisualState( this ).restoreState( savePoint );
1609 }
1610
1611 /*!Called on Delete GUI State popup command*/
1612 void SalomeApp_Application::onDeleteGUIState()
1613 {
1614   int savePoint = ::getSelectedSavePoint( selectionMgr() );
1615   if ( savePoint == -1 )
1616     return;
1617   SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1618   if ( !study )
1619     return;
1620
1621   study->removeSavePoint( savePoint );
1622   updateSavePointDataObjects( study );
1623 }
1624
1625 /*!Called on New study operation*/
1626 void SalomeApp_Application::onStudyCreated( SUIT_Study* study )
1627 {
1628   LightApp_Application::onStudyCreated( study );
1629
1630 //#ifndef DISABLE_PYCONSOLE
1631 //  desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
1632 //                               windowDock( getWindow( WT_ObjectBrowser ) ) );
1633 //#endif
1634
1635   loadDockWindowsState();
1636
1637   objectBrowserColumnsVisibility();
1638 }
1639
1640 /*!Called on Open study operation*/
1641 void SalomeApp_Application::onStudyOpened( SUIT_Study* study )
1642 {
1643   LightApp_Application::onStudyOpened( study );
1644
1645 //#ifndef DISABLE_PYCONSOLE
1646 //  desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
1647 //                               windowDock( getWindow( WT_ObjectBrowser ) ) );
1648 //#endif
1649
1650   loadDockWindowsState();
1651
1652   objectBrowserColumnsVisibility();
1653
1654   // temporary commented
1655   /*if ( objectBrowser() ) {
1656     updateSavePointDataObjects( dynamic_cast<SalomeApp_Study*>( study ) );
1657     objectBrowser()->updateTree( study->root() );
1658   }*/
1659 }
1660
1661 /*! updateSavePointDataObjects: syncronize data objects that correspond to save points (gui states)*/
1662 void SalomeApp_Application::updateSavePointDataObjects( SalomeApp_Study* study )
1663 {
1664
1665   SUIT_DataBrowser* ob = objectBrowser();
1666   LightApp_SelectionMgr* selMgr = selectionMgr();
1667
1668   if ( !study || !ob || !selMgr )
1669     return;
1670
1671   // find GUI states root object
1672   SUIT_DataObject* guiRootObj = 0;
1673   DataObjectList ch;
1674   study->root()->children( ch );
1675   DataObjectList::const_iterator it = ch.begin(), last = ch.end();
1676   for ( ; it != last ; ++it ) {
1677     if ( dynamic_cast<SalomeApp_SavePointRootObject*>( *it ) ) {
1678       guiRootObj = *it;
1679       break;
1680     }
1681   }
1682   std::vector<int> savePoints = study->getSavePoints();
1683   // case 1: no more save points but they existed in study's tree
1684   if ( savePoints.empty() && guiRootObj ) {
1685     //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state"
1686     //    : set auto update to true for removing SalomeApp_SavePointRootObject from the SUIT_TreeModel
1687     const bool isAutoUpdate = ob->autoUpdate();
1688     selMgr->clearSelected();
1689     ob->setAutoUpdate(true);
1690     DataObjectList ch = guiRootObj->children();
1691     for( int i = 0; i < ch.size(); i++ )
1692       delete ch[i];
1693     delete guiRootObj;
1694     ob->setAutoUpdate(isAutoUpdate);
1695     return;
1696   }
1697   // case 2: no more save points but root does not exist either
1698   if ( savePoints.empty() && !guiRootObj )
1699     return;
1700   // case 3: save points but no root for them - create it
1701   if ( !savePoints.empty() && !guiRootObj )
1702     guiRootObj = new SalomeApp_SavePointRootObject( study->root() );
1703   // case 4: everything already exists.. here may be a problem: we want "GUI states" root object
1704   // to be always the last one in the tree.  Here we check - if it is not the last one - remove and
1705   // re-create it.
1706   if ( guiRootObj->nextBrother() ) {
1707     study->root()->removeChild(guiRootObj);
1708     study->root()->appendChild(guiRootObj);
1709     //study->root()->dump();
1710   }
1711
1712   // store data objects in a map id-to-DataObject
1713   QMap<int,SalomeApp_SavePointObject*> mapDO;
1714   ch.clear();
1715   guiRootObj->children( ch );
1716   for( it = ch.begin(), last = ch.end(); it != last ; ++it ) {
1717     SalomeApp_SavePointObject* dobj = dynamic_cast<SalomeApp_SavePointObject*>( *it );
1718     if ( dobj )
1719       mapDO[dobj->getId()] = dobj;
1720   }
1721
1722   // iterate new save points.  if DataObject with such ID not found in map - create DataObject
1723   // if in the map - remove it from map.
1724   for ( size_t i = 0; i < savePoints.size(); i++ )
1725     if ( !mapDO.contains( savePoints[i] ) )
1726       new SalomeApp_SavePointObject( guiRootObj, savePoints[i], study );
1727     else
1728       mapDO.remove( savePoints[i] );
1729
1730   // delete DataObjects that are still in the map -- their IDs were not found in data model
1731   if( mapDO.size() > 0) {
1732     //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state"
1733     //    : set auto update to true for removing SalomeApp_SavePointObject from the SUIT_TreeModel
1734     selMgr->clearSelected();
1735     const bool isAutoUpdate = ob->autoUpdate();
1736     ob->setAutoUpdate(true);
1737     for ( QMap<int,SalomeApp_SavePointObject*>::Iterator it = mapDO.begin(); it != mapDO.end(); ++it )
1738       delete it.value();
1739     ob->setAutoUpdate(isAutoUpdate);
1740   }
1741 }
1742
1743 /*! Check data object */
1744 bool SalomeApp_Application::checkDataObject(LightApp_DataObject* theObj)
1745 {
1746   if (theObj)
1747     return true;
1748
1749   return false;
1750 }
1751
1752 /*!
1753   Opens other study into active Study. If Study is empty - creates it.
1754   \param theName - name of study
1755 */
1756 bool SalomeApp_Application::useStudy( const QString& theName )
1757 {
1758   createEmptyStudy();
1759   SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1760   bool res = false;
1761   if (aStudy)
1762     res = aStudy->loadDocument( theName );
1763   updateDesktopTitle();
1764   updateCommandsStatus();
1765   return res;
1766 }
1767
1768 /*! Show/hide object browser colums according to preferences */
1769 void SalomeApp_Application::objectBrowserColumnsVisibility()
1770 {
1771   if ( objectBrowser() )
1772     for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1773     {
1774       bool shown = resourceMgr()->booleanValue( "ObjectBrowser", QString( "visibility_column_id_%1" ).arg( i-1 ), true );
1775       objectBrowser()->treeView()->setColumnHidden( i, !shown );
1776     }
1777 }
1778
1779 #ifndef DISABLE_PYCONSOLE
1780 /*! Set SalomeApp_NoteBook pointer */
1781 void SalomeApp_Application::setNoteBook( SalomeApp_NoteBook* theNoteBook )
1782 {
1783   myNoteBook = theNoteBook;
1784 }
1785
1786 /*! Return SalomeApp_NoteBook pointer */
1787 SalomeApp_NoteBook* SalomeApp_Application::getNoteBook() const
1788 {
1789   return myNoteBook;
1790 }
1791 #endif
1792
1793 /*!
1794  * Define extra actions defined in module definition XML file.
1795  * Additional popup items sections can be defined by parameter "popupitems".
1796  * Supported attributes:
1797  * title - title of menu item,
1798  * attributelocalid - AttributeLocalId defined for selected data item where menu command has to be applied,
1799  * method - method which has to be called when menu item is selected
1800  * Example:
1801  * <section name="MODULENAME">
1802  *   <parameter name="popupitems" value="menuitem1:menuitem2:..."/>
1803  * </section>
1804  * <section name="importmed">
1805  *   <parameter name="title" value="My menu"/>
1806  *   <parameter name="objectid" value="VISU.Result"/>
1807  *   <parameter name="method" value="nameOfModuleMethod"/>
1808  * </section>
1809  */
1810 void SalomeApp_Application::createExtraActions()
1811 {
1812   myExtActions.clear();
1813   SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
1814
1815   QStringList aModules;
1816   modules(aModules, false);
1817   foreach(QString aModile, aModules) {
1818     QString aModName = moduleName(aModile);
1819     QString aSectionStr = resMgr->stringValue(aModName, "popupitems", QString());
1820     if (!aSectionStr.isNull()) {
1821       QStringList aSections = aSectionStr.split(':');
1822       foreach(QString aSection, aSections) {
1823         QString aTitle = resMgr->stringValue(aSection, "title",    QString());
1824         QString aId    = resMgr->stringValue(aSection, "objectid", QString());
1825         QString aSlot  = resMgr->stringValue(aSection, "method",   QString());
1826         if (aTitle.isEmpty() || aSlot.isEmpty() || aId.isEmpty())
1827           continue;
1828
1829         QString aModuleName = resMgr->stringValue(aSection, "module", QString());
1830         if (aModuleName.isNull())
1831           aModuleName = aModName;
1832
1833         QAction* aAction = new QAction(aTitle, this);
1834         QStringList aData;
1835         aData<<aModuleName<<aSlot;
1836         aAction->setData(aData);
1837         connect(aAction, SIGNAL(triggered()), this, SLOT(onExtAction()));
1838         myExtActions[aId] = aAction;
1839       }
1840     }
1841   }
1842 }
1843
1844 /*!
1845  * Called when extra action is selected
1846  */
1847 void SalomeApp_Application::onExtAction()
1848 {
1849   QAction* aAction = ::qobject_cast<QAction*>(sender());
1850   if (!aAction)
1851     return;
1852
1853   QVariant aData = aAction->data();
1854   QStringList aDataList = aData.value<QStringList>();
1855   if (aDataList.size() != 2)
1856     return;
1857
1858   LightApp_SelectionMgr* aSelectionMgr = selectionMgr();
1859   SALOME_ListIO aListIO;
1860   aSelectionMgr->selectedObjects(aListIO);
1861   const Handle(SALOME_InteractiveObject)& anIO = aListIO.First();
1862   if (aListIO.Extent() < 1)
1863     return;
1864   if (!anIO->hasEntry())
1865     return;
1866
1867   QString aEntry(anIO->getEntry());
1868
1869   QApplication::setOverrideCursor( Qt::WaitCursor );
1870   QString aModuleTitle = moduleTitle(aDataList[0]);
1871   activateModule(aModuleTitle);
1872   QApplication::restoreOverrideCursor();
1873
1874   QCoreApplication::processEvents();
1875
1876   CAM_Module* aModule = activeModule();
1877   if (!aModule)
1878     return;
1879
1880   if (!QMetaObject::invokeMethod(aModule, qPrintable(aDataList[1]), Q_ARG(QString, aEntry)))
1881     printf("Error: Can't Invoke method %s\n", qPrintable(aDataList[1]));
1882 }
1883
1884 /*!
1885   Checks that an object can be renamed.
1886   \param entry entry of the object
1887   \brief Return \c true if object can be renamed
1888 */
1889 bool SalomeApp_Application::renameAllowed( const QString& entry) const
1890 {
1891   return entry.startsWith( tr( "SAVE_POINT_DEF_NAME") );
1892 }
1893
1894 /*!
1895   Rename object by entry.
1896   \param entry entry of the object
1897   \param name new name of the object
1898   \brief Return \c true if rename operation finished successfully, \c false otherwise.
1899 */
1900 bool SalomeApp_Application::renameObject( const QString& entry, const QString& name )
1901 {
1902   SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1903
1904   int savePoint = ::getSelectedSavePoint( selectionMgr() );
1905
1906   if(!aStudy || savePoint == -1)
1907     return false;
1908
1909   if ( !name.isNull() && !name.isEmpty() ) {
1910     aStudy->setNameOfSavePoint( savePoint, name );
1911     updateSavePointDataObjects( aStudy );
1912
1913     //Mark study as modified
1914     aStudy->Modified();
1915     return true;
1916   }
1917   return false;
1918 }
1919
1920 #ifndef DISABLE_PYCONSOLE
1921 //============================================================================
1922 /*! Function : onUpdateStudy
1923  *  Purpose  : Slot to update the study.
1924  */
1925 //============================================================================
1926 void SalomeApp_Application::onUpdateStudy()
1927 {
1928   QApplication::setOverrideCursor( Qt::WaitCursor );
1929
1930   if( !updateStudy() )
1931     SUIT_MessageBox::warning( desktop(), tr( "ERROR" ), tr( "ERR_UPDATE_STUDY_FAILED" ) );
1932
1933   QApplication::restoreOverrideCursor();
1934 }
1935
1936 //============================================================================
1937 /*! Function : updateStudy
1938  *  Purpose  : Update study by dumping the study to Python script and loading it.
1939  *             It is used to apply variable modifications done in NoteBook to created objects.
1940  */
1941 //============================================================================
1942 bool SalomeApp_Application::updateStudy()
1943 {
1944   SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1945   if ( !study || !myNoteBook )
1946     return false;
1947
1948   myNoteBook->setIsDumpedStudySaved( study->isSaved() );
1949   myNoteBook->setDumpedStudyName( study->studyName() );
1950
1951   // get unique temporary directory name
1952   QString aTmpDir = QString::fromStdString( SALOMEDS_Tool::GetTmpDir() );
1953
1954   if( aTmpDir.isEmpty() )
1955     return false;
1956
1957   if( aTmpDir.right( 1 ).compare( QDir::separator() ) == 0 )
1958     aTmpDir.remove( aTmpDir.length() - 1, 1 );
1959
1960   // dump study to the temporary directory
1961   QString aScriptName( "notebook" );
1962   bool toPublish = true;
1963   bool isMultiFile = false;
1964   bool toSaveGUI = true;
1965
1966   int savePoint;
1967   _PTR(AttributeParameter) ap;
1968   _PTR(IParameters) ip = ClientFactory::getIParameters(ap);
1969   if(ip->isDumpPython()) ip->setDumpPython(); //Unset DumpPython flag.
1970   if ( toSaveGUI ) { //SRN: Store a visual state of the study at the save point for DumpStudy method
1971     ip->setDumpPython();
1972     savePoint = SalomeApp_VisualState( this ).storeState(); //SRN: create a temporary save point
1973   }
1974   bool ok = getStudy()->DumpStudy( aTmpDir.toStdString(), aScriptName.toStdString(), toPublish, isMultiFile );
1975   if ( toSaveGUI )
1976     study->removeSavePoint(savePoint); //SRN: remove the created temporary save point.
1977
1978   if( ok )
1979     myNoteBook->setDumpedStudyScript( aTmpDir + QDir::separator() + aScriptName + ".py" );
1980   else
1981     return false;
1982
1983   QList<SUIT_Application*> aList = SUIT_Session::session()->applications();
1984   int anIndex = aList.indexOf( this );
1985
1986   // Disconnect dialog from application desktop in case if:
1987   // 1) Application is not the first application in the session
1988   // 2) Application is the first application in session but not the only.
1989   bool changeDesktop = ((anIndex > 0) || (anIndex == 0 && aList.count() > 1));
1990   if( changeDesktop ) {
1991
1992     SalomeApp_Application* app = this;
1993     if( anIndex > 0 && anIndex < aList.count() )
1994       app = dynamic_cast<SalomeApp_Application*>( aList[ anIndex - 1 ] );
1995     else if(anIndex == 0 && aList.count() > 1)
1996       app = dynamic_cast<SalomeApp_Application*>( aList[ 1 ] );
1997
1998     if( !app )
1999       return false;
2000
2001     // creation a new study and restoring will be done in another application
2002     connect( this, SIGNAL( dumpedStudyClosed( const QString&, const QString&, bool ) ),
2003              app, SLOT( onRestoreStudy( const QString&, const QString&, bool ) ), Qt::UniqueConnection );
2004   }
2005
2006   QString aDumpScript = myNoteBook->getDumpedStudyScript();
2007   QString aStudyName = myNoteBook->getDumpedStudyName();
2008   bool isStudySaved = myNoteBook->isDumpedStudySaved();
2009   // clear a study (delete all objects)
2010   onCloseDoc( false );
2011
2012   if( !changeDesktop ) {
2013     ok = onRestoreStudy( aDumpScript,
2014                          aStudyName,
2015                          isStudySaved );
2016   }
2017
2018   return ok;
2019 }
2020 #endif
2021
2022 //============================================================================
2023 /*! Function : onRestoreStudy
2024  *  Purpose  : Load the dumped study from Python script
2025  */
2026 //============================================================================
2027 bool SalomeApp_Application::onRestoreStudy( const QString& theDumpScript,
2028                                             const QString& theStudyName,
2029                                             bool theIsStudySaved )
2030 {
2031   bool ok = true;
2032
2033   // create a new study
2034   onNewDoc();
2035
2036   // get active application
2037   SalomeApp_Application* app = dynamic_cast<SalomeApp_Application*>( SUIT_Session::session()->activeApplication() );
2038
2039   // load study from the temporary directory
2040   QFileInfo aScriptInfo = QFileInfo(theDumpScript);
2041   QString command = QString( "exec(open(\"%1\" ,\"rb\").read())" ).arg(aScriptInfo.canonicalFilePath());
2042
2043 #ifndef DISABLE_PYCONSOLE
2044   PyConsole_Console* pyConsole = app->pythonConsole();
2045   if ( pyConsole )
2046     pyConsole->execAndWait( command );
2047 #endif
2048
2049   // remove temporary directory
2050   QString aStudyName = aScriptInfo.baseName();
2051   QDir aDir = aScriptInfo.absoluteDir();
2052   QStringList aFiles = aDir.entryList( QStringList( "*.py*" ) );
2053   for( QStringList::iterator it = aFiles.begin(), itEnd = aFiles.end(); it != itEnd; ++it )
2054     ok = aDir.remove( *it ) && ok;
2055   if( ok )
2056     ok = aDir.rmdir( aDir.absolutePath() );
2057
2058   if( SalomeApp_Study* newStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() ) )
2059   {
2060 #ifndef DISABLE_PYCONSOLE
2061     if ( app->getNoteBook() )
2062       app->getNoteBook()->Init();
2063     newStudy->updateFromNotebook(theStudyName, theIsStudySaved);
2064     newStudy->Modified();
2065     updateDesktopTitle();
2066     updateActions();
2067 #endif
2068   }
2069   else
2070     ok = false;
2071
2072   return ok;
2073 }
2074
2075 /*!
2076   Close the Application
2077 */
2078 void SalomeApp_Application::afterCloseDoc()
2079 {
2080 #ifndef DISABLE_PYCONSOLE
2081   // emit signal to restore study from Python script
2082   if ( myNoteBook ) {
2083     emit dumpedStudyClosed( myNoteBook->getDumpedStudyScript(),
2084                             myNoteBook->getDumpedStudyName(),
2085                             myNoteBook->isDumpedStudySaved() );
2086   }
2087 #endif
2088   LightApp_Application::afterCloseDoc();
2089 }
2090
2091 bool SalomeApp_Application::canOpenDoc( const QString& url )
2092 {
2093   _PTR(Study) aStudyDS = getStudy();
2094   if ( aStudyDS )
2095     return aStudyDS->CanOpen( url.toUtf8().data() );
2096   return false;
2097 }
2098
2099 /*
2100   Asks to close existing document.
2101 */
2102 bool SalomeApp_Application::checkExistingDoc()
2103 {
2104   return LightApp_Application::checkExistingDoc();
2105 }
2106
2107
2108 #ifndef DISABLE_PYCONSOLE
2109
2110 PyConsole_Interp* SalomeApp_Application::createPyInterp()
2111 {
2112   return new SalomeApp_PyInterp;
2113 }
2114
2115 #endif // DISABLE_PYCONSOLE