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