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