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