1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
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.
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.
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
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File: SalomeApp_Application.cxx
24 // Created: 10/22/2004 3:23:45 PM
25 // Author: Sergey LITONIN
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
37 #ifndef DISABLE_PYCONSOLE
38 #include "SalomeApp_PyInterp.h"
39 #include "SalomeApp_NoteBook.h"
40 #include "LightApp_PyEditor.h"
41 #include "PyConsole_Console.h"
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"
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>
60 #include <CAM_Module.h>
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>
75 #include <QtxTreeView.h>
77 #include <SALOME_EventFilter.h>
79 // temporary commented
80 //#include <OB_ListItem.h>
83 #include <Utils_ORB_INIT.hxx>
84 #include <Utils_SINGLETON.hxx>
85 #include <SALOME_LifeCycleCORBA.hxx>
87 #include <QApplication>
92 #include <QPushButton>
94 #include <QListWidget>
95 #include <QGridLayout>
99 #include <SALOMEDSClient_ClientFactory.hxx>
100 #include <Basics_Utils.hxx>
102 #include <SALOME_ListIO.hxx>
103 #include <SALOME_Prs.h>
106 #include <ToolsGUI_CatalogGeneratorDlg.h>
107 #include <ToolsGUI_RegWidget.h>
111 #include <SALOMEDS_Tool.hxx>
113 /*!Internal class that updates object browser item properties */
114 // temporary commented
115 /*class SalomeApp_Updater : public OB_Updater
118 SalomeApp_Updater() : OB_Updater(){};
119 virtual ~SalomeApp_Updater(){};
120 virtual void update( SUIT_DataObject* theObj, OB_ListItem* theItem );
123 void SalomeApp_Updater::update( SUIT_DataObject* theObj, OB_ListItem* theItem )
125 if( !theObj || !theItem )
128 SalomeApp_DataObject* SAObj = dynamic_cast<SalomeApp_DataObject*>( theObj );
132 _PTR(SObject) SObj = SAObj->object();
135 _PTR( GenericAttribute ) anAttr;
138 if ( SObj->FindAttribute( anAttr, "AttributeSelectable" ) )
140 _PTR(AttributeSelectable) aAttrSel = anAttr;
141 theItem->setSelectable( aAttrSel->IsSelectable() );
144 if ( SObj->FindAttribute(anAttr, "AttributeExpandable") )
146 _PTR(AttributeExpandable) aAttrExpand = anAttr;
147 theItem->setExpandable( aAttrExpand->IsExpandable() );
150 //this attribute is not supported in the version of SALOME 3.x
151 //if ( SObj->FindAttribute(anAttr, "AttributeOpened") )
153 // _PTR(AttributeOpened) aAttrOpen = anAttr;
154 // theItem->setOpen( aAttrOpen->IsOpened() );
158 /*!Create new instance of SalomeApp_Application.*/
159 extern "C" SALOMEAPP_EXPORT SUIT_Application* createApplication()
161 return new SalomeApp_Application();
165 SalomeApp_Application::SalomeApp_Application()
166 : LightApp_Application(),
167 myIsCloseFromExit( false )
172 *\li Destroy event filter.
174 SalomeApp_Application::~SalomeApp_Application()
176 // Do not destroy. It's a singleton !
177 //SALOME_EventFilter::Destroy();
180 QStringList __getArgsList(QString argsString)
182 // Special process if some items of 'args:' list are themselves lists
183 // Note that an item can be a list, but not a list of lists...
184 // So we can have something like this:
185 // myscript.py args:['file1','file2'],val1,"done",[1,2,3],[True,False],"ok"
186 // With such a call, argsString variable contains the string representing "[file1,file2]", "val1", "done", "[1,2,3]", "[True,False]", "ok"
187 // We have to split argsString to obtain: [[file1,file2],val1,done,[1,2,3],[True,False],ok]
188 argsString.replace("\\\"", "'"); // replace escaped double quotes by simple quotes
189 bool containsList = (QRegExp("(\\[[^\\]]*\\])").indexIn(argsString) >= 0);
191 QStringList sl = argsString.split("\"", QString::SkipEmptyParts);
196 return argsString.split(",", QString::SkipEmptyParts);
199 /*!Start application.*/
200 void SalomeApp_Application::start()
202 // process the command line options before start: to createActions in accordance to the options
203 static bool isFirst = true;
210 for (int i = 1; i < qApp->arguments().size(); i++) {
211 QRegExp rxs ("--study-hdf=(.+)");
212 if ( rxs.indexIn( qApp->arguments()[i] ) >= 0 && rxs.capturedTexts().count() > 1 ) {
213 QString file = rxs.capturedTexts()[1];
214 QFileInfo fi ( file );
215 QString extension = fi.suffix().toLower();
216 if ( extension == "hdf" && fi.exists() )
217 hdffile = fi.absoluteFilePath();
220 QRegExp rxp ("--pyscript=\\[(.+)\\]");
221 if ( rxp.indexIn( qApp->arguments()[i] ) >= 0 && rxp.capturedTexts().count() > 1 ) {
223 QStringList dictList = rxp.capturedTexts()[1].split("},", QString::SkipEmptyParts);
224 for (int k = 0; k < dictList.count(); ++k) {
225 QRegExp rxd ("[\\s]*\\{?([^\\{\\}]+)\\}?[\\s]*");
226 if ( rxd.indexIn( dictList[k] ) >= 0 && rxd.capturedTexts().count() > 1 ) {
227 for (int m = 1; m < rxd.capturedTexts().count(); ++m) {
228 pyfiles += rxd.capturedTexts()[m];
235 // Here pyfiles elements are: "script_name": [list_of_"arg"s]
236 // For example: "/absolute/path/to/my_script.py": ["1", "2"]
238 LightApp_Application::start();
239 SALOME_EventFilter::Init();
241 setProperty("open_study_from_command_line", true);
242 if ( !hdffile.isEmpty() ) // open hdf file given as parameter
243 onOpenDoc( hdffile );
244 setProperty("open_study_from_command_line", QVariant());
246 #ifndef DISABLE_PYCONSOLE
247 // import/execute python scripts
248 if ( pyfiles.count() > 0 && activeStudy() ) {
249 SalomeApp_Study* appStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
250 PyConsole_Console* pyConsole = pythonConsole();
251 if ( appStudy && pyConsole ) {
252 if ( !getStudy()->GetProperties()->IsLocked() ) {
253 // pyfiles[j] is a dictionary: {"/absolute/path/to/script.py": [script_args]}
254 // Path is absolute, script has .py extension
255 for (uint j = 0; j < pyfiles.count(); j++ ) {
256 // Extract scripts and their arguments, if any
257 QRegExp rxp ("\"(.+)\":[\\s]*\\[(.*)\\]");
258 if ( rxp.indexIn( pyfiles[j] ) >= 0 && rxp.capturedTexts().count() == 3 ) {
259 QString script = rxp.capturedTexts()[1];
261 QStringList argList = __getArgsList(rxp.capturedTexts()[2]);
262 for (uint k = 0; k < argList.count(); k++ ) {
263 QString arg = argList[k].trimmed();
264 arg.remove( QRegExp("^[\"]") );
265 arg.remove( QRegExp("[\"]$") );
268 args.remove( QRegExp("[,]$") );
269 if (!args.isEmpty()) {
273 script.remove( QRegExp("^python.*[\\s]+") );
274 QString cmd = script+" "+args;
275 QString command = QString( "exec(open(\"%1\",encoding='utf-8').read())" ).arg(cmd.trimmed());
276 pyConsole->exec(command);
278 } // end for loop on pyfiles QStringList
284 LightApp_Application::start();
285 SALOME_EventFilter::Init();
290 void SalomeApp_Application::createActions()
292 LightApp_Application::createActions();
294 SUIT_Desktop* desk = desktop();
297 // "Save GUI State" command is moved to VISU module
298 // createAction( SaveGUIStateId, tr( "TOT_DESK_FILE_SAVE_GUI_STATE" ), QIcon(),
299 // tr( "MEN_DESK_FILE_SAVE_GUI_STATE" ), tr( "PRP_DESK_FILE_SAVE_GUI_STATE" ),
300 // 0, desk, false, this, SLOT( onSaveGUIState() ) );
303 createAction( DumpStudyId, tr( "TOT_DESK_FILE_DUMP_STUDY" ), QIcon(),
304 tr( "MEN_DESK_FILE_DUMP_STUDY" ), tr( "PRP_DESK_FILE_DUMP_STUDY" ),
305 Qt::CTRL+Qt::Key_D, desk, false, this, SLOT( onDumpStudy() ) );
308 createAction( LoadScriptId, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), QIcon(),
309 tr( "MEN_DESK_FILE_LOAD_SCRIPT" ), tr( "PRP_DESK_FILE_LOAD_SCRIPT" ),
310 Qt::CTRL+Qt::Key_T, desk, false, this, SLOT( onLoadScript() ) );
313 createAction( PropertiesId, tr( "TOT_DESK_PROPERTIES" ), QIcon(),
314 tr( "MEN_DESK_PROPERTIES" ), tr( "PRP_DESK_PROPERTIES" ),
315 0, desk, false, this, SLOT( onProperties() ) );
317 //! Catalog Generator
318 createAction( CatalogGenId, tr( "TOT_DESK_CATALOG_GENERATOR" ), QIcon(),
319 tr( "MEN_DESK_CATALOG_GENERATOR" ), tr( "PRP_DESK_CATALOG_GENERATOR" ),
320 Qt::ALT+Qt::SHIFT+Qt::Key_G, desk, false, this, SLOT( onCatalogGen() ) );
323 createAction( RegDisplayId, tr( "TOT_DESK_REGISTRY_DISPLAY" ), QIcon(),
324 tr( "MEN_DESK_REGISTRY_DISPLAY" ), tr( "PRP_DESK_REGISTRY_DISPLAY" ),
325 /*Qt::SHIFT+Qt::Key_D*/0, desk, false, this, SLOT( onRegDisplay() ) );
327 createAction( ConnectId, tr( "TOT_DESK_CONNECT_STUDY" ), QIcon(),
328 tr( "MEN_DESK_CONNECT" ), tr( "PRP_DESK_CONNECT" ),
329 Qt::CTRL+Qt::Key_L, desk, false, this, SLOT( onLoadDoc() ) );
330 //no need at this action for mono-study application because study is always exists
331 action( ConnectId )->setVisible( false );
333 createAction( DisconnectId, tr( "TOT_DESK_DISCONNECT_STUDY" ), QIcon(),
334 tr( "MEN_DESK_DISCONNECT" ), tr( "PRP_DESK_DISCONNECT" ),
335 Qt::CTRL+Qt::Key_U, desk, false, this, SLOT( onUnloadDoc() ) );
336 //no need at this action for mono-study application because study is always exists
337 action( DisconnectId )->setVisible( false );
340 int fileMenu = createMenu( tr( "MEN_DESK_FILE" ), -1 );
342 // "Save GUI State" command is renamed to "Save VISU State" and
343 // creation of menu item is moved to VISU
344 // createMenu( SaveGUIStateId, fileMenu, 10, -1 );
346 createMenu( ConnectId, fileMenu, 5 );
347 createMenu( DisconnectId, fileMenu, 5 );
348 createMenu( separator(), fileMenu, -1, 5 );
350 createMenu( DumpStudyId, fileMenu, 10, -1 );
351 createMenu( LoadScriptId, fileMenu, 10, -1 );
352 createMenu( separator(), fileMenu, -1, 10, -1 );
353 createMenu( PropertiesId, fileMenu, 10, -1 );
354 createMenu( separator(), fileMenu, -1, 10, -1 );
356 int toolsMenu = createMenu( tr( "MEN_DESK_TOOLS" ), -1, MenuToolsId, 50 );
357 createMenu( CatalogGenId, toolsMenu, 10, -1 );
358 createMenu( RegDisplayId, toolsMenu, 10, -1 );
359 createMenu( separator(), toolsMenu, -1, 15, -1 );
361 createExtraActions();
363 #ifndef DISABLE_PYCONSOLE
364 #ifndef DISABLE_SALOMEOBJECT
365 // import Python module that manages SALOME plugins
367 PyLockWrapper lck; // acquire GIL
368 PyObjWrapper pluginsmanager = PyImport_ImportModule((char*)"salome_pluginsmanager");
369 PyObjWrapper res = PyObject_CallMethod( pluginsmanager, (char*)"initialize", (char*)"isss",0,"salome",tr("MEN_DESK_TOOLS").toUtf8().data(),tr("MEN_DESK_PLUGINS").toUtf8().data());
373 // end of SALOME plugins loading
380 \brief Close application.
382 void SalomeApp_Application::onExit()
384 bool killServers = false;
387 if ( exitConfirmation() ) {
388 SalomeApp_ExitDlg dlg( desktop() );
389 result = dlg.exec() == QDialog::Accepted;
390 killServers = dlg.isServersShutdown();
394 if ( !killServers ) myIsCloseFromExit = true;
395 SUIT_Session::session()->closeSession( SUIT_Session::ASK, killServers );
396 if ( SUIT_Session::session()->applications().count() > 0 ) myIsCloseFromExit = false;
400 /*!SLOT. Load document.*/
401 void SalomeApp_Application::onLoadDoc()
405 // rnv: According to the single-study approach on the server side
406 // can be only one study. So if it is exists connect to them,
407 // overwise show warning message: "No active study on the server"
410 SUIT_Session* aSession = SUIT_Session::session();
411 QList<SUIT_Application*> aAppList = aSession->applications();
413 QStringList unloadedStudies;
415 for ( unsigned int ind = 0; ind < List.size(); ind++ ) {
416 studyName = List[ind].c_str();
417 // Add to list only unloaded studies
418 bool isAlreadyOpen = false;
419 QListIterator<SUIT_Application*> it( aAppList );
420 while ( it.hasNext() && !isAlreadyOpen ) {
421 SUIT_Application* aApp = it.next();
422 if( !aApp || !aApp->activeStudy() )
424 if ( aApp->activeStudy()->studyName() == studyName )
425 isAlreadyOpen = true;
428 if ( !isAlreadyOpen )
429 unloadedStudies << studyName;
431 studyName = SalomeApp_LoadStudiesDlg::selectStudy( desktop(), unloadedStudies );
432 if ( studyName.isEmpty() )
437 SUIT_MessageBox::warning( desktop(),
438 QObject::tr("WRN_WARNING"),
439 QObject::tr("WRN_NO_STUDY_ON SERV") );
443 studyName = activeStudy()->studyName();
446 // this code replaces marker of windows drive and path become invalid therefore
447 // defines placed there
448 studyName.replace( QRegExp(":"), "/" );
451 if ( onLoadDoc( studyName ) ) {
453 updateViewManagers();
454 updateObjectBrowser( true );
458 /*!SLOT. Unload document.*/
459 void SalomeApp_Application::onUnloadDoc( bool ask )
462 activeStudy()->abortAllOperations();
463 if ( activeStudy()->isModified() ) {
464 QString docName = activeStudy()->studyName().trimmed();
465 int answer = SUIT_MessageBox::question( desktop(), tr( "DISCONNECT_CAPTION" ),
466 tr( "DISCONNECT_DESCRIPTION" ),
467 tr( "DISCONNECT_SAVE" ),
468 tr( "DISCONNECT_WO_SAVE" ),
469 tr( "APPCLOSE_CANCEL" ), 0 );
470 if ( answer == 0 ) { // save before unload
471 if ( activeStudy()->isSaved() )
473 else if ( !onSaveAsDoc() )
476 else if ( answer == 2 ) // Cancel
480 closeActiveDoc( false );
483 /*!SLOT. Create new study and load script*/
484 void SalomeApp_Application::onNewWithScript()
486 QStringList filtersList;
487 filtersList.append(tr("PYTHON_FILES_FILTER"));
488 filtersList.append(tr("ALL_FILES_FILTER"));
490 QString anInitialPath = "";
491 if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
492 anInitialPath = QDir::currentPath();
494 QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true );
496 if ( !aFile.isEmpty() )
500 QString command = QString("exec(open(\"%1\",encoding='utf-8').read())").arg(aFile);
502 #ifndef DISABLE_PYCONSOLE
503 PyConsole_Console* pyConsole = pythonConsole();
506 pyConsole->exec( command );
512 /*!SLOT. Load document with \a aName.*/
513 bool SalomeApp_Application::onLoadDoc( const QString& aName )
515 if ( !LightApp_Application::closeDoc() )
519 if ( !activeStudy() ) {
520 // if no study - load in current desktop
521 res = useStudy( aName );
524 // if study exists - load in new desktop. Check: is the same file is loaded?
525 SUIT_Session* aSession = SUIT_Session::session();
526 QList<SUIT_Application*> aAppList = aSession->applications();
527 bool isAlreadyOpen = false;
528 SalomeApp_Application* aApp = 0;
529 for ( QList<SUIT_Application*>::iterator it = aAppList.begin();
530 it != aAppList.end() && !isAlreadyOpen; ++it ) {
531 aApp = dynamic_cast<SalomeApp_Application*>( *it );
532 if ( aApp && aApp->activeStudy()->studyName() == aName )
533 isAlreadyOpen = true;
535 if ( !isAlreadyOpen ) {
536 aApp = dynamic_cast<SalomeApp_Application*>( startApplication( 0, 0 ) );
538 res = aApp->useStudy( aName );
541 aApp->desktop()->activateWindow();
548 /*!SLOT. Parse message for desktop.*/
549 void SalomeApp_Application::onDesktopMessage( const QString& message )
551 if (message.indexOf("studyCreated") == 0) {
552 if (!activeStudy()) {
554 updateCommandsStatus();
557 if (message.indexOf("studyCleared") == 0) {
558 // Disconnect GUI from active study, because it was closed on DS side.
560 closeActiveDoc( false );
561 // Disable 'Connect' action
562 QAction* a = action( ConnectId );
564 a->setEnabled( false );
567 else if ( message.toLower() == "connect_to_study" ) {
570 if (message.indexOf("studyNameChanged") == 0) {
571 updateDesktopTitle();
573 LightApp_Application::onDesktopMessage( message );
576 /*!On module activation action.*/
577 void SalomeApp_Application::onModuleActivation( const QString& modName )
579 if (!activeStudy() && !modName.isEmpty())
582 LightApp_Application::onModuleActivation( modName );
585 /*!SLOT. Copy objects to study maneger from selection maneger..*/
586 void SalomeApp_Application::onCopy()
589 LightApp_SelectionMgr* mgr = selectionMgr();
590 mgr->selectedObjects(list);
592 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
593 if(study == NULL) return;
595 _PTR(Study) stdDS = getStudy();
598 SALOME_ListIteratorOfListIO it( list );
601 _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry());
604 onSelectionChanged();
611 /*!SLOT. Paste objects to study maneger from selection manager.*/
612 void SalomeApp_Application::onPaste()
615 LightApp_SelectionMgr* mgr = selectionMgr();
616 mgr->selectedObjects(list);
618 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
619 if(study == NULL) return;
621 _PTR(Study) stdDS = getStudy();
624 if ( stdDS->GetProperties()->IsLocked() ) {
625 SUIT_MessageBox::warning( desktop(),
626 QObject::tr("WRN_WARNING"),
627 QObject::tr("WRN_STUDY_LOCKED") );
631 SALOME_ListIteratorOfListIO it( list );
634 _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry());
637 updateObjectBrowser( true );
638 updateActions(); //SRN: BugID IPAL9377, case 3
645 /*!Check the application on closing.
646 * \retval true if possible, else false
648 bool SalomeApp_Application::isPossibleToClose( bool& closePermanently )
650 return LightApp_Application::isPossibleToClose( closePermanently );
653 /*! Check if the study is locked */
654 void SalomeApp_Application::onCloseDoc( bool ask )
656 if(getStudy()->IsStudyLocked()) {
657 if ( SUIT_MessageBox::question( desktop(),
658 QObject::tr( "WRN_WARNING" ),
659 QObject::tr( "CLOSE_LOCKED_STUDY" ),
660 SUIT_MessageBox::Yes | SUIT_MessageBox::No,
661 SUIT_MessageBox::No) == SUIT_MessageBox::No ) return;
664 LightApp_Application::onCloseDoc( ask );
666 // reinitialize study to have empty data
670 /*!Sets enable or disable some actions on selection changed.*/
671 void SalomeApp_Application::onSelectionChanged()
674 LightApp_SelectionMgr* mgr = selectionMgr();
675 mgr->selectedObjects(list);
677 bool canCopy = false;
678 bool canPaste = false;
680 LightApp_Module* m = dynamic_cast<LightApp_Module*>( activeModule() );
683 canCopy = m->canCopy();
684 canPaste = m->canPaste();
687 SALOME_ListIteratorOfListIO it ( list );
689 if (it.More() && list.Extent() == 1) {
690 _PTR(SObject) so = getStudy()->FindObjectID(it.Value()->getEntry());
693 canCopy = canCopy || getStudy()->CanCopy(so);
694 canPaste = canPaste || getStudy()->CanPaste(so);
698 action(EditCopyId)->setEnabled(canCopy);
699 action(EditPasteId)->setEnabled(canPaste);
702 /*!Delete references.*/
703 void SalomeApp_Application::onDeleteInvalidReferences()
706 LightApp_SelectionMgr* mgr = selectionMgr();
707 mgr->selectedObjects( aList, QString(), false );
709 if( aList.IsEmpty() )
712 _PTR(Study) aStudyDS = getStudy();
713 _PTR(StudyBuilder) aStudyBuilder = aStudyDS->NewBuilder();
716 for( SALOME_ListIteratorOfListIO it( aList ); it.More(); it.Next() )
717 if ( it.Value()->hasEntry() )
719 _PTR(SObject) aSObject = aStudyDS->FindObjectID( it.Value()->getEntry() ), aRefObj = aSObject;
720 while( aRefObj && aRefObj->ReferencedObject( anObj ) )
723 if( aRefObj && aRefObj!=aSObject && QString( aRefObj->GetName().c_str() ).isEmpty() )
724 aStudyBuilder->RemoveReference( aSObject );
726 updateObjectBrowser();
730 void SalomeApp_Application::onOpenWith()
732 QApplication::setOverrideCursor( Qt::WaitCursor );
734 LightApp_SelectionMgr* mgr = selectionMgr();
735 mgr->selectedObjects(aList);
736 if (aList.Extent() != 1)
738 QApplication::restoreOverrideCursor();
741 Handle(SALOME_InteractiveObject) aIObj = aList.First();
742 QString aModuleName(aIObj->getComponentDataType());
743 QString aModuleTitle = moduleTitle(aModuleName);
744 activateModule(aModuleTitle);
745 QApplication::restoreOverrideCursor();
751 SUIT_Study* SalomeApp_Application::createNewStudy()
753 SalomeApp_Study* aStudy = new SalomeApp_Study( this );
755 // Set up processing of major study-related events
756 connect( aStudy, SIGNAL( created( SUIT_Study* ) ), this, SLOT( onStudyCreated( SUIT_Study* ) ) );
757 connect( aStudy, SIGNAL( opened ( SUIT_Study* ) ), this, SLOT( onStudyOpened ( SUIT_Study* ) ) );
758 connect( aStudy, SIGNAL( saved ( SUIT_Study* ) ), this, SLOT( onStudySaved ( SUIT_Study* ) ) );
759 connect( aStudy, SIGNAL( closed ( SUIT_Study* ) ), this, SLOT( onStudyClosed ( SUIT_Study* ) ) );
761 #ifndef DISABLE_PYCONSOLE
762 //to receive signal in application that NoteBook's variable was modified
763 connect( aStudy, SIGNAL(notebookVarUpdated(QString)),
764 this, SIGNAL(notebookVarUpdated(QString)) );
771 Enable/Disable menu items and toolbar buttons. Rebuild menu
773 void SalomeApp_Application::updateCommandsStatus()
775 LightApp_Application::updateCommandsStatus();
778 QAction* a = action( DumpStudyId );
780 a->setEnabled( activeStudy() );
782 #ifndef DISABLE_PYCONSOLE
784 a = action( LoadScriptId );
786 a->setEnabled( pythonConsole() );
790 a = action( PropertiesId );
792 a->setEnabled( activeStudy() );
794 // Save GUI state menu
795 a = action( SaveGUIStateId );
797 a->setEnabled( activeStudy() );
799 // Connect study menu
800 a = action( ConnectId );
802 a->setEnabled( !activeStudy() );
804 // Disconnect study menu
805 a = action( DisconnectId );
807 a->setEnabled( activeStudy() );
809 // update state of Copy/Paste menu items
810 onSelectionChanged();
814 \class DumpStudyFileDlg
815 Private class used in Dump Study operation. Consists 2 check boxes:
816 "Publish in study" and "Save GUI parameters"
818 class DumpStudyFileDlg : public SUIT_FileDlg
821 DumpStudyFileDlg( QWidget* parent ) : SUIT_FileDlg( parent, false, true, true )
823 QGridLayout* grid = ::qobject_cast<QGridLayout*>( layout() );
826 QWidget *hB = new QWidget( this );
827 myPublishChk = new QCheckBox( tr("PUBLISH_IN_STUDY") );
828 myMultiFileChk = new QCheckBox( tr("MULTI_FILE_DUMP") );
829 mySaveGUIChk = new QCheckBox( tr("SAVE_GUI_STATE") );
831 QHBoxLayout *layout = new QHBoxLayout;
832 layout->addWidget(myPublishChk);
833 layout->addWidget(myMultiFileChk);
834 layout->addWidget(mySaveGUIChk);
835 hB->setLayout(layout);
837 QPushButton* pb = new QPushButton(this);
839 int row = grid->rowCount();
840 grid->addWidget( new QLabel("", this), row, 0 );
841 grid->addWidget( hB, row, 1, 1, 3 );
842 grid->addWidget( pb, row, 5 );
847 QCheckBox* myPublishChk;
848 QCheckBox* myMultiFileChk;
849 QCheckBox* mySaveGUIChk;
852 /*!Private SLOT. On dump study.*/
853 void SalomeApp_Application::onDumpStudy( )
855 SalomeApp_Study* appStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
856 if ( !appStudy ) return;
858 QStringList aFilters;
859 aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
861 bool anIsPublish = true;
862 bool anIsMultiFile = false;
863 bool anIsSaveGUI = true;
865 if ( SUIT_ResourceMgr* aResourceMgr = resourceMgr() ) {
866 anIsPublish = aResourceMgr->booleanValue( "Study", "pydump_publish", anIsPublish );
867 anIsMultiFile = aResourceMgr->booleanValue( "Study", "multi_file_dump", anIsMultiFile );
868 anIsSaveGUI = aResourceMgr->booleanValue( "Study", "pydump_save_gui", anIsSaveGUI );
871 DumpStudyFileDlg fd( desktop() );
872 fd.setValidator( new LightApp_PyFileValidator( &fd ) );
873 fd.setWindowTitle( tr( "TOT_DESK_FILE_DUMP_STUDY" ) );
874 fd.setNameFilters( aFilters );
875 fd.myPublishChk->setChecked( anIsPublish );
876 fd.myMultiFileChk->setChecked( anIsMultiFile );
877 fd.mySaveGUIChk->setChecked( anIsSaveGUI );
878 if ( fd.exec() == QDialog::Accepted )
880 QString aFileName = fd.selectedFile();
882 bool toPublish = fd.myPublishChk->isChecked();
883 bool isMultiFile = fd.myMultiFileChk->isChecked();
884 bool toSaveGUI = fd.mySaveGUIChk->isChecked();
886 if ( !aFileName.isEmpty() ) {
887 QFileInfo aFileInfo(aFileName);
888 if( aFileInfo.isDir() ) // IPAL19257
891 // Issue 21377 - dump study implementation moved to SalomeApp_Study class
894 SUIT_OverrideCursor wc;
895 res = appStudy->dump( aFileName, toPublish, isMultiFile, toSaveGUI );
898 SUIT_MessageBox::warning( desktop(),
899 QObject::tr("WRN_WARNING"),
900 tr("WRN_DUMP_STUDY_FAILED") );
905 /*!Private SLOT. On load script.*/
906 void SalomeApp_Application::onLoadScript( )
908 if ( getStudy()->GetProperties()->IsLocked() ) {
909 SUIT_MessageBox::warning( desktop(),
910 QObject::tr("WRN_WARNING"),
911 QObject::tr("WRN_STUDY_LOCKED") );
915 QStringList filtersList;
916 filtersList.append(tr("PYTHON_FILES_FILTER"));
917 filtersList.append(tr("ALL_FILES_FILTER"));
919 QString anInitialPath = "";
920 if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
921 anInitialPath = QDir::currentPath();
923 QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true );
925 if ( !aFile.isEmpty() )
927 QString command = QString("exec(open(\"%1\",encoding='utf-8').read())").arg(aFile);
929 #ifndef DISABLE_PYCONSOLE
930 PyConsole_Console* pyConsole = pythonConsole();
933 pyConsole->exec( command );
938 /*!Private SLOT. On save GUI state.*/
939 void SalomeApp_Application::onSaveGUIState()
941 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
943 SalomeApp_VisualState( this ).storeState();
944 updateSavePointDataObjects( study );
945 updateObjectBrowser();
950 /*!Public SLOT. Performs some actions when dockable windows are triggered.*/
951 void SalomeApp_Application::onDockWindowVisibilityChanged( bool theIsVisible )
953 LightApp_Application::onDockWindowVisibilityChanged( theIsVisible );
954 QAction* send = ::qobject_cast<QAction*>( sender() );
957 QString aWinName = send->data().toString();
958 if ( theIsVisible && aWinName == "objectBrowser" )
959 objectBrowserColumnsVisibility();
963 QWidget* SalomeApp_Application::createWindow( const int flag )
966 #ifndef DISABLE_PYCONSOLE
967 if ( flag != WT_PyConsole ) wid = LightApp_Application::createWindow(flag);
969 wid = LightApp_Application::createWindow(flag);
972 SUIT_ResourceMgr* resMgr = resourceMgr();
974 if ( flag == WT_ObjectBrowser )
976 SUIT_DataBrowser* ob = qobject_cast<SUIT_DataBrowser*>( wid );
978 // temporary commented
979 //ob->setUpdater( new SalomeApp_Updater() );
981 #ifdef WITH_SALOMEDS_OBSERVER
982 //do not activate the automatic update of Qt tree through signal/slot
983 ob->setAutoUpdate(false);
984 //activate update of modified objects only
985 ob->setUpdateModified(true);
988 connect( ob, SIGNAL( doubleClicked( SUIT_DataObject* ) ), this, SLOT( onDblClick( SUIT_DataObject* ) ) );
991 ValueCol = QObject::tr( "VALUE_COLUMN" ),
992 IORCol = QObject::tr( "IOR_COLUMN" ),
993 RefCol = QObject::tr( "REFENTRY_COLUMN" ),
994 EntryCol = QObject::tr( "ENTRY_COLUMN" );
996 SUIT_AbstractModel* treeModel = dynamic_cast<SUIT_AbstractModel*>( ob->model() );
997 treeModel->registerColumn( 0, EntryCol, SalomeApp_DataObject::EntryId );
998 treeModel->registerColumn( 0, ValueCol, SalomeApp_DataObject::ValueId );
999 treeModel->registerColumn( 0, IORCol, SalomeApp_DataObject::IORId );
1000 treeModel->registerColumn( 0, RefCol, SalomeApp_DataObject::RefEntryId );
1001 treeModel->setAppropriate( EntryCol, Qtx::Toggled );
1002 treeModel->setAppropriate( ValueCol, Qtx::Toggled );
1003 treeModel->setAppropriate( IORCol, Qtx::Toggled );
1004 treeModel->setAppropriate( RefCol, Qtx::Toggled );
1006 bool autoSize = resMgr->booleanValue( "ObjectBrowser", "auto_size", false );
1007 bool autoSizeFirst = resMgr->booleanValue( "ObjectBrowser", "auto_size_first", true );
1008 bool resizeOnExpandItem = resMgr->booleanValue( "ObjectBrowser", "resize_on_expand_item", true );
1010 ob->setAutoSizeFirstColumn(autoSizeFirst);
1011 ob->setAutoSizeColumns(autoSize);
1012 ob->setResizeOnExpandItem(resizeOnExpandItem);
1013 ob->setProperty( "shortcut", QKeySequence( "Alt+Shift+O" ) );
1015 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1017 bool shown = resourceMgr()->booleanValue( "ObjectBrowser", QString( "visibility_column_id_%1" ).arg( i-1 ), true );
1018 ob->treeView()->setColumnHidden( i, !shown );
1021 // temporary commented
1023 for ( int i = SalomeApp_DataObject::ValueIdx; i <= SalomeApp_DataObject::RefEntryIdx; i++ )
1025 ob->addColumn( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i ) ), i );
1026 ob->setColumnShown( i, resMgr->booleanValue( "ObjectBrowser",
1027 QString().sprintf( "visibility_column_%d", i ), true ) );
1031 // temporary commented
1033 ob->setWidthMode( autoSize ? QListView::Maximum : QListView::Manual );
1034 ob->listView()->setColumnWidthMode( 0, autoSizeFirst ? QListView::Maximum : QListView::Manual );
1035 ob->resize( desktop()->width()/3, ob->height() );
1039 #ifndef DISABLE_PYCONSOLE
1040 else if ( flag == WT_PyConsole )
1042 PyConsole_Console* pyCons = new PyConsole_Console( desktop(), new LightApp_PyEditor( getPyInterp() ) );
1043 pyCons->setObjectName( "pythonConsole" );
1044 pyCons->setWindowTitle( tr( "PYTHON_CONSOLE" ) );
1045 pyCons->setFont(resourceMgr()->fontValue( "PyConsole", "font" ));
1046 pyCons->setIsShowBanner(resourceMgr()->booleanValue( "PyConsole", "show_banner", true ));
1047 pyCons->setAutoCompletion( resMgr->booleanValue( "PyConsole", "auto_completion", true ) );
1048 pyCons->setProperty( "shortcut", QKeySequence( "Alt+Shift+P" ) );
1051 else if ( flag == WT_NoteBook )
1053 setNoteBook( new SalomeApp_NoteBook( desktop() ) );
1054 //to receive signal in NoteBook that it's variable was modified
1055 connect( this, SIGNAL( notebookVarUpdated( QString ) ),
1056 getNoteBook(), SLOT( onVarUpdate( QString ) ) );
1058 wid = getNoteBook();
1059 wid->setObjectName( "noteBook" );
1065 /*!Create preferences.*/
1066 void SalomeApp_Application::createPreferences( LightApp_Preferences* pref )
1068 LightApp_Application::createPreferences(pref);
1073 int salomeCat = pref->addPreference( tr( "PREF_CATEGORY_SALOME" ) );
1074 int obTab = pref->addPreference( tr( "PREF_TAB_OBJBROWSER" ), salomeCat );
1075 int defCols = pref->addPreference( tr( "PREF_GROUP_DEF_COLUMNS" ), obTab );
1076 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1078 pref->addPreference( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i-SalomeApp_DataObject::EntryId ).toLatin1() ), defCols,
1079 LightApp_Preferences::Bool, "ObjectBrowser", QString().sprintf( "visibility_column_id_%d", i-1 ) );
1081 pref->setItemProperty( "orientation", Qt::Vertical, defCols );
1083 // adding preference to LightApp_Application handled preferences.. a bit of hacking with resources..
1084 int genTab = pref->addPreference( LightApp_Application::tr( "PREF_TAB_GENERAL" ), salomeCat );
1085 int studyGroup = pref->addPreference( LightApp_Application::tr( "PREF_GROUP_STUDY" ), genTab );
1086 pref->addPreference( tr( "PREF_STORE_VISUAL_STATE" ), studyGroup, LightApp_Preferences::Bool, "Study", "store_visual_state" );
1087 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1088 pref->addPreference( tr( "PREF_PYDUMP_PUBLISH" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_publish" );
1089 pref->addPreference( tr( "PREF_PYDUMP_MULTI_FILE" ), studyGroup, LightApp_Preferences::Bool, "Study", "multi_file_dump" );
1090 pref->addPreference( tr( "PREF_PYDUMP_SAVE_GUI" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_save_gui" );
1091 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1092 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1095 /*!Update desktop title.*/
1096 void SalomeApp_Application::updateDesktopTitle() {
1097 QString aTitle = applicationName();
1098 QString aVer = applicationVersion();
1099 if ( !aVer.isEmpty() )
1100 aTitle += QString( " " ) + aVer;
1102 if ( activeStudy() )
1104 QString sName = SUIT_Tools::file( activeStudy()->studyName().trimmed(), false );
1105 if ( !sName.isEmpty() ) {
1106 if ( getStudy()->GetProperties()->IsLocked() ) {
1107 aTitle += QString( " - [%1 (%2)]").arg( sName ).arg( tr( "STUDY_LOCKED" ) );
1109 aTitle += QString( " - [%1]" ).arg( sName );
1114 desktop()->setWindowTitle( aTitle );
1117 int SalomeApp_Application::closeChoice( const QString& docName )
1119 QStringList buttons;
1120 QMap<int, int> choices;
1122 buttons << tr ("APPCLOSE_SAVE"); // Save & Clear
1123 choices.insert( idx++, CloseSave ); // ...
1124 buttons << tr ("APPCLOSE_CLOSE"); // Clear w/o saving
1125 choices.insert( idx++, CloseDiscard ); // ...
1126 if ( myIsCloseFromExit ) {
1127 buttons << tr ("APPCLOSE_UNLOAD_SAVE"); // Save & Disconnect
1128 choices.insert( idx++, CloseDisconnectSave ); // ...
1129 buttons << tr ("APPCLOSE_UNLOAD"); // Disconnect
1130 choices.insert( idx++, CloseDisconnect ); // ...
1132 buttons << tr ("APPCLOSE_CANCEL"); // Cancel
1133 choices.insert( idx++, CloseCancel ); // ...
1135 if( !activeStudy()->isModified() )
1137 int answer = SUIT_MessageBox::question( desktop(), tr( "APPCLOSE_CAPTION" ),
1138 tr( "APPCLOSE_DESCRIPTION" ), buttons, 0 );
1139 return choices[answer];
1142 bool SalomeApp_Application::closeAction( const int choice, bool& closePermanently )
1148 if ( activeStudy()->isSaved() )
1150 else if ( !onSaveAsDoc() )
1155 case CloseDisconnectSave:
1156 if ( activeStudy()->isSaved() )
1158 else if ( !onSaveAsDoc() )
1160 case CloseDisconnect:
1161 closeActiveDoc( false );
1162 closePermanently = false;
1171 int SalomeApp_Application::openChoice( const QString& aName )
1173 int choice = LightApp_Application::openChoice( aName );
1175 if ( QFileInfo( aName ).exists() ) {
1176 if ( choice == OpenNew ) { // The document isn't already open.
1178 if ( aName == getStudy()->Name().c_str() )
1180 // The document already exists in the study.
1181 // Do you want to reload it?
1183 int answer = SUIT_MessageBox::question( desktop(), tr( "WRN_WARNING" ), tr( "QUE_DOC_ALREADYEXIST" ).arg( aName ),
1184 SUIT_MessageBox::Yes | SUIT_MessageBox::No, SUIT_MessageBox::No );
1185 if ( answer == SUIT_MessageBox::Yes )
1186 choice = OpenRefresh;
1188 choice = OpenCancel;
1191 } else { // file is not exist on disk
1192 SUIT_MessageBox::warning( desktop(),
1193 QObject::tr("WRN_WARNING"),
1194 QObject::tr("WRN_FILE_NOT_EXIST").arg(aName.toLatin1().data()));
1201 bool SalomeApp_Application::openAction( const int aChoice, const QString& aName )
1204 int choice = aChoice;
1212 res = LightApp_Application::openAction( choice, aName );
1220 \brief Get map of the operations which can be performed
1221 on the module activation.
1223 The method should return the map of the kind \c {<id>:<name>}
1224 where \c <id> is an integer identifier of the operation and
1225 \c <name> is a title for the button to be added to the
1226 dialog box. After user selects the required operation by the
1227 clicking the corresponding button in the dialog box, its identifier
1228 is passed to the moduleActionSelected() method to process
1231 \return map of the operations
1232 \sa moduleActionSelected()
1234 QMap<int, QString> SalomeApp_Application::activateModuleActions() const
1236 QMap<int, QString> opmap = LightApp_Application::activateModuleActions();
1238 opmap.insert( LoadStudyId, tr( "ACTIVATE_MODULE_OP_LOAD" ) );
1240 opmap.insert( NewAndScriptId, tr( "ACTIVATE_MODULE_OP_SCRIPT" ) );
1245 \brief Called when the used selectes required operation chosen
1246 from "Activate module" dialog box.
1248 Performs the required operation according to the user choice.
1250 \param id operation identifier
1251 \sa activateModuleActions()
1253 void SalomeApp_Application::moduleActionSelected( const int id )
1259 case NewAndScriptId:
1263 LightApp_Application::moduleActionSelected( id );
1268 /*!Gets CORBA::ORB_var*/
1269 CORBA::ORB_var SalomeApp_Application::orb()
1271 static CORBA::ORB_var _orb;
1273 if ( CORBA::is_nil( _orb ) ) {
1274 Qtx::CmdLineArgs args;
1275 ORB_INIT& init = *SINGLETON_<ORB_INIT>::Instance();
1276 _orb = init( args.argc(), args.argv() );
1282 /*!Create and return SALOMEDS_Study.*/
1283 _PTR(Study) SalomeApp_Application::getStudy()
1285 static _PTR(Study) _study;
1287 CORBA::Object_var aSObject = namingService()->Resolve("/Study");
1288 SALOMEDS::Study_var aStudy = SALOMEDS::Study::_narrow(aSObject);
1289 _study = ClientFactory::Study(aStudy);
1294 /*!Create and return SALOME_NamingService.*/
1295 SALOME_NamingService* SalomeApp_Application::namingService()
1297 static SALOME_NamingService _ns(orb());
1301 /*!Create and return SALOME_LifeCycleCORBA.*/
1302 SALOME_LifeCycleCORBA* SalomeApp_Application::lcc()
1304 static SALOME_LifeCycleCORBA _lcc( namingService() );
1308 /*!Private SLOT. On preferences.*/
1309 void SalomeApp_Application::onProperties()
1311 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1315 _PTR(StudyBuilder) SB = study->studyDS()->NewBuilder();
1318 SalomeApp_StudyPropertiesDlg aDlg( desktop() );
1319 int res = aDlg.exec();
1320 if( res==QDialog::Accepted && aDlg.isChanged() )
1321 SB->CommitCommand();
1325 //study->updateCaptions();
1326 updateDesktopTitle();
1330 /*!Insert items in popup, which necessary for current application*/
1331 void SalomeApp_Application::contextMenuPopup( const QString& type, QMenu* thePopup, QString& title )
1333 LightApp_SelectionMgr* mgr = selectionMgr();
1334 bool cacheIsOn = mgr->isSelectionCacheEnabled();
1335 mgr->setSelectionCacheEnabled( true );
1337 LightApp_Application::contextMenuPopup( type, thePopup, title );
1339 // temporary commented
1340 /*OB_Browser* ob = objectBrowser();
1341 if ( !ob || type != ob->popupClientType() )
1344 // Get selected objects
1345 SALOME_ListIO aList;
1346 mgr->selectedObjects( aList, QString(), false );
1348 // add GUI state commands: restore, rename
1349 if ( aList.Extent() == 1 && aList.First()->hasEntry() &&
1350 QString( aList.First()->getEntry() ).startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) {
1351 thePopup->addSeparator();
1352 thePopup->addAction( tr( "MEN_RESTORE_VS" ), this, SLOT( onRestoreGUIState() ) );
1353 thePopup->addAction( tr( "MEN_RENAME_VS" ), objectBrowser(),
1354 SLOT( onStartEditing() ), objectBrowser()->shortcutKey(SUIT_DataBrowser::RenameShortcut) );
1355 thePopup->addAction( tr( "MEN_DELETE_VS" ), this, SLOT( onDeleteGUIState() ) );
1358 // "Delete reference" item should appear only for invalid references
1360 // isInvalidRefs will be true, if at least one of selected objects is invalid reference
1361 bool isInvalidRefs = false;
1363 _PTR(SObject) anObj;
1364 for( SALOME_ListIteratorOfListIO it( aList ); it.More() && !isInvalidRefs; it.Next() )
1366 if( it.Value()->hasEntry() )
1368 _PTR(SObject) aSObject = getStudy()->FindObjectID( it.Value()->getEntry() ), aRefObj = aSObject;
1369 while( aRefObj && aRefObj->ReferencedObject( anObj ) )
1372 if( aRefObj && aRefObj!=aSObject && QString( aRefObj->GetName().c_str() ).isEmpty() )
1373 isInvalidRefs = true;
1377 // Add "Delete reference" item to popup
1378 if ( isInvalidRefs )
1380 thePopup->addSeparator();
1381 thePopup->addAction( tr( "MEN_DELETE_INVALID_REFERENCE" ), this, SLOT( onDeleteInvalidReferences() ) );
1385 // "Activate module" item should appear only if it's necessary
1386 if ( aList.Extent() == 1 ) {
1388 mgr->selectedObjects( aList );
1390 Handle(SALOME_InteractiveObject) aIObj = aList.First();
1392 // add extra popup menu (defined in XML)
1393 if ( myExtActions.size() > 0 ) {
1394 // Use only first selected object
1395 _PTR(SObject) aSO = getStudy()->FindObjectID( aIObj->getEntry() );
1397 _PTR( GenericAttribute ) anAttr;
1398 std::string auid = "AttributeUserID";
1399 auid += Kernel_Utils::GetGUID(Kernel_Utils::ObjectdID);
1400 if ( aSO->FindAttribute( anAttr, auid ) ) {
1401 _PTR(AttributeUserID) aAttrID = anAttr;
1402 QString aId = aAttrID->Value().c_str();
1403 if ( myExtActions.contains( aId ) ) {
1404 thePopup->addAction(myExtActions[aId]);
1410 // check if item is a "GUI state" item (also a first level object)
1411 QString entry( aIObj->getEntry() );
1412 if ( !entry.startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) {
1413 QString aModuleName( aIObj->getComponentDataType() );
1414 QString aModuleTitle = moduleTitle( aModuleName );
1415 CAM_Module* currentModule = activeModule();
1416 if ( ( !currentModule || currentModule->moduleName() != aModuleTitle ) && !aModuleTitle.isEmpty() )
1417 thePopup->addAction( tr( "MEN_OPENWITH" ).arg( aModuleTitle ), this, SLOT( onOpenWith() ) );
1421 mgr->setSelectionCacheEnabled( cacheIsOn );
1424 /*!Update obect browser:
1425 1.if 'updateModels' true, update existing data models;
1426 2. update "non-existing" (not loaded yet) data models;
1427 3. update object browser if it exists */
1428 void SalomeApp_Application::updateObjectBrowser( const bool updateModels )
1430 // update "non-existing" (not loaded yet) data models
1431 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
1434 for ( _PTR(SComponentIterator) it ( getStudy()->NewComponentIterator() ); it->More(); it->Next() )
1436 _PTR(SComponent) aComponent ( it->Value() );
1438 #ifndef WITH_SALOMEDS_OBSERVER
1439 // with GUI observers this check is not needed anymore
1440 if ( aComponent->ComponentDataType() == study->getVisualComponentName().toLatin1().constData() )
1441 continue; // skip the magic "Interface Applicative" component
1443 if ( !objectBrowser() )
1444 getWindow( WT_ObjectBrowser );
1445 const bool isAutoUpdate = objectBrowser()->autoUpdate();
1446 objectBrowser()->setAutoUpdate( false );
1447 SalomeApp_DataModel::synchronize( aComponent, study );
1448 objectBrowser()->setAutoUpdate( isAutoUpdate );
1452 // create data objects that correspond to GUI state save points
1453 if ( study ) updateSavePointDataObjects( study );
1455 // update existing data models (already loaded SComponents)
1456 LightApp_Application::updateObjectBrowser( updateModels );
1459 /*!Display Catalog Genenerator dialog */
1460 void SalomeApp_Application::onCatalogGen()
1462 ToolsGUI_CatalogGeneratorDlg aDlg( desktop() );
1466 /*!Display Registry Display dialog */
1467 void SalomeApp_Application::onRegDisplay()
1469 CORBA::ORB_var anOrb = orb();
1470 ToolsGUI_RegWidget* regWnd = ToolsGUI_RegWidget::GetRegWidget( anOrb, desktop() );
1473 regWnd->activateWindow();
1476 /*!find original object by double click on item */
1477 void SalomeApp_Application::onDblClick( SUIT_DataObject* theObj )
1479 // Issue 21379: References are supported at LightApp_DataObject level
1480 LightApp_DataObject* obj = dynamic_cast<LightApp_DataObject*>( theObj );
1482 if( obj && obj->isReference() )
1484 QString entry = obj->refEntry();
1486 SUIT_DataOwnerPtrList aList;
1487 aList.append( new LightApp_DataOwner( entry ) );
1488 selectionMgr()->setSelected( aList, false );
1490 SUIT_DataBrowser* ob = objectBrowser();
1492 QModelIndexList aSelectedIndexes = ob->selectedIndexes();
1493 if ( !aSelectedIndexes.isEmpty() )
1494 ob->treeView()->scrollTo( aSelectedIndexes.first() );
1496 emit objectDoubleClicked( theObj );
1500 Creates new view manager
1501 \param type - type of view manager
1503 SUIT_ViewManager* SalomeApp_Application::newViewManager(const QString& type)
1505 return createViewManager(type);
1509 /*!Global utility function, returns selected GUI Save point object's ID */
1510 int getSelectedSavePoint( const LightApp_SelectionMgr* selMgr )
1512 SALOME_ListIO aList;
1513 selMgr->selectedObjects( aList );
1514 if( aList.Extent() > 0 ) {
1515 Handle(SALOME_InteractiveObject) aIObj = aList.First();
1516 QString entry( aIObj->getEntry() );
1517 QString startStr = QObject::tr( "SAVE_POINT_DEF_NAME" );
1518 if ( !entry.startsWith( startStr ) ) // it's a "GUI state" object
1520 bool ok; // conversion to integer is ok?
1521 int savePoint = entry.right( entry.length() - startStr.length() ).toInt( &ok );
1522 return ok ? savePoint : -1;
1527 /*!Called on Restore GUI State popup command*/
1528 void SalomeApp_Application::onRestoreGUIState()
1530 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1531 if ( savePoint == -1 )
1533 SalomeApp_VisualState( this ).restoreState( savePoint );
1536 /*!Called on Delete GUI State popup command*/
1537 void SalomeApp_Application::onDeleteGUIState()
1539 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1540 if ( savePoint == -1 )
1542 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1546 study->removeSavePoint( savePoint );
1547 updateSavePointDataObjects( study );
1550 /*!Called on New study operation*/
1551 void SalomeApp_Application::onStudyCreated( SUIT_Study* study )
1553 LightApp_Application::onStudyCreated( study );
1555 //#ifndef DISABLE_PYCONSOLE
1556 // desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
1557 // windowDock( getWindow( WT_ObjectBrowser ) ) );
1560 loadDockWindowsState();
1562 objectBrowserColumnsVisibility();
1565 /*!Called on Open study operation*/
1566 void SalomeApp_Application::onStudyOpened( SUIT_Study* study )
1568 LightApp_Application::onStudyOpened( study );
1570 //#ifndef DISABLE_PYCONSOLE
1571 // desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
1572 // windowDock( getWindow( WT_ObjectBrowser ) ) );
1575 loadDockWindowsState();
1577 objectBrowserColumnsVisibility();
1579 // temporary commented
1580 /*if ( objectBrowser() ) {
1581 updateSavePointDataObjects( dynamic_cast<SalomeApp_Study*>( study ) );
1582 objectBrowser()->updateTree( study->root() );
1586 /*! updateSavePointDataObjects: syncronize data objects that correspond to save points (gui states)*/
1587 void SalomeApp_Application::updateSavePointDataObjects( SalomeApp_Study* study )
1590 SUIT_DataBrowser* ob = objectBrowser();
1591 LightApp_SelectionMgr* selMgr = selectionMgr();
1593 if ( !study || !ob || !selMgr )
1596 // find GUI states root object
1597 SUIT_DataObject* guiRootObj = 0;
1599 study->root()->children( ch );
1600 DataObjectList::const_iterator it = ch.begin(), last = ch.end();
1601 for ( ; it != last ; ++it ) {
1602 if ( dynamic_cast<SalomeApp_SavePointRootObject*>( *it ) ) {
1607 std::vector<int> savePoints = study->getSavePoints();
1608 // case 1: no more save points but they existed in study's tree
1609 if ( savePoints.empty() && guiRootObj ) {
1610 //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state"
1611 // : set auto update to true for removing SalomeApp_SavePointRootObject from the SUIT_TreeModel
1612 const bool isAutoUpdate = ob->autoUpdate();
1613 selMgr->clearSelected();
1614 ob->setAutoUpdate(true);
1615 DataObjectList ch = guiRootObj->children();
1616 for( int i = 0; i < ch.size(); i++ )
1619 ob->setAutoUpdate(isAutoUpdate);
1622 // case 2: no more save points but root does not exist either
1623 if ( savePoints.empty() && !guiRootObj )
1625 // case 3: save points but no root for them - create it
1626 if ( !savePoints.empty() && !guiRootObj )
1627 guiRootObj = new SalomeApp_SavePointRootObject( study->root() );
1628 // case 4: everything already exists.. here may be a problem: we want "GUI states" root object
1629 // to be always the last one in the tree. Here we check - if it is not the last one - remove and
1631 if ( guiRootObj->nextBrother() ) {
1632 study->root()->removeChild(guiRootObj);
1633 study->root()->appendChild(guiRootObj);
1634 //study->root()->dump();
1637 // store data objects in a map id-to-DataObject
1638 QMap<int,SalomeApp_SavePointObject*> mapDO;
1640 guiRootObj->children( ch );
1641 for( it = ch.begin(), last = ch.end(); it != last ; ++it ) {
1642 SalomeApp_SavePointObject* dobj = dynamic_cast<SalomeApp_SavePointObject*>( *it );
1644 mapDO[dobj->getId()] = dobj;
1647 // iterate new save points. if DataObject with such ID not found in map - create DataObject
1648 // if in the map - remove it from map.
1649 for ( int i = 0; i < savePoints.size(); i++ )
1650 if ( !mapDO.contains( savePoints[i] ) )
1651 new SalomeApp_SavePointObject( guiRootObj, savePoints[i], study );
1653 mapDO.remove( savePoints[i] );
1655 // delete DataObjects that are still in the map -- their IDs were not found in data model
1656 if( mapDO.size() > 0) {
1657 //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state"
1658 // : set auto update to true for removing SalomeApp_SavePointObject from the SUIT_TreeModel
1659 selMgr->clearSelected();
1660 const bool isAutoUpdate = ob->autoUpdate();
1661 ob->setAutoUpdate(true);
1662 for ( QMap<int,SalomeApp_SavePointObject*>::Iterator it = mapDO.begin(); it != mapDO.end(); ++it )
1664 ob->setAutoUpdate(isAutoUpdate);
1668 /*! Check data object */
1669 bool SalomeApp_Application::checkDataObject(LightApp_DataObject* theObj)
1678 Opens other study into active Study. If Study is empty - creates it.
1679 \param theName - name of study
1681 bool SalomeApp_Application::useStudy( const QString& theName )
1684 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1687 res = aStudy->loadDocument( theName );
1688 updateDesktopTitle();
1689 updateCommandsStatus();
1693 /*! Show/hide object browser colums according to preferences */
1694 void SalomeApp_Application::objectBrowserColumnsVisibility()
1696 if ( objectBrowser() )
1697 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1699 bool shown = resourceMgr()->booleanValue( "ObjectBrowser", QString( "visibility_column_id_%1" ).arg( i-1 ), true );
1700 objectBrowser()->treeView()->setColumnHidden( i, !shown );
1704 #ifndef DISABLE_PYCONSOLE
1705 /*! Set SalomeApp_NoteBook pointer */
1706 void SalomeApp_Application::setNoteBook( SalomeApp_NoteBook* theNoteBook )
1708 myNoteBook = theNoteBook;
1711 /*! Return SalomeApp_NoteBook pointer */
1712 SalomeApp_NoteBook* SalomeApp_Application::getNoteBook() const
1719 * Define extra actions defined in module definition XML file.
1720 * Additional popup items sections can be defined by parameter "popupitems".
1721 * Supported attributes:
1722 * title - title of menu item,
1723 * attributelocalid - AttributeLocalId defined for selected data item where menu command has to be applied,
1724 * method - method which has to be called when menu item is selected
1726 * <section name="MODULENAME">
1727 * <parameter name="popupitems" value="menuitem1:menuitem2:..."/>
1729 * <section name="importmed">
1730 * <parameter name="title" value="My menu"/>
1731 * <parameter name="objectid" value="VISU.Result"/>
1732 * <parameter name="method" value="nameOfModuleMethod"/>
1735 void SalomeApp_Application::createExtraActions()
1737 myExtActions.clear();
1738 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
1740 QStringList aModules;
1741 modules(aModules, false);
1742 foreach(QString aModile, aModules) {
1743 QString aModName = moduleName(aModile);
1744 QString aSectionStr = resMgr->stringValue(aModName, "popupitems", QString());
1745 if (!aSectionStr.isNull()) {
1746 QStringList aSections = aSectionStr.split(':');
1747 foreach(QString aSection, aSections) {
1748 QString aTitle = resMgr->stringValue(aSection, "title", QString());
1749 QString aId = resMgr->stringValue(aSection, "objectid", QString());
1750 QString aSlot = resMgr->stringValue(aSection, "method", QString());
1751 if (aTitle.isEmpty() || aSlot.isEmpty() || aId.isEmpty())
1754 QString aModuleName = resMgr->stringValue(aSection, "module", QString());
1755 if (aModuleName.isNull())
1756 aModuleName = aModName;
1758 QAction* aAction = new QAction(aTitle, this);
1760 aData<<aModuleName<<aSlot;
1761 aAction->setData(aData);
1762 connect(aAction, SIGNAL(triggered()), this, SLOT(onExtAction()));
1763 myExtActions[aId] = aAction;
1770 * Called when extra action is selected
1772 void SalomeApp_Application::onExtAction()
1774 QAction* aAction = ::qobject_cast<QAction*>(sender());
1778 QVariant aData = aAction->data();
1779 QStringList aDataList = aData.value<QStringList>();
1780 if (aDataList.size() != 2)
1783 LightApp_SelectionMgr* aSelectionMgr = selectionMgr();
1784 SALOME_ListIO aListIO;
1785 aSelectionMgr->selectedObjects(aListIO);
1786 const Handle(SALOME_InteractiveObject)& anIO = aListIO.First();
1787 if (aListIO.Extent() < 1)
1789 if (!anIO->hasEntry())
1792 QString aEntry(anIO->getEntry());
1794 QApplication::setOverrideCursor( Qt::WaitCursor );
1795 QString aModuleTitle = moduleTitle(aDataList[0]);
1796 activateModule(aModuleTitle);
1797 QApplication::restoreOverrideCursor();
1799 QCoreApplication::processEvents();
1801 CAM_Module* aModule = activeModule();
1805 if (!QMetaObject::invokeMethod(aModule, qPrintable(aDataList[1]), Q_ARG(QString, aEntry)))
1806 printf("Error: Can't Invoke method %s\n", qPrintable(aDataList[1]));
1810 Checks that an object can be renamed.
1811 \param entry entry of the object
1812 \brief Return \c true if object can be renamed
1814 bool SalomeApp_Application::renameAllowed( const QString& entry) const
1816 return entry.startsWith( tr( "SAVE_POINT_DEF_NAME") );
1820 Rename object by entry.
1821 \param entry entry of the object
1822 \param name new name of the object
1823 \brief Return \c true if rename operation finished successfully, \c false otherwise.
1825 bool SalomeApp_Application::renameObject( const QString& entry, const QString& name )
1827 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1829 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1831 if(!aStudy || savePoint == -1)
1834 if ( !name.isNull() && !name.isEmpty() ) {
1835 aStudy->setNameOfSavePoint( savePoint, name );
1836 updateSavePointDataObjects( aStudy );
1838 //Mark study as modified
1845 #ifndef DISABLE_PYCONSOLE
1846 //============================================================================
1847 /*! Function : onUpdateStudy
1848 * Purpose : Slot to update the study.
1850 //============================================================================
1851 void SalomeApp_Application::onUpdateStudy()
1853 QApplication::setOverrideCursor( Qt::WaitCursor );
1855 if( !updateStudy() )
1856 SUIT_MessageBox::warning( desktop(), tr( "ERROR" ), tr( "ERR_UPDATE_STUDY_FAILED" ) );
1858 QApplication::restoreOverrideCursor();
1861 //============================================================================
1862 /*! Function : updateStudy
1863 * Purpose : Update study by dumping the study to Python script and loading it.
1864 * It is used to apply variable modifications done in NoteBook to created objects.
1866 //============================================================================
1867 bool SalomeApp_Application::updateStudy()
1869 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1870 if ( !study || !myNoteBook )
1873 myNoteBook->setIsDumpedStudySaved( study->isSaved() );
1874 myNoteBook->setDumpedStudyName( study->studyName() );
1876 // get unique temporary directory name
1877 QString aTmpDir = QString::fromStdString( SALOMEDS_Tool::GetTmpDir() );
1878 if( aTmpDir.isEmpty() )
1881 if( aTmpDir.right( 1 ).compare( QDir::separator() ) == 0 )
1882 aTmpDir.remove( aTmpDir.length() - 1, 1 );
1884 // dump study to the temporary directory
1885 QString aScriptName( "notebook" );
1886 bool toPublish = true;
1887 bool isMultiFile = false;
1888 bool toSaveGUI = true;
1891 _PTR(AttributeParameter) ap;
1892 _PTR(IParameters) ip = ClientFactory::getIParameters(ap);
1893 if(ip->isDumpPython()) ip->setDumpPython(); //Unset DumpPython flag.
1894 if ( toSaveGUI ) { //SRN: Store a visual state of the study at the save point for DumpStudy method
1895 ip->setDumpPython();
1896 savePoint = SalomeApp_VisualState( this ).storeState(); //SRN: create a temporary save point
1898 bool ok = getStudy()->DumpStudy( aTmpDir.toStdString(), aScriptName.toStdString(), toPublish, isMultiFile );
1900 study->removeSavePoint(savePoint); //SRN: remove the created temporary save point.
1903 myNoteBook->setDumpedStudyScript( aTmpDir + QDir::separator() + aScriptName + ".py" );
1907 QList<SUIT_Application*> aList = SUIT_Session::session()->applications();
1908 int anIndex = aList.indexOf( this );
1910 // Disconnect dialog from application desktop in case if:
1911 // 1) Application is not the first application in the session
1912 // 2) Application is the first application in session but not the only.
1913 bool changeDesktop = ((anIndex > 0) || (anIndex == 0 && aList.count() > 1));
1914 if( changeDesktop ) {
1916 SalomeApp_Application* app = this;
1917 if( anIndex > 0 && anIndex < aList.count() )
1918 app = dynamic_cast<SalomeApp_Application*>( aList[ anIndex - 1 ] );
1919 else if(anIndex == 0 && aList.count() > 1)
1920 app = dynamic_cast<SalomeApp_Application*>( aList[ 1 ] );
1925 // creation a new study and restoring will be done in another application
1926 connect( this, SIGNAL( dumpedStudyClosed( const QString&, const QString&, bool ) ),
1927 app, SLOT( onRestoreStudy( const QString&, const QString&, bool ) ), Qt::UniqueConnection );
1930 QString aDumpScript = myNoteBook->getDumpedStudyScript();
1931 QString aStudyName = myNoteBook->getDumpedStudyName();
1932 bool isStudySaved = myNoteBook->isDumpedStudySaved();
1933 // clear a study (delete all objects)
1934 onCloseDoc( false );
1936 if( !changeDesktop ) {
1937 ok = onRestoreStudy( aDumpScript,
1946 //============================================================================
1947 /*! Function : onRestoreStudy
1948 * Purpose : Load the dumped study from Python script
1950 //============================================================================
1951 bool SalomeApp_Application::onRestoreStudy( const QString& theDumpScript,
1952 const QString& theStudyName,
1953 bool theIsStudySaved )
1957 // get active application
1958 SalomeApp_Application* app = dynamic_cast<SalomeApp_Application*>( SUIT_Session::session()->activeApplication() );
1960 // load study from the temporary directory
1961 QString command = QString( "exec(open(\"%1\",encoding='utf-8').read())" ).arg( theDumpScript );
1963 #ifndef DISABLE_PYCONSOLE
1964 PyConsole_Console* pyConsole = app->pythonConsole();
1966 pyConsole->execAndWait( command );
1969 // remove temporary directory
1970 QFileInfo aScriptInfo = QFileInfo( theDumpScript );
1971 QString aStudyName = aScriptInfo.baseName();
1972 QDir aDir = aScriptInfo.absoluteDir();
1973 QStringList aFiles = aDir.entryList( QStringList( "*.py*" ) );
1974 for( QStringList::iterator it = aFiles.begin(), itEnd = aFiles.end(); it != itEnd; ++it )
1975 ok = aDir.remove( *it ) && ok;
1977 ok = aDir.rmdir( aDir.absolutePath() );
1979 if( SalomeApp_Study* newStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() ) )
1981 #ifndef DISABLE_PYCONSOLE
1982 if ( app->getNoteBook() )
1983 app->getNoteBook()->Init();
1984 newStudy->updateFromNotebook(theStudyName, theIsStudySaved);
1985 newStudy->Modified();
1986 updateDesktopTitle();
1997 Close the Application
1999 void SalomeApp_Application::afterCloseDoc()
2001 #ifndef DISABLE_PYCONSOLE
2002 // emit signal to restore study from Python script
2004 emit dumpedStudyClosed( myNoteBook->getDumpedStudyScript(),
2005 myNoteBook->getDumpedStudyName(),
2006 myNoteBook->isDumpedStudySaved() );
2009 LightApp_Application::afterCloseDoc();
2013 Asks to close existing document.
2015 bool SalomeApp_Application::checkExistingDoc()
2017 return LightApp_Application::checkExistingDoc();
2021 #ifndef DISABLE_PYCONSOLE
2023 PyConsole_Interp* SalomeApp_Application::createPyInterp()
2025 return new SalomeApp_PyInterp;
2028 #endif // DISABLE_PYCONSOLE