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