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