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