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