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