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