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