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 (int 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 (int 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;
276 QString command = QString( "exec(open(\"%1\", \"rb\").read())" ).arg(cmd.trimmed());
277 pyConsole->exec(command);
279 } // end for loop on pyfiles QStringList
285 LightApp_Application::start();
286 SALOME_EventFilter::Init();
291 void SalomeApp_Application::createActions()
293 LightApp_Application::createActions();
295 SUIT_Desktop* desk = desktop();
298 // "Save GUI State" command is moved to VISU module
299 // createAction( SaveGUIStateId, tr( "TOT_DESK_FILE_SAVE_GUI_STATE" ), QIcon(),
300 // tr( "MEN_DESK_FILE_SAVE_GUI_STATE" ), tr( "PRP_DESK_FILE_SAVE_GUI_STATE" ),
301 // 0, desk, false, this, SLOT( onSaveGUIState() ) );
304 createAction( DumpStudyId, tr( "TOT_DESK_FILE_DUMP_STUDY" ), QIcon(),
305 tr( "MEN_DESK_FILE_DUMP_STUDY" ), tr( "PRP_DESK_FILE_DUMP_STUDY" ),
306 Qt::CTRL+Qt::Key_D, desk, false, this, SLOT( onDumpStudy() ) );
309 createAction( LoadScriptId, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), QIcon(),
310 tr( "MEN_DESK_FILE_LOAD_SCRIPT" ), tr( "PRP_DESK_FILE_LOAD_SCRIPT" ),
311 Qt::CTRL+Qt::Key_T, desk, false, this, SLOT( onLoadScript() ) );
314 createAction( PropertiesId, tr( "TOT_DESK_PROPERTIES" ), QIcon(),
315 tr( "MEN_DESK_PROPERTIES" ), tr( "PRP_DESK_PROPERTIES" ),
316 0, desk, false, this, SLOT( onProperties() ) );
318 //! Catalog Generator
319 createAction( CatalogGenId, tr( "TOT_DESK_CATALOG_GENERATOR" ), QIcon(),
320 tr( "MEN_DESK_CATALOG_GENERATOR" ), tr( "PRP_DESK_CATALOG_GENERATOR" ),
321 Qt::ALT+Qt::SHIFT+Qt::Key_G, desk, false, this, SLOT( onCatalogGen() ) );
324 createAction( RegDisplayId, tr( "TOT_DESK_REGISTRY_DISPLAY" ), QIcon(),
325 tr( "MEN_DESK_REGISTRY_DISPLAY" ), tr( "PRP_DESK_REGISTRY_DISPLAY" ),
326 /*Qt::SHIFT+Qt::Key_D*/0, desk, false, this, SLOT( onRegDisplay() ) );
328 createAction( ConnectId, tr( "TOT_DESK_CONNECT_STUDY" ), QIcon(),
329 tr( "MEN_DESK_CONNECT" ), tr( "PRP_DESK_CONNECT" ),
330 Qt::CTRL+Qt::Key_L, desk, false, this, SLOT( onLoadDoc() ) );
331 //no need at this action for mono-study application because study is always exists
332 action( ConnectId )->setVisible( false );
334 createAction( DisconnectId, tr( "TOT_DESK_DISCONNECT_STUDY" ), QIcon(),
335 tr( "MEN_DESK_DISCONNECT" ), tr( "PRP_DESK_DISCONNECT" ),
336 Qt::CTRL+Qt::Key_U, desk, false, this, SLOT( onUnloadDoc() ) );
337 //no need at this action for mono-study application because study is always exists
338 action( DisconnectId )->setVisible( false );
341 int fileMenu = createMenu( tr( "MEN_DESK_FILE" ), -1 );
343 // "Save GUI State" command is renamed to "Save VISU State" and
344 // creation of menu item is moved to VISU
345 // createMenu( SaveGUIStateId, fileMenu, 10, -1 );
347 createMenu( ConnectId, fileMenu, 5 );
348 createMenu( DisconnectId, fileMenu, 5 );
349 createMenu( separator(), fileMenu, -1, 5 );
351 createMenu( DumpStudyId, fileMenu, 10, -1 );
352 createMenu( LoadScriptId, fileMenu, 10, -1 );
353 createMenu( separator(), fileMenu, -1, 10, -1 );
354 createMenu( PropertiesId, fileMenu, 10, -1 );
355 createMenu( separator(), fileMenu, -1, 10, -1 );
357 int toolsMenu = createMenu( tr( "MEN_DESK_TOOLS" ), -1, MenuToolsId, 50 );
358 createMenu( CatalogGenId, toolsMenu, 10, -1 );
359 createMenu( RegDisplayId, toolsMenu, 10, -1 );
360 createMenu( separator(), toolsMenu, -1, 15, -1 );
362 createExtraActions();
364 #ifndef DISABLE_PYCONSOLE
365 #ifndef DISABLE_SALOMEOBJECT
366 // import Python module that manages SALOME plugins
368 PyLockWrapper lck; // acquire GIL
369 PyObjWrapper pluginsmanager = PyImport_ImportModule((char*)"salome_pluginsmanager");
370 PyObjWrapper res = PyObject_CallMethod( pluginsmanager, (char*)"initialize", (char*)"isss",0,"salome",tr("MEN_DESK_TOOLS").toUtf8().data(),tr("MEN_DESK_PLUGINS").toUtf8().data());
374 // end of SALOME plugins loading
381 \brief Close application.
383 void SalomeApp_Application::onExit()
385 bool killServers = false;
388 if ( exitConfirmation() ) {
389 SalomeApp_ExitDlg dlg( desktop() );
390 result = dlg.exec() == QDialog::Accepted;
391 killServers = dlg.isServersShutdown();
395 if ( !killServers ) myIsCloseFromExit = true;
396 SUIT_Session::session()->closeSession( SUIT_Session::ASK, killServers );
397 if ( SUIT_Session::session()->applications().count() > 0 ) myIsCloseFromExit = false;
401 /*!SLOT. Load document.*/
402 void SalomeApp_Application::onLoadDoc()
406 // rnv: According to the single-study approach on the server side
407 // can be only one study. So if it is exists connect to them,
408 // overwise show warning message: "No active study on the server"
411 SUIT_Session* aSession = SUIT_Session::session();
412 QList<SUIT_Application*> aAppList = aSession->applications();
414 QStringList unloadedStudies;
416 for ( unsigned int ind = 0; ind < List.size(); ind++ ) {
417 studyName = List[ind].c_str();
418 // Add to list only unloaded studies
419 bool isAlreadyOpen = false;
420 QListIterator<SUIT_Application*> it( aAppList );
421 while ( it.hasNext() && !isAlreadyOpen ) {
422 SUIT_Application* aApp = it.next();
423 if( !aApp || !aApp->activeStudy() )
425 if ( aApp->activeStudy()->studyName() == studyName )
426 isAlreadyOpen = true;
429 if ( !isAlreadyOpen )
430 unloadedStudies << studyName;
432 studyName = SalomeApp_LoadStudiesDlg::selectStudy( desktop(), unloadedStudies );
433 if ( studyName.isEmpty() )
438 SUIT_MessageBox::warning( desktop(),
439 QObject::tr("WRN_WARNING"),
440 QObject::tr("WRN_NO_STUDY_ON SERV") );
444 studyName = activeStudy()->studyName();
447 // this code replaces marker of windows drive and path become invalid therefore
448 // defines placed there
449 studyName.replace( QRegExp(":"), "/" );
452 if ( onLoadDoc( studyName ) ) {
454 updateViewManagers();
455 updateObjectBrowser( true );
459 /*!SLOT. Unload document.*/
460 void SalomeApp_Application::onUnloadDoc( bool ask )
463 activeStudy()->abortAllOperations();
464 if ( activeStudy()->isModified() ) {
465 QString docName = activeStudy()->studyName().trimmed();
466 int answer = SUIT_MessageBox::question( desktop(), tr( "DISCONNECT_CAPTION" ),
467 tr( "DISCONNECT_DESCRIPTION" ),
468 tr( "DISCONNECT_SAVE" ),
469 tr( "DISCONNECT_WO_SAVE" ),
470 tr( "APPCLOSE_CANCEL" ), 0 );
471 if ( answer == 0 ) { // save before unload
472 if ( activeStudy()->isSaved() )
474 else if ( !onSaveAsDoc() )
477 else if ( answer == 2 ) // Cancel
481 closeActiveDoc( false );
484 /*!SLOT. Create new study and load script*/
485 void SalomeApp_Application::onNewWithScript()
487 QStringList filtersList;
488 filtersList.append(tr("PYTHON_FILES_FILTER"));
489 filtersList.append(tr("ALL_FILES_FILTER"));
491 QString anInitialPath = "";
492 if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
493 anInitialPath = QDir::currentPath();
495 QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true );
497 if ( !aFile.isEmpty() )
501 QString command = QString("exec(open(\"%1\", \"rb\").read())").arg(aFile);
503 #ifndef DISABLE_PYCONSOLE
504 PyConsole_Console* pyConsole = pythonConsole();
507 pyConsole->exec( command );
513 /*!SLOT. Load document with \a aName.*/
514 bool SalomeApp_Application::onLoadDoc( const QString& aName )
516 if ( !LightApp_Application::closeDoc() )
520 if ( !activeStudy() ) {
521 // if no study - load in current desktop
522 res = useStudy( aName );
525 // if study exists - load in new desktop. Check: is the same file is loaded?
526 SUIT_Session* aSession = SUIT_Session::session();
527 QList<SUIT_Application*> aAppList = aSession->applications();
528 bool isAlreadyOpen = false;
529 SalomeApp_Application* aApp = 0;
530 for ( QList<SUIT_Application*>::iterator it = aAppList.begin();
531 it != aAppList.end() && !isAlreadyOpen; ++it ) {
532 aApp = dynamic_cast<SalomeApp_Application*>( *it );
533 if ( aApp && aApp->activeStudy()->studyName() == aName )
534 isAlreadyOpen = true;
536 if ( !isAlreadyOpen ) {
537 aApp = dynamic_cast<SalomeApp_Application*>( startApplication( 0, 0 ) );
539 res = aApp->useStudy( aName );
542 aApp->desktop()->activateWindow();
549 /*!SLOT. Parse message for desktop.*/
550 void SalomeApp_Application::onDesktopMessage( const QString& message )
552 if (message.indexOf("studyCreated") == 0) {
553 if (!activeStudy()) {
555 updateCommandsStatus();
558 if (message.indexOf("studyCleared") == 0) {
559 // Disconnect GUI from active study, because it was closed on DS side.
561 closeActiveDoc( false );
562 // Disable 'Connect' action
563 QAction* a = action( ConnectId );
565 a->setEnabled( false );
568 else if ( message.toLower() == "connect_to_study" ) {
571 if (message.indexOf("studyNameChanged") == 0) {
572 updateDesktopTitle();
574 LightApp_Application::onDesktopMessage( message );
577 /*!On module activation action.*/
578 void SalomeApp_Application::onModuleActivation( const QString& modName )
580 if (!activeStudy() && !modName.isEmpty())
583 LightApp_Application::onModuleActivation( modName );
586 /*!SLOT. Copy objects to study maneger from selection maneger..*/
587 void SalomeApp_Application::onCopy()
589 LightApp_Application::onCopy();
592 LightApp_SelectionMgr* mgr = selectionMgr();
593 mgr->selectedObjects(list);
595 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
596 if(study == NULL) return;
598 _PTR(Study) stdDS = getStudy();
601 SALOME_ListIteratorOfListIO it( list );
604 _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry());
609 onSelectionChanged();
617 /*!SLOT. Paste objects to study maneger from selection manager.*/
618 void SalomeApp_Application::onPaste()
620 LightApp_Application::onPaste();
623 LightApp_SelectionMgr* mgr = selectionMgr();
624 mgr->selectedObjects(list);
626 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
627 if(study == NULL) return;
629 _PTR(Study) stdDS = getStudy();
632 if ( stdDS->GetProperties()->IsLocked() ) {
633 SUIT_MessageBox::warning( desktop(),
634 QObject::tr("WRN_WARNING"),
635 QObject::tr("WRN_STUDY_LOCKED") );
639 SALOME_ListIteratorOfListIO it( list );
642 _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry());
647 updateObjectBrowser( true );
648 updateActions(); //SRN: BugID IPAL9377, case 3
656 /*!Check the application on closing.
657 * \retval true if possible, else false
659 bool SalomeApp_Application::isPossibleToClose( bool& closePermanently )
661 return LightApp_Application::isPossibleToClose( closePermanently );
664 /*! Check if the study is locked */
665 void SalomeApp_Application::onCloseDoc( bool ask )
667 if(getStudy()->IsStudyLocked()) {
668 if ( SUIT_MessageBox::question( desktop(),
669 QObject::tr( "WRN_WARNING" ),
670 QObject::tr( "CLOSE_LOCKED_STUDY" ),
671 SUIT_MessageBox::Yes | SUIT_MessageBox::No,
672 SUIT_MessageBox::No) == SUIT_MessageBox::No ) return;
675 LightApp_Application::onCloseDoc( ask );
677 // reinitialize study to have empty data
681 /*!Sets enable or disable some actions on selection changed.*/
682 void SalomeApp_Application::onSelectionChanged()
685 LightApp_SelectionMgr* mgr = selectionMgr();
686 mgr->selectedObjects(list);
688 bool canCopy = false;
689 bool canPaste = false;
691 LightApp_Module* m = dynamic_cast<LightApp_Module*>( activeModule() );
694 canCopy = m->canCopy();
695 canPaste = m->canPaste();
698 SALOME_ListIteratorOfListIO it ( list );
700 if (it.More() && list.Extent() == 1) {
701 _PTR(SObject) so = getStudy()->FindObjectID(it.Value()->getEntry());
704 canCopy = canCopy || getStudy()->CanCopy(so);
705 canPaste = canPaste || getStudy()->CanPaste(so);
709 action(EditCopyId)->setEnabled(canCopy);
710 action(EditPasteId)->setEnabled(canPaste);
713 /*!Delete references.*/
714 void SalomeApp_Application::onDeleteInvalidReferences()
717 LightApp_SelectionMgr* mgr = selectionMgr();
718 mgr->selectedObjects( aList, QString(), false );
720 if( aList.IsEmpty() )
723 _PTR(Study) aStudyDS = getStudy();
724 _PTR(StudyBuilder) aStudyBuilder = aStudyDS->NewBuilder();
727 for( SALOME_ListIteratorOfListIO it( aList ); it.More(); it.Next() )
728 if ( it.Value()->hasEntry() )
730 _PTR(SObject) aSObject = aStudyDS->FindObjectID( it.Value()->getEntry() ), aRefObj = aSObject;
731 while( aRefObj && aRefObj->ReferencedObject( anObj ) )
734 if( aRefObj && aRefObj!=aSObject && QString( aRefObj->GetName().c_str() ).isEmpty() )
735 aStudyBuilder->RemoveReference( aSObject );
737 updateObjectBrowser();
741 void SalomeApp_Application::onOpenWith()
743 QApplication::setOverrideCursor( Qt::WaitCursor );
745 LightApp_SelectionMgr* mgr = selectionMgr();
746 mgr->selectedObjects(aList);
747 if (aList.Extent() != 1)
749 QApplication::restoreOverrideCursor();
752 Handle(SALOME_InteractiveObject) aIObj = aList.First();
753 QString aModuleName(aIObj->getComponentDataType());
754 QString aModuleTitle = moduleTitle(aModuleName);
755 activateModule(aModuleTitle);
756 QApplication::restoreOverrideCursor();
762 SUIT_Study* SalomeApp_Application::createNewStudy()
764 SalomeApp_Study* aStudy = new SalomeApp_Study( this );
766 // Set up processing of major study-related events
767 connect( aStudy, SIGNAL( created( SUIT_Study* ) ), this, SLOT( onStudyCreated( SUIT_Study* ) ) );
768 connect( aStudy, SIGNAL( opened ( SUIT_Study* ) ), this, SLOT( onStudyOpened ( SUIT_Study* ) ) );
769 connect( aStudy, SIGNAL( saved ( SUIT_Study* ) ), this, SLOT( onStudySaved ( SUIT_Study* ) ) );
770 connect( aStudy, SIGNAL( closed ( SUIT_Study* ) ), this, SLOT( onStudyClosed ( SUIT_Study* ) ) );
772 #ifndef DISABLE_PYCONSOLE
773 //to receive signal in application that NoteBook's variable was modified
774 connect( aStudy, SIGNAL(notebookVarUpdated(QString)),
775 this, SIGNAL(notebookVarUpdated(QString)) );
782 Enable/Disable menu items and toolbar buttons. Rebuild menu
784 void SalomeApp_Application::updateCommandsStatus()
786 LightApp_Application::updateCommandsStatus();
789 QAction* a = action( DumpStudyId );
791 a->setEnabled( activeStudy() );
793 #ifndef DISABLE_PYCONSOLE
795 a = action( LoadScriptId );
797 a->setEnabled( pythonConsole() );
801 a = action( PropertiesId );
803 a->setEnabled( activeStudy() );
805 // Save GUI state menu
806 a = action( SaveGUIStateId );
808 a->setEnabled( activeStudy() );
810 // Connect study menu
811 a = action( ConnectId );
813 a->setEnabled( !activeStudy() );
815 // Disconnect study menu
816 a = action( DisconnectId );
818 a->setEnabled( activeStudy() );
820 // update state of Copy/Paste menu items
821 onSelectionChanged();
825 \class DumpStudyFileDlg
826 Private class used in Dump Study operation. Consists 2 check boxes:
827 "Publish in study" and "Save GUI parameters"
829 class DumpStudyFileDlg : public SUIT_FileDlg
832 DumpStudyFileDlg( QWidget* parent ) : SUIT_FileDlg( parent, false, true, true )
834 QGridLayout* grid = ::qobject_cast<QGridLayout*>( layout() );
837 QWidget *hB = new QWidget( this );
838 myPublishChk = new QCheckBox( tr("PUBLISH_IN_STUDY") );
839 myMultiFileChk = new QCheckBox( tr("MULTI_FILE_DUMP") );
840 mySaveGUIChk = new QCheckBox( tr("SAVE_GUI_STATE") );
842 QHBoxLayout *layout = new QHBoxLayout;
843 layout->addWidget(myPublishChk);
844 layout->addWidget(myMultiFileChk);
845 layout->addWidget(mySaveGUIChk);
846 hB->setLayout(layout);
848 QPushButton* pb = new QPushButton(this);
850 int row = grid->rowCount();
851 grid->addWidget( new QLabel("", this), row, 0 );
852 grid->addWidget( hB, row, 1, 1, 3 );
853 grid->addWidget( pb, row, 5 );
858 QCheckBox* myPublishChk;
859 QCheckBox* myMultiFileChk;
860 QCheckBox* mySaveGUIChk;
863 /*!Private SLOT. On dump study.*/
864 void SalomeApp_Application::onDumpStudy( )
866 SalomeApp_Study* appStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
867 if ( !appStudy ) return;
869 QStringList aFilters;
870 aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
872 bool anIsPublish = true;
873 bool anIsMultiFile = false;
874 bool anIsSaveGUI = true;
876 if ( SUIT_ResourceMgr* aResourceMgr = resourceMgr() ) {
877 anIsPublish = aResourceMgr->booleanValue( "Study", "pydump_publish", anIsPublish );
878 anIsMultiFile = aResourceMgr->booleanValue( "Study", "multi_file_dump", anIsMultiFile );
879 anIsSaveGUI = aResourceMgr->booleanValue( "Study", "pydump_save_gui", anIsSaveGUI );
882 DumpStudyFileDlg fd( desktop() );
883 fd.setValidator( new LightApp_PyFileValidator( &fd ) );
884 fd.setWindowTitle( tr( "TOT_DESK_FILE_DUMP_STUDY" ) );
885 fd.setNameFilters( aFilters );
886 fd.myPublishChk->setChecked( anIsPublish );
887 fd.myMultiFileChk->setChecked( anIsMultiFile );
888 fd.mySaveGUIChk->setChecked( anIsSaveGUI );
889 if ( fd.exec() == QDialog::Accepted )
891 QString aFileName = fd.selectedFile();
893 bool toPublish = fd.myPublishChk->isChecked();
894 bool isMultiFile = fd.myMultiFileChk->isChecked();
895 bool toSaveGUI = fd.mySaveGUIChk->isChecked();
897 if ( !aFileName.isEmpty() ) {
898 QFileInfo aFileInfo(aFileName);
899 if( aFileInfo.isDir() ) // IPAL19257
902 // Issue 21377 - dump study implementation moved to SalomeApp_Study class
905 SUIT_OverrideCursor wc;
906 res = appStudy->dump( aFileName, toPublish, isMultiFile, toSaveGUI );
909 SUIT_MessageBox::warning( desktop(),
910 QObject::tr("WRN_WARNING"),
911 tr("WRN_DUMP_STUDY_FAILED") );
916 /*!Private SLOT. On load script.*/
917 void SalomeApp_Application::onLoadScript( )
919 if ( getStudy()->GetProperties()->IsLocked() ) {
920 SUIT_MessageBox::warning( desktop(),
921 QObject::tr("WRN_WARNING"),
922 QObject::tr("WRN_STUDY_LOCKED") );
926 QStringList filtersList;
927 filtersList.append(tr("PYTHON_FILES_FILTER"));
928 filtersList.append(tr("ALL_FILES_FILTER"));
930 QString anInitialPath = "";
931 if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
932 anInitialPath = QDir::currentPath();
934 QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true );
936 if ( !aFile.isEmpty() )
939 QString command = QString("exec(open(\"%1\", \"rb\").read())").arg(aFile);
941 #ifndef DISABLE_PYCONSOLE
942 PyConsole_Console* pyConsole = pythonConsole();
945 pyConsole->exec( command );
950 /*!Private SLOT. On save GUI state.*/
951 void SalomeApp_Application::onSaveGUIState()
953 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
955 SalomeApp_VisualState( this ).storeState();
956 updateSavePointDataObjects( study );
957 updateObjectBrowser();
962 /*!Public SLOT. Performs some actions when dockable windows are triggered.*/
963 void SalomeApp_Application::onDockWindowVisibilityChanged( bool theIsVisible )
965 LightApp_Application::onDockWindowVisibilityChanged( theIsVisible );
966 QAction* send = ::qobject_cast<QAction*>( sender() );
969 QString aWinName = send->data().toString();
970 if ( theIsVisible && aWinName == "objectBrowser" )
971 objectBrowserColumnsVisibility();
975 QWidget* SalomeApp_Application::createWindow( const int flag )
978 #ifndef DISABLE_PYCONSOLE
979 if ( flag != WT_PyConsole ) wid = LightApp_Application::createWindow(flag);
981 wid = LightApp_Application::createWindow(flag);
984 SUIT_ResourceMgr* resMgr = resourceMgr();
986 if ( flag == WT_ObjectBrowser )
988 SUIT_DataBrowser* ob = qobject_cast<SUIT_DataBrowser*>( wid );
990 // temporary commented
991 //ob->setUpdater( new SalomeApp_Updater() );
993 #ifdef WITH_SALOMEDS_OBSERVER
994 //do not activate the automatic update of Qt tree through signal/slot
995 ob->setAutoUpdate(false);
996 //activate update of modified objects only
997 ob->setUpdateModified(true);
1000 connect( ob, SIGNAL( doubleClicked( SUIT_DataObject* ) ), this, SLOT( onDblClick( SUIT_DataObject* ) ) );
1003 ValueCol = QObject::tr( "VALUE_COLUMN" ),
1004 IORCol = QObject::tr( "IOR_COLUMN" ),
1005 RefCol = QObject::tr( "REFENTRY_COLUMN" ),
1006 EntryCol = QObject::tr( "ENTRY_COLUMN" );
1008 SUIT_AbstractModel* treeModel = dynamic_cast<SUIT_AbstractModel*>( ob->model() );
1009 treeModel->registerColumn( 0, EntryCol, SalomeApp_DataObject::EntryId );
1010 treeModel->registerColumn( 0, ValueCol, SalomeApp_DataObject::ValueId );
1011 treeModel->registerColumn( 0, IORCol, SalomeApp_DataObject::IORId );
1012 treeModel->registerColumn( 0, RefCol, SalomeApp_DataObject::RefEntryId );
1013 treeModel->setAppropriate( EntryCol, Qtx::Toggled );
1014 treeModel->setAppropriate( ValueCol, Qtx::Toggled );
1015 treeModel->setAppropriate( IORCol, Qtx::Toggled );
1016 treeModel->setAppropriate( RefCol, Qtx::Toggled );
1018 bool autoSize = resMgr->booleanValue( "ObjectBrowser", "auto_size", false );
1019 bool autoSizeFirst = resMgr->booleanValue( "ObjectBrowser", "auto_size_first", true );
1020 bool resizeOnExpandItem = resMgr->booleanValue( "ObjectBrowser", "resize_on_expand_item", true );
1022 ob->setAutoSizeFirstColumn(autoSizeFirst);
1023 ob->setAutoSizeColumns(autoSize);
1024 ob->setResizeOnExpandItem(resizeOnExpandItem);
1025 ob->setProperty( "shortcut", QKeySequence( "Alt+Shift+O" ) );
1027 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1029 bool shown = resourceMgr()->booleanValue( "ObjectBrowser", QString( "visibility_column_id_%1" ).arg( i-1 ), true );
1030 ob->treeView()->setColumnHidden( i, !shown );
1033 // temporary commented
1035 for ( int i = SalomeApp_DataObject::ValueIdx; i <= SalomeApp_DataObject::RefEntryIdx; i++ )
1037 ob->addColumn( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i ) ), i );
1038 ob->setColumnShown( i, resMgr->booleanValue( "ObjectBrowser",
1039 QString().sprintf( "visibility_column_%d", i ), true ) );
1043 // temporary commented
1045 ob->setWidthMode( autoSize ? QListView::Maximum : QListView::Manual );
1046 ob->listView()->setColumnWidthMode( 0, autoSizeFirst ? QListView::Maximum : QListView::Manual );
1047 ob->resize( desktop()->width()/3, ob->height() );
1051 #ifndef DISABLE_PYCONSOLE
1052 else if ( flag == WT_PyConsole )
1054 PyConsole_Console* pyCons = new PyConsole_Console( desktop(), new LightApp_PyEditor( getPyInterp() ) );
1055 pyCons->setObjectName( "pythonConsole" );
1056 pyCons->setWindowTitle( tr( "PYTHON_CONSOLE" ) );
1057 pyCons->setFont(resourceMgr()->fontValue( "PyConsole", "font" ));
1058 pyCons->setIsShowBanner(resourceMgr()->booleanValue( "PyConsole", "show_banner", true ));
1059 pyCons->setAutoCompletion( resMgr->booleanValue( "PyConsole", "auto_completion", true ) );
1060 pyCons->setProperty( "shortcut", QKeySequence( "Alt+Shift+P" ) );
1063 else if ( flag == WT_NoteBook )
1065 setNoteBook( new SalomeApp_NoteBook( desktop() ) );
1066 //to receive signal in NoteBook that it's variable was modified
1067 connect( this, SIGNAL( notebookVarUpdated( QString ) ),
1068 getNoteBook(), SLOT( onVarUpdate( QString ) ) );
1070 wid = getNoteBook();
1071 wid->setObjectName( "noteBook" );
1077 /*!Create preferences.*/
1078 void SalomeApp_Application::createPreferences( LightApp_Preferences* pref )
1080 LightApp_Application::createPreferences(pref);
1085 int salomeCat = pref->addPreference( tr( "PREF_CATEGORY_SALOME" ) );
1086 int obTab = pref->addPreference( tr( "PREF_TAB_OBJBROWSER" ), salomeCat );
1087 int defCols = pref->addPreference( tr( "PREF_GROUP_DEF_COLUMNS" ), obTab );
1088 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1090 pref->addPreference( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i-SalomeApp_DataObject::EntryId ).toLatin1() ), defCols,
1091 LightApp_Preferences::Bool, "ObjectBrowser", QString().sprintf( "visibility_column_id_%d", i-1 ) );
1093 pref->setItemProperty( "orientation", Qt::Vertical, defCols );
1095 // adding preference to LightApp_Application handled preferences.. a bit of hacking with resources..
1096 int genTab = pref->addPreference( LightApp_Application::tr( "PREF_TAB_GENERAL" ), salomeCat );
1097 int studyGroup = pref->addPreference( LightApp_Application::tr( "PREF_GROUP_STUDY" ), genTab );
1098 pref->addPreference( tr( "PREF_STORE_VISUAL_STATE" ), studyGroup, LightApp_Preferences::Bool, "Study", "store_visual_state" );
1099 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1100 pref->addPreference( tr( "PREF_PYDUMP_PUBLISH" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_publish" );
1101 pref->addPreference( tr( "PREF_PYDUMP_MULTI_FILE" ), studyGroup, LightApp_Preferences::Bool, "Study", "multi_file_dump" );
1102 pref->addPreference( tr( "PREF_PYDUMP_SAVE_GUI" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_save_gui" );
1103 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1104 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1107 /*!Update desktop title.*/
1108 void SalomeApp_Application::updateDesktopTitle() {
1109 QString aTitle = applicationName();
1110 QString aVer = applicationVersion();
1111 if ( !aVer.isEmpty() )
1112 aTitle += QString( " " ) + aVer;
1114 if ( activeStudy() )
1116 QString sName = SUIT_Tools::file( activeStudy()->studyName().trimmed(), false );
1117 if ( !sName.isEmpty() ) {
1118 if ( getStudy()->GetProperties()->IsLocked() ) {
1119 aTitle += QString( " - [%1 (%2)]").arg( sName ).arg( tr( "STUDY_LOCKED" ) );
1121 aTitle += QString( " - [%1]" ).arg( sName );
1126 desktop()->setWindowTitle( aTitle );
1129 int SalomeApp_Application::closeChoice( const QString& docName )
1131 QStringList buttons;
1132 QMap<int, int> choices;
1134 buttons << tr ("APPCLOSE_SAVE"); // Save & Clear
1135 choices.insert( idx++, CloseSave ); // ...
1136 buttons << tr ("APPCLOSE_CLOSE"); // Clear w/o saving
1137 choices.insert( idx++, CloseDiscard ); // ...
1138 if ( myIsCloseFromExit ) {
1139 buttons << tr ("APPCLOSE_UNLOAD_SAVE"); // Save & Disconnect
1140 choices.insert( idx++, CloseDisconnectSave ); // ...
1141 buttons << tr ("APPCLOSE_UNLOAD"); // Disconnect
1142 choices.insert( idx++, CloseDisconnect ); // ...
1144 buttons << tr ("APPCLOSE_CANCEL"); // Cancel
1145 choices.insert( idx++, CloseCancel ); // ...
1147 if( !activeStudy()->isModified() )
1149 int answer = SUIT_MessageBox::question( desktop(), tr( "APPCLOSE_CAPTION" ),
1150 tr( "APPCLOSE_DESCRIPTION" ), buttons, 0 );
1151 return choices[answer];
1154 bool SalomeApp_Application::closeAction( const int choice, bool& closePermanently )
1160 if ( activeStudy()->isSaved() )
1162 else if ( !onSaveAsDoc() )
1167 case CloseDisconnectSave:
1168 if ( activeStudy()->isSaved() )
1170 else if ( !onSaveAsDoc() )
1172 case CloseDisconnect:
1173 closeActiveDoc( false );
1174 closePermanently = false;
1183 int SalomeApp_Application::openChoice( const QString& aName )
1185 int choice = LightApp_Application::openChoice( aName );
1187 if ( QFileInfo( aName ).exists() ) {
1188 if ( choice == OpenNew ) { // The document isn't already open.
1190 if ( aName == getStudy()->Name().c_str() )
1192 // The document already exists in the study.
1193 // Do you want to reload it?
1195 int answer = SUIT_MessageBox::question( desktop(), tr( "WRN_WARNING" ), tr( "QUE_DOC_ALREADYEXIST" ).arg( aName ),
1196 SUIT_MessageBox::Yes | SUIT_MessageBox::No, SUIT_MessageBox::No );
1197 if ( answer == SUIT_MessageBox::Yes )
1198 choice = OpenRefresh;
1200 choice = OpenCancel;
1203 } else { // file is not exist on disk
1204 SUIT_MessageBox::warning( desktop(),
1205 QObject::tr("WRN_WARNING"),
1206 QObject::tr("WRN_FILE_NOT_EXIST").arg(aName.toUtf8().data()));
1213 bool SalomeApp_Application::openAction( const int aChoice, const QString& aName )
1216 int choice = aChoice;
1224 res = LightApp_Application::openAction( choice, aName );
1232 \brief Get map of the operations which can be performed
1233 on the module activation.
1235 The method should return the map of the kind \c {<id>:<name>}
1236 where \c <id> is an integer identifier of the operation and
1237 \c <name> is a title for the button to be added to the
1238 dialog box. After user selects the required operation by the
1239 clicking the corresponding button in the dialog box, its identifier
1240 is passed to the moduleActionSelected() method to process
1243 \return map of the operations
1244 \sa moduleActionSelected()
1246 QMap<int, QString> SalomeApp_Application::activateModuleActions() const
1248 QMap<int, QString> opmap = LightApp_Application::activateModuleActions();
1250 opmap.insert( LoadStudyId, tr( "ACTIVATE_MODULE_OP_LOAD" ) );
1252 opmap.insert( NewAndScriptId, tr( "ACTIVATE_MODULE_OP_SCRIPT" ) );
1257 \brief Called when the used selectes required operation chosen
1258 from "Activate module" dialog box.
1260 Performs the required operation according to the user choice.
1262 \param id operation identifier
1263 \sa activateModuleActions()
1265 void SalomeApp_Application::moduleActionSelected( const int id )
1271 case NewAndScriptId:
1275 LightApp_Application::moduleActionSelected( id );
1280 /*!Gets CORBA::ORB_var*/
1281 CORBA::ORB_var SalomeApp_Application::orb()
1283 static CORBA::ORB_var _orb;
1285 if ( CORBA::is_nil( _orb ) ) {
1286 Qtx::CmdLineArgs args;
1287 ORB_INIT& init = *SINGLETON_<ORB_INIT>::Instance();
1288 _orb = init( args.argc(), args.argv() );
1294 /*!Create and return SALOMEDS_Study.*/
1295 _PTR(Study) SalomeApp_Application::getStudy()
1297 static _PTR(Study) _study;
1299 CORBA::Object_var aSObject = namingService()->Resolve("/Study");
1300 SALOMEDS::Study_var aStudy = SALOMEDS::Study::_narrow(aSObject);
1301 _study = ClientFactory::Study(aStudy);
1306 /*!Create and return SALOME_NamingService.*/
1307 SALOME_NamingService* SalomeApp_Application::namingService()
1309 static SALOME_NamingService _ns(orb());
1313 /*!Create and return SALOME_LifeCycleCORBA.*/
1314 SALOME_LifeCycleCORBA* SalomeApp_Application::lcc()
1316 static SALOME_LifeCycleCORBA _lcc( namingService() );
1320 /*!Private SLOT. On preferences.*/
1321 void SalomeApp_Application::onProperties()
1323 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1327 _PTR(StudyBuilder) SB = study->studyDS()->NewBuilder();
1330 SalomeApp_StudyPropertiesDlg aDlg( desktop() );
1331 int res = aDlg.exec();
1332 if( res==QDialog::Accepted && aDlg.isChanged() )
1333 SB->CommitCommand();
1337 //study->updateCaptions();
1338 updateDesktopTitle();
1342 /*!Insert items in popup, which necessary for current application*/
1343 void SalomeApp_Application::contextMenuPopup( const QString& type, QMenu* thePopup, QString& title )
1345 LightApp_SelectionMgr* mgr = selectionMgr();
1346 bool cacheIsOn = mgr->isSelectionCacheEnabled();
1347 mgr->setSelectionCacheEnabled( true );
1349 LightApp_Application::contextMenuPopup( type, thePopup, title );
1351 // temporary commented
1352 /*OB_Browser* ob = objectBrowser();
1353 if ( !ob || type != ob->popupClientType() )
1356 // Get selected objects
1357 SALOME_ListIO aList;
1358 mgr->selectedObjects( aList, QString(), false );
1360 // add GUI state commands: restore, rename
1361 if ( aList.Extent() == 1 && aList.First()->hasEntry() &&
1362 QString( aList.First()->getEntry() ).startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) {
1363 thePopup->addSeparator();
1364 thePopup->addAction( tr( "MEN_RESTORE_VS" ), this, SLOT( onRestoreGUIState() ) );
1365 thePopup->addAction( tr( "MEN_RENAME_VS" ), objectBrowser(),
1366 SLOT( onStartEditing() ), objectBrowser()->shortcutKey(SUIT_DataBrowser::RenameShortcut) );
1367 thePopup->addAction( tr( "MEN_DELETE_VS" ), this, SLOT( onDeleteGUIState() ) );
1370 // "Delete reference" item should appear only for invalid references
1372 // isInvalidRefs will be true, if at least one of selected objects is invalid reference
1373 bool isInvalidRefs = false;
1375 _PTR(SObject) anObj;
1376 for( SALOME_ListIteratorOfListIO it( aList ); it.More() && !isInvalidRefs; it.Next() )
1378 if( it.Value()->hasEntry() )
1380 _PTR(SObject) aSObject = getStudy()->FindObjectID( it.Value()->getEntry() ), aRefObj = aSObject;
1381 while( aRefObj && aRefObj->ReferencedObject( anObj ) )
1384 if( aRefObj && aRefObj!=aSObject && QString( aRefObj->GetName().c_str() ).isEmpty() )
1385 isInvalidRefs = true;
1389 // Add "Delete reference" item to popup
1390 if ( isInvalidRefs )
1392 thePopup->addSeparator();
1393 thePopup->addAction( tr( "MEN_DELETE_INVALID_REFERENCE" ), this, SLOT( onDeleteInvalidReferences() ) );
1397 // "Activate module" item should appear only if it's necessary
1398 if ( aList.Extent() == 1 ) {
1400 mgr->selectedObjects( aList );
1402 Handle(SALOME_InteractiveObject) aIObj = aList.First();
1404 // add extra popup menu (defined in XML)
1405 if ( myExtActions.size() > 0 ) {
1406 // Use only first selected object
1407 _PTR(SObject) aSO = getStudy()->FindObjectID( aIObj->getEntry() );
1409 _PTR( GenericAttribute ) anAttr;
1410 std::string auid = "AttributeUserID";
1411 auid += Kernel_Utils::GetGUID(Kernel_Utils::ObjectdID);
1412 if ( aSO->FindAttribute( anAttr, auid ) ) {
1413 _PTR(AttributeUserID) aAttrID = anAttr;
1414 QString aId = aAttrID->Value().c_str();
1415 if ( myExtActions.contains( aId ) ) {
1416 thePopup->addAction(myExtActions[aId]);
1422 // check if item is a "GUI state" item (also a first level object)
1423 QString entry( aIObj->getEntry() );
1424 if ( !entry.startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) {
1425 QString aModuleName( aIObj->getComponentDataType() );
1426 QString aModuleTitle = moduleTitle( aModuleName );
1427 CAM_Module* currentModule = activeModule();
1428 if ( ( !currentModule || currentModule->moduleName() != aModuleTitle ) && !aModuleTitle.isEmpty() )
1429 thePopup->addAction( tr( "MEN_OPENWITH" ).arg( aModuleTitle ), this, SLOT( onOpenWith() ) );
1433 mgr->setSelectionCacheEnabled( cacheIsOn );
1436 /*!Update obect browser:
1437 1.if 'updateModels' true, update existing data models;
1438 2. update "non-existing" (not loaded yet) data models;
1439 3. update object browser if it exists */
1440 void SalomeApp_Application::updateObjectBrowser( const bool updateModels )
1442 // update "non-existing" (not loaded yet) data models
1443 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
1446 for ( _PTR(SComponentIterator) it ( getStudy()->NewComponentIterator() ); it->More(); it->Next() )
1448 _PTR(SComponent) aComponent ( it->Value() );
1450 #ifndef WITH_SALOMEDS_OBSERVER
1451 // with GUI observers this check is not needed anymore
1452 if ( aComponent->ComponentDataType() == study->getVisualComponentName().toLatin1().constData() )
1453 continue; // skip the magic "Interface Applicative" component
1455 if ( !objectBrowser() )
1456 getWindow( WT_ObjectBrowser );
1457 const bool isAutoUpdate = objectBrowser()->autoUpdate();
1458 objectBrowser()->setAutoUpdate( false );
1459 SalomeApp_DataModel::synchronize( aComponent, study );
1460 objectBrowser()->setAutoUpdate( isAutoUpdate );
1464 // create data objects that correspond to GUI state save points
1465 if ( study ) updateSavePointDataObjects( study );
1467 // update existing data models (already loaded SComponents)
1468 LightApp_Application::updateObjectBrowser( updateModels );
1471 /*!Display Catalog Genenerator dialog */
1472 void SalomeApp_Application::onCatalogGen()
1474 ToolsGUI_CatalogGeneratorDlg aDlg( desktop() );
1478 /*!Display Registry Display dialog */
1479 void SalomeApp_Application::onRegDisplay()
1481 CORBA::ORB_var anOrb = orb();
1482 ToolsGUI_RegWidget* regWnd = ToolsGUI_RegWidget::GetRegWidget( anOrb, desktop() );
1485 regWnd->activateWindow();
1488 /*!find original object by double click on item */
1489 void SalomeApp_Application::onDblClick( SUIT_DataObject* theObj )
1491 // Issue 21379: References are supported at LightApp_DataObject level
1492 LightApp_DataObject* obj = dynamic_cast<LightApp_DataObject*>( theObj );
1494 if( obj && obj->isReference() )
1496 QString entry = obj->refEntry();
1498 SUIT_DataOwnerPtrList aList;
1499 aList.append( new LightApp_DataOwner( entry ) );
1500 selectionMgr()->setSelected( aList, false );
1502 SUIT_DataBrowser* ob = objectBrowser();
1504 QModelIndexList aSelectedIndexes = ob->selectedIndexes();
1505 if ( !aSelectedIndexes.isEmpty() )
1506 ob->treeView()->scrollTo( aSelectedIndexes.first() );
1508 emit objectDoubleClicked( theObj );
1512 Creates new view manager
1513 \param type - type of view manager
1515 SUIT_ViewManager* SalomeApp_Application::newViewManager(const QString& type)
1517 return createViewManager(type);
1521 /*!Global utility function, returns selected GUI Save point object's ID */
1522 int getSelectedSavePoint( const LightApp_SelectionMgr* selMgr )
1524 SALOME_ListIO aList;
1525 selMgr->selectedObjects( aList );
1526 if( aList.Extent() > 0 ) {
1527 Handle(SALOME_InteractiveObject) aIObj = aList.First();
1528 QString entry( aIObj->getEntry() );
1529 QString startStr = QObject::tr( "SAVE_POINT_DEF_NAME" );
1530 if ( !entry.startsWith( startStr ) ) // it's a "GUI state" object
1532 bool ok; // conversion to integer is ok?
1533 int savePoint = entry.right( entry.length() - startStr.length() ).toInt( &ok );
1534 return ok ? savePoint : -1;
1539 /*!Called on Restore GUI State popup command*/
1540 void SalomeApp_Application::onRestoreGUIState()
1542 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1543 if ( savePoint == -1 )
1545 SalomeApp_VisualState( this ).restoreState( savePoint );
1548 /*!Called on Delete GUI State popup command*/
1549 void SalomeApp_Application::onDeleteGUIState()
1551 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1552 if ( savePoint == -1 )
1554 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1558 study->removeSavePoint( savePoint );
1559 updateSavePointDataObjects( study );
1562 /*!Called on New study operation*/
1563 void SalomeApp_Application::onStudyCreated( SUIT_Study* study )
1565 LightApp_Application::onStudyCreated( study );
1567 //#ifndef DISABLE_PYCONSOLE
1568 // desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
1569 // windowDock( getWindow( WT_ObjectBrowser ) ) );
1572 loadDockWindowsState();
1574 objectBrowserColumnsVisibility();
1577 /*!Called on Open study operation*/
1578 void SalomeApp_Application::onStudyOpened( SUIT_Study* study )
1580 LightApp_Application::onStudyOpened( study );
1582 //#ifndef DISABLE_PYCONSOLE
1583 // desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
1584 // windowDock( getWindow( WT_ObjectBrowser ) ) );
1587 loadDockWindowsState();
1589 objectBrowserColumnsVisibility();
1591 // temporary commented
1592 /*if ( objectBrowser() ) {
1593 updateSavePointDataObjects( dynamic_cast<SalomeApp_Study*>( study ) );
1594 objectBrowser()->updateTree( study->root() );
1598 /*! updateSavePointDataObjects: syncronize data objects that correspond to save points (gui states)*/
1599 void SalomeApp_Application::updateSavePointDataObjects( SalomeApp_Study* study )
1602 SUIT_DataBrowser* ob = objectBrowser();
1603 LightApp_SelectionMgr* selMgr = selectionMgr();
1605 if ( !study || !ob || !selMgr )
1608 // find GUI states root object
1609 SUIT_DataObject* guiRootObj = 0;
1611 study->root()->children( ch );
1612 DataObjectList::const_iterator it = ch.begin(), last = ch.end();
1613 for ( ; it != last ; ++it ) {
1614 if ( dynamic_cast<SalomeApp_SavePointRootObject*>( *it ) ) {
1619 std::vector<int> savePoints = study->getSavePoints();
1620 // case 1: no more save points but they existed in study's tree
1621 if ( savePoints.empty() && guiRootObj ) {
1622 //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state"
1623 // : set auto update to true for removing SalomeApp_SavePointRootObject from the SUIT_TreeModel
1624 const bool isAutoUpdate = ob->autoUpdate();
1625 selMgr->clearSelected();
1626 ob->setAutoUpdate(true);
1627 DataObjectList ch = guiRootObj->children();
1628 for( int i = 0; i < ch.size(); i++ )
1631 ob->setAutoUpdate(isAutoUpdate);
1634 // case 2: no more save points but root does not exist either
1635 if ( savePoints.empty() && !guiRootObj )
1637 // case 3: save points but no root for them - create it
1638 if ( !savePoints.empty() && !guiRootObj )
1639 guiRootObj = new SalomeApp_SavePointRootObject( study->root() );
1640 // case 4: everything already exists.. here may be a problem: we want "GUI states" root object
1641 // to be always the last one in the tree. Here we check - if it is not the last one - remove and
1643 if ( guiRootObj->nextBrother() ) {
1644 study->root()->removeChild(guiRootObj);
1645 study->root()->appendChild(guiRootObj);
1646 //study->root()->dump();
1649 // store data objects in a map id-to-DataObject
1650 QMap<int,SalomeApp_SavePointObject*> mapDO;
1652 guiRootObj->children( ch );
1653 for( it = ch.begin(), last = ch.end(); it != last ; ++it ) {
1654 SalomeApp_SavePointObject* dobj = dynamic_cast<SalomeApp_SavePointObject*>( *it );
1656 mapDO[dobj->getId()] = dobj;
1659 // iterate new save points. if DataObject with such ID not found in map - create DataObject
1660 // if in the map - remove it from map.
1661 for ( size_t i = 0; i < savePoints.size(); i++ )
1662 if ( !mapDO.contains( savePoints[i] ) )
1663 new SalomeApp_SavePointObject( guiRootObj, savePoints[i], study );
1665 mapDO.remove( savePoints[i] );
1667 // delete DataObjects that are still in the map -- their IDs were not found in data model
1668 if( mapDO.size() > 0) {
1669 //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state"
1670 // : set auto update to true for removing SalomeApp_SavePointObject from the SUIT_TreeModel
1671 selMgr->clearSelected();
1672 const bool isAutoUpdate = ob->autoUpdate();
1673 ob->setAutoUpdate(true);
1674 for ( QMap<int,SalomeApp_SavePointObject*>::Iterator it = mapDO.begin(); it != mapDO.end(); ++it )
1676 ob->setAutoUpdate(isAutoUpdate);
1680 /*! Check data object */
1681 bool SalomeApp_Application::checkDataObject(LightApp_DataObject* theObj)
1690 Opens other study into active Study. If Study is empty - creates it.
1691 \param theName - name of study
1693 bool SalomeApp_Application::useStudy( const QString& theName )
1696 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1699 res = aStudy->loadDocument( theName );
1700 updateDesktopTitle();
1701 updateCommandsStatus();
1705 /*! Show/hide object browser colums according to preferences */
1706 void SalomeApp_Application::objectBrowserColumnsVisibility()
1708 if ( objectBrowser() )
1709 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1711 bool shown = resourceMgr()->booleanValue( "ObjectBrowser", QString( "visibility_column_id_%1" ).arg( i-1 ), true );
1712 objectBrowser()->treeView()->setColumnHidden( i, !shown );
1716 #ifndef DISABLE_PYCONSOLE
1717 /*! Set SalomeApp_NoteBook pointer */
1718 void SalomeApp_Application::setNoteBook( SalomeApp_NoteBook* theNoteBook )
1720 myNoteBook = theNoteBook;
1723 /*! Return SalomeApp_NoteBook pointer */
1724 SalomeApp_NoteBook* SalomeApp_Application::getNoteBook() const
1731 * Define extra actions defined in module definition XML file.
1732 * Additional popup items sections can be defined by parameter "popupitems".
1733 * Supported attributes:
1734 * title - title of menu item,
1735 * attributelocalid - AttributeLocalId defined for selected data item where menu command has to be applied,
1736 * method - method which has to be called when menu item is selected
1738 * <section name="MODULENAME">
1739 * <parameter name="popupitems" value="menuitem1:menuitem2:..."/>
1741 * <section name="importmed">
1742 * <parameter name="title" value="My menu"/>
1743 * <parameter name="objectid" value="VISU.Result"/>
1744 * <parameter name="method" value="nameOfModuleMethod"/>
1747 void SalomeApp_Application::createExtraActions()
1749 myExtActions.clear();
1750 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
1752 QStringList aModules;
1753 modules(aModules, false);
1754 foreach(QString aModile, aModules) {
1755 QString aModName = moduleName(aModile);
1756 QString aSectionStr = resMgr->stringValue(aModName, "popupitems", QString());
1757 if (!aSectionStr.isNull()) {
1758 QStringList aSections = aSectionStr.split(':');
1759 foreach(QString aSection, aSections) {
1760 QString aTitle = resMgr->stringValue(aSection, "title", QString());
1761 QString aId = resMgr->stringValue(aSection, "objectid", QString());
1762 QString aSlot = resMgr->stringValue(aSection, "method", QString());
1763 if (aTitle.isEmpty() || aSlot.isEmpty() || aId.isEmpty())
1766 QString aModuleName = resMgr->stringValue(aSection, "module", QString());
1767 if (aModuleName.isNull())
1768 aModuleName = aModName;
1770 QAction* aAction = new QAction(aTitle, this);
1772 aData<<aModuleName<<aSlot;
1773 aAction->setData(aData);
1774 connect(aAction, SIGNAL(triggered()), this, SLOT(onExtAction()));
1775 myExtActions[aId] = aAction;
1782 * Called when extra action is selected
1784 void SalomeApp_Application::onExtAction()
1786 QAction* aAction = ::qobject_cast<QAction*>(sender());
1790 QVariant aData = aAction->data();
1791 QStringList aDataList = aData.value<QStringList>();
1792 if (aDataList.size() != 2)
1795 LightApp_SelectionMgr* aSelectionMgr = selectionMgr();
1796 SALOME_ListIO aListIO;
1797 aSelectionMgr->selectedObjects(aListIO);
1798 const Handle(SALOME_InteractiveObject)& anIO = aListIO.First();
1799 if (aListIO.Extent() < 1)
1801 if (!anIO->hasEntry())
1804 QString aEntry(anIO->getEntry());
1806 QApplication::setOverrideCursor( Qt::WaitCursor );
1807 QString aModuleTitle = moduleTitle(aDataList[0]);
1808 activateModule(aModuleTitle);
1809 QApplication::restoreOverrideCursor();
1811 QCoreApplication::processEvents();
1813 CAM_Module* aModule = activeModule();
1817 if (!QMetaObject::invokeMethod(aModule, qPrintable(aDataList[1]), Q_ARG(QString, aEntry)))
1818 printf("Error: Can't Invoke method %s\n", qPrintable(aDataList[1]));
1822 Checks that an object can be renamed.
1823 \param entry entry of the object
1824 \brief Return \c true if object can be renamed
1826 bool SalomeApp_Application::renameAllowed( const QString& entry) const
1828 return entry.startsWith( tr( "SAVE_POINT_DEF_NAME") );
1832 Rename object by entry.
1833 \param entry entry of the object
1834 \param name new name of the object
1835 \brief Return \c true if rename operation finished successfully, \c false otherwise.
1837 bool SalomeApp_Application::renameObject( const QString& entry, const QString& name )
1839 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1841 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1843 if(!aStudy || savePoint == -1)
1846 if ( !name.isNull() && !name.isEmpty() ) {
1847 aStudy->setNameOfSavePoint( savePoint, name );
1848 updateSavePointDataObjects( aStudy );
1850 //Mark study as modified
1857 #ifndef DISABLE_PYCONSOLE
1858 //============================================================================
1859 /*! Function : onUpdateStudy
1860 * Purpose : Slot to update the study.
1862 //============================================================================
1863 void SalomeApp_Application::onUpdateStudy()
1865 QApplication::setOverrideCursor( Qt::WaitCursor );
1867 if( !updateStudy() )
1868 SUIT_MessageBox::warning( desktop(), tr( "ERROR" ), tr( "ERR_UPDATE_STUDY_FAILED" ) );
1870 QApplication::restoreOverrideCursor();
1873 //============================================================================
1874 /*! Function : updateStudy
1875 * Purpose : Update study by dumping the study to Python script and loading it.
1876 * It is used to apply variable modifications done in NoteBook to created objects.
1878 //============================================================================
1879 bool SalomeApp_Application::updateStudy()
1881 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1882 if ( !study || !myNoteBook )
1885 myNoteBook->setIsDumpedStudySaved( study->isSaved() );
1886 myNoteBook->setDumpedStudyName( study->studyName() );
1888 // get unique temporary directory name
1889 QString aTmpDir = QString::fromStdString( SALOMEDS_Tool::GetTmpDir() );
1890 if( aTmpDir.isEmpty() )
1893 if( aTmpDir.right( 1 ).compare( QDir::separator() ) == 0 )
1894 aTmpDir.remove( aTmpDir.length() - 1, 1 );
1896 // dump study to the temporary directory
1897 QString aScriptName( "notebook" );
1898 bool toPublish = true;
1899 bool isMultiFile = false;
1900 bool toSaveGUI = true;
1903 _PTR(AttributeParameter) ap;
1904 _PTR(IParameters) ip = ClientFactory::getIParameters(ap);
1905 if(ip->isDumpPython()) ip->setDumpPython(); //Unset DumpPython flag.
1906 if ( toSaveGUI ) { //SRN: Store a visual state of the study at the save point for DumpStudy method
1907 ip->setDumpPython();
1908 savePoint = SalomeApp_VisualState( this ).storeState(); //SRN: create a temporary save point
1910 bool ok = getStudy()->DumpStudy( aTmpDir.toStdString(), aScriptName.toStdString(), toPublish, isMultiFile );
1912 study->removeSavePoint(savePoint); //SRN: remove the created temporary save point.
1915 myNoteBook->setDumpedStudyScript( aTmpDir + QDir::separator() + aScriptName + ".py" );
1919 QList<SUIT_Application*> aList = SUIT_Session::session()->applications();
1920 int anIndex = aList.indexOf( this );
1922 // Disconnect dialog from application desktop in case if:
1923 // 1) Application is not the first application in the session
1924 // 2) Application is the first application in session but not the only.
1925 bool changeDesktop = ((anIndex > 0) || (anIndex == 0 && aList.count() > 1));
1926 if( changeDesktop ) {
1928 SalomeApp_Application* app = this;
1929 if( anIndex > 0 && anIndex < aList.count() )
1930 app = dynamic_cast<SalomeApp_Application*>( aList[ anIndex - 1 ] );
1931 else if(anIndex == 0 && aList.count() > 1)
1932 app = dynamic_cast<SalomeApp_Application*>( aList[ 1 ] );
1937 // creation a new study and restoring will be done in another application
1938 connect( this, SIGNAL( dumpedStudyClosed( const QString&, const QString&, bool ) ),
1939 app, SLOT( onRestoreStudy( const QString&, const QString&, bool ) ), Qt::UniqueConnection );
1942 QString aDumpScript = myNoteBook->getDumpedStudyScript();
1943 QString aStudyName = myNoteBook->getDumpedStudyName();
1944 bool isStudySaved = myNoteBook->isDumpedStudySaved();
1945 // clear a study (delete all objects)
1946 onCloseDoc( false );
1948 if( !changeDesktop ) {
1949 ok = onRestoreStudy( aDumpScript,
1958 //============================================================================
1959 /*! Function : onRestoreStudy
1960 * Purpose : Load the dumped study from Python script
1962 //============================================================================
1963 bool SalomeApp_Application::onRestoreStudy( const QString& theDumpScript,
1964 const QString& theStudyName,
1965 bool theIsStudySaved )
1969 // get active application
1970 SalomeApp_Application* app = dynamic_cast<SalomeApp_Application*>( SUIT_Session::session()->activeApplication() );
1972 // load study from the temporary directory
1974 QString command = QString( "exec(open(\"%1\" ,\"rb\").read())" ).arg( theDumpScript );
1976 #ifndef DISABLE_PYCONSOLE
1977 PyConsole_Console* pyConsole = app->pythonConsole();
1979 pyConsole->execAndWait( command );
1982 // remove temporary directory
1983 QFileInfo aScriptInfo = QFileInfo( theDumpScript );
1984 QString aStudyName = aScriptInfo.baseName();
1985 QDir aDir = aScriptInfo.absoluteDir();
1986 QStringList aFiles = aDir.entryList( QStringList( "*.py*" ) );
1987 for( QStringList::iterator it = aFiles.begin(), itEnd = aFiles.end(); it != itEnd; ++it )
1988 ok = aDir.remove( *it ) && ok;
1990 ok = aDir.rmdir( aDir.absolutePath() );
1992 if( SalomeApp_Study* newStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() ) )
1994 #ifndef DISABLE_PYCONSOLE
1995 if ( app->getNoteBook() )
1996 app->getNoteBook()->Init();
1997 newStudy->updateFromNotebook(theStudyName, theIsStudySaved);
1998 newStudy->Modified();
1999 updateDesktopTitle();
2010 Close the Application
2012 void SalomeApp_Application::afterCloseDoc()
2014 #ifndef DISABLE_PYCONSOLE
2015 // emit signal to restore study from Python script
2017 emit dumpedStudyClosed( myNoteBook->getDumpedStudyScript(),
2018 myNoteBook->getDumpedStudyName(),
2019 myNoteBook->isDumpedStudySaved() );
2022 LightApp_Application::afterCloseDoc();
2026 Asks to close existing document.
2028 bool SalomeApp_Application::checkExistingDoc()
2030 return LightApp_Application::checkExistingDoc();
2034 #ifndef DISABLE_PYCONSOLE
2036 PyConsole_Interp* SalomeApp_Application::createPyInterp()
2038 return new SalomeApp_PyInterp;
2041 #endif // DISABLE_PYCONSOLE