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() );
166 //! Constructor. Sets passed boolean flag to \c true.
167 MessageLocker( bool& Lock ) : myPrevState( Lock ), myLock( Lock ) { myLock = true; }
168 //! Destructor. Clear external boolean flag passed as parameter to the constructor to \c false.
169 ~MessageLocker() { myLock = myPrevState; }
172 bool& myLock; //! External 'Lock state' boolean flag
176 /*!Create new instance of SalomeApp_Application.*/
177 extern "C" SALOMEAPP_EXPORT SUIT_Application* createApplication()
179 return new SalomeApp_Application();
183 SalomeApp_Application::SalomeApp_Application()
184 : LightApp_Application(),
185 myIsCloseFromExit( false ),
186 myToIgnoreMessages( false )
191 *\li Destroy event filter.
193 SalomeApp_Application::~SalomeApp_Application()
195 // Do not destroy. It's a singleton !
196 //SALOME_EventFilter::Destroy();
199 QStringList __getArgsList(QString argsString)
201 // Special process if some items of 'args:' list are themselves lists
202 // Note that an item can be a list, but not a list of lists...
203 // So we can have something like this:
204 // myscript.py args:['file1','file2'],val1,"done",[1,2,3],[True,False],"ok"
205 // With such a call, argsString variable contains the string representing "[file1,file2]", "val1", "done", "[1,2,3]", "[True,False]", "ok"
206 // We have to split argsString to obtain: [[file1,file2],val1,done,[1,2,3],[True,False],ok]
207 argsString.replace("\\\"", "'"); // replace escaped double quotes by simple quotes
208 bool containsList = (QRegExp("(\\[[^\\]]*\\])").indexIn(argsString) >= 0);
210 QStringList sl = argsString.split("\"", QString::SkipEmptyParts);
215 return argsString.split(",", QString::SkipEmptyParts);
218 /*!Start application.*/
219 void SalomeApp_Application::start()
221 // process the command line options before start: to createActions in accordance to the options
222 static bool isFirst = true;
229 for (int i = 1; i < qApp->arguments().size(); i++) {
230 QRegExp rxs ("--study-hdf=(.+)");
231 if ( rxs.indexIn( qApp->arguments()[i] ) >= 0 && rxs.capturedTexts().count() > 1 ) {
232 QString file = rxs.capturedTexts()[1];
233 QFileInfo fi ( file );
234 QString extension = fi.suffix().toLower();
235 if ( extension == "hdf" && fi.exists() )
236 hdffile = fi.absoluteFilePath();
239 QRegExp rxp ("--pyscript=\\[(.+)\\]");
240 if ( rxp.indexIn( qApp->arguments()[i] ) >= 0 && rxp.capturedTexts().count() > 1 ) {
242 QStringList dictList = rxp.capturedTexts()[1].split("},", QString::SkipEmptyParts);
243 for (int k = 0; k < dictList.count(); ++k) {
244 QRegExp rxd ("[\\s]*\\{?([^\\{\\}]+)\\}?[\\s]*");
245 if ( rxd.indexIn( dictList[k] ) >= 0 && rxd.capturedTexts().count() > 1 ) {
246 for (int m = 1; m < rxd.capturedTexts().count(); ++m) {
247 pyfiles += rxd.capturedTexts()[m];
254 // Here pyfiles elements are: "script_name": [list_of_"arg"s]
255 // For example: "/absolute/path/to/my_script.py": ["1", "2"]
257 LightApp_Application::start();
258 SALOME_EventFilter::Init();
260 setProperty("open_study_from_command_line", true);
261 if ( !hdffile.isEmpty() ) // open hdf file given as parameter
262 onOpenDoc( hdffile );
263 setProperty("open_study_from_command_line", QVariant());
265 #ifndef DISABLE_PYCONSOLE
266 // import/execute python scripts
267 if ( pyfiles.count() > 0 && activeStudy() ) {
268 SalomeApp_Study* appStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
269 PyConsole_Console* pyConsole = pythonConsole();
270 if ( appStudy && pyConsole ) {
271 if ( !getStudy()->GetProperties()->IsLocked() ) {
272 // pyfiles[j] is a dictionary: {"/absolute/path/to/script.py": [script_args]}
273 // Path is absolute, script has .py extension
274 for (int j = 0; j < pyfiles.count(); j++ ) {
275 // Extract scripts and their arguments, if any
276 QRegExp rxp ("\"(.+)\":[\\s]*\\[(.*)\\]");
277 if ( rxp.indexIn( pyfiles[j] ) >= 0 && rxp.capturedTexts().count() == 3 ) {
278 QString script = rxp.capturedTexts()[1];
280 QStringList argList = __getArgsList(rxp.capturedTexts()[2]);
281 for (int k = 0; k < argList.count(); k++ ) {
282 QString arg = argList[k].trimmed();
283 arg.remove( QRegExp("^[\"]") );
284 arg.remove( QRegExp("[\"]$") );
287 args.remove( QRegExp("[,]$") );
288 if (!args.isEmpty()) {
292 script.remove( QRegExp("^python.*[\\s]+") );
293 QString cmd = script+" "+args;
295 QString command = QString( "exec(open(\"%1\", \"rb\").read())" ).arg(cmd.trimmed());
296 pyConsole->exec(command);
298 } // end for loop on pyfiles QStringList
304 LightApp_Application::start();
305 SALOME_EventFilter::Init();
310 void SalomeApp_Application::createActions()
312 LightApp_Application::createActions();
314 SUIT_Desktop* desk = desktop();
317 // "Save GUI State" command is moved to VISU module
318 // createAction( SaveGUIStateId, tr( "TOT_DESK_FILE_SAVE_GUI_STATE" ), QIcon(),
319 // tr( "MEN_DESK_FILE_SAVE_GUI_STATE" ), tr( "PRP_DESK_FILE_SAVE_GUI_STATE" ),
320 // 0, desk, false, this, SLOT( onSaveGUIState() ) );
323 createAction( DumpStudyId, tr( "TOT_DESK_FILE_DUMP_STUDY" ), QIcon(),
324 tr( "MEN_DESK_FILE_DUMP_STUDY" ), tr( "PRP_DESK_FILE_DUMP_STUDY" ),
325 Qt::CTRL+Qt::Key_D, desk, false, this, SLOT( onDumpStudy() ) );
328 createAction( LoadScriptId, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), QIcon(),
329 tr( "MEN_DESK_FILE_LOAD_SCRIPT" ), tr( "PRP_DESK_FILE_LOAD_SCRIPT" ),
330 Qt::CTRL+Qt::Key_T, desk, false, this, SLOT( onLoadScript() ) );
333 createAction( PropertiesId, tr( "TOT_DESK_PROPERTIES" ), QIcon(),
334 tr( "MEN_DESK_PROPERTIES" ), tr( "PRP_DESK_PROPERTIES" ),
335 0, desk, false, this, SLOT( onProperties() ) );
337 //! Catalog Generator
338 createAction( CatalogGenId, tr( "TOT_DESK_CATALOG_GENERATOR" ), QIcon(),
339 tr( "MEN_DESK_CATALOG_GENERATOR" ), tr( "PRP_DESK_CATALOG_GENERATOR" ),
340 Qt::ALT+Qt::SHIFT+Qt::Key_G, desk, false, this, SLOT( onCatalogGen() ) );
343 createAction( RegDisplayId, tr( "TOT_DESK_REGISTRY_DISPLAY" ), QIcon(),
344 tr( "MEN_DESK_REGISTRY_DISPLAY" ), tr( "PRP_DESK_REGISTRY_DISPLAY" ),
345 /*Qt::SHIFT+Qt::Key_D*/0, desk, false, this, SLOT( onRegDisplay() ) );
347 createAction( ConnectId, tr( "TOT_DESK_CONNECT_STUDY" ), QIcon(),
348 tr( "MEN_DESK_CONNECT" ), tr( "PRP_DESK_CONNECT" ),
349 Qt::CTRL+Qt::Key_L, desk, false, this, SLOT( onLoadDoc() ) );
350 //no need at this action for mono-study application because study is always exists
351 action( ConnectId )->setVisible( false );
353 createAction( DisconnectId, tr( "TOT_DESK_DISCONNECT_STUDY" ), QIcon(),
354 tr( "MEN_DESK_DISCONNECT" ), tr( "PRP_DESK_DISCONNECT" ),
355 Qt::CTRL+Qt::Key_U, desk, false, this, SLOT( onUnloadDoc() ) );
356 //no need at this action for mono-study application because study is always exists
357 action( DisconnectId )->setVisible( false );
360 int fileMenu = createMenu( tr( "MEN_DESK_FILE" ), -1 );
362 // "Save GUI State" command is renamed to "Save VISU State" and
363 // creation of menu item is moved to VISU
364 // createMenu( SaveGUIStateId, fileMenu, 10, -1 );
366 createMenu( ConnectId, fileMenu, 5 );
367 createMenu( DisconnectId, fileMenu, 5 );
368 createMenu( separator(), fileMenu, -1, 5 );
370 createMenu( DumpStudyId, fileMenu, 10, -1 );
371 createMenu( LoadScriptId, fileMenu, 10, -1 );
372 createMenu( separator(), fileMenu, -1, 10, -1 );
373 createMenu( PropertiesId, fileMenu, 10, -1 );
374 createMenu( separator(), fileMenu, -1, 10, -1 );
376 int toolsMenu = createMenu( tr( "MEN_DESK_TOOLS" ), -1, MenuToolsId, 50 );
377 createMenu( CatalogGenId, toolsMenu, 10, -1 );
378 createMenu( RegDisplayId, toolsMenu, 10, -1 );
379 createMenu( separator(), toolsMenu, -1, 15, -1 );
381 createExtraActions();
383 #ifndef DISABLE_PYCONSOLE
384 #ifndef DISABLE_SALOMEOBJECT
385 // import Python module that manages SALOME plugins
387 PyLockWrapper lck; // acquire GIL
388 PyObjWrapper pluginsmanager = PyImport_ImportModule((char*)"salome_pluginsmanager");
389 PyObjWrapper res = PyObject_CallMethod( pluginsmanager, (char*)"initialize", (char*)"isss",0,"salome",tr("MEN_DESK_TOOLS").toUtf8().data(),tr("MEN_DESK_PLUGINS").toUtf8().data());
393 // end of SALOME plugins loading
400 \brief Close application.
402 void SalomeApp_Application::onExit()
404 //MessageLocker ml( myToIgnoreMessages );
406 bool killServers = false;
409 if ( exitConfirmation() ) {
410 SalomeApp_ExitDlg dlg( desktop() );
411 result = dlg.exec() == QDialog::Accepted;
412 killServers = dlg.isServersShutdown();
416 if ( !killServers ) myIsCloseFromExit = true;
417 SUIT_Session::session()->closeSession( SUIT_Session::ASK, killServers );
418 if ( SUIT_Session::session()->applications().count() > 0 ) myIsCloseFromExit = false;
422 /*!SLOT. Create a document.*/
423 void SalomeApp_Application::onNewDoc()
425 MessageLocker ml( myToIgnoreMessages );
427 LightApp_Application::onNewDoc();
430 /*!SLOT. Load document.*/
431 void SalomeApp_Application::onLoadDoc()
433 MessageLocker ml( myToIgnoreMessages );
437 // rnv: According to the single-study approach on the server side
438 // can be only one study. So if it is exists connect to them,
439 // overwise show warning message: "No active study on the server"
442 SUIT_Session* aSession = SUIT_Session::session();
443 QList<SUIT_Application*> aAppList = aSession->applications();
445 QStringList unloadedStudies;
447 for ( unsigned int ind = 0; ind < List.size(); ind++ ) {
448 studyName = List[ind].c_str();
449 // Add to list only unloaded studies
450 bool isAlreadyOpen = false;
451 QListIterator<SUIT_Application*> it( aAppList );
452 while ( it.hasNext() && !isAlreadyOpen ) {
453 SUIT_Application* aApp = it.next();
454 if( !aApp || !aApp->activeStudy() )
456 if ( aApp->activeStudy()->studyName() == studyName )
457 isAlreadyOpen = true;
460 if ( !isAlreadyOpen )
461 unloadedStudies << studyName;
463 studyName = SalomeApp_LoadStudiesDlg::selectStudy( desktop(), unloadedStudies );
464 if ( studyName.isEmpty() )
469 SUIT_MessageBox::warning( desktop(),
470 QObject::tr("WRN_WARNING"),
471 QObject::tr("WRN_NO_STUDY_ON SERV") );
475 studyName = activeStudy()->studyName();
478 // this code replaces marker of windows drive and path become invalid therefore
479 // defines placed there
480 studyName.replace( QRegExp(":"), "/" );
483 if ( onLoadDoc( studyName ) ) {
485 updateViewManagers();
486 updateObjectBrowser( true );
490 /*!SLOT. Unload document.*/
491 void SalomeApp_Application::onUnloadDoc( bool ask )
494 activeStudy()->abortAllOperations();
495 if ( activeStudy()->isModified() ) {
496 QString docName = activeStudy()->studyName().trimmed();
497 int answer = SUIT_MessageBox::question( desktop(), tr( "DISCONNECT_CAPTION" ),
498 tr( "DISCONNECT_DESCRIPTION" ),
499 tr( "DISCONNECT_SAVE" ),
500 tr( "DISCONNECT_WO_SAVE" ),
501 tr( "APPCLOSE_CANCEL" ), 0 );
502 if ( answer == 0 ) { // save before unload
503 if ( activeStudy()->isSaved() )
505 else if ( !onSaveAsDoc() )
508 else if ( answer == 2 ) // Cancel
512 closeActiveDoc( false );
515 /*!SLOT. Create new study and load script*/
516 void SalomeApp_Application::onNewWithScript()
518 QStringList filtersList;
519 filtersList.append(tr("PYTHON_FILES_FILTER"));
520 filtersList.append(tr("ALL_FILES_FILTER"));
522 QString anInitialPath = "";
523 if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
524 anInitialPath = QDir::currentPath();
526 QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true );
528 if ( !aFile.isEmpty() )
532 QString command = QString("exec(open(\"%1\", \"rb\").read())").arg(aFile);
534 #ifndef DISABLE_PYCONSOLE
535 PyConsole_Console* pyConsole = pythonConsole();
538 pyConsole->exec( command );
544 /*!SLOT. Load document with \a aName.*/
545 bool SalomeApp_Application::onLoadDoc( const QString& aName )
547 if ( !LightApp_Application::closeDoc() )
551 if ( !activeStudy() ) {
552 // if no study - load in current desktop
553 res = useStudy( aName );
556 // if study exists - load in new desktop. Check: is the same file is loaded?
557 SUIT_Session* aSession = SUIT_Session::session();
558 QList<SUIT_Application*> aAppList = aSession->applications();
559 bool isAlreadyOpen = false;
560 SalomeApp_Application* aApp = 0;
561 for ( QList<SUIT_Application*>::iterator it = aAppList.begin();
562 it != aAppList.end() && !isAlreadyOpen; ++it ) {
563 aApp = dynamic_cast<SalomeApp_Application*>( *it );
564 if ( aApp && aApp->activeStudy()->studyName() == aName )
565 isAlreadyOpen = true;
567 if ( !isAlreadyOpen ) {
568 aApp = dynamic_cast<SalomeApp_Application*>( startApplication( 0, 0 ) );
570 res = aApp->useStudy( aName );
573 aApp->desktop()->activateWindow();
580 /*!SLOT. Parse message for desktop.*/
581 void SalomeApp_Application::onDesktopMessage( const QString& message )
583 if ( myToIgnoreMessages )
584 return; // a message from SALOMEDS is caused by GUI action
586 MessageLocker ml( myToIgnoreMessages );
588 if (message.indexOf("studyCreated") == 0) {
589 if (!activeStudy()) {
591 updateCommandsStatus();
594 if (message.indexOf("studyCleared") == 0) {
595 // Disconnect GUI from active study, because it was closed on DS side.
597 closeActiveDoc( false );
598 // Disable 'Connect' action
599 QAction* a = action( ConnectId );
601 a->setEnabled( false );
604 else if ( message.toLower() == "connect_to_study" ) {
606 useStudy( activeStudy()->studyName() );
608 if (message.indexOf("studyNameChanged") == 0) {
609 updateDesktopTitle();
611 LightApp_Application::onDesktopMessage( message );
614 /*!On module activation action.*/
615 void SalomeApp_Application::onModuleActivation( const QString& modName )
617 if (!activeStudy() && !modName.isEmpty())
620 LightApp_Application::onModuleActivation( modName );
623 /*!SLOT. Copy objects to study maneger from selection maneger..*/
624 void SalomeApp_Application::onCopy()
626 LightApp_Application::onCopy();
629 LightApp_SelectionMgr* mgr = selectionMgr();
630 mgr->selectedObjects(list);
632 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
633 if(study == NULL) return;
635 _PTR(Study) stdDS = getStudy();
638 SALOME_ListIteratorOfListIO it( list );
641 _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry());
646 onSelectionChanged();
654 /*!SLOT. Paste objects to study maneger from selection manager.*/
655 void SalomeApp_Application::onPaste()
657 LightApp_Application::onPaste();
660 LightApp_SelectionMgr* mgr = selectionMgr();
661 mgr->selectedObjects(list);
663 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
664 if(study == NULL) return;
666 _PTR(Study) stdDS = getStudy();
669 if ( stdDS->GetProperties()->IsLocked() ) {
670 SUIT_MessageBox::warning( desktop(),
671 QObject::tr("WRN_WARNING"),
672 QObject::tr("WRN_STUDY_LOCKED") );
676 SALOME_ListIteratorOfListIO it( list );
679 _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry());
684 updateObjectBrowser( true );
685 updateActions(); //SRN: BugID IPAL9377, case 3
693 /*!Check the application on closing.
694 * \retval true if possible, else false
696 bool SalomeApp_Application::isPossibleToClose( bool& closePermanently )
698 return LightApp_Application::isPossibleToClose( closePermanently );
701 /*! Check if the study is locked */
702 void SalomeApp_Application::onCloseDoc( bool ask )
704 if(getStudy()->IsStudyLocked()) {
705 if ( SUIT_MessageBox::question( desktop(),
706 QObject::tr( "WRN_WARNING" ),
707 QObject::tr( "CLOSE_LOCKED_STUDY" ),
708 SUIT_MessageBox::Yes | SUIT_MessageBox::No,
709 SUIT_MessageBox::No) == SUIT_MessageBox::No ) return;
712 MessageLocker ml( myToIgnoreMessages );
714 LightApp_Application::onCloseDoc( ask );
716 // reinitialize study to have empty data
717 //getStudy()->Init();
720 /*!SLOT. Reload document from the file.*/
721 bool SalomeApp_Application::onReopenDoc()
723 MessageLocker ml( myToIgnoreMessages );
725 return LightApp_Application::onReopenDoc();
729 /*!SLOT. Load document.*/
730 void SalomeApp_Application::onOpenDoc()
732 MessageLocker ml( myToIgnoreMessages );
734 LightApp_Application::onOpenDoc();
737 /*!SLOT. Load document.*/
738 bool SalomeApp_Application::onOpenDoc(const QString& name)
740 MessageLocker ml( myToIgnoreMessages );
742 return LightApp_Application::onOpenDoc(name);
745 /*!Sets enable or disable some actions on selection changed.*/
746 void SalomeApp_Application::onSelectionChanged()
749 LightApp_SelectionMgr* mgr = selectionMgr();
750 mgr->selectedObjects(list);
752 bool canCopy = false;
753 bool canPaste = false;
755 LightApp_Module* m = dynamic_cast<LightApp_Module*>( activeModule() );
758 canCopy = m->canCopy();
759 canPaste = m->canPaste();
762 SALOME_ListIteratorOfListIO it ( list );
764 if (it.More() && list.Extent() == 1) {
765 _PTR(SObject) so = getStudy()->FindObjectID(it.Value()->getEntry());
768 canCopy = canCopy || getStudy()->CanCopy(so);
769 canPaste = canPaste || getStudy()->CanPaste(so);
773 action(EditCopyId)->setEnabled(canCopy);
774 action(EditPasteId)->setEnabled(canPaste);
777 /*!Delete references.*/
778 void SalomeApp_Application::onDeleteInvalidReferences()
781 LightApp_SelectionMgr* mgr = selectionMgr();
782 mgr->selectedObjects( aList, QString(), false );
784 if( aList.IsEmpty() )
787 _PTR(Study) aStudyDS = getStudy();
788 _PTR(StudyBuilder) aStudyBuilder = aStudyDS->NewBuilder();
791 for( SALOME_ListIteratorOfListIO it( aList ); it.More(); it.Next() )
792 if ( it.Value()->hasEntry() )
794 _PTR(SObject) aSObject = aStudyDS->FindObjectID( it.Value()->getEntry() ), aRefObj = aSObject;
795 while( aRefObj && aRefObj->ReferencedObject( anObj ) )
798 if( aRefObj && aRefObj!=aSObject && QString( aRefObj->GetName().c_str() ).isEmpty() )
799 aStudyBuilder->RemoveReference( aSObject );
801 updateObjectBrowser();
805 void SalomeApp_Application::onOpenWith()
807 QApplication::setOverrideCursor( Qt::WaitCursor );
809 LightApp_SelectionMgr* mgr = selectionMgr();
810 mgr->selectedObjects(aList);
811 if (aList.Extent() != 1)
813 QApplication::restoreOverrideCursor();
816 Handle(SALOME_InteractiveObject) aIObj = aList.First();
817 QString aModuleName(aIObj->getComponentDataType());
818 QString aModuleTitle = moduleTitle(aModuleName);
819 activateModule(aModuleTitle);
820 QApplication::restoreOverrideCursor();
826 SUIT_Study* SalomeApp_Application::createNewStudy()
828 SalomeApp_Study* aStudy = new SalomeApp_Study( this );
830 // Set up processing of major study-related events
831 connect( aStudy, SIGNAL( created( SUIT_Study* ) ), this, SLOT( onStudyCreated( SUIT_Study* ) ) );
832 connect( aStudy, SIGNAL( opened ( SUIT_Study* ) ), this, SLOT( onStudyOpened ( SUIT_Study* ) ) );
833 connect( aStudy, SIGNAL( saved ( SUIT_Study* ) ), this, SLOT( onStudySaved ( SUIT_Study* ) ) );
834 connect( aStudy, SIGNAL( closed ( SUIT_Study* ) ), this, SLOT( onStudyClosed ( SUIT_Study* ) ) );
836 #ifndef DISABLE_PYCONSOLE
837 //to receive signal in application that NoteBook's variable was modified
838 connect( aStudy, SIGNAL(notebookVarUpdated(QString)),
839 this, SIGNAL(notebookVarUpdated(QString)) );
848 Enable/Disable menu items and toolbar buttons. Rebuild menu
850 void SalomeApp_Application::updateCommandsStatus()
852 LightApp_Application::updateCommandsStatus();
855 QAction* a = action( DumpStudyId );
857 a->setEnabled( activeStudy() );
859 #ifndef DISABLE_PYCONSOLE
861 a = action( LoadScriptId );
863 a->setEnabled( pythonConsole() );
867 a = action( PropertiesId );
869 a->setEnabled( activeStudy() );
871 // Save GUI state menu
872 a = action( SaveGUIStateId );
874 a->setEnabled( activeStudy() );
876 // Connect study menu
877 a = action( ConnectId );
879 a->setEnabled( !activeStudy() );
881 // Disconnect study menu
882 a = action( DisconnectId );
884 a->setEnabled( activeStudy() );
886 // update state of Copy/Paste menu items
887 onSelectionChanged();
891 \class DumpStudyFileDlg
892 Private class used in Dump Study operation. Consists 2 check boxes:
893 "Publish in study" and "Save GUI parameters"
895 class DumpStudyFileDlg : public SUIT_FileDlg
898 DumpStudyFileDlg( QWidget* parent ) : SUIT_FileDlg( parent, false, true, true )
900 QGridLayout* grid = ::qobject_cast<QGridLayout*>( layout() );
903 QWidget *hB = new QWidget( this );
904 myPublishChk = new QCheckBox( tr("PUBLISH_IN_STUDY") );
905 myMultiFileChk = new QCheckBox( tr("MULTI_FILE_DUMP") );
906 mySaveGUIChk = new QCheckBox( tr("SAVE_GUI_STATE") );
908 QHBoxLayout *layout = new QHBoxLayout;
909 layout->addWidget(myPublishChk);
910 layout->addWidget(myMultiFileChk);
911 layout->addWidget(mySaveGUIChk);
912 hB->setLayout(layout);
914 QPushButton* pb = new QPushButton(this);
916 int row = grid->rowCount();
917 grid->addWidget( new QLabel("", this), row, 0 );
918 grid->addWidget( hB, row, 1, 1, 3 );
919 grid->addWidget( pb, row, 5 );
924 QCheckBox* myPublishChk;
925 QCheckBox* myMultiFileChk;
926 QCheckBox* mySaveGUIChk;
929 /*!Private SLOT. On dump study.*/
930 void SalomeApp_Application::onDumpStudy( )
932 SalomeApp_Study* appStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
933 if ( !appStudy ) return;
935 QStringList aFilters;
936 aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
938 bool anIsPublish = true;
939 bool anIsMultiFile = false;
940 bool anIsSaveGUI = true;
942 if ( SUIT_ResourceMgr* aResourceMgr = resourceMgr() ) {
943 anIsPublish = aResourceMgr->booleanValue( "Study", "pydump_publish", anIsPublish );
944 anIsMultiFile = aResourceMgr->booleanValue( "Study", "multi_file_dump", anIsMultiFile );
945 anIsSaveGUI = aResourceMgr->booleanValue( "Study", "pydump_save_gui", anIsSaveGUI );
948 DumpStudyFileDlg fd( desktop() );
949 fd.setValidator( new LightApp_PyFileValidator( &fd ) );
950 fd.setWindowTitle( tr( "TOT_DESK_FILE_DUMP_STUDY" ) );
951 fd.setNameFilters( aFilters );
952 fd.myPublishChk->setChecked( anIsPublish );
953 fd.myMultiFileChk->setChecked( anIsMultiFile );
954 fd.mySaveGUIChk->setChecked( anIsSaveGUI );
955 if ( fd.exec() == QDialog::Accepted )
957 QString aFileName = fd.selectedFile();
959 bool toPublish = fd.myPublishChk->isChecked();
960 bool isMultiFile = fd.myMultiFileChk->isChecked();
961 bool toSaveGUI = fd.mySaveGUIChk->isChecked();
963 if ( !aFileName.isEmpty() ) {
964 QFileInfo aFileInfo(aFileName);
965 if( aFileInfo.isDir() ) // IPAL19257
968 // Issue 21377 - dump study implementation moved to SalomeApp_Study class
971 SUIT_OverrideCursor wc;
972 res = appStudy->dump( aFileName, toPublish, isMultiFile, toSaveGUI );
975 SUIT_MessageBox::warning( desktop(),
976 QObject::tr("WRN_WARNING"),
977 tr("WRN_DUMP_STUDY_FAILED") );
982 /*!Private SLOT. On load script.*/
983 void SalomeApp_Application::onLoadScript( )
985 if ( getStudy()->GetProperties()->IsLocked() ) {
986 SUIT_MessageBox::warning( desktop(),
987 QObject::tr("WRN_WARNING"),
988 QObject::tr("WRN_STUDY_LOCKED") );
992 QStringList filtersList;
993 filtersList.append(tr("PYTHON_FILES_FILTER"));
994 filtersList.append(tr("ALL_FILES_FILTER"));
996 QString anInitialPath = "";
997 if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
998 anInitialPath = QDir::currentPath();
1000 QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true );
1002 if ( !aFile.isEmpty() )
1005 QString command = QString("exec(open(\"%1\", \"rb\").read())").arg(aFile);
1007 #ifndef DISABLE_PYCONSOLE
1008 PyConsole_Console* pyConsole = pythonConsole();
1011 pyConsole->exec( command );
1016 /*!Private SLOT. On save GUI state.*/
1017 void SalomeApp_Application::onSaveGUIState()
1019 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1021 SalomeApp_VisualState( this ).storeState();
1022 updateSavePointDataObjects( study );
1023 updateObjectBrowser();
1028 /*!Public SLOT. Performs some actions when dockable windows are triggered.*/
1029 void SalomeApp_Application::onDockWindowVisibilityChanged( bool theIsVisible )
1031 LightApp_Application::onDockWindowVisibilityChanged( theIsVisible );
1032 QAction* send = ::qobject_cast<QAction*>( sender() );
1035 QString aWinName = send->data().toString();
1036 if ( theIsVisible && aWinName == "objectBrowser" )
1037 objectBrowserColumnsVisibility();
1041 QWidget* SalomeApp_Application::createWindow( const int flag )
1044 #ifndef DISABLE_PYCONSOLE
1045 if ( flag != WT_PyConsole ) wid = LightApp_Application::createWindow(flag);
1047 wid = LightApp_Application::createWindow(flag);
1050 SUIT_ResourceMgr* resMgr = resourceMgr();
1052 if ( flag == WT_ObjectBrowser )
1054 SUIT_DataBrowser* ob = qobject_cast<SUIT_DataBrowser*>( wid );
1056 // temporary commented
1057 //ob->setUpdater( new SalomeApp_Updater() );
1059 #ifdef WITH_SALOMEDS_OBSERVER
1060 //do not activate the automatic update of Qt tree through signal/slot
1061 ob->setAutoUpdate(false);
1062 //activate update of modified objects only
1063 ob->setUpdateModified(true);
1066 connect( ob, SIGNAL( doubleClicked( SUIT_DataObject* ) ), this, SLOT( onDblClick( SUIT_DataObject* ) ) );
1069 ValueCol = QObject::tr( "VALUE_COLUMN" ),
1070 IORCol = QObject::tr( "IOR_COLUMN" ),
1071 RefCol = QObject::tr( "REFENTRY_COLUMN" ),
1072 EntryCol = QObject::tr( "ENTRY_COLUMN" );
1074 SUIT_AbstractModel* treeModel = dynamic_cast<SUIT_AbstractModel*>( ob->model() );
1075 treeModel->registerColumn( 0, EntryCol, SalomeApp_DataObject::EntryId );
1076 treeModel->registerColumn( 0, ValueCol, SalomeApp_DataObject::ValueId );
1077 treeModel->registerColumn( 0, IORCol, SalomeApp_DataObject::IORId );
1078 treeModel->registerColumn( 0, RefCol, SalomeApp_DataObject::RefEntryId );
1079 treeModel->setAppropriate( EntryCol, Qtx::Toggled );
1080 treeModel->setAppropriate( ValueCol, Qtx::Toggled );
1081 treeModel->setAppropriate( IORCol, Qtx::Toggled );
1082 treeModel->setAppropriate( RefCol, Qtx::Toggled );
1084 bool autoSize = resMgr->booleanValue( "ObjectBrowser", "auto_size", false );
1085 bool autoSizeFirst = resMgr->booleanValue( "ObjectBrowser", "auto_size_first", true );
1086 bool resizeOnExpandItem = resMgr->booleanValue( "ObjectBrowser", "resize_on_expand_item", true );
1088 ob->setAutoSizeFirstColumn(autoSizeFirst);
1089 ob->setAutoSizeColumns(autoSize);
1090 ob->setResizeOnExpandItem(resizeOnExpandItem);
1091 ob->setProperty( "shortcut", QKeySequence( "Alt+Shift+O" ) );
1093 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1095 bool shown = resourceMgr()->booleanValue( "ObjectBrowser", QString( "visibility_column_id_%1" ).arg( i-1 ), true );
1096 ob->treeView()->setColumnHidden( i, !shown );
1099 // temporary commented
1101 for ( int i = SalomeApp_DataObject::ValueIdx; i <= SalomeApp_DataObject::RefEntryIdx; i++ )
1103 ob->addColumn( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i ) ), i );
1104 ob->setColumnShown( i, resMgr->booleanValue( "ObjectBrowser",
1105 QString().sprintf( "visibility_column_%d", i ), true ) );
1109 // temporary commented
1111 ob->setWidthMode( autoSize ? QListView::Maximum : QListView::Manual );
1112 ob->listView()->setColumnWidthMode( 0, autoSizeFirst ? QListView::Maximum : QListView::Manual );
1113 ob->resize( desktop()->width()/3, ob->height() );
1117 #ifndef DISABLE_PYCONSOLE
1118 else if ( flag == WT_PyConsole )
1120 PyConsole_Console* pyCons = new PyConsole_Console( desktop(), new LightApp_PyEditor( getPyInterp() ) );
1121 pyCons->setObjectName( "pythonConsole" );
1122 pyCons->setWindowTitle( tr( "PYTHON_CONSOLE" ) );
1123 pyCons->setFont(resourceMgr()->fontValue( "PyConsole", "font" ));
1124 pyCons->setIsShowBanner(resourceMgr()->booleanValue( "PyConsole", "show_banner", true ));
1125 pyCons->setAutoCompletion( resMgr->booleanValue( "PyConsole", "auto_completion", true ) );
1126 pyCons->setProperty( "shortcut", QKeySequence( "Alt+Shift+P" ) );
1129 else if ( flag == WT_NoteBook )
1131 setNoteBook( new SalomeApp_NoteBook( desktop() ) );
1132 //to receive signal in NoteBook that it's variable was modified
1133 connect( this, SIGNAL( notebookVarUpdated( QString ) ),
1134 getNoteBook(), SLOT( onVarUpdate( QString ) ) );
1136 wid = getNoteBook();
1137 wid->setObjectName( "noteBook" );
1143 /*!Create preferences.*/
1144 void SalomeApp_Application::createPreferences( LightApp_Preferences* pref )
1146 LightApp_Application::createPreferences(pref);
1151 int salomeCat = pref->addPreference( tr( "PREF_CATEGORY_SALOME" ) );
1152 int obTab = pref->addPreference( tr( "PREF_TAB_OBJBROWSER" ), salomeCat );
1153 int defCols = pref->addPreference( tr( "PREF_GROUP_DEF_COLUMNS" ), obTab );
1154 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1156 pref->addPreference( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i-SalomeApp_DataObject::EntryId ).toLatin1() ), defCols,
1157 LightApp_Preferences::Bool, "ObjectBrowser", QString().sprintf( "visibility_column_id_%d", i-1 ) );
1159 pref->setItemProperty( "orientation", Qt::Vertical, defCols );
1161 // adding preference to LightApp_Application handled preferences.. a bit of hacking with resources..
1162 int genTab = pref->addPreference( LightApp_Application::tr( "PREF_TAB_GENERAL" ), salomeCat );
1163 int studyGroup = pref->addPreference( LightApp_Application::tr( "PREF_GROUP_STUDY" ), genTab );
1164 pref->addPreference( tr( "PREF_STORE_VISUAL_STATE" ), studyGroup, LightApp_Preferences::Bool, "Study", "store_visual_state" );
1165 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1166 pref->addPreference( tr( "PREF_PYDUMP_PUBLISH" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_publish" );
1167 pref->addPreference( tr( "PREF_PYDUMP_MULTI_FILE" ), studyGroup, LightApp_Preferences::Bool, "Study", "multi_file_dump" );
1168 pref->addPreference( tr( "PREF_PYDUMP_SAVE_GUI" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_save_gui" );
1169 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1170 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1173 /*!Update desktop title.*/
1174 void SalomeApp_Application::updateDesktopTitle() {
1175 QString aTitle = applicationName();
1176 QString aVer = applicationVersion();
1177 if ( !aVer.isEmpty() )
1178 aTitle += QString( " " ) + aVer;
1180 if ( activeStudy() )
1182 QString sName = SUIT_Tools::file( activeStudy()->studyName().trimmed(), false );
1183 if ( !sName.isEmpty() ) {
1184 if ( getStudy()->GetProperties()->IsLocked() ) {
1185 aTitle += QString( " - [%1 (%2)]").arg( sName ).arg( tr( "STUDY_LOCKED" ) );
1187 aTitle += QString( " - [%1]" ).arg( sName );
1192 desktop()->setWindowTitle( aTitle );
1195 int SalomeApp_Application::closeChoice( const QString& docName )
1197 QStringList buttons;
1198 QMap<int, int> choices;
1200 buttons << tr ("APPCLOSE_SAVE"); // Save & Clear
1201 choices.insert( idx++, CloseSave ); // ...
1202 buttons << tr ("APPCLOSE_CLOSE"); // Clear w/o saving
1203 choices.insert( idx++, CloseDiscard ); // ...
1204 if ( myIsCloseFromExit ) {
1205 buttons << tr ("APPCLOSE_UNLOAD_SAVE"); // Save & Disconnect
1206 choices.insert( idx++, CloseDisconnectSave ); // ...
1207 buttons << tr ("APPCLOSE_UNLOAD"); // Disconnect
1208 choices.insert( idx++, CloseDisconnect ); // ...
1210 buttons << tr ("APPCLOSE_CANCEL"); // Cancel
1211 choices.insert( idx++, CloseCancel ); // ...
1213 if( !activeStudy()->isModified() )
1215 int answer = SUIT_MessageBox::question( desktop(), tr( "APPCLOSE_CAPTION" ),
1216 tr( "APPCLOSE_DESCRIPTION" ), buttons, 0 );
1217 return choices[answer];
1220 bool SalomeApp_Application::closeAction( const int choice, bool& closePermanently )
1226 if ( activeStudy()->isSaved() )
1228 else if ( !onSaveAsDoc() )
1233 case CloseDisconnectSave:
1234 if ( activeStudy()->isSaved() )
1236 else if ( !onSaveAsDoc() )
1238 case CloseDisconnect:
1239 closeActiveDoc( false );
1240 closePermanently = false;
1249 int SalomeApp_Application::openChoice( const QString& aName )
1251 int choice = LightApp_Application::openChoice( aName );
1253 if ( QFileInfo( aName ).exists() ) {
1254 if ( choice == OpenNew ) { // The document isn't already open.
1256 if ( aName == getStudy()->Name().c_str() )
1258 // The document already exists in the study.
1259 // Do you want to reload it?
1261 int answer = SUIT_MessageBox::question( desktop(), tr( "WRN_WARNING" ), tr( "QUE_DOC_ALREADYEXIST" ).arg( aName ),
1262 SUIT_MessageBox::Yes | SUIT_MessageBox::No, SUIT_MessageBox::No );
1263 if ( answer == SUIT_MessageBox::Yes )
1264 choice = OpenRefresh;
1266 choice = OpenCancel;
1269 } else { // file is not exist on disk
1270 SUIT_MessageBox::warning( desktop(),
1271 QObject::tr("WRN_WARNING"),
1272 QObject::tr("WRN_FILE_NOT_EXIST").arg(aName.toUtf8().data()));
1279 bool SalomeApp_Application::openAction( const int aChoice, const QString& aName )
1282 int choice = aChoice;
1290 res = LightApp_Application::openAction( choice, aName );
1298 \brief Get map of the operations which can be performed
1299 on the module activation.
1301 The method should return the map of the kind \c {<id>:<name>}
1302 where \c <id> is an integer identifier of the operation and
1303 \c <name> is a title for the button to be added to the
1304 dialog box. After user selects the required operation by the
1305 clicking the corresponding button in the dialog box, its identifier
1306 is passed to the moduleActionSelected() method to process
1309 \return map of the operations
1310 \sa moduleActionSelected()
1312 QMap<int, QString> SalomeApp_Application::activateModuleActions() const
1314 QMap<int, QString> opmap = LightApp_Application::activateModuleActions();
1316 opmap.insert( LoadStudyId, tr( "ACTIVATE_MODULE_OP_LOAD" ) );
1318 opmap.insert( NewAndScriptId, tr( "ACTIVATE_MODULE_OP_SCRIPT" ) );
1323 \brief Called when the used selectes required operation chosen
1324 from "Activate module" dialog box.
1326 Performs the required operation according to the user choice.
1328 \param id operation identifier
1329 \sa activateModuleActions()
1331 void SalomeApp_Application::moduleActionSelected( const int id )
1337 case NewAndScriptId:
1341 LightApp_Application::moduleActionSelected( id );
1346 /*!Gets CORBA::ORB_var*/
1347 CORBA::ORB_var SalomeApp_Application::orb()
1349 static CORBA::ORB_var _orb;
1351 if ( CORBA::is_nil( _orb ) ) {
1352 Qtx::CmdLineArgs args;
1353 ORB_INIT& init = *SINGLETON_<ORB_INIT>::Instance();
1354 _orb = init( args.argc(), args.argv() );
1360 /*!Create and return SALOMEDS_Study.*/
1361 _PTR(Study) SalomeApp_Application::getStudy()
1363 static _PTR(Study) _study;
1365 CORBA::Object_var aSObject = namingService()->Resolve("/Study");
1366 SALOMEDS::Study_var aStudy = SALOMEDS::Study::_narrow(aSObject);
1367 _study = ClientFactory::Study(aStudy);
1372 /*!Create and return SALOME_NamingService.*/
1373 SALOME_NamingService* SalomeApp_Application::namingService()
1375 static SALOME_NamingService _ns(orb());
1379 /*!Create and return SALOME_LifeCycleCORBA.*/
1380 SALOME_LifeCycleCORBA* SalomeApp_Application::lcc()
1382 static SALOME_LifeCycleCORBA _lcc( namingService() );
1386 /*!Private SLOT. On preferences.*/
1387 void SalomeApp_Application::onProperties()
1389 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1393 _PTR(StudyBuilder) SB = study->studyDS()->NewBuilder();
1396 SalomeApp_StudyPropertiesDlg aDlg( desktop() );
1397 int res = aDlg.exec();
1398 if( res==QDialog::Accepted && aDlg.isChanged() )
1399 SB->CommitCommand();
1403 //study->updateCaptions();
1404 updateDesktopTitle();
1408 /*!Insert items in popup, which necessary for current application*/
1409 void SalomeApp_Application::contextMenuPopup( const QString& type, QMenu* thePopup, QString& title )
1411 LightApp_SelectionMgr* mgr = selectionMgr();
1412 bool cacheIsOn = mgr->isSelectionCacheEnabled();
1413 mgr->setSelectionCacheEnabled( true );
1415 LightApp_Application::contextMenuPopup( type, thePopup, title );
1417 // temporary commented
1418 /*OB_Browser* ob = objectBrowser();
1419 if ( !ob || type != ob->popupClientType() )
1422 // Get selected objects
1423 SALOME_ListIO aList;
1424 mgr->selectedObjects( aList, QString(), false );
1426 // add GUI state commands: restore, rename
1427 if ( aList.Extent() == 1 && aList.First()->hasEntry() &&
1428 QString( aList.First()->getEntry() ).startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) {
1429 thePopup->addSeparator();
1430 thePopup->addAction( tr( "MEN_RESTORE_VS" ), this, SLOT( onRestoreGUIState() ) );
1431 thePopup->addAction( tr( "MEN_RENAME_VS" ), objectBrowser(),
1432 SLOT( onStartEditing() ), objectBrowser()->shortcutKey(SUIT_DataBrowser::RenameShortcut) );
1433 thePopup->addAction( tr( "MEN_DELETE_VS" ), this, SLOT( onDeleteGUIState() ) );
1436 // "Delete reference" item should appear only for invalid references
1438 // isInvalidRefs will be true, if at least one of selected objects is invalid reference
1439 bool isInvalidRefs = false;
1441 _PTR(SObject) anObj;
1442 for( SALOME_ListIteratorOfListIO it( aList ); it.More() && !isInvalidRefs; it.Next() )
1444 if( it.Value()->hasEntry() )
1446 _PTR(SObject) aSObject = getStudy()->FindObjectID( it.Value()->getEntry() ), aRefObj = aSObject;
1447 while( aRefObj && aRefObj->ReferencedObject( anObj ) )
1450 if( aRefObj && aRefObj!=aSObject && QString( aRefObj->GetName().c_str() ).isEmpty() )
1451 isInvalidRefs = true;
1455 // Add "Delete reference" item to popup
1456 if ( isInvalidRefs )
1458 thePopup->addSeparator();
1459 thePopup->addAction( tr( "MEN_DELETE_INVALID_REFERENCE" ), this, SLOT( onDeleteInvalidReferences() ) );
1463 // "Activate module" item should appear only if it's necessary
1464 if ( aList.Extent() == 1 ) {
1466 mgr->selectedObjects( aList );
1468 Handle(SALOME_InteractiveObject) aIObj = aList.First();
1470 // add extra popup menu (defined in XML)
1471 if ( myExtActions.size() > 0 ) {
1472 // Use only first selected object
1473 _PTR(SObject) aSO = getStudy()->FindObjectID( aIObj->getEntry() );
1475 _PTR( GenericAttribute ) anAttr;
1476 std::string auid = "AttributeUserID";
1477 auid += Kernel_Utils::GetGUID(Kernel_Utils::ObjectdID);
1478 if ( aSO->FindAttribute( anAttr, auid ) ) {
1479 _PTR(AttributeUserID) aAttrID = anAttr;
1480 QString aId = aAttrID->Value().c_str();
1481 if ( myExtActions.contains( aId ) ) {
1482 thePopup->addAction(myExtActions[aId]);
1488 // check if item is a "GUI state" item (also a first level object)
1489 QString entry( aIObj->getEntry() );
1490 if ( !entry.startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) {
1491 QString aModuleName( aIObj->getComponentDataType() );
1492 QString aModuleTitle = moduleTitle( aModuleName );
1493 CAM_Module* currentModule = activeModule();
1494 if ( ( !currentModule || currentModule->moduleName() != aModuleTitle ) && !aModuleTitle.isEmpty() )
1495 thePopup->addAction( tr( "MEN_OPENWITH" ).arg( aModuleTitle ), this, SLOT( onOpenWith() ) );
1499 mgr->setSelectionCacheEnabled( cacheIsOn );
1502 /*!Update obect browser:
1503 1.if 'updateModels' true, update existing data models;
1504 2. update "non-existing" (not loaded yet) data models;
1505 3. update object browser if it exists */
1506 void SalomeApp_Application::updateObjectBrowser( const bool updateModels )
1508 // update "non-existing" (not loaded yet) data models
1509 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
1512 for ( _PTR(SComponentIterator) it ( getStudy()->NewComponentIterator() ); it->More(); it->Next() )
1514 _PTR(SComponent) aComponent ( it->Value() );
1516 #ifndef WITH_SALOMEDS_OBSERVER
1517 // with GUI observers this check is not needed anymore
1518 if ( aComponent->ComponentDataType() == study->getVisualComponentName().toLatin1().constData() )
1519 continue; // skip the magic "Interface Applicative" component
1521 if ( !objectBrowser() )
1522 getWindow( WT_ObjectBrowser );
1523 const bool isAutoUpdate = objectBrowser()->autoUpdate();
1524 objectBrowser()->setAutoUpdate( false );
1525 SalomeApp_DataModel::synchronize( aComponent, study );
1526 objectBrowser()->setAutoUpdate( isAutoUpdate );
1530 // create data objects that correspond to GUI state save points
1531 if ( study ) updateSavePointDataObjects( study );
1533 // update existing data models (already loaded SComponents)
1534 LightApp_Application::updateObjectBrowser( updateModels );
1537 /*!Display Catalog Genenerator dialog */
1538 void SalomeApp_Application::onCatalogGen()
1540 ToolsGUI_CatalogGeneratorDlg aDlg( desktop() );
1544 /*!Display Registry Display dialog */
1545 void SalomeApp_Application::onRegDisplay()
1547 CORBA::ORB_var anOrb = orb();
1548 ToolsGUI_RegWidget* regWnd = ToolsGUI_RegWidget::GetRegWidget( anOrb, desktop() );
1551 regWnd->activateWindow();
1554 /*!find original object by double click on item */
1555 void SalomeApp_Application::onDblClick( SUIT_DataObject* theObj )
1557 // Issue 21379: References are supported at LightApp_DataObject level
1558 LightApp_DataObject* obj = dynamic_cast<LightApp_DataObject*>( theObj );
1560 if( obj && obj->isReference() )
1562 QString entry = obj->refEntry();
1564 SUIT_DataOwnerPtrList aList;
1565 aList.append( new LightApp_DataOwner( entry ) );
1566 selectionMgr()->setSelected( aList, false );
1568 SUIT_DataBrowser* ob = objectBrowser();
1570 QModelIndexList aSelectedIndexes = ob->selectedIndexes();
1571 if ( !aSelectedIndexes.isEmpty() )
1572 ob->treeView()->scrollTo( aSelectedIndexes.first() );
1574 emit objectDoubleClicked( theObj );
1578 Creates new view manager
1579 \param type - type of view manager
1581 SUIT_ViewManager* SalomeApp_Application::newViewManager(const QString& type)
1583 return createViewManager(type);
1587 /*!Global utility function, returns selected GUI Save point object's ID */
1588 int getSelectedSavePoint( const LightApp_SelectionMgr* selMgr )
1590 SALOME_ListIO aList;
1591 selMgr->selectedObjects( aList );
1592 if( aList.Extent() > 0 ) {
1593 Handle(SALOME_InteractiveObject) aIObj = aList.First();
1594 QString entry( aIObj->getEntry() );
1595 QString startStr = QObject::tr( "SAVE_POINT_DEF_NAME" );
1596 if ( !entry.startsWith( startStr ) ) // it's a "GUI state" object
1598 bool ok; // conversion to integer is ok?
1599 int savePoint = entry.right( entry.length() - startStr.length() ).toInt( &ok );
1600 return ok ? savePoint : -1;
1605 /*!Called on Restore GUI State popup command*/
1606 void SalomeApp_Application::onRestoreGUIState()
1608 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1609 if ( savePoint == -1 )
1611 SalomeApp_VisualState( this ).restoreState( savePoint );
1614 /*!Called on Delete GUI State popup command*/
1615 void SalomeApp_Application::onDeleteGUIState()
1617 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1618 if ( savePoint == -1 )
1620 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1624 study->removeSavePoint( savePoint );
1625 updateSavePointDataObjects( study );
1628 /*!Called on New study operation*/
1629 void SalomeApp_Application::onStudyCreated( SUIT_Study* study )
1631 LightApp_Application::onStudyCreated( study );
1633 //#ifndef DISABLE_PYCONSOLE
1634 // desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
1635 // windowDock( getWindow( WT_ObjectBrowser ) ) );
1638 loadDockWindowsState();
1640 objectBrowserColumnsVisibility();
1643 /*!Called on Open study operation*/
1644 void SalomeApp_Application::onStudyOpened( SUIT_Study* study )
1646 LightApp_Application::onStudyOpened( study );
1648 //#ifndef DISABLE_PYCONSOLE
1649 // desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
1650 // windowDock( getWindow( WT_ObjectBrowser ) ) );
1653 loadDockWindowsState();
1655 objectBrowserColumnsVisibility();
1657 // temporary commented
1658 /*if ( objectBrowser() ) {
1659 updateSavePointDataObjects( dynamic_cast<SalomeApp_Study*>( study ) );
1660 objectBrowser()->updateTree( study->root() );
1664 /*! updateSavePointDataObjects: syncronize data objects that correspond to save points (gui states)*/
1665 void SalomeApp_Application::updateSavePointDataObjects( SalomeApp_Study* study )
1668 SUIT_DataBrowser* ob = objectBrowser();
1669 LightApp_SelectionMgr* selMgr = selectionMgr();
1671 if ( !study || !ob || !selMgr )
1674 // find GUI states root object
1675 SUIT_DataObject* guiRootObj = 0;
1677 study->root()->children( ch );
1678 DataObjectList::const_iterator it = ch.begin(), last = ch.end();
1679 for ( ; it != last ; ++it ) {
1680 if ( dynamic_cast<SalomeApp_SavePointRootObject*>( *it ) ) {
1685 std::vector<int> savePoints = study->getSavePoints();
1686 // case 1: no more save points but they existed in study's tree
1687 if ( savePoints.empty() && guiRootObj ) {
1688 //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state"
1689 // : set auto update to true for removing SalomeApp_SavePointRootObject from the SUIT_TreeModel
1690 const bool isAutoUpdate = ob->autoUpdate();
1691 selMgr->clearSelected();
1692 ob->setAutoUpdate(true);
1693 DataObjectList ch = guiRootObj->children();
1694 for( int i = 0; i < ch.size(); i++ )
1697 ob->setAutoUpdate(isAutoUpdate);
1700 // case 2: no more save points but root does not exist either
1701 if ( savePoints.empty() && !guiRootObj )
1703 // case 3: save points but no root for them - create it
1704 if ( !savePoints.empty() && !guiRootObj )
1705 guiRootObj = new SalomeApp_SavePointRootObject( study->root() );
1706 // case 4: everything already exists.. here may be a problem: we want "GUI states" root object
1707 // to be always the last one in the tree. Here we check - if it is not the last one - remove and
1709 if ( guiRootObj->nextBrother() ) {
1710 study->root()->removeChild(guiRootObj);
1711 study->root()->appendChild(guiRootObj);
1712 //study->root()->dump();
1715 // store data objects in a map id-to-DataObject
1716 QMap<int,SalomeApp_SavePointObject*> mapDO;
1718 guiRootObj->children( ch );
1719 for( it = ch.begin(), last = ch.end(); it != last ; ++it ) {
1720 SalomeApp_SavePointObject* dobj = dynamic_cast<SalomeApp_SavePointObject*>( *it );
1722 mapDO[dobj->getId()] = dobj;
1725 // iterate new save points. if DataObject with such ID not found in map - create DataObject
1726 // if in the map - remove it from map.
1727 for ( size_t i = 0; i < savePoints.size(); i++ )
1728 if ( !mapDO.contains( savePoints[i] ) )
1729 new SalomeApp_SavePointObject( guiRootObj, savePoints[i], study );
1731 mapDO.remove( savePoints[i] );
1733 // delete DataObjects that are still in the map -- their IDs were not found in data model
1734 if( mapDO.size() > 0) {
1735 //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state"
1736 // : set auto update to true for removing SalomeApp_SavePointObject from the SUIT_TreeModel
1737 selMgr->clearSelected();
1738 const bool isAutoUpdate = ob->autoUpdate();
1739 ob->setAutoUpdate(true);
1740 for ( QMap<int,SalomeApp_SavePointObject*>::Iterator it = mapDO.begin(); it != mapDO.end(); ++it )
1742 ob->setAutoUpdate(isAutoUpdate);
1746 /*! Check data object */
1747 bool SalomeApp_Application::checkDataObject(LightApp_DataObject* theObj)
1756 Opens other study into active Study. If Study is empty - creates it.
1757 \param theName - name of study
1759 bool SalomeApp_Application::useStudy( const QString& theName )
1762 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1765 res = aStudy->loadDocument( theName );
1766 updateDesktopTitle();
1767 updateCommandsStatus();
1771 /*! Show/hide object browser colums according to preferences */
1772 void SalomeApp_Application::objectBrowserColumnsVisibility()
1774 if ( objectBrowser() )
1775 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1777 bool shown = resourceMgr()->booleanValue( "ObjectBrowser", QString( "visibility_column_id_%1" ).arg( i-1 ), true );
1778 objectBrowser()->treeView()->setColumnHidden( i, !shown );
1782 #ifndef DISABLE_PYCONSOLE
1783 /*! Set SalomeApp_NoteBook pointer */
1784 void SalomeApp_Application::setNoteBook( SalomeApp_NoteBook* theNoteBook )
1786 myNoteBook = theNoteBook;
1789 /*! Return SalomeApp_NoteBook pointer */
1790 SalomeApp_NoteBook* SalomeApp_Application::getNoteBook() const
1797 * Define extra actions defined in module definition XML file.
1798 * Additional popup items sections can be defined by parameter "popupitems".
1799 * Supported attributes:
1800 * title - title of menu item,
1801 * attributelocalid - AttributeLocalId defined for selected data item where menu command has to be applied,
1802 * method - method which has to be called when menu item is selected
1804 * <section name="MODULENAME">
1805 * <parameter name="popupitems" value="menuitem1:menuitem2:..."/>
1807 * <section name="importmed">
1808 * <parameter name="title" value="My menu"/>
1809 * <parameter name="objectid" value="VISU.Result"/>
1810 * <parameter name="method" value="nameOfModuleMethod"/>
1813 void SalomeApp_Application::createExtraActions()
1815 myExtActions.clear();
1816 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
1818 QStringList aModules;
1819 modules(aModules, false);
1820 foreach(QString aModile, aModules) {
1821 QString aModName = moduleName(aModile);
1822 QString aSectionStr = resMgr->stringValue(aModName, "popupitems", QString());
1823 if (!aSectionStr.isNull()) {
1824 QStringList aSections = aSectionStr.split(':');
1825 foreach(QString aSection, aSections) {
1826 QString aTitle = resMgr->stringValue(aSection, "title", QString());
1827 QString aId = resMgr->stringValue(aSection, "objectid", QString());
1828 QString aSlot = resMgr->stringValue(aSection, "method", QString());
1829 if (aTitle.isEmpty() || aSlot.isEmpty() || aId.isEmpty())
1832 QString aModuleName = resMgr->stringValue(aSection, "module", QString());
1833 if (aModuleName.isNull())
1834 aModuleName = aModName;
1836 QAction* aAction = new QAction(aTitle, this);
1838 aData<<aModuleName<<aSlot;
1839 aAction->setData(aData);
1840 connect(aAction, SIGNAL(triggered()), this, SLOT(onExtAction()));
1841 myExtActions[aId] = aAction;
1848 * Called when extra action is selected
1850 void SalomeApp_Application::onExtAction()
1852 QAction* aAction = ::qobject_cast<QAction*>(sender());
1856 QVariant aData = aAction->data();
1857 QStringList aDataList = aData.value<QStringList>();
1858 if (aDataList.size() != 2)
1861 LightApp_SelectionMgr* aSelectionMgr = selectionMgr();
1862 SALOME_ListIO aListIO;
1863 aSelectionMgr->selectedObjects(aListIO);
1864 const Handle(SALOME_InteractiveObject)& anIO = aListIO.First();
1865 if (aListIO.Extent() < 1)
1867 if (!anIO->hasEntry())
1870 QString aEntry(anIO->getEntry());
1872 QApplication::setOverrideCursor( Qt::WaitCursor );
1873 QString aModuleTitle = moduleTitle(aDataList[0]);
1874 activateModule(aModuleTitle);
1875 QApplication::restoreOverrideCursor();
1877 QCoreApplication::processEvents();
1879 CAM_Module* aModule = activeModule();
1883 if (!QMetaObject::invokeMethod(aModule, qPrintable(aDataList[1]), Q_ARG(QString, aEntry)))
1884 printf("Error: Can't Invoke method %s\n", qPrintable(aDataList[1]));
1888 Checks that an object can be renamed.
1889 \param entry entry of the object
1890 \brief Return \c true if object can be renamed
1892 bool SalomeApp_Application::renameAllowed( const QString& entry) const
1894 return entry.startsWith( tr( "SAVE_POINT_DEF_NAME") );
1898 Rename object by entry.
1899 \param entry entry of the object
1900 \param name new name of the object
1901 \brief Return \c true if rename operation finished successfully, \c false otherwise.
1903 bool SalomeApp_Application::renameObject( const QString& entry, const QString& name )
1905 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1907 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1909 if(!aStudy || savePoint == -1)
1912 if ( !name.isNull() && !name.isEmpty() ) {
1913 aStudy->setNameOfSavePoint( savePoint, name );
1914 updateSavePointDataObjects( aStudy );
1916 //Mark study as modified
1923 #ifndef DISABLE_PYCONSOLE
1924 //============================================================================
1925 /*! Function : onUpdateStudy
1926 * Purpose : Slot to update the study.
1928 //============================================================================
1929 void SalomeApp_Application::onUpdateStudy()
1931 QApplication::setOverrideCursor( Qt::WaitCursor );
1933 if( !updateStudy() )
1934 SUIT_MessageBox::warning( desktop(), tr( "ERROR" ), tr( "ERR_UPDATE_STUDY_FAILED" ) );
1936 QApplication::restoreOverrideCursor();
1939 //============================================================================
1940 /*! Function : updateStudy
1941 * Purpose : Update study by dumping the study to Python script and loading it.
1942 * It is used to apply variable modifications done in NoteBook to created objects.
1944 //============================================================================
1945 bool SalomeApp_Application::updateStudy()
1947 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1948 if ( !study || !myNoteBook )
1951 myNoteBook->setIsDumpedStudySaved( study->isSaved() );
1952 myNoteBook->setDumpedStudyName( study->studyName() );
1954 // get unique temporary directory name
1955 QString aTmpDir = QString::fromStdString( SALOMEDS_Tool::GetTmpDir() );
1956 if( aTmpDir.isEmpty() )
1959 if( aTmpDir.right( 1 ).compare( QDir::separator() ) == 0 )
1960 aTmpDir.remove( aTmpDir.length() - 1, 1 );
1962 // dump study to the temporary directory
1963 QString aScriptName( "notebook" );
1964 bool toPublish = true;
1965 bool isMultiFile = false;
1966 bool toSaveGUI = true;
1969 _PTR(AttributeParameter) ap;
1970 _PTR(IParameters) ip = ClientFactory::getIParameters(ap);
1971 if(ip->isDumpPython()) ip->setDumpPython(); //Unset DumpPython flag.
1972 if ( toSaveGUI ) { //SRN: Store a visual state of the study at the save point for DumpStudy method
1973 ip->setDumpPython();
1974 savePoint = SalomeApp_VisualState( this ).storeState(); //SRN: create a temporary save point
1976 bool ok = getStudy()->DumpStudy( aTmpDir.toStdString(), aScriptName.toStdString(), toPublish, isMultiFile );
1978 study->removeSavePoint(savePoint); //SRN: remove the created temporary save point.
1981 myNoteBook->setDumpedStudyScript( aTmpDir + QDir::separator() + aScriptName + ".py" );
1985 QList<SUIT_Application*> aList = SUIT_Session::session()->applications();
1986 int anIndex = aList.indexOf( this );
1988 // Disconnect dialog from application desktop in case if:
1989 // 1) Application is not the first application in the session
1990 // 2) Application is the first application in session but not the only.
1991 bool changeDesktop = ((anIndex > 0) || (anIndex == 0 && aList.count() > 1));
1992 if( changeDesktop ) {
1994 SalomeApp_Application* app = this;
1995 if( anIndex > 0 && anIndex < aList.count() )
1996 app = dynamic_cast<SalomeApp_Application*>( aList[ anIndex - 1 ] );
1997 else if(anIndex == 0 && aList.count() > 1)
1998 app = dynamic_cast<SalomeApp_Application*>( aList[ 1 ] );
2003 // creation a new study and restoring will be done in another application
2004 connect( this, SIGNAL( dumpedStudyClosed( const QString&, const QString&, bool ) ),
2005 app, SLOT( onRestoreStudy( const QString&, const QString&, bool ) ), Qt::UniqueConnection );
2008 QString aDumpScript = myNoteBook->getDumpedStudyScript();
2009 QString aStudyName = myNoteBook->getDumpedStudyName();
2010 bool isStudySaved = myNoteBook->isDumpedStudySaved();
2011 // clear a study (delete all objects)
2012 onCloseDoc( false );
2014 if( !changeDesktop ) {
2015 ok = onRestoreStudy( aDumpScript,
2024 //============================================================================
2025 /*! Function : onRestoreStudy
2026 * Purpose : Load the dumped study from Python script
2028 //============================================================================
2029 bool SalomeApp_Application::onRestoreStudy( const QString& theDumpScript,
2030 const QString& theStudyName,
2031 bool theIsStudySaved )
2035 // create a new study
2038 // get active application
2039 SalomeApp_Application* app = dynamic_cast<SalomeApp_Application*>( SUIT_Session::session()->activeApplication() );
2041 // load study from the temporary directory
2043 QString command = QString( "exec(open(\"%1\" ,\"rb\").read())" ).arg( theDumpScript );
2045 #ifndef DISABLE_PYCONSOLE
2046 PyConsole_Console* pyConsole = app->pythonConsole();
2048 pyConsole->execAndWait( command );
2051 // remove temporary directory
2052 QFileInfo aScriptInfo = QFileInfo( theDumpScript );
2053 QString aStudyName = aScriptInfo.baseName();
2054 QDir aDir = aScriptInfo.absoluteDir();
2055 QStringList aFiles = aDir.entryList( QStringList( "*.py*" ) );
2056 for( QStringList::iterator it = aFiles.begin(), itEnd = aFiles.end(); it != itEnd; ++it )
2057 ok = aDir.remove( *it ) && ok;
2059 ok = aDir.rmdir( aDir.absolutePath() );
2061 if( SalomeApp_Study* newStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() ) )
2063 #ifndef DISABLE_PYCONSOLE
2064 if ( app->getNoteBook() )
2065 app->getNoteBook()->Init();
2066 newStudy->updateFromNotebook(theStudyName, theIsStudySaved);
2067 newStudy->Modified();
2068 updateDesktopTitle();
2079 Close the Application
2081 void SalomeApp_Application::afterCloseDoc()
2083 #ifndef DISABLE_PYCONSOLE
2084 // emit signal to restore study from Python script
2086 emit dumpedStudyClosed( myNoteBook->getDumpedStudyScript(),
2087 myNoteBook->getDumpedStudyName(),
2088 myNoteBook->isDumpedStudySaved() );
2091 LightApp_Application::afterCloseDoc();
2095 Asks to close existing document.
2097 bool SalomeApp_Application::checkExistingDoc()
2099 return LightApp_Application::checkExistingDoc();
2103 #ifndef DISABLE_PYCONSOLE
2105 PyConsole_Interp* SalomeApp_Application::createPyInterp()
2107 return new SalomeApp_PyInterp;
2110 #endif // DISABLE_PYCONSOLE