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\").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\").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()
588 LightApp_Application::onCopy();
591 LightApp_SelectionMgr* mgr = selectionMgr();
592 mgr->selectedObjects(list);
594 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
595 if(study == NULL) return;
597 _PTR(Study) stdDS = getStudy();
600 SALOME_ListIteratorOfListIO it( list );
603 _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry());
608 onSelectionChanged();
616 /*!SLOT. Paste objects to study maneger from selection manager.*/
617 void SalomeApp_Application::onPaste()
619 LightApp_Application::onPaste();
622 LightApp_SelectionMgr* mgr = selectionMgr();
623 mgr->selectedObjects(list);
625 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
626 if(study == NULL) return;
628 _PTR(Study) stdDS = getStudy();
631 if ( stdDS->GetProperties()->IsLocked() ) {
632 SUIT_MessageBox::warning( desktop(),
633 QObject::tr("WRN_WARNING"),
634 QObject::tr("WRN_STUDY_LOCKED") );
638 SALOME_ListIteratorOfListIO it( list );
641 _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry());
646 updateObjectBrowser( true );
647 updateActions(); //SRN: BugID IPAL9377, case 3
655 /*!Check the application on closing.
656 * \retval true if possible, else false
658 bool SalomeApp_Application::isPossibleToClose( bool& closePermanently )
660 return LightApp_Application::isPossibleToClose( closePermanently );
663 /*! Check if the study is locked */
664 void SalomeApp_Application::onCloseDoc( bool ask )
666 if(getStudy()->IsStudyLocked()) {
667 if ( SUIT_MessageBox::question( desktop(),
668 QObject::tr( "WRN_WARNING" ),
669 QObject::tr( "CLOSE_LOCKED_STUDY" ),
670 SUIT_MessageBox::Yes | SUIT_MessageBox::No,
671 SUIT_MessageBox::No) == SUIT_MessageBox::No ) return;
674 LightApp_Application::onCloseDoc( ask );
676 // reinitialize study to have empty data
680 /*!Sets enable or disable some actions on selection changed.*/
681 void SalomeApp_Application::onSelectionChanged()
684 LightApp_SelectionMgr* mgr = selectionMgr();
685 mgr->selectedObjects(list);
687 bool canCopy = false;
688 bool canPaste = false;
690 LightApp_Module* m = dynamic_cast<LightApp_Module*>( activeModule() );
693 canCopy = m->canCopy();
694 canPaste = m->canPaste();
697 SALOME_ListIteratorOfListIO it ( list );
699 if (it.More() && list.Extent() == 1) {
700 _PTR(SObject) so = getStudy()->FindObjectID(it.Value()->getEntry());
703 canCopy = canCopy || getStudy()->CanCopy(so);
704 canPaste = canPaste || getStudy()->CanPaste(so);
708 action(EditCopyId)->setEnabled(canCopy);
709 action(EditPasteId)->setEnabled(canPaste);
712 /*!Delete references.*/
713 void SalomeApp_Application::onDeleteInvalidReferences()
716 LightApp_SelectionMgr* mgr = selectionMgr();
717 mgr->selectedObjects( aList, QString(), false );
719 if( aList.IsEmpty() )
722 _PTR(Study) aStudyDS = getStudy();
723 _PTR(StudyBuilder) aStudyBuilder = aStudyDS->NewBuilder();
726 for( SALOME_ListIteratorOfListIO it( aList ); it.More(); it.Next() )
727 if ( it.Value()->hasEntry() )
729 _PTR(SObject) aSObject = aStudyDS->FindObjectID( it.Value()->getEntry() ), aRefObj = aSObject;
730 while( aRefObj && aRefObj->ReferencedObject( anObj ) )
733 if( aRefObj && aRefObj!=aSObject && QString( aRefObj->GetName().c_str() ).isEmpty() )
734 aStudyBuilder->RemoveReference( aSObject );
736 updateObjectBrowser();
740 void SalomeApp_Application::onOpenWith()
742 QApplication::setOverrideCursor( Qt::WaitCursor );
744 LightApp_SelectionMgr* mgr = selectionMgr();
745 mgr->selectedObjects(aList);
746 if (aList.Extent() != 1)
748 QApplication::restoreOverrideCursor();
751 Handle(SALOME_InteractiveObject) aIObj = aList.First();
752 QString aModuleName(aIObj->getComponentDataType());
753 QString aModuleTitle = moduleTitle(aModuleName);
754 activateModule(aModuleTitle);
755 QApplication::restoreOverrideCursor();
761 SUIT_Study* SalomeApp_Application::createNewStudy()
763 SalomeApp_Study* aStudy = new SalomeApp_Study( this );
765 // Set up processing of major study-related events
766 connect( aStudy, SIGNAL( created( SUIT_Study* ) ), this, SLOT( onStudyCreated( SUIT_Study* ) ) );
767 connect( aStudy, SIGNAL( opened ( SUIT_Study* ) ), this, SLOT( onStudyOpened ( SUIT_Study* ) ) );
768 connect( aStudy, SIGNAL( saved ( SUIT_Study* ) ), this, SLOT( onStudySaved ( SUIT_Study* ) ) );
769 connect( aStudy, SIGNAL( closed ( SUIT_Study* ) ), this, SLOT( onStudyClosed ( SUIT_Study* ) ) );
771 #ifndef DISABLE_PYCONSOLE
772 //to receive signal in application that NoteBook's variable was modified
773 connect( aStudy, SIGNAL(notebookVarUpdated(QString)),
774 this, SIGNAL(notebookVarUpdated(QString)) );
781 Enable/Disable menu items and toolbar buttons. Rebuild menu
783 void SalomeApp_Application::updateCommandsStatus()
785 LightApp_Application::updateCommandsStatus();
788 QAction* a = action( DumpStudyId );
790 a->setEnabled( activeStudy() );
792 #ifndef DISABLE_PYCONSOLE
794 a = action( LoadScriptId );
796 a->setEnabled( pythonConsole() );
800 a = action( PropertiesId );
802 a->setEnabled( activeStudy() );
804 // Save GUI state menu
805 a = action( SaveGUIStateId );
807 a->setEnabled( activeStudy() );
809 // Connect study menu
810 a = action( ConnectId );
812 a->setEnabled( !activeStudy() );
814 // Disconnect study menu
815 a = action( DisconnectId );
817 a->setEnabled( activeStudy() );
819 // update state of Copy/Paste menu items
820 onSelectionChanged();
824 \class DumpStudyFileDlg
825 Private class used in Dump Study operation. Consists 2 check boxes:
826 "Publish in study" and "Save GUI parameters"
828 class DumpStudyFileDlg : public SUIT_FileDlg
831 DumpStudyFileDlg( QWidget* parent ) : SUIT_FileDlg( parent, false, true, true )
833 QGridLayout* grid = ::qobject_cast<QGridLayout*>( layout() );
836 QWidget *hB = new QWidget( this );
837 myPublishChk = new QCheckBox( tr("PUBLISH_IN_STUDY") );
838 myMultiFileChk = new QCheckBox( tr("MULTI_FILE_DUMP") );
839 mySaveGUIChk = new QCheckBox( tr("SAVE_GUI_STATE") );
841 QHBoxLayout *layout = new QHBoxLayout;
842 layout->addWidget(myPublishChk);
843 layout->addWidget(myMultiFileChk);
844 layout->addWidget(mySaveGUIChk);
845 hB->setLayout(layout);
847 QPushButton* pb = new QPushButton(this);
849 int row = grid->rowCount();
850 grid->addWidget( new QLabel("", this), row, 0 );
851 grid->addWidget( hB, row, 1, 1, 3 );
852 grid->addWidget( pb, row, 5 );
857 QCheckBox* myPublishChk;
858 QCheckBox* myMultiFileChk;
859 QCheckBox* mySaveGUIChk;
862 /*!Private SLOT. On dump study.*/
863 void SalomeApp_Application::onDumpStudy( )
865 SalomeApp_Study* appStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
866 if ( !appStudy ) return;
868 QStringList aFilters;
869 aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
871 bool anIsPublish = true;
872 bool anIsMultiFile = false;
873 bool anIsSaveGUI = true;
875 if ( SUIT_ResourceMgr* aResourceMgr = resourceMgr() ) {
876 anIsPublish = aResourceMgr->booleanValue( "Study", "pydump_publish", anIsPublish );
877 anIsMultiFile = aResourceMgr->booleanValue( "Study", "multi_file_dump", anIsMultiFile );
878 anIsSaveGUI = aResourceMgr->booleanValue( "Study", "pydump_save_gui", anIsSaveGUI );
881 DumpStudyFileDlg fd( desktop() );
882 fd.setValidator( new LightApp_PyFileValidator( &fd ) );
883 fd.setWindowTitle( tr( "TOT_DESK_FILE_DUMP_STUDY" ) );
884 fd.setNameFilters( aFilters );
885 fd.myPublishChk->setChecked( anIsPublish );
886 fd.myMultiFileChk->setChecked( anIsMultiFile );
887 fd.mySaveGUIChk->setChecked( anIsSaveGUI );
888 if ( fd.exec() == QDialog::Accepted )
890 QString aFileName = fd.selectedFile();
892 bool toPublish = fd.myPublishChk->isChecked();
893 bool isMultiFile = fd.myMultiFileChk->isChecked();
894 bool toSaveGUI = fd.mySaveGUIChk->isChecked();
896 if ( !aFileName.isEmpty() ) {
897 QFileInfo aFileInfo(aFileName);
898 if( aFileInfo.isDir() ) // IPAL19257
901 // Issue 21377 - dump study implementation moved to SalomeApp_Study class
904 SUIT_OverrideCursor wc;
905 res = appStudy->dump( aFileName, toPublish, isMultiFile, toSaveGUI );
908 SUIT_MessageBox::warning( desktop(),
909 QObject::tr("WRN_WARNING"),
910 tr("WRN_DUMP_STUDY_FAILED") );
915 /*!Private SLOT. On load script.*/
916 void SalomeApp_Application::onLoadScript( )
918 if ( getStudy()->GetProperties()->IsLocked() ) {
919 SUIT_MessageBox::warning( desktop(),
920 QObject::tr("WRN_WARNING"),
921 QObject::tr("WRN_STUDY_LOCKED") );
925 QStringList filtersList;
926 filtersList.append(tr("PYTHON_FILES_FILTER"));
927 filtersList.append(tr("ALL_FILES_FILTER"));
929 QString anInitialPath = "";
930 if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
931 anInitialPath = QDir::currentPath();
933 QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true );
935 if ( !aFile.isEmpty() )
937 QString command = QString("exec(open(\"%1\").read())").arg(aFile);
939 #ifndef DISABLE_PYCONSOLE
940 PyConsole_Console* pyConsole = pythonConsole();
943 pyConsole->exec( command );
948 /*!Private SLOT. On save GUI state.*/
949 void SalomeApp_Application::onSaveGUIState()
951 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
953 SalomeApp_VisualState( this ).storeState();
954 updateSavePointDataObjects( study );
955 updateObjectBrowser();
960 /*!Public SLOT. Performs some actions when dockable windows are triggered.*/
961 void SalomeApp_Application::onDockWindowVisibilityChanged( bool theIsVisible )
963 LightApp_Application::onDockWindowVisibilityChanged( theIsVisible );
964 QAction* send = ::qobject_cast<QAction*>( sender() );
967 QString aWinName = send->data().toString();
968 if ( theIsVisible && aWinName == "objectBrowser" )
969 objectBrowserColumnsVisibility();
973 QWidget* SalomeApp_Application::createWindow( const int flag )
976 #ifndef DISABLE_PYCONSOLE
977 if ( flag != WT_PyConsole ) wid = LightApp_Application::createWindow(flag);
979 wid = LightApp_Application::createWindow(flag);
982 SUIT_ResourceMgr* resMgr = resourceMgr();
984 if ( flag == WT_ObjectBrowser )
986 SUIT_DataBrowser* ob = qobject_cast<SUIT_DataBrowser*>( wid );
988 // temporary commented
989 //ob->setUpdater( new SalomeApp_Updater() );
991 #ifdef WITH_SALOMEDS_OBSERVER
992 //do not activate the automatic update of Qt tree through signal/slot
993 ob->setAutoUpdate(false);
994 //activate update of modified objects only
995 ob->setUpdateModified(true);
998 connect( ob, SIGNAL( doubleClicked( SUIT_DataObject* ) ), this, SLOT( onDblClick( SUIT_DataObject* ) ) );
1001 ValueCol = QObject::tr( "VALUE_COLUMN" ),
1002 IORCol = QObject::tr( "IOR_COLUMN" ),
1003 RefCol = QObject::tr( "REFENTRY_COLUMN" ),
1004 EntryCol = QObject::tr( "ENTRY_COLUMN" );
1006 SUIT_AbstractModel* treeModel = dynamic_cast<SUIT_AbstractModel*>( ob->model() );
1007 treeModel->registerColumn( 0, EntryCol, SalomeApp_DataObject::EntryId );
1008 treeModel->registerColumn( 0, ValueCol, SalomeApp_DataObject::ValueId );
1009 treeModel->registerColumn( 0, IORCol, SalomeApp_DataObject::IORId );
1010 treeModel->registerColumn( 0, RefCol, SalomeApp_DataObject::RefEntryId );
1011 treeModel->setAppropriate( EntryCol, Qtx::Toggled );
1012 treeModel->setAppropriate( ValueCol, Qtx::Toggled );
1013 treeModel->setAppropriate( IORCol, Qtx::Toggled );
1014 treeModel->setAppropriate( RefCol, Qtx::Toggled );
1016 bool autoSize = resMgr->booleanValue( "ObjectBrowser", "auto_size", false );
1017 bool autoSizeFirst = resMgr->booleanValue( "ObjectBrowser", "auto_size_first", true );
1018 bool resizeOnExpandItem = resMgr->booleanValue( "ObjectBrowser", "resize_on_expand_item", true );
1020 ob->setAutoSizeFirstColumn(autoSizeFirst);
1021 ob->setAutoSizeColumns(autoSize);
1022 ob->setResizeOnExpandItem(resizeOnExpandItem);
1023 ob->setProperty( "shortcut", QKeySequence( "Alt+Shift+O" ) );
1025 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1027 bool shown = resourceMgr()->booleanValue( "ObjectBrowser", QString( "visibility_column_id_%1" ).arg( i-1 ), true );
1028 ob->treeView()->setColumnHidden( i, !shown );
1031 // temporary commented
1033 for ( int i = SalomeApp_DataObject::ValueIdx; i <= SalomeApp_DataObject::RefEntryIdx; i++ )
1035 ob->addColumn( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i ) ), i );
1036 ob->setColumnShown( i, resMgr->booleanValue( "ObjectBrowser",
1037 QString().sprintf( "visibility_column_%d", i ), true ) );
1041 // temporary commented
1043 ob->setWidthMode( autoSize ? QListView::Maximum : QListView::Manual );
1044 ob->listView()->setColumnWidthMode( 0, autoSizeFirst ? QListView::Maximum : QListView::Manual );
1045 ob->resize( desktop()->width()/3, ob->height() );
1049 #ifndef DISABLE_PYCONSOLE
1050 else if ( flag == WT_PyConsole )
1052 PyConsole_Console* pyCons = new PyConsole_Console( desktop(), new LightApp_PyEditor( getPyInterp() ) );
1053 pyCons->setObjectName( "pythonConsole" );
1054 pyCons->setWindowTitle( tr( "PYTHON_CONSOLE" ) );
1055 pyCons->setFont(resourceMgr()->fontValue( "PyConsole", "font" ));
1056 pyCons->setIsShowBanner(resourceMgr()->booleanValue( "PyConsole", "show_banner", true ));
1057 pyCons->setAutoCompletion( resMgr->booleanValue( "PyConsole", "auto_completion", true ) );
1058 pyCons->setProperty( "shortcut", QKeySequence( "Alt+Shift+P" ) );
1061 else if ( flag == WT_NoteBook )
1063 setNoteBook( new SalomeApp_NoteBook( desktop() ) );
1064 //to receive signal in NoteBook that it's variable was modified
1065 connect( this, SIGNAL( notebookVarUpdated( QString ) ),
1066 getNoteBook(), SLOT( onVarUpdate( QString ) ) );
1068 wid = getNoteBook();
1069 wid->setObjectName( "noteBook" );
1075 /*!Create preferences.*/
1076 void SalomeApp_Application::createPreferences( LightApp_Preferences* pref )
1078 LightApp_Application::createPreferences(pref);
1083 int salomeCat = pref->addPreference( tr( "PREF_CATEGORY_SALOME" ) );
1084 int obTab = pref->addPreference( tr( "PREF_TAB_OBJBROWSER" ), salomeCat );
1085 int defCols = pref->addPreference( tr( "PREF_GROUP_DEF_COLUMNS" ), obTab );
1086 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1088 pref->addPreference( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i-SalomeApp_DataObject::EntryId ).toLatin1() ), defCols,
1089 LightApp_Preferences::Bool, "ObjectBrowser", QString().sprintf( "visibility_column_id_%d", i-1 ) );
1091 pref->setItemProperty( "orientation", Qt::Vertical, defCols );
1093 // adding preference to LightApp_Application handled preferences.. a bit of hacking with resources..
1094 int genTab = pref->addPreference( LightApp_Application::tr( "PREF_TAB_GENERAL" ), salomeCat );
1095 int studyGroup = pref->addPreference( LightApp_Application::tr( "PREF_GROUP_STUDY" ), genTab );
1096 pref->addPreference( tr( "PREF_STORE_VISUAL_STATE" ), studyGroup, LightApp_Preferences::Bool, "Study", "store_visual_state" );
1097 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1098 pref->addPreference( tr( "PREF_PYDUMP_PUBLISH" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_publish" );
1099 pref->addPreference( tr( "PREF_PYDUMP_MULTI_FILE" ), studyGroup, LightApp_Preferences::Bool, "Study", "multi_file_dump" );
1100 pref->addPreference( tr( "PREF_PYDUMP_SAVE_GUI" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_save_gui" );
1101 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1102 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1105 /*!Update desktop title.*/
1106 void SalomeApp_Application::updateDesktopTitle() {
1107 QString aTitle = applicationName();
1108 QString aVer = applicationVersion();
1109 if ( !aVer.isEmpty() )
1110 aTitle += QString( " " ) + aVer;
1112 if ( activeStudy() )
1114 QString sName = SUIT_Tools::file( activeStudy()->studyName().trimmed(), false );
1115 if ( !sName.isEmpty() ) {
1116 if ( getStudy()->GetProperties()->IsLocked() ) {
1117 aTitle += QString( " - [%1 (%2)]").arg( sName ).arg( tr( "STUDY_LOCKED" ) );
1119 aTitle += QString( " - [%1]" ).arg( sName );
1124 desktop()->setWindowTitle( aTitle );
1127 int SalomeApp_Application::closeChoice( const QString& docName )
1129 QStringList buttons;
1130 QMap<int, int> choices;
1132 buttons << tr ("APPCLOSE_SAVE"); // Save & Clear
1133 choices.insert( idx++, CloseSave ); // ...
1134 buttons << tr ("APPCLOSE_CLOSE"); // Clear w/o saving
1135 choices.insert( idx++, CloseDiscard ); // ...
1136 if ( myIsCloseFromExit ) {
1137 buttons << tr ("APPCLOSE_UNLOAD_SAVE"); // Save & Disconnect
1138 choices.insert( idx++, CloseDisconnectSave ); // ...
1139 buttons << tr ("APPCLOSE_UNLOAD"); // Disconnect
1140 choices.insert( idx++, CloseDisconnect ); // ...
1142 buttons << tr ("APPCLOSE_CANCEL"); // Cancel
1143 choices.insert( idx++, CloseCancel ); // ...
1145 if( !activeStudy()->isModified() )
1147 int answer = SUIT_MessageBox::question( desktop(), tr( "APPCLOSE_CAPTION" ),
1148 tr( "APPCLOSE_DESCRIPTION" ), buttons, 0 );
1149 return choices[answer];
1152 bool SalomeApp_Application::closeAction( const int choice, bool& closePermanently )
1158 if ( activeStudy()->isSaved() )
1160 else if ( !onSaveAsDoc() )
1165 case CloseDisconnectSave:
1166 if ( activeStudy()->isSaved() )
1168 else if ( !onSaveAsDoc() )
1170 case CloseDisconnect:
1171 closeActiveDoc( false );
1172 closePermanently = false;
1181 int SalomeApp_Application::openChoice( const QString& aName )
1183 int choice = LightApp_Application::openChoice( aName );
1185 if ( QFileInfo( aName ).exists() ) {
1186 if ( choice == OpenNew ) { // The document isn't already open.
1188 if ( aName == getStudy()->Name().c_str() )
1190 // The document already exists in the study.
1191 // Do you want to reload it?
1193 int answer = SUIT_MessageBox::question( desktop(), tr( "WRN_WARNING" ), tr( "QUE_DOC_ALREADYEXIST" ).arg( aName ),
1194 SUIT_MessageBox::Yes | SUIT_MessageBox::No, SUIT_MessageBox::No );
1195 if ( answer == SUIT_MessageBox::Yes )
1196 choice = OpenRefresh;
1198 choice = OpenCancel;
1201 } else { // file is not exist on disk
1202 SUIT_MessageBox::warning( desktop(),
1203 QObject::tr("WRN_WARNING"),
1204 QObject::tr("WRN_FILE_NOT_EXIST").arg(aName.toLatin1().data()));
1211 bool SalomeApp_Application::openAction( const int aChoice, const QString& aName )
1214 int choice = aChoice;
1222 res = LightApp_Application::openAction( choice, aName );
1230 \brief Get map of the operations which can be performed
1231 on the module activation.
1233 The method should return the map of the kind \c {<id>:<name>}
1234 where \c <id> is an integer identifier of the operation and
1235 \c <name> is a title for the button to be added to the
1236 dialog box. After user selects the required operation by the
1237 clicking the corresponding button in the dialog box, its identifier
1238 is passed to the moduleActionSelected() method to process
1241 \return map of the operations
1242 \sa moduleActionSelected()
1244 QMap<int, QString> SalomeApp_Application::activateModuleActions() const
1246 QMap<int, QString> opmap = LightApp_Application::activateModuleActions();
1248 opmap.insert( LoadStudyId, tr( "ACTIVATE_MODULE_OP_LOAD" ) );
1250 opmap.insert( NewAndScriptId, tr( "ACTIVATE_MODULE_OP_SCRIPT" ) );
1255 \brief Called when the used selectes required operation chosen
1256 from "Activate module" dialog box.
1258 Performs the required operation according to the user choice.
1260 \param id operation identifier
1261 \sa activateModuleActions()
1263 void SalomeApp_Application::moduleActionSelected( const int id )
1269 case NewAndScriptId:
1273 LightApp_Application::moduleActionSelected( id );
1278 /*!Gets CORBA::ORB_var*/
1279 CORBA::ORB_var SalomeApp_Application::orb()
1281 static CORBA::ORB_var _orb;
1283 if ( CORBA::is_nil( _orb ) ) {
1284 Qtx::CmdLineArgs args;
1285 ORB_INIT& init = *SINGLETON_<ORB_INIT>::Instance();
1286 _orb = init( args.argc(), args.argv() );
1292 /*!Create and return SALOMEDS_Study.*/
1293 _PTR(Study) SalomeApp_Application::getStudy()
1295 static _PTR(Study) _study;
1297 CORBA::Object_var aSObject = namingService()->Resolve("/Study");
1298 SALOMEDS::Study_var aStudy = SALOMEDS::Study::_narrow(aSObject);
1299 _study = ClientFactory::Study(aStudy);
1304 /*!Create and return SALOME_NamingService.*/
1305 SALOME_NamingService* SalomeApp_Application::namingService()
1307 static SALOME_NamingService _ns(orb());
1311 /*!Create and return SALOME_LifeCycleCORBA.*/
1312 SALOME_LifeCycleCORBA* SalomeApp_Application::lcc()
1314 static SALOME_LifeCycleCORBA _lcc( namingService() );
1318 /*!Private SLOT. On preferences.*/
1319 void SalomeApp_Application::onProperties()
1321 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1325 _PTR(StudyBuilder) SB = study->studyDS()->NewBuilder();
1328 SalomeApp_StudyPropertiesDlg aDlg( desktop() );
1329 int res = aDlg.exec();
1330 if( res==QDialog::Accepted && aDlg.isChanged() )
1331 SB->CommitCommand();
1335 //study->updateCaptions();
1336 updateDesktopTitle();
1340 /*!Insert items in popup, which necessary for current application*/
1341 void SalomeApp_Application::contextMenuPopup( const QString& type, QMenu* thePopup, QString& title )
1343 LightApp_SelectionMgr* mgr = selectionMgr();
1344 bool cacheIsOn = mgr->isSelectionCacheEnabled();
1345 mgr->setSelectionCacheEnabled( true );
1347 LightApp_Application::contextMenuPopup( type, thePopup, title );
1349 // temporary commented
1350 /*OB_Browser* ob = objectBrowser();
1351 if ( !ob || type != ob->popupClientType() )
1354 // Get selected objects
1355 SALOME_ListIO aList;
1356 mgr->selectedObjects( aList, QString(), false );
1358 // add GUI state commands: restore, rename
1359 if ( aList.Extent() == 1 && aList.First()->hasEntry() &&
1360 QString( aList.First()->getEntry() ).startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) {
1361 thePopup->addSeparator();
1362 thePopup->addAction( tr( "MEN_RESTORE_VS" ), this, SLOT( onRestoreGUIState() ) );
1363 thePopup->addAction( tr( "MEN_RENAME_VS" ), objectBrowser(),
1364 SLOT( onStartEditing() ), objectBrowser()->shortcutKey(SUIT_DataBrowser::RenameShortcut) );
1365 thePopup->addAction( tr( "MEN_DELETE_VS" ), this, SLOT( onDeleteGUIState() ) );
1368 // "Delete reference" item should appear only for invalid references
1370 // isInvalidRefs will be true, if at least one of selected objects is invalid reference
1371 bool isInvalidRefs = false;
1373 _PTR(SObject) anObj;
1374 for( SALOME_ListIteratorOfListIO it( aList ); it.More() && !isInvalidRefs; it.Next() )
1376 if( it.Value()->hasEntry() )
1378 _PTR(SObject) aSObject = getStudy()->FindObjectID( it.Value()->getEntry() ), aRefObj = aSObject;
1379 while( aRefObj && aRefObj->ReferencedObject( anObj ) )
1382 if( aRefObj && aRefObj!=aSObject && QString( aRefObj->GetName().c_str() ).isEmpty() )
1383 isInvalidRefs = true;
1387 // Add "Delete reference" item to popup
1388 if ( isInvalidRefs )
1390 thePopup->addSeparator();
1391 thePopup->addAction( tr( "MEN_DELETE_INVALID_REFERENCE" ), this, SLOT( onDeleteInvalidReferences() ) );
1395 // "Activate module" item should appear only if it's necessary
1396 if ( aList.Extent() == 1 ) {
1398 mgr->selectedObjects( aList );
1400 Handle(SALOME_InteractiveObject) aIObj = aList.First();
1402 // add extra popup menu (defined in XML)
1403 if ( myExtActions.size() > 0 ) {
1404 // Use only first selected object
1405 _PTR(SObject) aSO = getStudy()->FindObjectID( aIObj->getEntry() );
1407 _PTR( GenericAttribute ) anAttr;
1408 std::string auid = "AttributeUserID";
1409 auid += Kernel_Utils::GetGUID(Kernel_Utils::ObjectdID);
1410 if ( aSO->FindAttribute( anAttr, auid ) ) {
1411 _PTR(AttributeUserID) aAttrID = anAttr;
1412 QString aId = aAttrID->Value().c_str();
1413 if ( myExtActions.contains( aId ) ) {
1414 thePopup->addAction(myExtActions[aId]);
1420 // check if item is a "GUI state" item (also a first level object)
1421 QString entry( aIObj->getEntry() );
1422 if ( !entry.startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) {
1423 QString aModuleName( aIObj->getComponentDataType() );
1424 QString aModuleTitle = moduleTitle( aModuleName );
1425 CAM_Module* currentModule = activeModule();
1426 if ( ( !currentModule || currentModule->moduleName() != aModuleTitle ) && !aModuleTitle.isEmpty() )
1427 thePopup->addAction( tr( "MEN_OPENWITH" ).arg( aModuleTitle ), this, SLOT( onOpenWith() ) );
1431 mgr->setSelectionCacheEnabled( cacheIsOn );
1434 /*!Update obect browser:
1435 1.if 'updateModels' true, update existing data models;
1436 2. update "non-existing" (not loaded yet) data models;
1437 3. update object browser if it exists */
1438 void SalomeApp_Application::updateObjectBrowser( const bool updateModels )
1440 // update "non-existing" (not loaded yet) data models
1441 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
1444 for ( _PTR(SComponentIterator) it ( getStudy()->NewComponentIterator() ); it->More(); it->Next() )
1446 _PTR(SComponent) aComponent ( it->Value() );
1448 #ifndef WITH_SALOMEDS_OBSERVER
1449 // with GUI observers this check is not needed anymore
1450 if ( aComponent->ComponentDataType() == study->getVisualComponentName().toLatin1().constData() )
1451 continue; // skip the magic "Interface Applicative" component
1453 if ( !objectBrowser() )
1454 getWindow( WT_ObjectBrowser );
1455 const bool isAutoUpdate = objectBrowser()->autoUpdate();
1456 objectBrowser()->setAutoUpdate( false );
1457 SalomeApp_DataModel::synchronize( aComponent, study );
1458 objectBrowser()->setAutoUpdate( isAutoUpdate );
1462 // create data objects that correspond to GUI state save points
1463 if ( study ) updateSavePointDataObjects( study );
1465 // update existing data models (already loaded SComponents)
1466 LightApp_Application::updateObjectBrowser( updateModels );
1469 /*!Display Catalog Genenerator dialog */
1470 void SalomeApp_Application::onCatalogGen()
1472 ToolsGUI_CatalogGeneratorDlg aDlg( desktop() );
1476 /*!Display Registry Display dialog */
1477 void SalomeApp_Application::onRegDisplay()
1479 CORBA::ORB_var anOrb = orb();
1480 ToolsGUI_RegWidget* regWnd = ToolsGUI_RegWidget::GetRegWidget( anOrb, desktop() );
1483 regWnd->activateWindow();
1486 /*!find original object by double click on item */
1487 void SalomeApp_Application::onDblClick( SUIT_DataObject* theObj )
1489 // Issue 21379: References are supported at LightApp_DataObject level
1490 LightApp_DataObject* obj = dynamic_cast<LightApp_DataObject*>( theObj );
1492 if( obj && obj->isReference() )
1494 QString entry = obj->refEntry();
1496 SUIT_DataOwnerPtrList aList;
1497 aList.append( new LightApp_DataOwner( entry ) );
1498 selectionMgr()->setSelected( aList, false );
1500 SUIT_DataBrowser* ob = objectBrowser();
1502 QModelIndexList aSelectedIndexes = ob->selectedIndexes();
1503 if ( !aSelectedIndexes.isEmpty() )
1504 ob->treeView()->scrollTo( aSelectedIndexes.first() );
1506 emit objectDoubleClicked( theObj );
1510 Creates new view manager
1511 \param type - type of view manager
1513 SUIT_ViewManager* SalomeApp_Application::newViewManager(const QString& type)
1515 return createViewManager(type);
1519 /*!Global utility function, returns selected GUI Save point object's ID */
1520 int getSelectedSavePoint( const LightApp_SelectionMgr* selMgr )
1522 SALOME_ListIO aList;
1523 selMgr->selectedObjects( aList );
1524 if( aList.Extent() > 0 ) {
1525 Handle(SALOME_InteractiveObject) aIObj = aList.First();
1526 QString entry( aIObj->getEntry() );
1527 QString startStr = QObject::tr( "SAVE_POINT_DEF_NAME" );
1528 if ( !entry.startsWith( startStr ) ) // it's a "GUI state" object
1530 bool ok; // conversion to integer is ok?
1531 int savePoint = entry.right( entry.length() - startStr.length() ).toInt( &ok );
1532 return ok ? savePoint : -1;
1537 /*!Called on Restore GUI State popup command*/
1538 void SalomeApp_Application::onRestoreGUIState()
1540 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1541 if ( savePoint == -1 )
1543 SalomeApp_VisualState( this ).restoreState( savePoint );
1546 /*!Called on Delete GUI State popup command*/
1547 void SalomeApp_Application::onDeleteGUIState()
1549 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1550 if ( savePoint == -1 )
1552 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1556 study->removeSavePoint( savePoint );
1557 updateSavePointDataObjects( study );
1560 /*!Called on New study operation*/
1561 void SalomeApp_Application::onStudyCreated( SUIT_Study* study )
1563 LightApp_Application::onStudyCreated( study );
1565 //#ifndef DISABLE_PYCONSOLE
1566 // desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
1567 // windowDock( getWindow( WT_ObjectBrowser ) ) );
1570 loadDockWindowsState();
1572 objectBrowserColumnsVisibility();
1575 /*!Called on Open study operation*/
1576 void SalomeApp_Application::onStudyOpened( SUIT_Study* study )
1578 LightApp_Application::onStudyOpened( study );
1580 //#ifndef DISABLE_PYCONSOLE
1581 // desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
1582 // windowDock( getWindow( WT_ObjectBrowser ) ) );
1585 loadDockWindowsState();
1587 objectBrowserColumnsVisibility();
1589 // temporary commented
1590 /*if ( objectBrowser() ) {
1591 updateSavePointDataObjects( dynamic_cast<SalomeApp_Study*>( study ) );
1592 objectBrowser()->updateTree( study->root() );
1596 /*! updateSavePointDataObjects: syncronize data objects that correspond to save points (gui states)*/
1597 void SalomeApp_Application::updateSavePointDataObjects( SalomeApp_Study* study )
1600 SUIT_DataBrowser* ob = objectBrowser();
1601 LightApp_SelectionMgr* selMgr = selectionMgr();
1603 if ( !study || !ob || !selMgr )
1606 // find GUI states root object
1607 SUIT_DataObject* guiRootObj = 0;
1609 study->root()->children( ch );
1610 DataObjectList::const_iterator it = ch.begin(), last = ch.end();
1611 for ( ; it != last ; ++it ) {
1612 if ( dynamic_cast<SalomeApp_SavePointRootObject*>( *it ) ) {
1617 std::vector<int> savePoints = study->getSavePoints();
1618 // case 1: no more save points but they existed in study's tree
1619 if ( savePoints.empty() && guiRootObj ) {
1620 //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state"
1621 // : set auto update to true for removing SalomeApp_SavePointRootObject from the SUIT_TreeModel
1622 const bool isAutoUpdate = ob->autoUpdate();
1623 selMgr->clearSelected();
1624 ob->setAutoUpdate(true);
1625 DataObjectList ch = guiRootObj->children();
1626 for( int i = 0; i < ch.size(); i++ )
1629 ob->setAutoUpdate(isAutoUpdate);
1632 // case 2: no more save points but root does not exist either
1633 if ( savePoints.empty() && !guiRootObj )
1635 // case 3: save points but no root for them - create it
1636 if ( !savePoints.empty() && !guiRootObj )
1637 guiRootObj = new SalomeApp_SavePointRootObject( study->root() );
1638 // case 4: everything already exists.. here may be a problem: we want "GUI states" root object
1639 // to be always the last one in the tree. Here we check - if it is not the last one - remove and
1641 if ( guiRootObj->nextBrother() ) {
1642 study->root()->removeChild(guiRootObj);
1643 study->root()->appendChild(guiRootObj);
1644 //study->root()->dump();
1647 // store data objects in a map id-to-DataObject
1648 QMap<int,SalomeApp_SavePointObject*> mapDO;
1650 guiRootObj->children( ch );
1651 for( it = ch.begin(), last = ch.end(); it != last ; ++it ) {
1652 SalomeApp_SavePointObject* dobj = dynamic_cast<SalomeApp_SavePointObject*>( *it );
1654 mapDO[dobj->getId()] = dobj;
1657 // iterate new save points. if DataObject with such ID not found in map - create DataObject
1658 // if in the map - remove it from map.
1659 for ( int i = 0; i < savePoints.size(); i++ )
1660 if ( !mapDO.contains( savePoints[i] ) )
1661 new SalomeApp_SavePointObject( guiRootObj, savePoints[i], study );
1663 mapDO.remove( savePoints[i] );
1665 // delete DataObjects that are still in the map -- their IDs were not found in data model
1666 if( mapDO.size() > 0) {
1667 //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state"
1668 // : set auto update to true for removing SalomeApp_SavePointObject from the SUIT_TreeModel
1669 selMgr->clearSelected();
1670 const bool isAutoUpdate = ob->autoUpdate();
1671 ob->setAutoUpdate(true);
1672 for ( QMap<int,SalomeApp_SavePointObject*>::Iterator it = mapDO.begin(); it != mapDO.end(); ++it )
1674 ob->setAutoUpdate(isAutoUpdate);
1678 /*! Check data object */
1679 bool SalomeApp_Application::checkDataObject(LightApp_DataObject* theObj)
1688 Opens other study into active Study. If Study is empty - creates it.
1689 \param theName - name of study
1691 bool SalomeApp_Application::useStudy( const QString& theName )
1694 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1697 res = aStudy->loadDocument( theName );
1698 updateDesktopTitle();
1699 updateCommandsStatus();
1703 /*! Show/hide object browser colums according to preferences */
1704 void SalomeApp_Application::objectBrowserColumnsVisibility()
1706 if ( objectBrowser() )
1707 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1709 bool shown = resourceMgr()->booleanValue( "ObjectBrowser", QString( "visibility_column_id_%1" ).arg( i-1 ), true );
1710 objectBrowser()->treeView()->setColumnHidden( i, !shown );
1714 #ifndef DISABLE_PYCONSOLE
1715 /*! Set SalomeApp_NoteBook pointer */
1716 void SalomeApp_Application::setNoteBook( SalomeApp_NoteBook* theNoteBook )
1718 myNoteBook = theNoteBook;
1721 /*! Return SalomeApp_NoteBook pointer */
1722 SalomeApp_NoteBook* SalomeApp_Application::getNoteBook() const
1729 * Define extra actions defined in module definition XML file.
1730 * Additional popup items sections can be defined by parameter "popupitems".
1731 * Supported attributes:
1732 * title - title of menu item,
1733 * attributelocalid - AttributeLocalId defined for selected data item where menu command has to be applied,
1734 * method - method which has to be called when menu item is selected
1736 * <section name="MODULENAME">
1737 * <parameter name="popupitems" value="menuitem1:menuitem2:..."/>
1739 * <section name="importmed">
1740 * <parameter name="title" value="My menu"/>
1741 * <parameter name="objectid" value="VISU.Result"/>
1742 * <parameter name="method" value="nameOfModuleMethod"/>
1745 void SalomeApp_Application::createExtraActions()
1747 myExtActions.clear();
1748 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
1750 QStringList aModules;
1751 modules(aModules, false);
1752 foreach(QString aModile, aModules) {
1753 QString aModName = moduleName(aModile);
1754 QString aSectionStr = resMgr->stringValue(aModName, "popupitems", QString());
1755 if (!aSectionStr.isNull()) {
1756 QStringList aSections = aSectionStr.split(':');
1757 foreach(QString aSection, aSections) {
1758 QString aTitle = resMgr->stringValue(aSection, "title", QString());
1759 QString aId = resMgr->stringValue(aSection, "objectid", QString());
1760 QString aSlot = resMgr->stringValue(aSection, "method", QString());
1761 if (aTitle.isEmpty() || aSlot.isEmpty() || aId.isEmpty())
1764 QString aModuleName = resMgr->stringValue(aSection, "module", QString());
1765 if (aModuleName.isNull())
1766 aModuleName = aModName;
1768 QAction* aAction = new QAction(aTitle, this);
1770 aData<<aModuleName<<aSlot;
1771 aAction->setData(aData);
1772 connect(aAction, SIGNAL(triggered()), this, SLOT(onExtAction()));
1773 myExtActions[aId] = aAction;
1780 * Called when extra action is selected
1782 void SalomeApp_Application::onExtAction()
1784 QAction* aAction = ::qobject_cast<QAction*>(sender());
1788 QVariant aData = aAction->data();
1789 QStringList aDataList = aData.value<QStringList>();
1790 if (aDataList.size() != 2)
1793 LightApp_SelectionMgr* aSelectionMgr = selectionMgr();
1794 SALOME_ListIO aListIO;
1795 aSelectionMgr->selectedObjects(aListIO);
1796 const Handle(SALOME_InteractiveObject)& anIO = aListIO.First();
1797 if (aListIO.Extent() < 1)
1799 if (!anIO->hasEntry())
1802 QString aEntry(anIO->getEntry());
1804 QApplication::setOverrideCursor( Qt::WaitCursor );
1805 QString aModuleTitle = moduleTitle(aDataList[0]);
1806 activateModule(aModuleTitle);
1807 QApplication::restoreOverrideCursor();
1809 QCoreApplication::processEvents();
1811 CAM_Module* aModule = activeModule();
1815 if (!QMetaObject::invokeMethod(aModule, qPrintable(aDataList[1]), Q_ARG(QString, aEntry)))
1816 printf("Error: Can't Invoke method %s\n", qPrintable(aDataList[1]));
1820 Checks that an object can be renamed.
1821 \param entry entry of the object
1822 \brief Return \c true if object can be renamed
1824 bool SalomeApp_Application::renameAllowed( const QString& entry) const
1826 return entry.startsWith( tr( "SAVE_POINT_DEF_NAME") );
1830 Rename object by entry.
1831 \param entry entry of the object
1832 \param name new name of the object
1833 \brief Return \c true if rename operation finished successfully, \c false otherwise.
1835 bool SalomeApp_Application::renameObject( const QString& entry, const QString& name )
1837 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1839 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1841 if(!aStudy || savePoint == -1)
1844 if ( !name.isNull() && !name.isEmpty() ) {
1845 aStudy->setNameOfSavePoint( savePoint, name );
1846 updateSavePointDataObjects( aStudy );
1848 //Mark study as modified
1855 #ifndef DISABLE_PYCONSOLE
1856 //============================================================================
1857 /*! Function : onUpdateStudy
1858 * Purpose : Slot to update the study.
1860 //============================================================================
1861 void SalomeApp_Application::onUpdateStudy()
1863 QApplication::setOverrideCursor( Qt::WaitCursor );
1865 if( !updateStudy() )
1866 SUIT_MessageBox::warning( desktop(), tr( "ERROR" ), tr( "ERR_UPDATE_STUDY_FAILED" ) );
1868 QApplication::restoreOverrideCursor();
1871 //============================================================================
1872 /*! Function : updateStudy
1873 * Purpose : Update study by dumping the study to Python script and loading it.
1874 * It is used to apply variable modifications done in NoteBook to created objects.
1876 //============================================================================
1877 bool SalomeApp_Application::updateStudy()
1879 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1880 if ( !study || !myNoteBook )
1883 myNoteBook->setIsDumpedStudySaved( study->isSaved() );
1884 myNoteBook->setDumpedStudyName( study->studyName() );
1886 // get unique temporary directory name
1887 QString aTmpDir = QString::fromStdString( SALOMEDS_Tool::GetTmpDir() );
1888 if( aTmpDir.isEmpty() )
1891 if( aTmpDir.right( 1 ).compare( QDir::separator() ) == 0 )
1892 aTmpDir.remove( aTmpDir.length() - 1, 1 );
1894 // dump study to the temporary directory
1895 QString aScriptName( "notebook" );
1896 bool toPublish = true;
1897 bool isMultiFile = false;
1898 bool toSaveGUI = true;
1901 _PTR(AttributeParameter) ap;
1902 _PTR(IParameters) ip = ClientFactory::getIParameters(ap);
1903 if(ip->isDumpPython()) ip->setDumpPython(); //Unset DumpPython flag.
1904 if ( toSaveGUI ) { //SRN: Store a visual state of the study at the save point for DumpStudy method
1905 ip->setDumpPython();
1906 savePoint = SalomeApp_VisualState( this ).storeState(); //SRN: create a temporary save point
1908 bool ok = getStudy()->DumpStudy( aTmpDir.toStdString(), aScriptName.toStdString(), toPublish, isMultiFile );
1910 study->removeSavePoint(savePoint); //SRN: remove the created temporary save point.
1913 myNoteBook->setDumpedStudyScript( aTmpDir + QDir::separator() + aScriptName + ".py" );
1917 QList<SUIT_Application*> aList = SUIT_Session::session()->applications();
1918 int anIndex = aList.indexOf( this );
1920 // Disconnect dialog from application desktop in case if:
1921 // 1) Application is not the first application in the session
1922 // 2) Application is the first application in session but not the only.
1923 bool changeDesktop = ((anIndex > 0) || (anIndex == 0 && aList.count() > 1));
1924 if( changeDesktop ) {
1926 SalomeApp_Application* app = this;
1927 if( anIndex > 0 && anIndex < aList.count() )
1928 app = dynamic_cast<SalomeApp_Application*>( aList[ anIndex - 1 ] );
1929 else if(anIndex == 0 && aList.count() > 1)
1930 app = dynamic_cast<SalomeApp_Application*>( aList[ 1 ] );
1935 // creation a new study and restoring will be done in another application
1936 connect( this, SIGNAL( dumpedStudyClosed( const QString&, const QString&, bool ) ),
1937 app, SLOT( onRestoreStudy( const QString&, const QString&, bool ) ), Qt::UniqueConnection );
1940 QString aDumpScript = myNoteBook->getDumpedStudyScript();
1941 QString aStudyName = myNoteBook->getDumpedStudyName();
1942 bool isStudySaved = myNoteBook->isDumpedStudySaved();
1943 // clear a study (delete all objects)
1944 onCloseDoc( false );
1946 if( !changeDesktop ) {
1947 ok = onRestoreStudy( aDumpScript,
1956 //============================================================================
1957 /*! Function : onRestoreStudy
1958 * Purpose : Load the dumped study from Python script
1960 //============================================================================
1961 bool SalomeApp_Application::onRestoreStudy( const QString& theDumpScript,
1962 const QString& theStudyName,
1963 bool theIsStudySaved )
1967 // get active application
1968 SalomeApp_Application* app = dynamic_cast<SalomeApp_Application*>( SUIT_Session::session()->activeApplication() );
1970 // load study from the temporary directory
1971 QString command = QString( "exec(open(\"%1\").read())" ).arg( theDumpScript );
1973 #ifndef DISABLE_PYCONSOLE
1974 PyConsole_Console* pyConsole = app->pythonConsole();
1976 pyConsole->execAndWait( command );
1979 // remove temporary directory
1980 QFileInfo aScriptInfo = QFileInfo( theDumpScript );
1981 QString aStudyName = aScriptInfo.baseName();
1982 QDir aDir = aScriptInfo.absoluteDir();
1983 QStringList aFiles = aDir.entryList( QStringList( "*.py*" ) );
1984 for( QStringList::iterator it = aFiles.begin(), itEnd = aFiles.end(); it != itEnd; ++it )
1985 ok = aDir.remove( *it ) && ok;
1987 ok = aDir.rmdir( aDir.absolutePath() );
1989 if( SalomeApp_Study* newStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() ) )
1991 #ifndef DISABLE_PYCONSOLE
1992 if ( app->getNoteBook() )
1993 app->getNoteBook()->Init();
1994 newStudy->updateFromNotebook(theStudyName, theIsStudySaved);
1995 newStudy->Modified();
1996 updateDesktopTitle();
2007 Close the Application
2009 void SalomeApp_Application::afterCloseDoc()
2011 #ifndef DISABLE_PYCONSOLE
2012 // emit signal to restore study from Python script
2014 emit dumpedStudyClosed( myNoteBook->getDumpedStudyScript(),
2015 myNoteBook->getDumpedStudyName(),
2016 myNoteBook->isDumpedStudySaved() );
2019 LightApp_Application::afterCloseDoc();
2023 Asks to close existing document.
2025 bool SalomeApp_Application::checkExistingDoc()
2027 return LightApp_Application::checkExistingDoc();
2031 #ifndef DISABLE_PYCONSOLE
2033 PyConsole_Interp* SalomeApp_Application::createPyInterp()
2035 return new SalomeApp_PyInterp;
2038 #endif // DISABLE_PYCONSOLE