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