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