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