1 // Copyright (C) 2007-2015 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" // WARNING! This include must be the first!
39 #include <PyConsole_Console.h>
40 #include "SalomeApp_NoteBook.h"
42 #include "SalomeApp_Application.h"
43 #include "SalomeApp_Study.h"
44 #include "SalomeApp_DataModel.h"
45 #include "SalomeApp_DataObject.h"
46 #include "SalomeApp_VisualState.h"
47 #include "SalomeApp_StudyPropertiesDlg.h"
48 #include "SalomeApp_LoadStudiesDlg.h"
49 #include "SalomeApp_ExitDlg.h"
51 #include <LightApp_Application.h>
52 #include <LightApp_Module.h>
53 #include <LightApp_Preferences.h>
54 #include <LightApp_SelectionMgr.h>
55 #include <LightApp_NameDlg.h>
56 #include <LightApp_DataOwner.h>
58 #include <CAM_Module.h>
60 #include <SUIT_Tools.h>
61 #include <SUIT_Session.h>
62 #include <SUIT_Desktop.h>
63 #include <SUIT_DataBrowser.h>
64 #include <SUIT_FileDlg.h>
65 #include <SUIT_FileValidator.h>
66 #include <SUIT_MessageBox.h>
67 #include <SUIT_ResourceMgr.h>
68 #include <SUIT_TreeModel.h>
69 #include <SUIT_ViewWindow.h>
70 #include <SUIT_ViewManager.h>
71 #include <SUIT_ViewModel.h>
72 #include <SUIT_OverrideCursor.h>
74 #include <QtxTreeView.h>
76 #include <SALOME_EventFilter.h>
78 // temporary commented
79 //#include <OB_ListItem.h>
82 #include <Utils_ORB_INIT.hxx>
83 #include <Utils_SINGLETON.hxx>
84 #include <SALOME_LifeCycleCORBA.hxx>
86 #include <QApplication>
90 #include <QPushButton>
92 #include <QListWidget>
93 #include <QGridLayout>
97 #include <SALOMEDSClient_ClientFactory.hxx>
98 #include <Basics_Utils.hxx>
100 #include <SALOME_ListIO.hxx>
101 #include <SALOME_Prs.h>
104 #include <ToolsGUI_CatalogGeneratorDlg.h>
105 #include <ToolsGUI_RegWidget.h>
109 #include <SALOMEDS_Tool.hxx>
111 /*!Internal class that updates object browser item properties */
112 // temporary commented
113 /*class SalomeApp_Updater : public OB_Updater
116 SalomeApp_Updater() : OB_Updater(){};
117 virtual ~SalomeApp_Updater(){};
118 virtual void update( SUIT_DataObject* theObj, OB_ListItem* theItem );
121 void SalomeApp_Updater::update( SUIT_DataObject* theObj, OB_ListItem* theItem )
123 if( !theObj || !theItem )
126 SalomeApp_DataObject* SAObj = dynamic_cast<SalomeApp_DataObject*>( theObj );
130 _PTR(SObject) SObj = SAObj->object();
133 _PTR( GenericAttribute ) anAttr;
136 if ( SObj->FindAttribute( anAttr, "AttributeSelectable" ) )
138 _PTR(AttributeSelectable) aAttrSel = anAttr;
139 theItem->setSelectable( aAttrSel->IsSelectable() );
142 if ( SObj->FindAttribute(anAttr, "AttributeExpandable") )
144 _PTR(AttributeExpandable) aAttrExpand = anAttr;
145 theItem->setExpandable( aAttrExpand->IsExpandable() );
148 //this attribute is not supported in the version of SALOME 3.x
149 //if ( SObj->FindAttribute(anAttr, "AttributeOpened") )
151 // _PTR(AttributeOpened) aAttrOpen = anAttr;
152 // theItem->setOpen( aAttrOpen->IsOpened() );
156 /*!Create new instance of SalomeApp_Application.*/
157 extern "C" SALOMEAPP_EXPORT SUIT_Application* createApplication()
159 return new SalomeApp_Application();
163 SalomeApp_Application::SalomeApp_Application()
164 : LightApp_Application(),
165 myIsCloseFromExit( false )
170 *\li Destroy event filter.
172 SalomeApp_Application::~SalomeApp_Application()
174 // Do not destroy. It's a singleton !
175 //SALOME_EventFilter::Destroy();
178 /*!Start application.*/
179 void SalomeApp_Application::start()
181 // process the command line options before start: to createActions in accordance to the options
182 static bool isFirst = true;
190 for (int i = 1; i < qApp->argc(); i++) {
191 QRegExp rxs ("--study-hdf=(.+)");
192 if ( rxs.indexIn( QString(qApp->argv()[i]) ) >= 0 && rxs.capturedTexts().count() > 1 ) {
193 QString file = rxs.capturedTexts()[1];
194 QFileInfo fi ( file );
195 QString extension = fi.suffix().toLower();
196 if ( extension == "hdf" && fi.exists() )
197 hdffile = fi.absoluteFilePath();
200 QRegExp rxp ("--pyscript=\\[(.+)\\]");
201 if ( rxp.indexIn( QString(qApp->argv()[i]) ) >= 0 && rxp.capturedTexts().count() > 1 ) {
203 QStringList dictList = rxp.capturedTexts()[1].split("},", QString::SkipEmptyParts);
204 for (int k = 0; k < dictList.count(); ++k) {
205 QRegExp rxd ("[\\s]*\\{?([^\\{\\}]+)\\}?[\\s]*");
206 if ( rxd.indexIn( dictList[k] ) >= 0 && rxd.capturedTexts().count() > 1 ) {
207 for (int m = 1; m < rxd.capturedTexts().count(); ++m) {
208 pyfiles += rxd.capturedTexts()[m];
215 // Here pyfiles elements are: "script_name": [list_of_"arg"s]
216 // For example: "/absolute/path/to/my_script.py": ["1", "2"]
218 LightApp_Application::start();
219 SALOME_EventFilter::Init();
221 if ( !hdffile.isEmpty() ) // open hdf file given as parameter
222 onOpenDoc( hdffile );
223 else if ( pyfiles.count() > 0 ) // create new study
225 else if (!loadStudy.isEmpty()) {// load study by name
226 if (onLoadDoc(loadStudy))
227 updateObjectBrowser(true);
230 #ifndef DISABLE_PYCONSOLE
231 // import/execute python scripts
232 if ( pyfiles.count() > 0 && activeStudy() ) {
233 SalomeApp_Study* appStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
234 PyConsole_Console* pyConsole = pythonConsole();
235 if ( appStudy && pyConsole ) {
236 _PTR(Study) aStudy = appStudy->studyDS();
237 if ( !aStudy->GetProperties()->IsLocked() ) {
238 // pyfiles[j] is a dictionary: {"/absolute/path/to/script.py": [script_args]}
239 // Path is absolute, script has .py extension
240 for (uint j = 0; j < pyfiles.count(); j++ ) {
241 // Extract scripts and their arguments, if any
242 QRegExp rxp ("\"(.+)\":[\\s]*\\[(.*)\\]");
243 if ( rxp.indexIn( pyfiles[j] ) >= 0 && rxp.capturedTexts().count() == 3 ) {
244 QString script = rxp.capturedTexts()[1];
246 QStringList argList = rxp.capturedTexts()[2].split(",", QString::SkipEmptyParts);
247 for (uint k = 0; k < argList.count(); k++ ) {
248 QString arg = argList[k].trimmed();
249 arg.remove( QRegExp("^[\"]") );
250 arg.remove( QRegExp("[\"]$") );
253 args.remove( QRegExp("[,]$") );
254 if (!args.isEmpty()) {
258 script.remove( QRegExp("^python.*[\\s]+") );
259 QString cmd = script+" "+args;
260 QString command = QString( "execfile(r\"%1\")" ).arg(cmd.trimmed());
261 pyConsole->exec(command);
263 } // end for loop on pyfiles QStringList
269 LightApp_Application::start();
270 SALOME_EventFilter::Init();
275 void SalomeApp_Application::createActions()
277 LightApp_Application::createActions();
279 SUIT_Desktop* desk = desktop();
282 // "Save GUI State" command is moved to VISU module
283 // createAction( SaveGUIStateId, tr( "TOT_DESK_FILE_SAVE_GUI_STATE" ), QIcon(),
284 // tr( "MEN_DESK_FILE_SAVE_GUI_STATE" ), tr( "PRP_DESK_FILE_SAVE_GUI_STATE" ),
285 // 0, desk, false, this, SLOT( onSaveGUIState() ) );
288 createAction( DumpStudyId, tr( "TOT_DESK_FILE_DUMP_STUDY" ), QIcon(),
289 tr( "MEN_DESK_FILE_DUMP_STUDY" ), tr( "PRP_DESK_FILE_DUMP_STUDY" ),
290 Qt::CTRL+Qt::Key_D, desk, false, this, SLOT( onDumpStudy() ) );
293 createAction( LoadScriptId, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), QIcon(),
294 tr( "MEN_DESK_FILE_LOAD_SCRIPT" ), tr( "PRP_DESK_FILE_LOAD_SCRIPT" ),
295 Qt::CTRL+Qt::Key_T, desk, false, this, SLOT( onLoadScript() ) );
298 createAction( PropertiesId, tr( "TOT_DESK_PROPERTIES" ), QIcon(),
299 tr( "MEN_DESK_PROPERTIES" ), tr( "PRP_DESK_PROPERTIES" ),
300 Qt::CTRL+Qt::Key_P, desk, false, this, SLOT( onProperties() ) );
302 //! Catalog Generator
303 createAction( CatalogGenId, tr( "TOT_DESK_CATALOG_GENERATOR" ), QIcon(),
304 tr( "MEN_DESK_CATALOG_GENERATOR" ), tr( "PRP_DESK_CATALOG_GENERATOR" ),
305 Qt::ALT+Qt::SHIFT+Qt::Key_G, desk, false, this, SLOT( onCatalogGen() ) );
308 createAction( RegDisplayId, tr( "TOT_DESK_REGISTRY_DISPLAY" ), QIcon(),
309 tr( "MEN_DESK_REGISTRY_DISPLAY" ), tr( "PRP_DESK_REGISTRY_DISPLAY" ),
310 /*Qt::SHIFT+Qt::Key_D*/0, desk, false, this, SLOT( onRegDisplay() ) );
312 createAction( ConnectId, tr( "TOT_DESK_CONNECT_STUDY" ), QIcon(),
313 tr( "MEN_DESK_CONNECT" ), tr( "PRP_DESK_CONNECT" ),
314 Qt::CTRL+Qt::Key_L, desk, false, this, SLOT( onLoadDoc() ) );
316 createAction( DisconnectId, tr( "TOT_DESK_DISCONNECT_STUDY" ), QIcon(),
317 tr( "MEN_DESK_DISCONNECT" ), tr( "PRP_DESK_DISCONNECT" ),
318 Qt::CTRL+Qt::Key_U, desk, false, this, SLOT( onUnloadDoc() ) );
321 int fileMenu = createMenu( tr( "MEN_DESK_FILE" ), -1 );
323 // "Save GUI State" command is renamed to "Save VISU State" and
324 // creation of menu item is moved to VISU
325 // createMenu( SaveGUIStateId, fileMenu, 10, -1 );
327 createMenu( ConnectId, fileMenu, 5 );
328 createMenu( DisconnectId, fileMenu, 5 );
329 createMenu( separator(), fileMenu, -1, 5 );
331 createMenu( DumpStudyId, fileMenu, 10, -1 );
332 createMenu( LoadScriptId, fileMenu, 10, -1 );
333 createMenu( separator(), fileMenu, -1, 10, -1 );
334 createMenu( PropertiesId, fileMenu, 10, -1 );
335 createMenu( separator(), fileMenu, -1, 10, -1 );
337 int toolsMenu = createMenu( tr( "MEN_DESK_TOOLS" ), -1, MenuToolsId, 50 );
338 createMenu( CatalogGenId, toolsMenu, 10, -1 );
339 createMenu( RegDisplayId, toolsMenu, 10, -1 );
340 createMenu( separator(), toolsMenu, -1, 15, -1 );
342 createExtraActions();
344 #ifndef DISABLE_PYCONSOLE
345 #ifndef DISABLE_SALOMEOBJECT
346 // import Python module that manages SALOME plugins
348 PyLockWrapper lck; // acquire GIL
349 PyObjWrapper pluginsmanager = PyImport_ImportModule((char*)"salome_pluginsmanager");
350 PyObjWrapper res = PyObject_CallMethod( pluginsmanager, (char*)"initialize", (char*)"isss",0,"salome",tr("MEN_DESK_TOOLS").toUtf8().data(),tr("MEN_DESK_PLUGINS").toUtf8().data());
354 // end of SALOME plugins loading
361 \brief Close application.
363 void SalomeApp_Application::onExit()
365 bool killServers = false;
368 if ( exitConfirmation() ) {
369 SalomeApp_ExitDlg dlg( desktop() );
370 result = dlg.exec() == QDialog::Accepted;
371 killServers = dlg.isServersShutdown();
375 if ( !killServers ) myIsCloseFromExit = true;
376 SUIT_Session::session()->closeSession( SUIT_Session::ASK, killServers );
377 if ( SUIT_Session::session()->applications().count() > 0 ) myIsCloseFromExit = false;
381 /*!SLOT. Load document.*/
382 void SalomeApp_Application::onLoadDoc()
386 std::vector<std::string> List = studyMgr()->GetOpenStudies();
388 // rnv: According to the single-study approach on the server side
389 // can be only one study. So if it is exists connect to them,
390 // overwise show warning message: "No active study on the server"
393 SUIT_Session* aSession = SUIT_Session::session();
394 QList<SUIT_Application*> aAppList = aSession->applications();
396 QStringList unloadedStudies;
398 for ( unsigned int ind = 0; ind < List.size(); ind++ ) {
399 studyName = List[ind].c_str();
400 // Add to list only unloaded studies
401 bool isAlreadyOpen = false;
402 QListIterator<SUIT_Application*> it( aAppList );
403 while ( it.hasNext() && !isAlreadyOpen ) {
404 SUIT_Application* aApp = it.next();
405 if( !aApp || !aApp->activeStudy() )
407 if ( aApp->activeStudy()->studyName() == studyName )
408 isAlreadyOpen = true;
411 if ( !isAlreadyOpen )
412 unloadedStudies << studyName;
414 studyName = SalomeApp_LoadStudiesDlg::selectStudy( desktop(), unloadedStudies );
415 if ( studyName.isEmpty() )
419 if(List.size() <= 0) {
420 SUIT_MessageBox::warning( desktop(),
421 QObject::tr("WRN_WARNING"),
422 QObject::tr("WRN_NO_STUDY_ON SERV") );
426 studyName = List[0].c_str();
429 // this code replaces marker of windows drive and path become invalid therefore
430 // defines placed there
431 studyName.replace( QRegExp(":"), "/" );
434 if ( onLoadDoc( studyName ) ) {
436 updateViewManagers();
437 updateObjectBrowser( true );
441 /*!SLOT. Unload document.*/
442 void SalomeApp_Application::onUnloadDoc( bool ask )
445 activeStudy()->abortAllOperations();
446 if ( activeStudy()->isModified() ) {
447 QString docName = activeStudy()->studyName().trimmed();
448 int answer = SUIT_MessageBox::question( desktop(), tr( "DISCONNECT_CAPTION" ),
449 tr( "DISCONNECT_DESCRIPTION" ),
450 tr( "DISCONNECT_SAVE" ),
451 tr( "DISCONNECT_WO_SAVE" ),
452 tr( "APPCLOSE_CANCEL" ), 0 );
453 if ( answer == 0 ) { // save before unload
454 if ( activeStudy()->isSaved() )
456 else if ( !onSaveAsDoc() )
459 else if ( answer == 2 ) // Cancel
463 closeActiveDoc( false );
466 /*!SLOT. Create new study and load script*/
467 void SalomeApp_Application::onNewWithScript()
469 QStringList filtersList;
470 filtersList.append(tr("PYTHON_FILES_FILTER"));
471 filtersList.append(tr("ALL_FILES_FILTER"));
473 QString anInitialPath = "";
474 if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
475 anInitialPath = QDir::currentPath();
477 QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true );
479 if ( !aFile.isEmpty() )
483 QString command = QString("execfile(r\"%1\")").arg(aFile);
485 #ifndef DISABLE_PYCONSOLE
486 PyConsole_Console* pyConsole = pythonConsole();
489 pyConsole->exec( command );
495 /*!SLOT. Load document with \a aName.*/
496 bool SalomeApp_Application::onLoadDoc( const QString& aName )
498 #ifdef SINGLE_DESKTOP
499 if ( !LightApp_Application::closeDoc() )
503 if ( !activeStudy() ) {
504 // if no study - load in current desktop
505 res = useStudy( aName );
508 // if study exists - load in new desktop. Check: is the same file is loaded?
509 SUIT_Session* aSession = SUIT_Session::session();
510 QList<SUIT_Application*> aAppList = aSession->applications();
511 bool isAlreadyOpen = false;
512 SalomeApp_Application* aApp = 0;
513 for ( QList<SUIT_Application*>::iterator it = aAppList.begin();
514 it != aAppList.end() && !isAlreadyOpen; ++it ) {
515 aApp = dynamic_cast<SalomeApp_Application*>( *it );
516 if ( aApp && aApp->activeStudy()->studyName() == aName )
517 isAlreadyOpen = true;
519 if ( !isAlreadyOpen ) {
520 aApp = dynamic_cast<SalomeApp_Application*>( startApplication( 0, 0 ) );
522 res = aApp->useStudy( aName );
525 aApp->desktop()->activateWindow();
532 /*!SLOT. Parse message for desktop.*/
533 void SalomeApp_Application::onDesktopMessage( const QString& message )
535 if (message.indexOf("studyCreated:") == 0) {
536 // Enable 'Connect' action
537 updateCommandsStatus();
539 else if (message.indexOf("studyClosed:") == 0) {
540 /* message also contains ID of the closed study,
541 but as soon as SALOME is mono-study application for the moment,
542 this ID is not needed now.*/
543 //long aStudyId = message.section(':', 1).toLong();
544 // Disconnect GUI from active study, because it was closed on DS side.
545 closeActiveDoc( false );
546 // Disable 'Connect' action
547 QAction* a = action( ConnectId );
549 a->setEnabled( false );
551 else if ( message.toLower() == "connect_to_study" ) {
554 LightApp_Application::onDesktopMessage( message );
557 /*!SLOT. Copy objects to study maneger from selection maneger..*/
558 void SalomeApp_Application::onCopy()
561 LightApp_SelectionMgr* mgr = selectionMgr();
562 mgr->selectedObjects(list);
564 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
565 if(study == NULL) return;
567 _PTR(Study) stdDS = study->studyDS();
570 SALOME_ListIteratorOfListIO it( list );
573 _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry());
575 studyMgr()->Copy(so);
576 onSelectionChanged();
583 /*!SLOT. Paste objects to study maneger from selection manager.*/
584 void SalomeApp_Application::onPaste()
587 LightApp_SelectionMgr* mgr = selectionMgr();
588 mgr->selectedObjects(list);
590 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
591 if(study == NULL) return;
593 _PTR(Study) stdDS = study->studyDS();
596 if ( stdDS->GetProperties()->IsLocked() ) {
597 SUIT_MessageBox::warning( desktop(),
598 QObject::tr("WRN_WARNING"),
599 QObject::tr("WRN_STUDY_LOCKED") );
603 SALOME_ListIteratorOfListIO it( list );
606 _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry());
608 studyMgr()->Paste(so);
609 updateObjectBrowser( true );
610 updateActions(); //SRN: BugID IPAL9377, case 3
617 /*!Check the application on closing.
618 * \retval true if possible, else false
620 bool SalomeApp_Application::isPossibleToClose( bool& closePermanently )
622 return LightApp_Application::isPossibleToClose( closePermanently );
625 /*! Check if the study is locked */
626 void SalomeApp_Application::onCloseDoc( bool ask )
628 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
631 _PTR(Study) stdDS = study->studyDS();
632 if(stdDS && stdDS->IsStudyLocked()) {
633 if ( SUIT_MessageBox::question( desktop(),
634 QObject::tr( "WRN_WARNING" ),
635 QObject::tr( "CLOSE_LOCKED_STUDY" ),
636 SUIT_MessageBox::Yes | SUIT_MessageBox::No,
637 SUIT_MessageBox::No) == SUIT_MessageBox::No ) return;
641 LightApp_Application::onCloseDoc( ask );
644 /*!Sets enable or disable some actions on selection changed.*/
645 void SalomeApp_Application::onSelectionChanged()
648 LightApp_SelectionMgr* mgr = selectionMgr();
649 mgr->selectedObjects(list);
651 bool canCopy = false;
652 bool canPaste = false;
654 LightApp_Module* m = dynamic_cast<LightApp_Module*>( activeModule() );
657 canCopy = m->canCopy();
658 canPaste = m->canPaste();
661 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
663 _PTR(Study) stdDS = study->studyDS();
666 SALOME_ListIteratorOfListIO it ( list );
668 if (it.More() && list.Extent() == 1) {
669 _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry());
672 canCopy = canCopy || studyMgr()->CanCopy(so);
673 canPaste = canPaste || studyMgr()->CanPaste(so);
679 action(EditCopyId)->setEnabled(canCopy);
680 action(EditPasteId)->setEnabled(canPaste);
683 /*!Delete references.*/
684 void SalomeApp_Application::onDeleteInvalidReferences()
687 LightApp_SelectionMgr* mgr = selectionMgr();
688 mgr->selectedObjects( aList, QString(), false );
690 if( aList.IsEmpty() )
693 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>(activeStudy());
694 _PTR(Study) aStudyDS = aStudy->studyDS();
695 _PTR(StudyBuilder) aStudyBuilder = aStudyDS->NewBuilder();
698 for( SALOME_ListIteratorOfListIO it( aList ); it.More(); it.Next() )
699 if ( it.Value()->hasEntry() )
701 _PTR(SObject) aSObject = aStudyDS->FindObjectID( it.Value()->getEntry() ), aRefObj = aSObject;
702 while( aRefObj && aRefObj->ReferencedObject( anObj ) )
705 if( aRefObj && aRefObj!=aSObject && QString( aRefObj->GetName().c_str() ).isEmpty() )
706 aStudyBuilder->RemoveReference( aSObject );
708 updateObjectBrowser();
712 void SalomeApp_Application::onOpenWith()
714 QApplication::setOverrideCursor( Qt::WaitCursor );
716 LightApp_SelectionMgr* mgr = selectionMgr();
717 mgr->selectedObjects(aList);
718 if (aList.Extent() != 1)
720 QApplication::restoreOverrideCursor();
723 Handle(SALOME_InteractiveObject) aIObj = aList.First();
724 QString aModuleName(aIObj->getComponentDataType());
725 QString aModuleTitle = moduleTitle(aModuleName);
726 activateModule(aModuleTitle);
727 QApplication::restoreOverrideCursor();
733 SUIT_Study* SalomeApp_Application::createNewStudy()
735 SalomeApp_Study* aStudy = new SalomeApp_Study( this );
737 // Set up processing of major study-related events
738 connect( aStudy, SIGNAL( created( SUIT_Study* ) ), this, SLOT( onStudyCreated( SUIT_Study* ) ) );
739 connect( aStudy, SIGNAL( opened ( SUIT_Study* ) ), this, SLOT( onStudyOpened ( SUIT_Study* ) ) );
740 connect( aStudy, SIGNAL( saved ( SUIT_Study* ) ), this, SLOT( onStudySaved ( SUIT_Study* ) ) );
741 connect( aStudy, SIGNAL( closed ( SUIT_Study* ) ), this, SLOT( onStudyClosed ( SUIT_Study* ) ) );
743 #ifndef DISABLE_PYCONSOLE
744 //to receive signal in application that NoteBook's variable was modified
745 connect( aStudy, SIGNAL(notebookVarUpdated(QString)),
746 this, SIGNAL(notebookVarUpdated(QString)) );
753 Enable/Disable menu items and toolbar buttons. Rebuild menu
755 void SalomeApp_Application::updateCommandsStatus()
757 LightApp_Application::updateCommandsStatus();
760 QAction* a = action( DumpStudyId );
762 a->setEnabled( activeStudy() );
764 #ifndef DISABLE_PYCONSOLE
766 a = action( LoadScriptId );
768 a->setEnabled( pythonConsole() );
772 a = action( PropertiesId );
774 a->setEnabled( activeStudy() );
776 // Save GUI state menu
777 a = action( SaveGUIStateId );
779 a->setEnabled( activeStudy() );
781 // Connect study menu
782 a = action( ConnectId );
784 a->setEnabled( !activeStudy() && studyMgr()->GetOpenStudies().size() > 0 );
786 // Disconnect study menu
787 a = action( DisconnectId );
789 a->setEnabled( activeStudy() );
791 // update state of Copy/Paste menu items
792 onSelectionChanged();
796 \class DumpStudyFileDlg
797 Private class used in Dump Study operation. Consists 2 check boxes:
798 "Publish in study" and "Save GUI parameters"
800 class DumpStudyFileDlg : public SUIT_FileDlg
803 DumpStudyFileDlg( QWidget* parent ) : SUIT_FileDlg( parent, false, true, true )
805 QGridLayout* grid = ::qobject_cast<QGridLayout*>( layout() );
808 QWidget *hB = new QWidget( this );
809 myPublishChk = new QCheckBox( tr("PUBLISH_IN_STUDY") );
810 myMultiFileChk = new QCheckBox( tr("MULTI_FILE_DUMP") );
811 mySaveGUIChk = new QCheckBox( tr("SAVE_GUI_STATE") );
813 QHBoxLayout *layout = new QHBoxLayout;
814 layout->addWidget(myPublishChk);
815 layout->addWidget(myMultiFileChk);
816 layout->addWidget(mySaveGUIChk);
817 hB->setLayout(layout);
819 QPushButton* pb = new QPushButton(this);
821 int row = grid->rowCount();
822 grid->addWidget( new QLabel("", this), row, 0 );
823 grid->addWidget( hB, row, 1, 1, 3 );
824 grid->addWidget( pb, row, 5 );
829 QCheckBox* myPublishChk;
830 QCheckBox* myMultiFileChk;
831 QCheckBox* mySaveGUIChk;
834 class DumpStudyFileValidator : public SUIT_FileValidator
837 DumpStudyFileValidator( QWidget* parent) : SUIT_FileValidator ( parent ) {};
838 virtual ~DumpStudyFileValidator() {};
839 virtual bool canSave( const QString& file, bool permissions );
842 bool DumpStudyFileValidator::canSave(const QString& file, bool permissions)
844 QFileInfo fi( file );
845 if ( !QRegExp( "[A-Za-z_][A-Za-z0-9_]*" ).exactMatch( fi.completeBaseName() ) ) {
846 SUIT_MessageBox::critical( parent(),
847 QObject::tr("WRN_WARNING"),
848 QObject::tr("WRN_FILE_NAME_BAD") );
851 return SUIT_FileValidator::canSave( file, permissions);
854 /*!Private SLOT. On dump study.*/
855 void SalomeApp_Application::onDumpStudy( )
857 SalomeApp_Study* appStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
858 if ( !appStudy ) return;
859 _PTR(Study) aStudy = appStudy->studyDS();
861 QStringList aFilters;
862 aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
864 bool anIsPublish = true;
865 bool anIsMultiFile = false;
866 bool anIsSaveGUI = true;
868 if ( SUIT_ResourceMgr* aResourceMgr = resourceMgr() ) {
869 anIsPublish = aResourceMgr->booleanValue( "Study", "pydump_publish", anIsPublish );
870 anIsMultiFile = aResourceMgr->booleanValue( "Study", "multi_file_dump", anIsMultiFile );
871 anIsSaveGUI = aResourceMgr->booleanValue( "Study", "pydump_save_gui", anIsSaveGUI );
874 DumpStudyFileDlg fd( desktop() );
875 fd.setValidator( new DumpStudyFileValidator( &fd ) );
876 fd.setWindowTitle( tr( "TOT_DESK_FILE_DUMP_STUDY" ) );
877 fd.setFilters( aFilters );
878 fd.myPublishChk->setChecked( anIsPublish );
879 fd.myMultiFileChk->setChecked( anIsMultiFile );
880 fd.mySaveGUIChk->setChecked( anIsSaveGUI );
881 if ( fd.exec() == QDialog::Accepted )
883 QString aFileName = fd.selectedFile();
885 bool toPublish = fd.myPublishChk->isChecked();
886 bool isMultiFile = fd.myMultiFileChk->isChecked();
887 bool toSaveGUI = fd.mySaveGUIChk->isChecked();
889 if ( !aFileName.isEmpty() ) {
890 QFileInfo aFileInfo(aFileName);
891 if( aFileInfo.isDir() ) // IPAL19257
894 // Issue 21377 - dump study implementation moved to SalomeApp_Study class
897 SUIT_OverrideCursor wc;
898 res = appStudy->dump( aFileName, toPublish, isMultiFile, toSaveGUI );
901 SUIT_MessageBox::warning( desktop(),
902 QObject::tr("WRN_WARNING"),
903 tr("WRN_DUMP_STUDY_FAILED") );
908 /*!Private SLOT. On load script.*/
909 void SalomeApp_Application::onLoadScript( )
911 SalomeApp_Study* appStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
913 _PTR(Study) aStudy = appStudy->studyDS();
914 if ( aStudy->GetProperties()->IsLocked() ) {
915 SUIT_MessageBox::warning( desktop(),
916 QObject::tr("WRN_WARNING"),
917 QObject::tr("WRN_STUDY_LOCKED") );
922 QStringList filtersList;
923 filtersList.append(tr("PYTHON_FILES_FILTER"));
924 filtersList.append(tr("ALL_FILES_FILTER"));
926 QString anInitialPath = "";
927 if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
928 anInitialPath = QDir::currentPath();
930 QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true );
932 if ( !aFile.isEmpty() )
934 QString command = QString("execfile(r\"%1\")").arg(aFile);
936 #ifndef DISABLE_PYCONSOLE
937 PyConsole_Console* pyConsole = pythonConsole();
940 pyConsole->exec( command );
945 /*!Private SLOT. On save GUI state.*/
946 void SalomeApp_Application::onSaveGUIState()
948 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
950 SalomeApp_VisualState( this ).storeState();
951 updateSavePointDataObjects( study );
952 updateObjectBrowser();
957 /*!Public SLOT. Performs some actions when dockable windows are triggered.*/
958 void SalomeApp_Application::onDockWindowVisibilityChanged( bool theIsVisible )
960 LightApp_Application::onDockWindowVisibilityChanged( theIsVisible );
961 QAction* send = ::qobject_cast<QAction*>( sender() );
964 QString aWinName = send->data().toString();
965 if ( theIsVisible && aWinName == "objectBrowser" )
966 objectBrowserColumnsVisibility();
970 *\retval QString "(*.hdf)"
972 QString SalomeApp_Application::getFileFilter() const
978 QWidget* SalomeApp_Application::createWindow( const int flag )
981 #ifndef DISABLE_PYCONSOLE
982 if ( flag != WT_PyConsole ) wid = LightApp_Application::createWindow(flag);
984 wid = LightApp_Application::createWindow(flag);
987 SUIT_ResourceMgr* resMgr = resourceMgr();
989 if ( flag == WT_ObjectBrowser )
991 SUIT_DataBrowser* ob = qobject_cast<SUIT_DataBrowser*>( wid );
993 // temporary commented
994 //ob->setUpdater( new SalomeApp_Updater() );
996 #ifdef WITH_SALOMEDS_OBSERVER
997 //do not activate the automatic update of Qt tree through signal/slot
998 ob->setAutoUpdate(false);
999 //activate update of modified objects only
1000 ob->setUpdateModified(true);
1003 connect( ob, SIGNAL( doubleClicked( SUIT_DataObject* ) ), this, SLOT( onDblClick( SUIT_DataObject* ) ) );
1006 ValueCol = QObject::tr( "VALUE_COLUMN" ),
1007 IORCol = QObject::tr( "IOR_COLUMN" ),
1008 RefCol = QObject::tr( "REFENTRY_COLUMN" ),
1009 EntryCol = QObject::tr( "ENTRY_COLUMN" );
1011 SUIT_AbstractModel* treeModel = dynamic_cast<SUIT_AbstractModel*>( ob->model() );
1012 treeModel->registerColumn( 0, EntryCol, SalomeApp_DataObject::EntryId );
1013 treeModel->registerColumn( 0, ValueCol, SalomeApp_DataObject::ValueId );
1014 treeModel->registerColumn( 0, IORCol, SalomeApp_DataObject::IORId );
1015 treeModel->registerColumn( 0, RefCol, SalomeApp_DataObject::RefEntryId );
1016 treeModel->setAppropriate( EntryCol, Qtx::Toggled );
1017 treeModel->setAppropriate( ValueCol, Qtx::Toggled );
1018 treeModel->setAppropriate( IORCol, Qtx::Toggled );
1019 treeModel->setAppropriate( RefCol, Qtx::Toggled );
1021 bool autoSize = resMgr->booleanValue( "ObjectBrowser", "auto_size", false );
1022 bool autoSizeFirst = resMgr->booleanValue( "ObjectBrowser", "auto_size_first", true );
1023 bool resizeOnExpandItem = resMgr->booleanValue( "ObjectBrowser", "resize_on_expand_item", true );
1025 ob->setAutoSizeFirstColumn(autoSizeFirst);
1026 ob->setAutoSizeColumns(autoSize);
1027 ob->setResizeOnExpandItem(resizeOnExpandItem);
1028 ob->setProperty( "shortcut", QKeySequence( "Alt+Shift+O" ) );
1030 // temporary commented
1032 for ( int i = SalomeApp_DataObject::ValueIdx; i <= SalomeApp_DataObject::RefEntryIdx; i++ )
1034 ob->addColumn( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i ) ), i );
1035 ob->setColumnShown( i, resMgr->booleanValue( "ObjectBrowser",
1036 QString().sprintf( "visibility_column_%d", i ), true ) );
1040 // temporary commented
1042 ob->setWidthMode( autoSize ? QListView::Maximum : QListView::Manual );
1043 ob->listView()->setColumnWidthMode( 0, autoSizeFirst ? QListView::Maximum : QListView::Manual );
1044 ob->resize( desktop()->width()/3, ob->height() );
1048 #ifndef DISABLE_PYCONSOLE
1049 else if ( flag == WT_PyConsole )
1051 PyConsole_Console* pyCons = new PyConsole_EnhConsole( desktop(), getPyInterp() );
1052 pyCons->setObjectName( "pythonConsole" );
1053 pyCons->setWindowTitle( tr( "PYTHON_CONSOLE" ) );
1054 pyCons->setFont(resourceMgr()->fontValue( "PyConsole", "font" ));
1055 pyCons->setIsShowBanner(resourceMgr()->booleanValue( "PyConsole", "show_banner", true ));
1056 pyCons->setProperty( "shortcut", QKeySequence( "Alt+Shift+P" ) );
1058 //pyCons->resize( pyCons->width(), desktop()->height()/4 );
1059 pyCons->connectPopupRequest( this, SLOT( onConnectPopupRequest( SUIT_PopupClient*, QContextMenuEvent* ) ) );
1061 else if ( flag == WT_NoteBook )
1063 SalomeApp_Study* appStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1065 _PTR(Study) aStudy = appStudy->studyDS();
1066 setNoteBook( new SalomeApp_NoteBook( desktop(), aStudy ) );
1067 //to receive signal in NoteBook that it's variable was modified
1068 connect( this, SIGNAL( notebookVarUpdated( QString ) ),
1069 getNoteBook(), SLOT( onVarUpdate( QString ) ) );
1071 wid = getNoteBook();
1072 wid->setObjectName( "noteBook" );
1078 /*!Create preferences.*/
1079 void SalomeApp_Application::createPreferences( LightApp_Preferences* pref )
1081 LightApp_Application::createPreferences(pref);
1086 int salomeCat = pref->addPreference( tr( "PREF_CATEGORY_SALOME" ) );
1087 int obTab = pref->addPreference( tr( "PREF_TAB_OBJBROWSER" ), salomeCat );
1088 int defCols = pref->addPreference( tr( "PREF_GROUP_DEF_COLUMNS" ), obTab );
1089 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1091 pref->addPreference( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i-SalomeApp_DataObject::EntryId ).toLatin1() ), defCols,
1092 LightApp_Preferences::Bool, "ObjectBrowser", QString().sprintf( "visibility_column_id_%d", i-1 ) );
1094 pref->setItemProperty( "orientation", Qt::Vertical, defCols );
1096 // adding preference to LightApp_Application handled preferences.. a bit of hacking with resources..
1097 int genTab = pref->addPreference( LightApp_Application::tr( "PREF_TAB_GENERAL" ), salomeCat );
1098 int studyGroup = pref->addPreference( LightApp_Application::tr( "PREF_GROUP_STUDY" ), genTab );
1099 pref->addPreference( tr( "PREF_STORE_VISUAL_STATE" ), studyGroup, LightApp_Preferences::Bool, "Study", "store_visual_state" );
1100 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1101 pref->addPreference( tr( "PREF_PYDUMP_PUBLISH" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_publish" );
1102 pref->addPreference( tr( "PREF_PYDUMP_MULTI_FILE" ), studyGroup, LightApp_Preferences::Bool, "Study", "multi_file_dump" );
1103 pref->addPreference( tr( "PREF_PYDUMP_SAVE_GUI" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_save_gui" );
1104 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1105 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1108 /*!Update desktop title.*/
1109 void SalomeApp_Application::updateDesktopTitle() {
1110 QString aTitle = applicationName();
1111 QString aVer = applicationVersion();
1112 if ( !aVer.isEmpty() )
1113 aTitle += QString( " " ) + aVer;
1115 if ( activeStudy() )
1117 QString sName = SUIT_Tools::file( activeStudy()->studyName().trimmed(), false );
1118 if ( !sName.isEmpty() ) {
1119 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
1121 _PTR(Study) stdDS = study->studyDS();
1123 if ( stdDS->GetProperties()->IsLocked() ) {
1124 aTitle += QString( " - [%1 (%2)]").arg( sName ).arg( tr( "STUDY_LOCKED" ) );
1126 aTitle += QString( " - [%1]" ).arg( sName );
1133 desktop()->setWindowTitle( aTitle );
1136 int SalomeApp_Application::closeChoice( const QString& docName )
1138 QStringList buttons;
1139 QMap<int, int> choices;
1141 buttons << tr ("APPCLOSE_SAVE"); // Save & Close
1142 choices.insert( idx++, CloseSave ); // ...
1143 buttons << tr ("APPCLOSE_CLOSE"); // Close w/o saving
1144 choices.insert( idx++, CloseDiscard ); // ...
1145 if ( myIsCloseFromExit ) {
1146 buttons << tr ("APPCLOSE_UNLOAD_SAVE"); // Save & Disconnect
1147 choices.insert( idx++, CloseDisconnectSave ); // ...
1148 buttons << tr ("APPCLOSE_UNLOAD"); // Disconnect
1149 choices.insert( idx++, CloseDisconnect ); // ...
1151 buttons << tr ("APPCLOSE_CANCEL"); // Cancel
1152 choices.insert( idx++, CloseCancel ); // ...
1154 int answer = SUIT_MessageBox::question( desktop(), tr( "APPCLOSE_CAPTION" ),
1155 tr( "APPCLOSE_DESCRIPTION" ), buttons, 0 );
1156 return choices[answer];
1159 bool SalomeApp_Application::closeAction( const int choice, bool& closePermanently )
1165 if ( activeStudy()->isSaved() )
1167 else if ( !onSaveAsDoc() )
1172 case CloseDisconnectSave:
1173 if ( activeStudy()->isSaved() )
1175 else if ( !onSaveAsDoc() )
1177 case CloseDisconnect:
1178 closeActiveDoc( false );
1179 closePermanently = false;
1188 int SalomeApp_Application::openChoice( const QString& aName )
1190 int choice = LightApp_Application::openChoice( aName );
1192 if ( QFileInfo( aName ).exists() ) {
1193 if ( choice == OpenNew ) { // The document isn't already open.
1195 std::vector<std::string> lst = studyMgr()->GetOpenStudies();
1196 for ( uint i = 0; i < lst.size() && !exist; i++ ) {
1197 if ( aName == QString( lst[i].c_str() ) )
1200 // The document already exists in the study manager.
1201 // Do you want to reload it?
1203 int answer = SUIT_MessageBox::question( desktop(), tr( "WRN_WARNING" ), tr( "QUE_DOC_ALREADYEXIST" ).arg( aName ),
1204 SUIT_MessageBox::Yes | SUIT_MessageBox::No, SUIT_MessageBox::No );
1205 if ( answer == SUIT_MessageBox::Yes )
1206 choice = OpenRefresh;
1208 choice = OpenCancel;
1211 } else { // file is not exist on disk
1212 SUIT_MessageBox::warning( desktop(),
1213 QObject::tr("WRN_WARNING"),
1214 QObject::tr("WRN_FILE_NOT_EXIST").arg(aName.toLatin1().data()));
1221 bool SalomeApp_Application::openAction( const int aChoice, const QString& aName )
1224 int choice = aChoice;
1229 _PTR(Study) aStudy = studyMgr()->GetStudyByName( aName.toStdString() );
1232 studyMgr()->Close( aStudy );
1237 res = LightApp_Application::openAction( choice, aName );
1245 \brief Get map of the operations which can be performed
1246 on the module activation.
1248 The method should return the map of the kind \c {<id>:<name>}
1249 where \c <id> is an integer identifier of the operation and
1250 \c <name> is a title for the button to be added to the
1251 dialog box. After user selects the required operation by the
1252 clicking the corresponding button in the dialog box, its identifier
1253 is passed to the moduleActionSelected() method to process
1256 \return map of the operations
1257 \sa moduleActionSelected()
1259 QMap<int, QString> SalomeApp_Application::activateModuleActions() const
1261 QMap<int, QString> opmap = LightApp_Application::activateModuleActions();
1263 opmap.insert( LoadStudyId, tr( "ACTIVATE_MODULE_OP_LOAD" ) );
1265 opmap.insert( NewAndScriptId, tr( "ACTIVATE_MODULE_OP_SCRIPT" ) );
1270 \brief Called when the used selectes required operation chosen
1271 from "Activate module" dialog box.
1273 Performs the required operation according to the user choice.
1275 \param id operation identifier
1276 \sa activateModuleActions()
1278 void SalomeApp_Application::moduleActionSelected( const int id )
1284 case NewAndScriptId:
1288 LightApp_Application::moduleActionSelected( id );
1293 /*!Gets CORBA::ORB_var*/
1294 CORBA::ORB_var SalomeApp_Application::orb()
1296 ORB_INIT& init = *SINGLETON_<ORB_INIT>::Instance();
1297 static CORBA::ORB_var _orb = init( qApp->argc(), qApp->argv() );
1301 /*!Create and return SALOMEDS_StudyManager.*/
1302 SALOMEDSClient_StudyManager* SalomeApp_Application::studyMgr()
1304 static _PTR(StudyManager) _sm;
1305 if(!_sm) _sm = ClientFactory::StudyManager();
1309 /*!Create and return SALOME_NamingService.*/
1310 SALOME_NamingService* SalomeApp_Application::namingService()
1312 static SALOME_NamingService _ns(orb());
1316 /*!Create and return SALOME_LifeCycleCORBA.*/
1317 SALOME_LifeCycleCORBA* SalomeApp_Application::lcc()
1319 static SALOME_LifeCycleCORBA _lcc( namingService() );
1323 /*!Private SLOT. On preferences.*/
1324 void SalomeApp_Application::onProperties()
1326 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1330 _PTR(StudyBuilder) SB = study->studyDS()->NewBuilder();
1333 SalomeApp_StudyPropertiesDlg aDlg( desktop() );
1334 int res = aDlg.exec();
1335 if( res==QDialog::Accepted && aDlg.isChanged() )
1336 SB->CommitCommand();
1340 //study->updateCaptions();
1341 updateDesktopTitle();
1345 /*!Insert items in popup, which necessary for current application*/
1346 void SalomeApp_Application::contextMenuPopup( const QString& type, QMenu* thePopup, QString& title )
1348 LightApp_SelectionMgr* mgr = selectionMgr();
1349 bool cacheIsOn = mgr->isSelectionCacheEnabled();
1350 mgr->setSelectionCacheEnabled( true );
1352 LightApp_Application::contextMenuPopup( type, thePopup, title );
1354 // temporary commented
1355 /*OB_Browser* ob = objectBrowser();
1356 if ( !ob || type != ob->popupClientType() )
1359 // Get selected objects
1360 SALOME_ListIO aList;
1361 mgr->selectedObjects( aList, QString(), false );
1363 // add GUI state commands: restore, rename
1364 if ( aList.Extent() == 1 && aList.First()->hasEntry() &&
1365 QString( aList.First()->getEntry() ).startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) {
1366 thePopup->addSeparator();
1367 thePopup->addAction( tr( "MEN_RESTORE_VS" ), this, SLOT( onRestoreGUIState() ) );
1368 thePopup->addAction( tr( "MEN_RENAME_VS" ), objectBrowser(),
1369 SLOT( onStartEditing() ), objectBrowser()->shortcutKey(SUIT_DataBrowser::RenameShortcut) );
1370 thePopup->addAction( tr( "MEN_DELETE_VS" ), this, SLOT( onDeleteGUIState() ) );
1373 // "Delete reference" item should appear only for invalid references
1375 // isInvalidRefs will be true, if at least one of selected objects is invalid reference
1376 bool isInvalidRefs = false;
1377 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>(activeStudy());
1379 _PTR(Study) aStudyDS = aStudy->studyDS();
1380 _PTR(SObject) anObj;
1382 for( SALOME_ListIteratorOfListIO it( aList ); it.More() && !isInvalidRefs; it.Next() )
1384 if( it.Value()->hasEntry() )
1386 _PTR(SObject) aSObject = aStudyDS->FindObjectID( it.Value()->getEntry() ), aRefObj = aSObject;
1387 while( aRefObj && aRefObj->ReferencedObject( anObj ) )
1390 if( aRefObj && aRefObj!=aSObject && QString( aRefObj->GetName().c_str() ).isEmpty() )
1391 isInvalidRefs = true;
1395 // Add "Delete reference" item to popup
1396 if ( isInvalidRefs )
1398 thePopup->addSeparator();
1399 thePopup->addAction( tr( "MEN_DELETE_INVALID_REFERENCE" ), this, SLOT( onDeleteInvalidReferences() ) );
1403 // "Activate module" item should appear only if it's necessary
1404 if ( aList.Extent() == 1 ) {
1406 mgr->selectedObjects( aList );
1408 Handle(SALOME_InteractiveObject) aIObj = aList.First();
1410 // add extra popup menu (defined in XML)
1411 if ( myExtActions.size() > 0 ) {
1412 // Use only first selected object
1413 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1415 _PTR(Study) stdDS = study->studyDS();
1417 _PTR(SObject) aSO = stdDS->FindObjectID( aIObj->getEntry() );
1419 _PTR( GenericAttribute ) anAttr;
1420 std::string auid = "AttributeUserID";
1421 auid += Kernel_Utils::GetGUID(Kernel_Utils::ObjectdID);
1422 if ( aSO->FindAttribute( anAttr, auid ) ) {
1423 _PTR(AttributeUserID) aAttrID = anAttr;
1424 QString aId = aAttrID->Value().c_str();
1425 if ( myExtActions.contains( aId ) ) {
1426 thePopup->addAction(myExtActions[aId]);
1434 // check if item is a "GUI state" item (also a first level object)
1435 QString entry( aIObj->getEntry() );
1436 if ( !entry.startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) {
1437 QString aModuleName( aIObj->getComponentDataType() );
1438 QString aModuleTitle = moduleTitle( aModuleName );
1439 CAM_Module* currentModule = activeModule();
1440 if ( ( !currentModule || currentModule->moduleName() != aModuleTitle ) && !aModuleTitle.isEmpty() )
1441 thePopup->addAction( tr( "MEN_OPENWITH" ).arg( aModuleTitle ), this, SLOT( onOpenWith() ) );
1446 mgr->setSelectionCacheEnabled( cacheIsOn );
1449 /*!Update obect browser:
1450 1.if 'updateModels' true, update existing data models;
1451 2. update "non-existing" (not loaded yet) data models;
1452 3. update object browser if it exists */
1453 void SalomeApp_Application::updateObjectBrowser( const bool updateModels )
1455 // update "non-existing" (not loaded yet) data models
1456 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
1459 _PTR(Study) stdDS = study->studyDS();
1462 for ( _PTR(SComponentIterator) it ( stdDS->NewComponentIterator() ); it->More(); it->Next() )
1464 _PTR(SComponent) aComponent ( it->Value() );
1466 #ifndef WITH_SALOMEDS_OBSERVER
1467 // with GUI observers this check is not needed anymore
1468 if ( aComponent->ComponentDataType() == study->getVisualComponentName().toLatin1().constData() )
1469 continue; // skip the magic "Interface Applicative" component
1471 if ( !objectBrowser() )
1472 getWindow( WT_ObjectBrowser );
1473 const bool isAutoUpdate = objectBrowser()->autoUpdate();
1474 objectBrowser()->setAutoUpdate( false );
1475 SalomeApp_DataModel::synchronize( aComponent, study );
1476 objectBrowser()->setAutoUpdate( isAutoUpdate );
1481 // create data objects that correspond to GUI state save points
1482 if ( study ) updateSavePointDataObjects( study );
1484 // update existing data models (already loaded SComponents)
1485 LightApp_Application::updateObjectBrowser( updateModels );
1488 /*!Display Catalog Genenerator dialog */
1489 void SalomeApp_Application::onCatalogGen()
1491 ToolsGUI_CatalogGeneratorDlg aDlg( desktop() );
1495 /*!Display Registry Display dialog */
1496 void SalomeApp_Application::onRegDisplay()
1498 CORBA::ORB_var anOrb = orb();
1499 ToolsGUI_RegWidget* regWnd = ToolsGUI_RegWidget::GetRegWidget( anOrb, desktop() );
1502 regWnd->activateWindow();
1505 /*!find original object by double click on item */
1506 void SalomeApp_Application::onDblClick( SUIT_DataObject* theObj )
1508 // Issue 21379: References are supported at LightApp_DataObject level
1509 LightApp_DataObject* obj = dynamic_cast<LightApp_DataObject*>( theObj );
1511 if( obj && obj->isReference() )
1513 QString entry = obj->refEntry();
1515 SUIT_DataOwnerPtrList aList;
1516 aList.append( new LightApp_DataOwner( entry ) );
1517 selectionMgr()->setSelected( aList, false );
1519 SUIT_DataBrowser* ob = objectBrowser();
1521 QModelIndexList aSelectedIndexes = ob->selectedIndexes();
1522 if ( !aSelectedIndexes.isEmpty() )
1523 ob->treeView()->scrollTo( aSelectedIndexes.first() );
1528 Creates new view manager
1529 \param type - type of view manager
1531 SUIT_ViewManager* SalomeApp_Application::newViewManager(const QString& type)
1533 return createViewManager(type);
1537 /*!Global utility function, returns selected GUI Save point object's ID */
1538 int getSelectedSavePoint( const LightApp_SelectionMgr* selMgr )
1540 SALOME_ListIO aList;
1541 selMgr->selectedObjects( aList );
1542 if( aList.Extent() > 0 ) {
1543 Handle(SALOME_InteractiveObject) aIObj = aList.First();
1544 QString entry( aIObj->getEntry() );
1545 QString startStr = QObject::tr( "SAVE_POINT_DEF_NAME" );
1546 if ( !entry.startsWith( startStr ) ) // it's a "GUI state" object
1548 bool ok; // conversion to integer is ok?
1549 int savePoint = entry.right( entry.length() - startStr.length() ).toInt( &ok );
1550 return ok ? savePoint : -1;
1555 /*!Called on Restore GUI State popup command*/
1556 void SalomeApp_Application::onRestoreGUIState()
1558 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1559 if ( savePoint == -1 )
1561 SalomeApp_VisualState( this ).restoreState( savePoint );
1564 /*!Called on Delete GUI State popup command*/
1565 void SalomeApp_Application::onDeleteGUIState()
1567 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1568 if ( savePoint == -1 )
1570 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1574 study->removeSavePoint( savePoint );
1575 updateSavePointDataObjects( study );
1578 /*!Called on New study operation*/
1579 void SalomeApp_Application::onStudyCreated( SUIT_Study* study )
1581 LightApp_Application::onStudyCreated( study );
1583 //#ifndef DISABLE_PYCONSOLE
1584 // desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
1585 // windowDock( getWindow( WT_ObjectBrowser ) ) );
1588 loadDockWindowsState();
1590 objectBrowserColumnsVisibility();
1593 /*!Called on Open study operation*/
1594 void SalomeApp_Application::onStudyOpened( SUIT_Study* study )
1596 LightApp_Application::onStudyOpened( study );
1598 //#ifndef DISABLE_PYCONSOLE
1599 // desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
1600 // windowDock( getWindow( WT_ObjectBrowser ) ) );
1603 loadDockWindowsState();
1605 objectBrowserColumnsVisibility();
1607 // temporary commented
1608 /*if ( objectBrowser() ) {
1609 updateSavePointDataObjects( dynamic_cast<SalomeApp_Study*>( study ) );
1610 objectBrowser()->updateTree( study->root() );
1614 /*! updateSavePointDataObjects: syncronize data objects that correspond to save points (gui states)*/
1615 void SalomeApp_Application::updateSavePointDataObjects( SalomeApp_Study* study )
1618 SUIT_DataBrowser* ob = objectBrowser();
1619 LightApp_SelectionMgr* selMgr = selectionMgr();
1621 if ( !study || !ob || !selMgr )
1624 // find GUI states root object
1625 SUIT_DataObject* guiRootObj = 0;
1627 study->root()->children( ch );
1628 DataObjectList::const_iterator it = ch.begin(), last = ch.end();
1629 for ( ; it != last ; ++it ) {
1630 if ( dynamic_cast<SalomeApp_SavePointRootObject*>( *it ) ) {
1635 std::vector<int> savePoints = study->getSavePoints();
1636 // case 1: no more save points but they existed in study's tree
1637 if ( savePoints.empty() && guiRootObj ) {
1638 //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state"
1639 // : set auto update to true for removing SalomeApp_SavePointRootObject from the SUIT_TreeModel
1640 const bool isAutoUpdate = ob->autoUpdate();
1641 selMgr->clearSelected();
1642 ob->setAutoUpdate(true);
1643 DataObjectList ch = guiRootObj->children();
1644 for( int i = 0; i < ch.size(); i++ )
1647 ob->setAutoUpdate(isAutoUpdate);
1650 // case 2: no more save points but root does not exist either
1651 if ( savePoints.empty() && !guiRootObj )
1653 // case 3: save points but no root for them - create it
1654 if ( !savePoints.empty() && !guiRootObj )
1655 guiRootObj = new SalomeApp_SavePointRootObject( study->root() );
1656 // case 4: everything already exists.. here may be a problem: we want "GUI states" root object
1657 // to be always the last one in the tree. Here we check - if it is not the last one - remove and
1659 if ( guiRootObj->nextBrother() ) {
1660 study->root()->removeChild(guiRootObj);
1661 study->root()->appendChild(guiRootObj);
1662 //study->root()->dump();
1665 // store data objects in a map id-to-DataObject
1666 QMap<int,SalomeApp_SavePointObject*> mapDO;
1668 guiRootObj->children( ch );
1669 for( it = ch.begin(), last = ch.end(); it != last ; ++it ) {
1670 SalomeApp_SavePointObject* dobj = dynamic_cast<SalomeApp_SavePointObject*>( *it );
1672 mapDO[dobj->getId()] = dobj;
1675 // iterate new save points. if DataObject with such ID not found in map - create DataObject
1676 // if in the map - remove it from map.
1677 for ( int i = 0; i < savePoints.size(); i++ )
1678 if ( !mapDO.contains( savePoints[i] ) )
1679 new SalomeApp_SavePointObject( guiRootObj, savePoints[i], study );
1681 mapDO.remove( savePoints[i] );
1683 // delete DataObjects that are still in the map -- their IDs were not found in data model
1684 if( mapDO.size() > 0) {
1685 //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state"
1686 // : set auto update to true for removing SalomeApp_SavePointObject from the SUIT_TreeModel
1687 selMgr->clearSelected();
1688 const bool isAutoUpdate = ob->autoUpdate();
1689 ob->setAutoUpdate(true);
1690 for ( QMap<int,SalomeApp_SavePointObject*>::Iterator it = mapDO.begin(); it != mapDO.end(); ++it )
1692 ob->setAutoUpdate(isAutoUpdate);
1696 /*! Check data object */
1697 bool SalomeApp_Application::checkDataObject(LightApp_DataObject* theObj)
1706 Opens other study into active Study. If Study is empty - creates it.
1707 \param theName - name of study
1709 bool SalomeApp_Application::useStudy( const QString& theName )
1712 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1715 res = aStudy->loadDocument( theName );
1716 updateDesktopTitle();
1717 updateCommandsStatus();
1721 /*! Show/hide object browser colums according to preferences */
1722 void SalomeApp_Application::objectBrowserColumnsVisibility()
1724 if ( objectBrowser() )
1725 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1727 bool shown = resourceMgr()->booleanValue( "ObjectBrowser", QString( "visibility_column_id_%1" ).arg( i-1 ), true );
1728 objectBrowser()->treeView()->setColumnHidden( i, !shown );
1732 #ifndef DISABLE_PYCONSOLE
1733 /*! Set SalomeApp_NoteBook pointer */
1734 void SalomeApp_Application::setNoteBook( SalomeApp_NoteBook* theNoteBook )
1736 myNoteBook = theNoteBook;
1739 /*! Return SalomeApp_NoteBook pointer */
1740 SalomeApp_NoteBook* SalomeApp_Application::getNoteBook() const
1747 * Define extra actions defined in module definition XML file.
1748 * Additional popup items sections can be defined by parameter "popupitems".
1749 * Supported attributes:
1750 * title - title of menu item,
1751 * attributelocalid - AttributeLocalId defined for selected data item where menu command has to be applied,
1752 * method - method which has to be called when menu item is selected
1754 * <section name="MODULENAME">
1755 * <parameter name="popupitems" value="menuitem1:menuitem2:..."/>
1757 * <section name="importmed">
1758 * <parameter name="title" value="My menu"/>
1759 * <parameter name="objectid" value="VISU.Result"/>
1760 * <parameter name="method" value="nameOfModuleMethod"/>
1763 void SalomeApp_Application::createExtraActions()
1765 myExtActions.clear();
1766 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
1768 QStringList aModules;
1769 modules(aModules, false);
1770 foreach(QString aModile, aModules) {
1771 QString aModName = moduleName(aModile);
1772 QString aSectionStr = resMgr->stringValue(aModName, "popupitems", QString());
1773 if (!aSectionStr.isNull()) {
1774 QStringList aSections = aSectionStr.split(':');
1775 foreach(QString aSection, aSections) {
1776 QString aTitle = resMgr->stringValue(aSection, "title", QString());
1777 QString aId = resMgr->stringValue(aSection, "objectid", QString());
1778 QString aSlot = resMgr->stringValue(aSection, "method", QString());
1779 if (aTitle.isEmpty() || aSlot.isEmpty() || aId.isEmpty())
1782 QString aModuleName = resMgr->stringValue(aSection, "module", QString());
1783 if (aModuleName.isNull())
1784 aModuleName = aModName;
1786 QAction* aAction = new QAction(aTitle, this);
1788 aData<<aModuleName<<aSlot;
1789 aAction->setData(aData);
1790 connect(aAction, SIGNAL(triggered()), this, SLOT(onExtAction()));
1791 myExtActions[aId] = aAction;
1798 * Called when extra action is selected
1800 void SalomeApp_Application::onExtAction()
1802 QAction* aAction = ::qobject_cast<QAction*>(sender());
1806 QVariant aData = aAction->data();
1807 QStringList aDataList = aData.value<QStringList>();
1808 if (aDataList.size() != 2)
1811 LightApp_SelectionMgr* aSelectionMgr = selectionMgr();
1812 SALOME_ListIO aListIO;
1813 aSelectionMgr->selectedObjects(aListIO);
1814 const Handle(SALOME_InteractiveObject)& anIO = aListIO.First();
1815 if (aListIO.Extent() < 1)
1817 if (!anIO->hasEntry())
1820 QString aEntry(anIO->getEntry());
1822 QApplication::setOverrideCursor( Qt::WaitCursor );
1823 QString aModuleTitle = moduleTitle(aDataList[0]);
1824 activateModule(aModuleTitle);
1825 QApplication::restoreOverrideCursor();
1827 QCoreApplication::processEvents();
1829 CAM_Module* aModule = activeModule();
1833 if (!QMetaObject::invokeMethod(aModule, qPrintable(aDataList[1]), Q_ARG(QString, aEntry)))
1834 printf("Error: Can't Invoke method %s\n", qPrintable(aDataList[1]));
1838 Checks that an object can be renamed.
1839 \param entry entry of the object
1840 \brief Return \c true if object can be renamed
1842 bool SalomeApp_Application::renameAllowed( const QString& entry) const
1844 return entry.startsWith( tr( "SAVE_POINT_DEF_NAME") );
1848 Rename object by entry.
1849 \param entry entry of the object
1850 \param name new name of the object
1851 \brief Return \c true if rename operation finished successfully, \c false otherwise.
1853 bool SalomeApp_Application::renameObject( const QString& entry, const QString& name )
1855 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1857 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1859 if(!aStudy || savePoint == -1)
1862 if ( !name.isNull() && !name.isEmpty() ) {
1863 aStudy->setNameOfSavePoint( savePoint, name );
1864 updateSavePointDataObjects( aStudy );
1866 //Mark study as modified
1873 #ifndef DISABLE_PYCONSOLE
1874 //============================================================================
1875 /*! Function : onUpdateStudy
1876 * Purpose : Slot to update the study.
1878 //============================================================================
1879 void SalomeApp_Application::onUpdateStudy()
1881 QApplication::setOverrideCursor( Qt::WaitCursor );
1883 if( !updateStudy() )
1884 SUIT_MessageBox::warning( desktop(), tr( "ERROR" ), tr( "ERR_UPDATE_STUDY_FAILED" ) );
1886 QApplication::restoreOverrideCursor();
1889 //============================================================================
1890 /*! Function : updateStudy
1891 * Purpose : Update study by dumping the study to Python script and loading it.
1892 * It is used to apply variable modifications done in NoteBook to created objects.
1894 //============================================================================
1895 bool SalomeApp_Application::updateStudy()
1897 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1898 if ( !study || !myNoteBook )
1901 myNoteBook->setIsDumpedStudySaved( study->isSaved() );
1902 myNoteBook->setDumpedStudyName( study->studyName() );
1904 _PTR(Study) studyDS = study->studyDS();
1906 // get unique temporary directory name
1907 QString aTmpDir = QString::fromStdString( SALOMEDS_Tool::GetTmpDir() );
1908 if( aTmpDir.isEmpty() )
1911 if( aTmpDir.right( 1 ).compare( QDir::separator() ) == 0 )
1912 aTmpDir.remove( aTmpDir.length() - 1, 1 );
1914 // dump study to the temporary directory
1915 QString aScriptName( "notebook" );
1916 bool toPublish = true;
1917 bool isMultiFile = false;
1918 bool toSaveGUI = true;
1921 _PTR(AttributeParameter) ap;
1922 _PTR(IParameters) ip = ClientFactory::getIParameters(ap);
1923 if(ip->isDumpPython(studyDS)) ip->setDumpPython(studyDS); //Unset DumpPython flag.
1924 if ( toSaveGUI ) { //SRN: Store a visual state of the study at the save point for DumpStudy method
1925 ip->setDumpPython(studyDS);
1926 savePoint = SalomeApp_VisualState( this ).storeState(); //SRN: create a temporary save point
1928 bool ok = studyDS->DumpStudy( aTmpDir.toStdString(), aScriptName.toStdString(), toPublish, isMultiFile );
1930 study->removeSavePoint(savePoint); //SRN: remove the created temporary save point.
1933 myNoteBook->setDumpedStudyScript( aTmpDir + QDir::separator() + aScriptName + ".py" );
1937 QList<SUIT_Application*> aList = SUIT_Session::session()->applications();
1938 int anIndex = aList.indexOf( this );
1940 // Disconnect dialog from application desktop in case if:
1941 // 1) Application is not the first application in the session
1942 // 2) Application is the first application in session but not the only.
1943 bool changeDesktop = ((anIndex > 0) || (anIndex == 0 && aList.count() > 1));
1944 if( changeDesktop ) {
1946 SalomeApp_Application* app = this;
1947 if( anIndex > 0 && anIndex < aList.count() )
1948 app = dynamic_cast<SalomeApp_Application*>( aList[ anIndex - 1 ] );
1949 else if(anIndex == 0 && aList.count() > 1)
1950 app = dynamic_cast<SalomeApp_Application*>( aList[ 1 ] );
1955 // creation a new study and restoring will be done in another application
1956 connect( this, SIGNAL( dumpedStudyClosed( const QString&, const QString&, bool ) ),
1957 app, SLOT( onRestoreStudy( const QString&, const QString&, bool ) ), Qt::UniqueConnection );
1960 QString aDumpScript = myNoteBook->getDumpedStudyScript();
1961 QString aStudyName = myNoteBook->getDumpedStudyName();
1962 bool isStudySaved = myNoteBook->isDumpedStudySaved();
1963 // clear a study (delete all objects)
1964 onCloseDoc( false );
1966 if( !changeDesktop ) {
1967 ok = onRestoreStudy( aDumpScript,
1976 //============================================================================
1977 /*! Function : onRestoreStudy
1978 * Purpose : Load the dumped study from Python script
1980 //============================================================================
1981 bool SalomeApp_Application::onRestoreStudy( const QString& theDumpScript,
1982 const QString& theStudyName,
1983 bool theIsStudySaved )
1987 // create a new study
1990 // get active application
1991 SalomeApp_Application* app = dynamic_cast<SalomeApp_Application*>( SUIT_Session::session()->activeApplication() );
1993 // load study from the temporary directory
1994 QString command = QString( "execfile(r\"%1\")" ).arg( theDumpScript );
1996 #ifndef DISABLE_PYCONSOLE
1997 PyConsole_Console* pyConsole = app->pythonConsole();
1999 pyConsole->execAndWait( command );
2002 // remove temporary directory
2003 QFileInfo aScriptInfo = QFileInfo( theDumpScript );
2004 QString aStudyName = aScriptInfo.baseName();
2005 QDir aDir = aScriptInfo.absoluteDir();
2006 QStringList aFiles = aDir.entryList( QStringList( "*.py*" ) );
2007 for( QStringList::iterator it = aFiles.begin(), itEnd = aFiles.end(); it != itEnd; ++it )
2008 ok = aDir.remove( *it ) && ok;
2010 ok = aDir.rmdir( aDir.absolutePath() );
2012 if( SalomeApp_Study* newStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() ) )
2014 #ifndef DISABLE_PYCONSOLE
2015 _PTR(Study) aStudyDS = newStudy->studyDS();
2016 app->getNoteBook()->Init( aStudyDS );
2017 newStudy->updateFromNotebook(theStudyName, theIsStudySaved);
2018 newStudy->Modified();
2019 updateDesktopTitle();
2030 Close the Application
2032 void SalomeApp_Application::afterCloseDoc()
2034 #ifndef DISABLE_PYCONSOLE
2035 // emit signal to restore study from Python script
2037 emit dumpedStudyClosed( myNoteBook->getDumpedStudyScript(),
2038 myNoteBook->getDumpedStudyName(),
2039 myNoteBook->isDumpedStudySaved() );
2042 LightApp_Application::afterCloseDoc();
2046 Asks to close existing document.
2048 bool SalomeApp_Application::checkExistingDoc()
2050 bool result = LightApp_Application::checkExistingDoc();
2051 if ( result && !activeStudy() ) {
2052 SALOMEDSClient_StudyManager* aMgr = studyMgr();
2054 std::vector<std::string> List = studyMgr()->GetOpenStudies();
2055 if( List.size() > 0 ) {
2056 SUIT_MessageBox::critical( desktop(), tr( "WRN_WARNING" ), tr( "ERR_ACTIVEDOC_LOAD" ));
2065 #ifndef DISABLE_PYCONSOLE
2067 PyConsole_Interp* SalomeApp_Application::createPyInterp()
2069 return new SalomeApp_PyInterp();
2072 #endif // DISABLE_PYCONSOLE