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