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("[\"]$") );
285 args << QString("\"%1\"").arg(arg);
287 if (args.count() == 1)
290 script.remove( QRegExp("^python.*[\\s]+") );
291 QString command = QString( "exec(open(\"%1\", \"rb\").read(), args=(%2))" ).arg(script).arg(args.join(","));
292 pyConsole->exec(command);
294 } // end for loop on pyfiles QStringList
300 LightApp_Application::start();
301 SALOME_EventFilter::Init();
306 void SalomeApp_Application::createActions()
308 LightApp_Application::createActions();
310 SUIT_Desktop* desk = desktop();
313 // "Save GUI State" command is moved to VISU module
314 // createAction( SaveGUIStateId, tr( "TOT_DESK_FILE_SAVE_GUI_STATE" ), QIcon(),
315 // tr( "MEN_DESK_FILE_SAVE_GUI_STATE" ), tr( "PRP_DESK_FILE_SAVE_GUI_STATE" ),
316 // 0, desk, false, this, SLOT( onSaveGUIState() ) );
319 createAction( DumpStudyId, tr( "TOT_DESK_FILE_DUMP_STUDY" ), QIcon(),
320 tr( "MEN_DESK_FILE_DUMP_STUDY" ), tr( "PRP_DESK_FILE_DUMP_STUDY" ),
321 Qt::CTRL+Qt::Key_D, desk, false, this, SLOT( onDumpStudy() ) );
324 createAction( LoadScriptId, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), QIcon(),
325 tr( "MEN_DESK_FILE_LOAD_SCRIPT" ), tr( "PRP_DESK_FILE_LOAD_SCRIPT" ),
326 Qt::CTRL+Qt::Key_T, desk, false, this, SLOT( onLoadScript() ) );
329 createAction( PropertiesId, tr( "TOT_DESK_PROPERTIES" ), QIcon(),
330 tr( "MEN_DESK_PROPERTIES" ), tr( "PRP_DESK_PROPERTIES" ),
331 0, desk, false, this, SLOT( onProperties() ) );
333 //! Catalog Generator
334 createAction( CatalogGenId, tr( "TOT_DESK_CATALOG_GENERATOR" ), QIcon(),
335 tr( "MEN_DESK_CATALOG_GENERATOR" ), tr( "PRP_DESK_CATALOG_GENERATOR" ),
336 Qt::ALT+Qt::SHIFT+Qt::Key_G, desk, false, this, SLOT( onCatalogGen() ) );
339 createAction( RegDisplayId, tr( "TOT_DESK_REGISTRY_DISPLAY" ), QIcon(),
340 tr( "MEN_DESK_REGISTRY_DISPLAY" ), tr( "PRP_DESK_REGISTRY_DISPLAY" ),
341 /*Qt::SHIFT+Qt::Key_D*/0, desk, false, this, SLOT( onRegDisplay() ) );
343 createAction( ConnectId, tr( "TOT_DESK_CONNECT_STUDY" ), QIcon(),
344 tr( "MEN_DESK_CONNECT" ), tr( "PRP_DESK_CONNECT" ),
345 Qt::CTRL+Qt::Key_L, desk, false, this, SLOT( onLoadDoc() ) );
346 //no need at this action for mono-study application because study is always exists
347 action( ConnectId )->setVisible( false );
349 createAction( DisconnectId, tr( "TOT_DESK_DISCONNECT_STUDY" ), QIcon(),
350 tr( "MEN_DESK_DISCONNECT" ), tr( "PRP_DESK_DISCONNECT" ),
351 Qt::CTRL+Qt::Key_U, desk, false, this, SLOT( onUnloadDoc() ) );
352 //no need at this action for mono-study application because study is always exists
353 action( DisconnectId )->setVisible( false );
356 int fileMenu = createMenu( tr( "MEN_DESK_FILE" ), -1 );
358 // "Save GUI State" command is renamed to "Save VISU State" and
359 // creation of menu item is moved to VISU
360 // createMenu( SaveGUIStateId, fileMenu, 10, -1 );
362 createMenu( ConnectId, fileMenu, 5 );
363 createMenu( DisconnectId, fileMenu, 5 );
364 createMenu( separator(), fileMenu, -1, 5 );
366 createMenu( DumpStudyId, fileMenu, 10, -1 );
367 createMenu( LoadScriptId, fileMenu, 10, -1 );
368 createMenu( separator(), fileMenu, -1, 10, -1 );
369 createMenu( PropertiesId, fileMenu, 10, -1 );
370 createMenu( separator(), fileMenu, -1, 10, -1 );
372 int toolsMenu = createMenu( tr( "MEN_DESK_TOOLS" ), -1, MenuToolsId, 50 );
373 createMenu( CatalogGenId, toolsMenu, 10, -1 );
374 createMenu( RegDisplayId, toolsMenu, 10, -1 );
375 createMenu( separator(), toolsMenu, -1, 15, -1 );
377 createExtraActions();
379 #ifndef DISABLE_PYCONSOLE
380 #ifndef DISABLE_SALOMEOBJECT
381 // import Python module that manages SALOME plugins
383 PyLockWrapper lck; // acquire GIL
384 PyObjWrapper pluginsmanager = PyImport_ImportModule((char*)"salome_pluginsmanager");
385 PyObjWrapper res = PyObject_CallMethod( pluginsmanager, (char*)"initialize", (char*)"isss",0,"salome",tr("MEN_DESK_TOOLS").toUtf8().data(),tr("MEN_DESK_PLUGINS").toUtf8().data());
389 // end of SALOME plugins loading
396 \brief Close application.
398 void SalomeApp_Application::onExit()
400 //MessageLocker ml( myToIgnoreMessages );
402 bool killServers = false;
405 if ( exitConfirmation() ) {
406 SalomeApp_ExitDlg dlg( desktop() );
407 result = dlg.exec() == QDialog::Accepted;
408 killServers = dlg.isServersShutdown();
412 if ( !killServers ) myIsCloseFromExit = true;
413 SUIT_Session::session()->closeSession( SUIT_Session::ASK, killServers );
414 if ( SUIT_Session::session()->applications().count() > 0 ) myIsCloseFromExit = false;
418 /*!SLOT. Create a document.*/
419 void SalomeApp_Application::onNewDoc()
421 MessageLocker ml( myToIgnoreMessages );
423 LightApp_Application::onNewDoc();
426 /*!SLOT. Load document.*/
427 void SalomeApp_Application::onLoadDoc()
429 MessageLocker ml( myToIgnoreMessages );
433 // rnv: According to the single-study approach on the server side
434 // can be only one study. So if it is exists connect to them,
435 // overwise show warning message: "No active study on the server"
438 SUIT_Session* aSession = SUIT_Session::session();
439 QList<SUIT_Application*> aAppList = aSession->applications();
441 QStringList unloadedStudies;
443 for ( unsigned int ind = 0; ind < List.size(); ind++ ) {
444 studyName = List[ind].c_str();
445 // Add to list only unloaded studies
446 bool isAlreadyOpen = false;
447 QListIterator<SUIT_Application*> it( aAppList );
448 while ( it.hasNext() && !isAlreadyOpen ) {
449 SUIT_Application* aApp = it.next();
450 if( !aApp || !aApp->activeStudy() )
452 if ( aApp->activeStudy()->studyName() == studyName )
453 isAlreadyOpen = true;
456 if ( !isAlreadyOpen )
457 unloadedStudies << studyName;
459 studyName = SalomeApp_LoadStudiesDlg::selectStudy( desktop(), unloadedStudies );
460 if ( studyName.isEmpty() )
465 SUIT_MessageBox::warning( desktop(),
466 QObject::tr("WRN_WARNING"),
467 QObject::tr("WRN_NO_STUDY_ON SERV") );
471 studyName = activeStudy()->studyName();
474 // this code replaces marker of windows drive and path become invalid therefore
475 // defines placed there
476 studyName.replace( QRegExp(":"), "/" );
479 if ( onLoadDoc( studyName ) ) {
481 updateViewManagers();
482 updateObjectBrowser( true );
486 /*!SLOT. Unload document.*/
487 void SalomeApp_Application::onUnloadDoc( bool ask )
490 activeStudy()->abortAllOperations();
491 if ( activeStudy()->isModified() ) {
492 QString docName = activeStudy()->studyName().trimmed();
493 int answer = SUIT_MessageBox::question( desktop(), tr( "DISCONNECT_CAPTION" ),
494 tr( "DISCONNECT_DESCRIPTION" ),
495 tr( "DISCONNECT_SAVE" ),
496 tr( "DISCONNECT_WO_SAVE" ),
497 tr( "APPCLOSE_CANCEL" ), 0 );
498 if ( answer == 0 ) { // save before unload
499 if ( activeStudy()->isSaved() )
501 else if ( !onSaveAsDoc() )
504 else if ( answer == 2 ) // Cancel
508 closeActiveDoc( false );
511 /*!SLOT. Create new study and load script*/
512 void SalomeApp_Application::onNewWithScript()
514 QStringList filtersList;
515 filtersList.append(tr("PYTHON_FILES_FILTER"));
516 filtersList.append(tr("ALL_FILES_FILTER"));
518 QString anInitialPath = "";
519 if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
520 anInitialPath = QDir::currentPath();
522 QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true );
524 if ( !aFile.isEmpty() )
528 QString command = QString("exec(open(\"%1\", \"rb\").read())").arg(aFile);
530 #ifndef DISABLE_PYCONSOLE
531 PyConsole_Console* pyConsole = pythonConsole();
534 pyConsole->exec( command );
540 /*!SLOT. Load document with \a aName.*/
541 bool SalomeApp_Application::onLoadDoc( const QString& aName )
543 if ( !LightApp_Application::closeDoc() )
547 if ( !activeStudy() ) {
548 // if no study - load in current desktop
549 res = useStudy( aName );
552 // if study exists - load in new desktop. Check: is the same file is loaded?
553 SUIT_Session* aSession = SUIT_Session::session();
554 QList<SUIT_Application*> aAppList = aSession->applications();
555 bool isAlreadyOpen = false;
556 SalomeApp_Application* aApp = 0;
557 for ( QList<SUIT_Application*>::iterator it = aAppList.begin();
558 it != aAppList.end() && !isAlreadyOpen; ++it ) {
559 aApp = dynamic_cast<SalomeApp_Application*>( *it );
560 if ( aApp && aApp->activeStudy()->studyName() == aName )
561 isAlreadyOpen = true;
563 if ( !isAlreadyOpen ) {
564 aApp = dynamic_cast<SalomeApp_Application*>( startApplication( 0, 0 ) );
566 res = aApp->useStudy( aName );
569 aApp->desktop()->activateWindow();
576 /*!SLOT. Parse message for desktop.*/
577 void SalomeApp_Application::onDesktopMessage( const QString& message )
579 if ( myToIgnoreMessages )
580 return; // a message from SALOMEDS is caused by GUI action
582 MessageLocker ml( myToIgnoreMessages );
584 if (message.indexOf("studyCreated") == 0) {
585 if (!activeStudy()) {
587 updateCommandsStatus();
590 if (message.indexOf("studyCleared") == 0) {
591 // Disconnect GUI from active study, because it was closed on DS side.
593 closeActiveDoc( false );
594 // Disable 'Connect' action
595 QAction* a = action( ConnectId );
597 a->setEnabled( false );
600 else if ( message.toLower() == "connect_to_study" ) {
602 useStudy( activeStudy()->studyName() );
604 if (message.indexOf("studyNameChanged") == 0) {
605 updateDesktopTitle();
607 LightApp_Application::onDesktopMessage( message );
610 /*!On module activation action.*/
611 void SalomeApp_Application::onModuleActivation( const QString& modName )
613 if (!activeStudy() && !modName.isEmpty())
616 LightApp_Application::onModuleActivation( modName );
619 /*!SLOT. Copy objects to study maneger from selection maneger..*/
620 void SalomeApp_Application::onCopy()
622 LightApp_Application::onCopy();
625 LightApp_SelectionMgr* mgr = selectionMgr();
626 mgr->selectedObjects(list);
628 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
629 if(study == NULL) return;
631 _PTR(Study) stdDS = getStudy();
634 SALOME_ListIteratorOfListIO it( list );
637 _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry());
642 onSelectionChanged();
650 /*!SLOT. Paste objects to study maneger from selection manager.*/
651 void SalomeApp_Application::onPaste()
653 LightApp_Application::onPaste();
656 LightApp_SelectionMgr* mgr = selectionMgr();
657 mgr->selectedObjects(list);
659 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
660 if(study == NULL) return;
662 _PTR(Study) stdDS = getStudy();
665 if ( stdDS->GetProperties()->IsLocked() ) {
666 SUIT_MessageBox::warning( desktop(),
667 QObject::tr("WRN_WARNING"),
668 QObject::tr("WRN_STUDY_LOCKED") );
672 SALOME_ListIteratorOfListIO it( list );
675 _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry());
680 updateObjectBrowser( true );
681 updateActions(); //SRN: BugID IPAL9377, case 3
689 /*!Check the application on closing.
690 * \retval true if possible, else false
692 bool SalomeApp_Application::isPossibleToClose( bool& closePermanently )
694 return LightApp_Application::isPossibleToClose( closePermanently );
697 /*! Check if the study is locked */
698 void SalomeApp_Application::onCloseDoc( bool ask )
700 if(getStudy()->IsStudyLocked()) {
701 if ( SUIT_MessageBox::question( desktop(),
702 QObject::tr( "WRN_WARNING" ),
703 QObject::tr( "CLOSE_LOCKED_STUDY" ),
704 SUIT_MessageBox::Yes | SUIT_MessageBox::No,
705 SUIT_MessageBox::No) == SUIT_MessageBox::No ) return;
708 MessageLocker ml( myToIgnoreMessages );
710 LightApp_Application::onCloseDoc( ask );
712 // reinitialize study to have empty data
713 //getStudy()->Init();
716 /*!SLOT. Reload document from the file.*/
717 bool SalomeApp_Application::onReopenDoc()
719 MessageLocker ml( myToIgnoreMessages );
721 return LightApp_Application::onReopenDoc();
725 /*!SLOT. Load document.*/
726 void SalomeApp_Application::onOpenDoc()
728 MessageLocker ml( myToIgnoreMessages );
730 LightApp_Application::onOpenDoc();
733 /*!SLOT. Load document.*/
734 bool SalomeApp_Application::onOpenDoc(const QString& name)
736 MessageLocker ml( myToIgnoreMessages );
738 return LightApp_Application::onOpenDoc(name);
741 /*!Sets enable or disable some actions on selection changed.*/
742 void SalomeApp_Application::onSelectionChanged()
745 LightApp_SelectionMgr* mgr = selectionMgr();
746 mgr->selectedObjects(list);
748 bool canCopy = false;
749 bool canPaste = false;
751 LightApp_Module* m = dynamic_cast<LightApp_Module*>( activeModule() );
754 canCopy = m->canCopy();
755 canPaste = m->canPaste();
758 SALOME_ListIteratorOfListIO it ( list );
760 if (it.More() && list.Extent() == 1) {
761 _PTR(SObject) so = getStudy()->FindObjectID(it.Value()->getEntry());
764 canCopy = canCopy || getStudy()->CanCopy(so);
765 canPaste = canPaste || getStudy()->CanPaste(so);
769 action(EditCopyId)->setEnabled(canCopy);
770 action(EditPasteId)->setEnabled(canPaste);
773 /*!Delete references.*/
774 void SalomeApp_Application::onDeleteInvalidReferences()
777 LightApp_SelectionMgr* mgr = selectionMgr();
778 mgr->selectedObjects( aList, QString(), false );
780 if( aList.IsEmpty() )
783 _PTR(Study) aStudyDS = getStudy();
784 _PTR(StudyBuilder) aStudyBuilder = aStudyDS->NewBuilder();
787 for( SALOME_ListIteratorOfListIO it( aList ); it.More(); it.Next() )
788 if ( it.Value()->hasEntry() )
790 _PTR(SObject) aSObject = aStudyDS->FindObjectID( it.Value()->getEntry() ), aRefObj = aSObject;
791 while( aRefObj && aRefObj->ReferencedObject( anObj ) )
794 if( aRefObj && aRefObj!=aSObject && QString( aRefObj->GetName().c_str() ).isEmpty() )
795 aStudyBuilder->RemoveReference( aSObject );
797 updateObjectBrowser();
801 void SalomeApp_Application::onOpenWith()
803 QApplication::setOverrideCursor( Qt::WaitCursor );
805 LightApp_SelectionMgr* mgr = selectionMgr();
806 mgr->selectedObjects(aList);
807 if (aList.Extent() != 1)
809 QApplication::restoreOverrideCursor();
812 Handle(SALOME_InteractiveObject) aIObj = aList.First();
813 QString aModuleName(aIObj->getComponentDataType());
814 QString aModuleTitle = moduleTitle(aModuleName);
815 activateModule(aModuleTitle);
816 QApplication::restoreOverrideCursor();
822 SUIT_Study* SalomeApp_Application::createNewStudy()
824 SalomeApp_Study* aStudy = new SalomeApp_Study( this );
826 // Set up processing of major study-related events
827 connect( aStudy, SIGNAL( created( SUIT_Study* ) ), this, SLOT( onStudyCreated( SUIT_Study* ) ) );
828 connect( aStudy, SIGNAL( opened ( SUIT_Study* ) ), this, SLOT( onStudyOpened ( SUIT_Study* ) ) );
829 connect( aStudy, SIGNAL( saved ( SUIT_Study* ) ), this, SLOT( onStudySaved ( SUIT_Study* ) ) );
830 connect( aStudy, SIGNAL( closed ( SUIT_Study* ) ), this, SLOT( onStudyClosed ( SUIT_Study* ) ) );
832 #ifndef DISABLE_PYCONSOLE
833 //to receive signal in application that NoteBook's variable was modified
834 connect( aStudy, SIGNAL(notebookVarUpdated(QString)),
835 this, SIGNAL(notebookVarUpdated(QString)) );
844 Enable/Disable menu items and toolbar buttons. Rebuild menu
846 void SalomeApp_Application::updateCommandsStatus()
848 LightApp_Application::updateCommandsStatus();
851 QAction* a = action( DumpStudyId );
853 a->setEnabled( activeStudy() );
855 #ifndef DISABLE_PYCONSOLE
857 a = action( LoadScriptId );
859 a->setEnabled( pythonConsole() );
863 a = action( PropertiesId );
865 a->setEnabled( activeStudy() );
867 // Save GUI state menu
868 a = action( SaveGUIStateId );
870 a->setEnabled( activeStudy() );
872 // Connect study menu
873 a = action( ConnectId );
875 a->setEnabled( !activeStudy() );
877 // Disconnect study menu
878 a = action( DisconnectId );
880 a->setEnabled( activeStudy() );
882 // update state of Copy/Paste menu items
883 onSelectionChanged();
887 \class DumpStudyFileDlg
888 Private class used in Dump Study operation. Consists 2 check boxes:
889 "Publish in study" and "Save GUI parameters"
891 class DumpStudyFileDlg : public SUIT_FileDlg
894 DumpStudyFileDlg( QWidget* parent ) : SUIT_FileDlg( parent, false, true, true )
896 QGridLayout* grid = ::qobject_cast<QGridLayout*>( layout() );
899 QWidget *hB = new QWidget( this );
900 myPublishChk = new QCheckBox( tr("PUBLISH_IN_STUDY") );
901 myMultiFileChk = new QCheckBox( tr("MULTI_FILE_DUMP") );
902 mySaveGUIChk = new QCheckBox( tr("SAVE_GUI_STATE") );
904 QHBoxLayout *layout = new QHBoxLayout;
905 layout->addWidget(myPublishChk);
906 layout->addWidget(myMultiFileChk);
907 layout->addWidget(mySaveGUIChk);
908 hB->setLayout(layout);
910 QPushButton* pb = new QPushButton(this);
912 int row = grid->rowCount();
913 grid->addWidget( new QLabel("", this), row, 0 );
914 grid->addWidget( hB, row, 1, 1, 3 );
915 grid->addWidget( pb, row, 5 );
920 QCheckBox* myPublishChk;
921 QCheckBox* myMultiFileChk;
922 QCheckBox* mySaveGUIChk;
925 /*!Private SLOT. On dump study.*/
926 void SalomeApp_Application::onDumpStudy( )
928 SalomeApp_Study* appStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
929 if ( !appStudy ) return;
931 QStringList aFilters;
932 aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
934 bool anIsPublish = true;
935 bool anIsMultiFile = false;
936 bool anIsSaveGUI = true;
938 if ( SUIT_ResourceMgr* aResourceMgr = resourceMgr() ) {
939 anIsPublish = aResourceMgr->booleanValue( "Study", "pydump_publish", anIsPublish );
940 anIsMultiFile = aResourceMgr->booleanValue( "Study", "multi_file_dump", anIsMultiFile );
941 anIsSaveGUI = aResourceMgr->booleanValue( "Study", "pydump_save_gui", anIsSaveGUI );
944 DumpStudyFileDlg fd( desktop() );
945 fd.setValidator( new LightApp_PyFileValidator( &fd ) );
946 fd.setWindowTitle( tr( "TOT_DESK_FILE_DUMP_STUDY" ) );
947 fd.setNameFilters( aFilters );
948 fd.myPublishChk->setChecked( anIsPublish );
949 fd.myMultiFileChk->setChecked( anIsMultiFile );
950 fd.mySaveGUIChk->setChecked( anIsSaveGUI );
951 if ( fd.exec() == QDialog::Accepted )
953 QString aFileName = fd.selectedFile();
955 bool toPublish = fd.myPublishChk->isChecked();
956 bool isMultiFile = fd.myMultiFileChk->isChecked();
957 bool toSaveGUI = fd.mySaveGUIChk->isChecked();
959 if ( !aFileName.isEmpty() ) {
960 QFileInfo aFileInfo(aFileName);
961 if( aFileInfo.isDir() ) // IPAL19257
964 // Issue 21377 - dump study implementation moved to SalomeApp_Study class
967 SUIT_OverrideCursor wc;
968 res = appStudy->dump( aFileName, toPublish, isMultiFile, toSaveGUI );
971 SUIT_MessageBox::warning( desktop(),
972 QObject::tr("WRN_WARNING"),
973 tr("WRN_DUMP_STUDY_FAILED") );
978 /*!Private SLOT. On load script.*/
979 void SalomeApp_Application::onLoadScript( )
981 if ( getStudy()->GetProperties()->IsLocked() ) {
982 SUIT_MessageBox::warning( desktop(),
983 QObject::tr("WRN_WARNING"),
984 QObject::tr("WRN_STUDY_LOCKED") );
988 QStringList filtersList;
989 filtersList.append(tr("PYTHON_FILES_FILTER"));
990 filtersList.append(tr("ALL_FILES_FILTER"));
992 QString anInitialPath = "";
993 if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
994 anInitialPath = QDir::currentPath();
996 QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true );
998 if ( !aFile.isEmpty() )
1001 QString command = QString("exec(open(\"%1\", \"rb\").read())").arg(aFile);
1003 #ifndef DISABLE_PYCONSOLE
1004 PyConsole_Console* pyConsole = pythonConsole();
1007 pyConsole->exec( command );
1012 /*!Private SLOT. On save GUI state.*/
1013 void SalomeApp_Application::onSaveGUIState()
1015 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1017 SalomeApp_VisualState( this ).storeState();
1018 updateSavePointDataObjects( study );
1019 updateObjectBrowser();
1024 /*!Public SLOT. Performs some actions when dockable windows are triggered.*/
1025 void SalomeApp_Application::onDockWindowVisibilityChanged( bool theIsVisible )
1027 LightApp_Application::onDockWindowVisibilityChanged( theIsVisible );
1028 QAction* send = ::qobject_cast<QAction*>( sender() );
1031 QString aWinName = send->data().toString();
1032 if ( theIsVisible && aWinName == "objectBrowser" )
1033 objectBrowserColumnsVisibility();
1037 QWidget* SalomeApp_Application::createWindow( const int flag )
1040 #ifndef DISABLE_PYCONSOLE
1041 if ( flag != WT_PyConsole ) wid = LightApp_Application::createWindow(flag);
1043 wid = LightApp_Application::createWindow(flag);
1046 SUIT_ResourceMgr* resMgr = resourceMgr();
1048 if ( flag == WT_ObjectBrowser )
1050 SUIT_DataBrowser* ob = qobject_cast<SUIT_DataBrowser*>( wid );
1052 // temporary commented
1053 //ob->setUpdater( new SalomeApp_Updater() );
1055 #ifdef WITH_SALOMEDS_OBSERVER
1056 //do not activate the automatic update of Qt tree through signal/slot
1057 ob->setAutoUpdate(false);
1058 //activate update of modified objects only
1059 ob->setUpdateModified(true);
1062 connect( ob, SIGNAL( doubleClicked( SUIT_DataObject* ) ), this, SLOT( onDblClick( SUIT_DataObject* ) ) );
1065 ValueCol = QObject::tr( "VALUE_COLUMN" ),
1066 IORCol = QObject::tr( "IOR_COLUMN" ),
1067 RefCol = QObject::tr( "REFENTRY_COLUMN" ),
1068 EntryCol = QObject::tr( "ENTRY_COLUMN" );
1070 SUIT_AbstractModel* treeModel = dynamic_cast<SUIT_AbstractModel*>( ob->model() );
1071 treeModel->registerColumn( 0, EntryCol, SalomeApp_DataObject::EntryId );
1072 treeModel->registerColumn( 0, ValueCol, SalomeApp_DataObject::ValueId );
1073 treeModel->registerColumn( 0, IORCol, SalomeApp_DataObject::IORId );
1074 treeModel->registerColumn( 0, RefCol, SalomeApp_DataObject::RefEntryId );
1075 treeModel->setAppropriate( EntryCol, Qtx::Toggled );
1076 treeModel->setAppropriate( ValueCol, Qtx::Toggled );
1077 treeModel->setAppropriate( IORCol, Qtx::Toggled );
1078 treeModel->setAppropriate( RefCol, Qtx::Toggled );
1080 bool autoSize = resMgr->booleanValue( "ObjectBrowser", "auto_size", false );
1081 bool autoSizeFirst = resMgr->booleanValue( "ObjectBrowser", "auto_size_first", true );
1082 bool resizeOnExpandItem = resMgr->booleanValue( "ObjectBrowser", "resize_on_expand_item", true );
1084 ob->setAutoSizeFirstColumn(autoSizeFirst);
1085 ob->setAutoSizeColumns(autoSize);
1086 ob->setResizeOnExpandItem(resizeOnExpandItem);
1087 ob->setProperty( "shortcut", QKeySequence( "Alt+Shift+O" ) );
1089 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1091 bool shown = resourceMgr()->booleanValue( "ObjectBrowser", QString( "visibility_column_id_%1" ).arg( i-1 ), true );
1092 ob->treeView()->setColumnHidden( i, !shown );
1095 // temporary commented
1097 for ( int i = SalomeApp_DataObject::ValueIdx; i <= SalomeApp_DataObject::RefEntryIdx; i++ )
1099 ob->addColumn( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i ) ), i );
1100 ob->setColumnShown( i, resMgr->booleanValue( "ObjectBrowser",
1101 QString().sprintf( "visibility_column_%d", i ), true ) );
1105 // temporary commented
1107 ob->setWidthMode( autoSize ? QListView::Maximum : QListView::Manual );
1108 ob->listView()->setColumnWidthMode( 0, autoSizeFirst ? QListView::Maximum : QListView::Manual );
1109 ob->resize( desktop()->width()/3, ob->height() );
1113 #ifndef DISABLE_PYCONSOLE
1114 else if ( flag == WT_PyConsole )
1116 PyConsole_Console* pyCons = new PyConsole_Console( desktop(), new LightApp_PyEditor( getPyInterp() ) );
1117 pyCons->setObjectName( "pythonConsole" );
1118 pyCons->setWindowTitle( tr( "PYTHON_CONSOLE" ) );
1119 pyCons->setFont(resourceMgr()->fontValue( "PyConsole", "font" ));
1120 pyCons->setIsShowBanner(resourceMgr()->booleanValue( "PyConsole", "show_banner", true ));
1121 pyCons->setAutoCompletion( resMgr->booleanValue( "PyConsole", "auto_completion", true ) );
1122 pyCons->setProperty( "shortcut", QKeySequence( "Alt+Shift+P" ) );
1125 else if ( flag == WT_NoteBook )
1127 setNoteBook( new SalomeApp_NoteBook( desktop() ) );
1128 //to receive signal in NoteBook that it's variable was modified
1129 connect( this, SIGNAL( notebookVarUpdated( QString ) ),
1130 getNoteBook(), SLOT( onVarUpdate( QString ) ) );
1132 wid = getNoteBook();
1133 wid->setObjectName( "noteBook" );
1139 /*!Create preferences.*/
1140 void SalomeApp_Application::createPreferences( LightApp_Preferences* pref )
1142 LightApp_Application::createPreferences(pref);
1147 int salomeCat = pref->addPreference( tr( "PREF_CATEGORY_SALOME" ) );
1148 int obTab = pref->addPreference( tr( "PREF_TAB_OBJBROWSER" ), salomeCat );
1149 int defCols = pref->addPreference( tr( "PREF_GROUP_DEF_COLUMNS" ), obTab );
1150 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1152 pref->addPreference( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i-SalomeApp_DataObject::EntryId ).toLatin1() ), defCols,
1153 LightApp_Preferences::Bool, "ObjectBrowser", QString().sprintf( "visibility_column_id_%d", i-1 ) );
1155 pref->setItemProperty( "orientation", Qt::Vertical, defCols );
1157 // adding preference to LightApp_Application handled preferences.. a bit of hacking with resources..
1158 int genTab = pref->addPreference( LightApp_Application::tr( "PREF_TAB_GENERAL" ), salomeCat );
1159 int studyGroup = pref->addPreference( LightApp_Application::tr( "PREF_GROUP_STUDY" ), genTab );
1160 pref->addPreference( tr( "PREF_STORE_VISUAL_STATE" ), studyGroup, LightApp_Preferences::Bool, "Study", "store_visual_state" );
1161 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1162 pref->addPreference( tr( "PREF_PYDUMP_PUBLISH" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_publish" );
1163 pref->addPreference( tr( "PREF_PYDUMP_MULTI_FILE" ), studyGroup, LightApp_Preferences::Bool, "Study", "multi_file_dump" );
1164 pref->addPreference( tr( "PREF_PYDUMP_SAVE_GUI" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_save_gui" );
1165 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1166 pref->addPreference( "", studyGroup, LightApp_Preferences::Space );
1169 /*!Update desktop title.*/
1170 void SalomeApp_Application::updateDesktopTitle() {
1171 QString aTitle = applicationName();
1172 QString aVer = applicationVersion();
1173 if ( !aVer.isEmpty() )
1174 aTitle += QString( " " ) + aVer;
1176 if ( activeStudy() )
1178 QString sName = SUIT_Tools::file( activeStudy()->studyName().trimmed(), false );
1179 if ( !sName.isEmpty() ) {
1180 if ( getStudy()->GetProperties()->IsLocked() ) {
1181 aTitle += QString( " - [%1 (%2)]").arg( sName ).arg( tr( "STUDY_LOCKED" ) );
1183 aTitle += QString( " - [%1]" ).arg( sName );
1188 desktop()->setWindowTitle( aTitle );
1191 int SalomeApp_Application::closeChoice( const QString& docName )
1193 QStringList buttons;
1194 QMap<int, int> choices;
1196 buttons << tr ("APPCLOSE_SAVE"); // Save & Clear
1197 choices.insert( idx++, CloseSave ); // ...
1198 buttons << tr ("APPCLOSE_CLOSE"); // Clear w/o saving
1199 choices.insert( idx++, CloseDiscard ); // ...
1200 if ( myIsCloseFromExit ) {
1201 buttons << tr ("APPCLOSE_UNLOAD_SAVE"); // Save & Disconnect
1202 choices.insert( idx++, CloseDisconnectSave ); // ...
1203 buttons << tr ("APPCLOSE_UNLOAD"); // Disconnect
1204 choices.insert( idx++, CloseDisconnect ); // ...
1206 buttons << tr ("APPCLOSE_CANCEL"); // Cancel
1207 choices.insert( idx++, CloseCancel ); // ...
1209 if( !activeStudy()->isModified() )
1211 int answer = SUIT_MessageBox::question( desktop(), tr( "APPCLOSE_CAPTION" ),
1212 tr( "APPCLOSE_DESCRIPTION" ), buttons, 0 );
1213 return choices[answer];
1216 bool SalomeApp_Application::closeAction( const int choice, bool& closePermanently )
1222 if ( activeStudy()->isSaved() )
1224 else if ( !onSaveAsDoc() )
1229 case CloseDisconnectSave:
1230 if ( activeStudy()->isSaved() )
1232 else if ( !onSaveAsDoc() )
1234 case CloseDisconnect:
1235 closeActiveDoc( false );
1236 closePermanently = false;
1245 int SalomeApp_Application::openChoice( const QString& aName )
1247 int choice = LightApp_Application::openChoice( aName );
1249 if ( QFileInfo( aName ).exists() ) {
1250 if ( choice == OpenNew ) { // The document isn't already open.
1252 if ( aName == getStudy()->Name().c_str() )
1254 // The document already exists in the study.
1255 // Do you want to reload it?
1257 int answer = SUIT_MessageBox::question( desktop(), tr( "WRN_WARNING" ), tr( "QUE_DOC_ALREADYEXIST" ).arg( aName ),
1258 SUIT_MessageBox::Yes | SUIT_MessageBox::No, SUIT_MessageBox::No );
1259 if ( answer == SUIT_MessageBox::Yes )
1260 choice = OpenRefresh;
1262 choice = OpenCancel;
1265 } else { // file is not exist on disk
1266 SUIT_MessageBox::warning( desktop(),
1267 QObject::tr("WRN_WARNING"),
1268 QObject::tr("WRN_FILE_NOT_EXIST").arg(aName.toUtf8().data()));
1275 bool SalomeApp_Application::openAction( const int aChoice, const QString& aName )
1278 int choice = aChoice;
1286 res = LightApp_Application::openAction( choice, aName );
1294 \brief Get map of the operations which can be performed
1295 on the module activation.
1297 The method should return the map of the kind \c {<id>:<name>}
1298 where \c <id> is an integer identifier of the operation and
1299 \c <name> is a title for the button to be added to the
1300 dialog box. After user selects the required operation by the
1301 clicking the corresponding button in the dialog box, its identifier
1302 is passed to the moduleActionSelected() method to process
1305 \return map of the operations
1306 \sa moduleActionSelected()
1308 QMap<int, QString> SalomeApp_Application::activateModuleActions() const
1310 QMap<int, QString> opmap = LightApp_Application::activateModuleActions();
1312 opmap.insert( LoadStudyId, tr( "ACTIVATE_MODULE_OP_LOAD" ) );
1314 opmap.insert( NewAndScriptId, tr( "ACTIVATE_MODULE_OP_SCRIPT" ) );
1319 \brief Called when the used selectes required operation chosen
1320 from "Activate module" dialog box.
1322 Performs the required operation according to the user choice.
1324 \param id operation identifier
1325 \sa activateModuleActions()
1327 void SalomeApp_Application::moduleActionSelected( const int id )
1333 case NewAndScriptId:
1337 LightApp_Application::moduleActionSelected( id );
1342 /*!Gets CORBA::ORB_var*/
1343 CORBA::ORB_var SalomeApp_Application::orb()
1345 static CORBA::ORB_var _orb;
1347 if ( CORBA::is_nil( _orb ) ) {
1348 Qtx::CmdLineArgs args;
1349 ORB_INIT& init = *SINGLETON_<ORB_INIT>::Instance();
1350 _orb = init( args.argc(), args.argv() );
1356 /*!Create and return SALOMEDS_Study.*/
1357 _PTR(Study) SalomeApp_Application::getStudy()
1359 static _PTR(Study) _study;
1361 CORBA::Object_var aSObject = namingService()->Resolve("/Study");
1362 SALOMEDS::Study_var aStudy = SALOMEDS::Study::_narrow(aSObject);
1363 _study = ClientFactory::Study(aStudy);
1368 /*!Create and return SALOME_NamingService.*/
1369 SALOME_NamingService* SalomeApp_Application::namingService()
1371 static SALOME_NamingService _ns(orb());
1375 /*!Create and return SALOME_LifeCycleCORBA.*/
1376 SALOME_LifeCycleCORBA* SalomeApp_Application::lcc()
1378 static SALOME_LifeCycleCORBA _lcc( namingService() );
1382 /*!Private SLOT. On preferences.*/
1383 void SalomeApp_Application::onProperties()
1385 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1389 _PTR(StudyBuilder) SB = study->studyDS()->NewBuilder();
1392 SalomeApp_StudyPropertiesDlg aDlg( desktop() );
1393 int res = aDlg.exec();
1394 if( res==QDialog::Accepted && aDlg.isChanged() )
1395 SB->CommitCommand();
1399 //study->updateCaptions();
1400 updateDesktopTitle();
1404 /*!Insert items in popup, which necessary for current application*/
1405 void SalomeApp_Application::contextMenuPopup( const QString& type, QMenu* thePopup, QString& title )
1407 LightApp_SelectionMgr* mgr = selectionMgr();
1408 bool cacheIsOn = mgr->isSelectionCacheEnabled();
1409 mgr->setSelectionCacheEnabled( true );
1411 LightApp_Application::contextMenuPopup( type, thePopup, title );
1413 // temporary commented
1414 /*OB_Browser* ob = objectBrowser();
1415 if ( !ob || type != ob->popupClientType() )
1418 // Get selected objects
1419 SALOME_ListIO aList;
1420 mgr->selectedObjects( aList, QString(), false );
1422 // add GUI state commands: restore, rename
1423 if ( aList.Extent() == 1 && aList.First()->hasEntry() &&
1424 QString( aList.First()->getEntry() ).startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) {
1425 thePopup->addSeparator();
1426 thePopup->addAction( tr( "MEN_RESTORE_VS" ), this, SLOT( onRestoreGUIState() ) );
1427 thePopup->addAction( tr( "MEN_RENAME_VS" ), objectBrowser(),
1428 SLOT( onStartEditing() ), objectBrowser()->shortcutKey(SUIT_DataBrowser::RenameShortcut) );
1429 thePopup->addAction( tr( "MEN_DELETE_VS" ), this, SLOT( onDeleteGUIState() ) );
1432 // "Delete reference" item should appear only for invalid references
1434 // isInvalidRefs will be true, if at least one of selected objects is invalid reference
1435 bool isInvalidRefs = false;
1437 _PTR(SObject) anObj;
1438 for( SALOME_ListIteratorOfListIO it( aList ); it.More() && !isInvalidRefs; it.Next() )
1440 if( it.Value()->hasEntry() )
1442 _PTR(SObject) aSObject = getStudy()->FindObjectID( it.Value()->getEntry() ), aRefObj = aSObject;
1443 while( aRefObj && aRefObj->ReferencedObject( anObj ) )
1446 if( aRefObj && aRefObj!=aSObject && QString( aRefObj->GetName().c_str() ).isEmpty() )
1447 isInvalidRefs = true;
1451 // Add "Delete reference" item to popup
1452 if ( isInvalidRefs )
1454 thePopup->addSeparator();
1455 thePopup->addAction( tr( "MEN_DELETE_INVALID_REFERENCE" ), this, SLOT( onDeleteInvalidReferences() ) );
1459 // "Activate module" item should appear only if it's necessary
1460 if ( aList.Extent() == 1 ) {
1462 mgr->selectedObjects( aList );
1464 Handle(SALOME_InteractiveObject) aIObj = aList.First();
1466 // add extra popup menu (defined in XML)
1467 if ( myExtActions.size() > 0 ) {
1468 // Use only first selected object
1469 _PTR(SObject) aSO = getStudy()->FindObjectID( aIObj->getEntry() );
1471 _PTR( GenericAttribute ) anAttr;
1472 std::string auid = "AttributeUserID";
1473 auid += Kernel_Utils::GetGUID(Kernel_Utils::ObjectdID);
1474 if ( aSO->FindAttribute( anAttr, auid ) ) {
1475 _PTR(AttributeUserID) aAttrID = anAttr;
1476 QString aId = aAttrID->Value().c_str();
1477 if ( myExtActions.contains( aId ) ) {
1478 thePopup->addAction(myExtActions[aId]);
1484 // check if item is a "GUI state" item (also a first level object)
1485 QString entry( aIObj->getEntry() );
1486 if ( !entry.startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) {
1487 QString aModuleName( aIObj->getComponentDataType() );
1488 QString aModuleTitle = moduleTitle( aModuleName );
1489 CAM_Module* currentModule = activeModule();
1490 if ( ( !currentModule || currentModule->moduleName() != aModuleTitle ) && !aModuleTitle.isEmpty() )
1491 thePopup->addAction( tr( "MEN_OPENWITH" ).arg( aModuleTitle ), this, SLOT( onOpenWith() ) );
1495 mgr->setSelectionCacheEnabled( cacheIsOn );
1498 /*!Update obect browser:
1499 1.if 'updateModels' true, update existing data models;
1500 2. update "non-existing" (not loaded yet) data models;
1501 3. update object browser if it exists */
1502 void SalomeApp_Application::updateObjectBrowser( const bool updateModels )
1504 // update "non-existing" (not loaded yet) data models
1505 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
1508 for ( _PTR(SComponentIterator) it ( getStudy()->NewComponentIterator() ); it->More(); it->Next() )
1510 _PTR(SComponent) aComponent ( it->Value() );
1512 #ifndef WITH_SALOMEDS_OBSERVER
1513 // with GUI observers this check is not needed anymore
1514 if ( aComponent->ComponentDataType() == study->getVisualComponentName().toLatin1().constData() )
1515 continue; // skip the magic "Interface Applicative" component
1517 if ( !objectBrowser() )
1518 getWindow( WT_ObjectBrowser );
1519 const bool isAutoUpdate = objectBrowser()->autoUpdate();
1520 objectBrowser()->setAutoUpdate( false );
1521 SalomeApp_DataModel::synchronize( aComponent, study );
1522 objectBrowser()->setAutoUpdate( isAutoUpdate );
1526 // create data objects that correspond to GUI state save points
1527 if ( study ) updateSavePointDataObjects( study );
1529 // update existing data models (already loaded SComponents)
1530 LightApp_Application::updateObjectBrowser( updateModels );
1533 /*!Display Catalog Genenerator dialog */
1534 void SalomeApp_Application::onCatalogGen()
1536 ToolsGUI_CatalogGeneratorDlg aDlg( desktop() );
1540 /*!Display Registry Display dialog */
1541 void SalomeApp_Application::onRegDisplay()
1543 CORBA::ORB_var anOrb = orb();
1544 ToolsGUI_RegWidget* regWnd = ToolsGUI_RegWidget::GetRegWidget( anOrb, desktop() );
1547 regWnd->activateWindow();
1550 /*!find original object by double click on item */
1551 void SalomeApp_Application::onDblClick( SUIT_DataObject* theObj )
1553 // Issue 21379: References are supported at LightApp_DataObject level
1554 LightApp_DataObject* obj = dynamic_cast<LightApp_DataObject*>( theObj );
1556 if( obj && obj->isReference() )
1558 QString entry = obj->refEntry();
1560 SUIT_DataOwnerPtrList aList;
1561 aList.append( new LightApp_DataOwner( entry ) );
1562 selectionMgr()->setSelected( aList, false );
1564 SUIT_DataBrowser* ob = objectBrowser();
1566 QModelIndexList aSelectedIndexes = ob->selectedIndexes();
1567 if ( !aSelectedIndexes.isEmpty() )
1568 ob->treeView()->scrollTo( aSelectedIndexes.first() );
1570 emit objectDoubleClicked( theObj );
1574 Creates new view manager
1575 \param type - type of view manager
1577 SUIT_ViewManager* SalomeApp_Application::newViewManager(const QString& type)
1579 return createViewManager(type);
1583 /*!Global utility function, returns selected GUI Save point object's ID */
1584 int getSelectedSavePoint( const LightApp_SelectionMgr* selMgr )
1586 SALOME_ListIO aList;
1587 selMgr->selectedObjects( aList );
1588 if( aList.Extent() > 0 ) {
1589 Handle(SALOME_InteractiveObject) aIObj = aList.First();
1590 QString entry( aIObj->getEntry() );
1591 QString startStr = QObject::tr( "SAVE_POINT_DEF_NAME" );
1592 if ( !entry.startsWith( startStr ) ) // it's a "GUI state" object
1594 bool ok; // conversion to integer is ok?
1595 int savePoint = entry.right( entry.length() - startStr.length() ).toInt( &ok );
1596 return ok ? savePoint : -1;
1601 /*!Called on Restore GUI State popup command*/
1602 void SalomeApp_Application::onRestoreGUIState()
1604 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1605 if ( savePoint == -1 )
1607 SalomeApp_VisualState( this ).restoreState( savePoint );
1610 /*!Called on Delete GUI State popup command*/
1611 void SalomeApp_Application::onDeleteGUIState()
1613 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1614 if ( savePoint == -1 )
1616 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1620 study->removeSavePoint( savePoint );
1621 updateSavePointDataObjects( study );
1624 /*!Called on New study operation*/
1625 void SalomeApp_Application::onStudyCreated( SUIT_Study* study )
1627 LightApp_Application::onStudyCreated( study );
1629 //#ifndef DISABLE_PYCONSOLE
1630 // desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
1631 // windowDock( getWindow( WT_ObjectBrowser ) ) );
1634 loadDockWindowsState();
1636 objectBrowserColumnsVisibility();
1639 /*!Called on Open study operation*/
1640 void SalomeApp_Application::onStudyOpened( SUIT_Study* study )
1642 LightApp_Application::onStudyOpened( study );
1644 //#ifndef DISABLE_PYCONSOLE
1645 // desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
1646 // windowDock( getWindow( WT_ObjectBrowser ) ) );
1649 loadDockWindowsState();
1651 objectBrowserColumnsVisibility();
1653 // temporary commented
1654 /*if ( objectBrowser() ) {
1655 updateSavePointDataObjects( dynamic_cast<SalomeApp_Study*>( study ) );
1656 objectBrowser()->updateTree( study->root() );
1660 /*! updateSavePointDataObjects: syncronize data objects that correspond to save points (gui states)*/
1661 void SalomeApp_Application::updateSavePointDataObjects( SalomeApp_Study* study )
1664 SUIT_DataBrowser* ob = objectBrowser();
1665 LightApp_SelectionMgr* selMgr = selectionMgr();
1667 if ( !study || !ob || !selMgr )
1670 // find GUI states root object
1671 SUIT_DataObject* guiRootObj = 0;
1673 study->root()->children( ch );
1674 DataObjectList::const_iterator it = ch.begin(), last = ch.end();
1675 for ( ; it != last ; ++it ) {
1676 if ( dynamic_cast<SalomeApp_SavePointRootObject*>( *it ) ) {
1681 std::vector<int> savePoints = study->getSavePoints();
1682 // case 1: no more save points but they existed in study's tree
1683 if ( savePoints.empty() && guiRootObj ) {
1684 //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state"
1685 // : set auto update to true for removing SalomeApp_SavePointRootObject from the SUIT_TreeModel
1686 const bool isAutoUpdate = ob->autoUpdate();
1687 selMgr->clearSelected();
1688 ob->setAutoUpdate(true);
1689 DataObjectList ch = guiRootObj->children();
1690 for( int i = 0; i < ch.size(); i++ )
1693 ob->setAutoUpdate(isAutoUpdate);
1696 // case 2: no more save points but root does not exist either
1697 if ( savePoints.empty() && !guiRootObj )
1699 // case 3: save points but no root for them - create it
1700 if ( !savePoints.empty() && !guiRootObj )
1701 guiRootObj = new SalomeApp_SavePointRootObject( study->root() );
1702 // case 4: everything already exists.. here may be a problem: we want "GUI states" root object
1703 // to be always the last one in the tree. Here we check - if it is not the last one - remove and
1705 if ( guiRootObj->nextBrother() ) {
1706 study->root()->removeChild(guiRootObj);
1707 study->root()->appendChild(guiRootObj);
1708 //study->root()->dump();
1711 // store data objects in a map id-to-DataObject
1712 QMap<int,SalomeApp_SavePointObject*> mapDO;
1714 guiRootObj->children( ch );
1715 for( it = ch.begin(), last = ch.end(); it != last ; ++it ) {
1716 SalomeApp_SavePointObject* dobj = dynamic_cast<SalomeApp_SavePointObject*>( *it );
1718 mapDO[dobj->getId()] = dobj;
1721 // iterate new save points. if DataObject with such ID not found in map - create DataObject
1722 // if in the map - remove it from map.
1723 for ( size_t i = 0; i < savePoints.size(); i++ )
1724 if ( !mapDO.contains( savePoints[i] ) )
1725 new SalomeApp_SavePointObject( guiRootObj, savePoints[i], study );
1727 mapDO.remove( savePoints[i] );
1729 // delete DataObjects that are still in the map -- their IDs were not found in data model
1730 if( mapDO.size() > 0) {
1731 //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state"
1732 // : set auto update to true for removing SalomeApp_SavePointObject from the SUIT_TreeModel
1733 selMgr->clearSelected();
1734 const bool isAutoUpdate = ob->autoUpdate();
1735 ob->setAutoUpdate(true);
1736 for ( QMap<int,SalomeApp_SavePointObject*>::Iterator it = mapDO.begin(); it != mapDO.end(); ++it )
1738 ob->setAutoUpdate(isAutoUpdate);
1742 /*! Check data object */
1743 bool SalomeApp_Application::checkDataObject(LightApp_DataObject* theObj)
1752 Opens other study into active Study. If Study is empty - creates it.
1753 \param theName - name of study
1755 bool SalomeApp_Application::useStudy( const QString& theName )
1758 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1761 res = aStudy->loadDocument( theName );
1762 updateDesktopTitle();
1763 updateCommandsStatus();
1767 /*! Show/hide object browser colums according to preferences */
1768 void SalomeApp_Application::objectBrowserColumnsVisibility()
1770 if ( objectBrowser() )
1771 for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
1773 bool shown = resourceMgr()->booleanValue( "ObjectBrowser", QString( "visibility_column_id_%1" ).arg( i-1 ), true );
1774 objectBrowser()->treeView()->setColumnHidden( i, !shown );
1778 #ifndef DISABLE_PYCONSOLE
1779 /*! Set SalomeApp_NoteBook pointer */
1780 void SalomeApp_Application::setNoteBook( SalomeApp_NoteBook* theNoteBook )
1782 myNoteBook = theNoteBook;
1785 /*! Return SalomeApp_NoteBook pointer */
1786 SalomeApp_NoteBook* SalomeApp_Application::getNoteBook() const
1793 * Define extra actions defined in module definition XML file.
1794 * Additional popup items sections can be defined by parameter "popupitems".
1795 * Supported attributes:
1796 * title - title of menu item,
1797 * attributelocalid - AttributeLocalId defined for selected data item where menu command has to be applied,
1798 * method - method which has to be called when menu item is selected
1800 * <section name="MODULENAME">
1801 * <parameter name="popupitems" value="menuitem1:menuitem2:..."/>
1803 * <section name="importmed">
1804 * <parameter name="title" value="My menu"/>
1805 * <parameter name="objectid" value="VISU.Result"/>
1806 * <parameter name="method" value="nameOfModuleMethod"/>
1809 void SalomeApp_Application::createExtraActions()
1811 myExtActions.clear();
1812 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
1814 QStringList aModules;
1815 modules(aModules, false);
1816 foreach(QString aModile, aModules) {
1817 QString aModName = moduleName(aModile);
1818 QString aSectionStr = resMgr->stringValue(aModName, "popupitems", QString());
1819 if (!aSectionStr.isNull()) {
1820 QStringList aSections = aSectionStr.split(':');
1821 foreach(QString aSection, aSections) {
1822 QString aTitle = resMgr->stringValue(aSection, "title", QString());
1823 QString aId = resMgr->stringValue(aSection, "objectid", QString());
1824 QString aSlot = resMgr->stringValue(aSection, "method", QString());
1825 if (aTitle.isEmpty() || aSlot.isEmpty() || aId.isEmpty())
1828 QString aModuleName = resMgr->stringValue(aSection, "module", QString());
1829 if (aModuleName.isNull())
1830 aModuleName = aModName;
1832 QAction* aAction = new QAction(aTitle, this);
1834 aData<<aModuleName<<aSlot;
1835 aAction->setData(aData);
1836 connect(aAction, SIGNAL(triggered()), this, SLOT(onExtAction()));
1837 myExtActions[aId] = aAction;
1844 * Called when extra action is selected
1846 void SalomeApp_Application::onExtAction()
1848 QAction* aAction = ::qobject_cast<QAction*>(sender());
1852 QVariant aData = aAction->data();
1853 QStringList aDataList = aData.value<QStringList>();
1854 if (aDataList.size() != 2)
1857 LightApp_SelectionMgr* aSelectionMgr = selectionMgr();
1858 SALOME_ListIO aListIO;
1859 aSelectionMgr->selectedObjects(aListIO);
1860 const Handle(SALOME_InteractiveObject)& anIO = aListIO.First();
1861 if (aListIO.Extent() < 1)
1863 if (!anIO->hasEntry())
1866 QString aEntry(anIO->getEntry());
1868 QApplication::setOverrideCursor( Qt::WaitCursor );
1869 QString aModuleTitle = moduleTitle(aDataList[0]);
1870 activateModule(aModuleTitle);
1871 QApplication::restoreOverrideCursor();
1873 QCoreApplication::processEvents();
1875 CAM_Module* aModule = activeModule();
1879 if (!QMetaObject::invokeMethod(aModule, qPrintable(aDataList[1]), Q_ARG(QString, aEntry)))
1880 printf("Error: Can't Invoke method %s\n", qPrintable(aDataList[1]));
1884 Checks that an object can be renamed.
1885 \param entry entry of the object
1886 \brief Return \c true if object can be renamed
1888 bool SalomeApp_Application::renameAllowed( const QString& entry) const
1890 return entry.startsWith( tr( "SAVE_POINT_DEF_NAME") );
1894 Rename object by entry.
1895 \param entry entry of the object
1896 \param name new name of the object
1897 \brief Return \c true if rename operation finished successfully, \c false otherwise.
1899 bool SalomeApp_Application::renameObject( const QString& entry, const QString& name )
1901 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1903 int savePoint = ::getSelectedSavePoint( selectionMgr() );
1905 if(!aStudy || savePoint == -1)
1908 if ( !name.isNull() && !name.isEmpty() ) {
1909 aStudy->setNameOfSavePoint( savePoint, name );
1910 updateSavePointDataObjects( aStudy );
1912 //Mark study as modified
1919 #ifndef DISABLE_PYCONSOLE
1920 //============================================================================
1921 /*! Function : onUpdateStudy
1922 * Purpose : Slot to update the study.
1924 //============================================================================
1925 void SalomeApp_Application::onUpdateStudy()
1927 QApplication::setOverrideCursor( Qt::WaitCursor );
1929 if( !updateStudy() )
1930 SUIT_MessageBox::warning( desktop(), tr( "ERROR" ), tr( "ERR_UPDATE_STUDY_FAILED" ) );
1932 QApplication::restoreOverrideCursor();
1935 //============================================================================
1936 /*! Function : updateStudy
1937 * Purpose : Update study by dumping the study to Python script and loading it.
1938 * It is used to apply variable modifications done in NoteBook to created objects.
1940 //============================================================================
1941 bool SalomeApp_Application::updateStudy()
1943 SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
1944 if ( !study || !myNoteBook )
1947 myNoteBook->setIsDumpedStudySaved( study->isSaved() );
1948 myNoteBook->setDumpedStudyName( study->studyName() );
1950 // get unique temporary directory name
1951 QString aTmpDir = QString::fromStdString( SALOMEDS_Tool::GetTmpDir() );
1952 if( aTmpDir.isEmpty() )
1955 if( aTmpDir.right( 1 ).compare( QDir::separator() ) == 0 )
1956 aTmpDir.remove( aTmpDir.length() - 1, 1 );
1958 // dump study to the temporary directory
1959 QString aScriptName( "notebook" );
1960 bool toPublish = true;
1961 bool isMultiFile = false;
1962 bool toSaveGUI = true;
1965 _PTR(AttributeParameter) ap;
1966 _PTR(IParameters) ip = ClientFactory::getIParameters(ap);
1967 if(ip->isDumpPython()) ip->setDumpPython(); //Unset DumpPython flag.
1968 if ( toSaveGUI ) { //SRN: Store a visual state of the study at the save point for DumpStudy method
1969 ip->setDumpPython();
1970 savePoint = SalomeApp_VisualState( this ).storeState(); //SRN: create a temporary save point
1972 bool ok = getStudy()->DumpStudy( aTmpDir.toStdString(), aScriptName.toStdString(), toPublish, isMultiFile );
1974 study->removeSavePoint(savePoint); //SRN: remove the created temporary save point.
1977 myNoteBook->setDumpedStudyScript( aTmpDir + QDir::separator() + aScriptName + ".py" );
1981 QList<SUIT_Application*> aList = SUIT_Session::session()->applications();
1982 int anIndex = aList.indexOf( this );
1984 // Disconnect dialog from application desktop in case if:
1985 // 1) Application is not the first application in the session
1986 // 2) Application is the first application in session but not the only.
1987 bool changeDesktop = ((anIndex > 0) || (anIndex == 0 && aList.count() > 1));
1988 if( changeDesktop ) {
1990 SalomeApp_Application* app = this;
1991 if( anIndex > 0 && anIndex < aList.count() )
1992 app = dynamic_cast<SalomeApp_Application*>( aList[ anIndex - 1 ] );
1993 else if(anIndex == 0 && aList.count() > 1)
1994 app = dynamic_cast<SalomeApp_Application*>( aList[ 1 ] );
1999 // creation a new study and restoring will be done in another application
2000 connect( this, SIGNAL( dumpedStudyClosed( const QString&, const QString&, bool ) ),
2001 app, SLOT( onRestoreStudy( const QString&, const QString&, bool ) ), Qt::UniqueConnection );
2004 QString aDumpScript = myNoteBook->getDumpedStudyScript();
2005 QString aStudyName = myNoteBook->getDumpedStudyName();
2006 bool isStudySaved = myNoteBook->isDumpedStudySaved();
2007 // clear a study (delete all objects)
2008 onCloseDoc( false );
2010 if( !changeDesktop ) {
2011 ok = onRestoreStudy( aDumpScript,
2020 //============================================================================
2021 /*! Function : onRestoreStudy
2022 * Purpose : Load the dumped study from Python script
2024 //============================================================================
2025 bool SalomeApp_Application::onRestoreStudy( const QString& theDumpScript,
2026 const QString& theStudyName,
2027 bool theIsStudySaved )
2031 // create a new study
2034 // get active application
2035 SalomeApp_Application* app = dynamic_cast<SalomeApp_Application*>( SUIT_Session::session()->activeApplication() );
2037 // load study from the temporary directory
2039 QString command = QString( "exec(open(\"%1\" ,\"rb\").read())" ).arg( theDumpScript );
2041 #ifndef DISABLE_PYCONSOLE
2042 PyConsole_Console* pyConsole = app->pythonConsole();
2044 pyConsole->execAndWait( command );
2047 // remove temporary directory
2048 QFileInfo aScriptInfo = QFileInfo( theDumpScript );
2049 QString aStudyName = aScriptInfo.baseName();
2050 QDir aDir = aScriptInfo.absoluteDir();
2051 QStringList aFiles = aDir.entryList( QStringList( "*.py*" ) );
2052 for( QStringList::iterator it = aFiles.begin(), itEnd = aFiles.end(); it != itEnd; ++it )
2053 ok = aDir.remove( *it ) && ok;
2055 ok = aDir.rmdir( aDir.absolutePath() );
2057 if( SalomeApp_Study* newStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() ) )
2059 #ifndef DISABLE_PYCONSOLE
2060 if ( app->getNoteBook() )
2061 app->getNoteBook()->Init();
2062 newStudy->updateFromNotebook(theStudyName, theIsStudySaved);
2063 newStudy->Modified();
2064 updateDesktopTitle();
2075 Close the Application
2077 void SalomeApp_Application::afterCloseDoc()
2079 #ifndef DISABLE_PYCONSOLE
2080 // emit signal to restore study from Python script
2082 emit dumpedStudyClosed( myNoteBook->getDumpedStudyScript(),
2083 myNoteBook->getDumpedStudyName(),
2084 myNoteBook->isDumpedStudySaved() );
2087 LightApp_Application::afterCloseDoc();
2091 Asks to close existing document.
2093 bool SalomeApp_Application::checkExistingDoc()
2095 return LightApp_Application::checkExistingDoc();
2099 #ifndef DISABLE_PYCONSOLE
2101 PyConsole_Interp* SalomeApp_Application::createPyInterp()
2103 return new SalomeApp_PyInterp;
2106 #endif // DISABLE_PYCONSOLE