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