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