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