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