1 //=============================================================================
2 // File : SALOME_PYQT_Module.cxx
4 // Author : Vadim SANDLER
6 // Copyright : 2003-2005 CEA/DEN, EDF R&D
8 //=============================================================================
10 #include "SALOME_PYQT_Module.h"
12 #include "PyInterp_Dispatcher.h"
13 #include "SUIT_ResourceMgr.h"
14 #include "STD_MDIDesktop.h"
15 #include "SalomeApp_Application.h"
16 #include "SalomeApp_Study.h"
17 #include "SalomeApp_DataModel.h"
19 #include <SALOME_LifeCycleCORBA.hxx>
23 #include <qworkspace.h>
24 #include <qpopupmenu.h>
26 #if QT_VERSION > 0x030005
27 #include "sipAPISalomePyQtGUI.h"
29 #include "sipSalomePyQtGUIDeclSalomePyQtGUI.h"
32 #include <sipqtQWidget.h>
33 #include <sipqtQPopupMenu.h>
37 // Default name of the module, should be replaced at the moment
39 #define __DEFAULT_NAME__ "SALOME_PYQT_Module"
40 // Comment this define to block invoking of obsolete Python module's
41 // methods like setSetting(), definePopup(), etc.
42 #define __CALL_OLD_METHODS__
44 //=============================================================================
45 // General rule for Python requests created by SALOME_PYQT_Module:
46 // all requests should be executed SYNCHRONOUSLY within the main GUI thread.
47 // However, it is obligatory that ANY Python call is wrapped with a request object,
48 // so that ALL Python API calls are serialized with PyInterp_Dispatcher.
49 //=============================================================================
52 //=============================================================================
53 // The default PyQt module data model.
54 // Reuses common data model from SalomeApp.
55 //=============================================================================
56 class SALOME_PYQT_DataModel: public SalomeApp_DataModel
59 SALOME_PYQT_DataModel( CAM_Module* theModule ) : SalomeApp_DataModel( theModule ) {}
60 bool isModified() const { return false; }
61 bool isSaved() const { return false; }
64 //=============================================================================
65 // The class for parsing of the XML resource files.
66 // Used for backward compatibility with existing Python modules.
67 //=============================================================================
68 class SALOME_PYQT_XmlHandler
71 SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName );
73 void createPopup ( QPopupMenu* menu,
74 const QString& context,
75 const QString& parent,
76 const QString& object );
79 void createToolBar ( QDomNode& parentNode );
80 void createMenu ( QDomNode& parentNode,
81 const int parentMenuId = -1 );
83 void insertPopupItems( QDomNode& parentNode,
87 SALOME_PYQT_Module* myModule;
91 //=============================================================================
92 // SALOME_PYQT_Module class implementation (implements CAM_Module API for
93 // all Python-based SALOME module
94 //=============================================================================
97 * This function creates an instance of SALOME_PYQT_Module object by request
98 * of and application object when the module is loaded.
101 SALOME_PYQT_EXPORT CAM_Module* createModule() {
102 return new SALOME_PYQT_Module();
107 * Static variables definition
109 SALOME_PYQT_Module::InterpMap SALOME_PYQT_Module::myInterpMap;
114 SALOME_PYQT_Module::SALOME_PYQT_Module() :
115 SalomeApp_Module( __DEFAULT_NAME__ ), myModule( 0 ), myXmlHandler ( 0 )
117 myMenuActionList.setAutoDelete( false );
118 myPopupActionList.setAutoDelete( false );
119 myToolbarActionList.setAutoDelete( false );
125 SALOME_PYQT_Module::~SALOME_PYQT_Module()
127 myMenuActionList.clear();
128 myPopupActionList.clear();
129 myToolbarActionList.clear();
135 * Creates data model for the module.
136 * Reimplemented from CAM_Module.
138 CAM_DataModel* SALOME_PYQT_Module::createDataModel()
140 // VSR: this is a temporary solution :
141 // should reuse default data model from SalomeApp
142 return new SALOME_PYQT_DataModel( this );
146 * Initialization of the module.
147 * Inherited from CAM_Module.
149 * This method is used for creation of the menus, toolbars and other staff.
150 * There are two ways:
151 * - for obsolete modules this method first tries to read <module>_<language>.xml
152 * resource file which contains a menu, toolbars and popup menus description.
153 * - new modules can create menus by by calling the corresponding methods of SalomePyQt
154 * Python API in the Python module's initialize() method which is called from here.
155 * NOTE: if postponed modules loading is not used, the active study might be not defined
156 * yet at this stage, so initialize() method should not perform any study-based initialization.
158 void SALOME_PYQT_Module::initialize( CAM_Application* app )
160 SalomeApp_Module::initialize( app );
162 // Try to get XML resource file name
163 SUIT_ResourceMgr* aResMgr = getApp()->resourceMgr();
164 QString aLang = aResMgr->stringValue( "language", "language", QString::null );
165 if ( aLang.isEmpty() ) aLang = QString( "en" );
166 QString aName = name( "" );
167 QString aFileName = aName + "_" + aLang + ".xml";
168 aFileName = aResMgr->path( "resources", aName, aFileName );
170 // parse XML file if it is found and create actions
171 if ( !myXmlHandler && !aFileName.isEmpty() ) {
172 myXmlHandler = new SALOME_PYQT_XmlHandler( this, aFileName );
173 myXmlHandler->createActions();
176 // perform internal initialization and call module's initialize() method
177 // InitializeReq: request class for internal init() operation
178 class InitializeReq : public PyInterp_Request
181 InitializeReq( CAM_Application* _app,
182 SALOME_PYQT_Module* _obj )
183 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
188 virtual void execute()
190 myObj->init( myApp );
194 CAM_Application* myApp;
195 SALOME_PYQT_Module* myObj;
198 // Posting the request
199 PyInterp_Dispatcher::Get()->Exec( new InitializeReq( app, this ) );
203 * Activation of the module.
204 * Inherited from CAM_Module.
206 bool SALOME_PYQT_Module::activateModule( SUIT_Study* theStudy )
208 MESSAGE( "SALOME_PYQT_Module::activateModule" );
210 bool res = SalomeApp_Module::activateModule( theStudy );
215 // activate menus, toolbars, etc
216 setMenuShown( true );
217 setToolShown( true );
219 // ActivateReq: request class for internal activate() operation
220 class ActivateReq : public PyInterp_Request
223 ActivateReq( SUIT_Study* _study,
224 SALOME_PYQT_Module* _obj )
225 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
230 virtual void execute()
232 myObj->activate( myStudy );
237 SALOME_PYQT_Module* myObj;
240 // Posting the request
241 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( theStudy, this ) );
243 // connect desktop activation signal
244 connect( application()->desktop(), SIGNAL( activated() ), this, SLOT( onDesktopActivated() ) );
250 * Deactivation of the module.
251 * Inherited from CAM_Module.
253 bool SALOME_PYQT_Module::deactivateModule( SUIT_Study* theStudy )
255 MESSAGE( "SALOME_PYQT_Module::deactivateModule" );
257 bool res = SalomeApp_Module::deactivateModule( theStudy );
259 // deactivate menus, toolbars, etc
260 setMenuShown( false );
261 setToolShown( false );
263 // DeactivateReq: request class for internal deactivate() operation
264 class DeactivateReq : public PyInterp_LockRequest
267 DeactivateReq( PyInterp_base* _py_interp,
269 SALOME_PYQT_Module* _obj )
270 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
275 virtual void execute()
277 myObj->deactivate( myStudy );
282 SALOME_PYQT_Module* myObj;
285 // Posting the request
286 PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, theStudy, this ) );
288 // disconnect desktop activation signal
289 disconnect( application()->desktop(), SIGNAL( activated() ), this, SLOT( onDesktopActivated() ) );
295 * Processes GUI action (from main menu, toolbar or context popup menu)
297 void SALOME_PYQT_Module::onGUIEvent()
300 const QObject* obj = sender();
301 if ( !obj || !obj->inherits( "QAction" ) )
303 QAction* action = (QAction*)obj;
306 int id = actionId( action );
307 if ( myMenuActionList.contains( action ) )
308 id -= PYQT_ACTION_MENU;
309 if ( myToolbarActionList.contains( action ) )
310 id -= PYQT_ACTION_TOOLBAL;
311 if ( myPopupActionList.contains( action ) )
312 id -= PYQT_ACTION_POPUP;
313 MESSAGE( "SALOME_PYQT_Module::onGUIEvent: id = " << id );
315 // perform synchronous request to Python event dispatcher
316 class GUIEvent : public PyInterp_LockRequest
319 GUIEvent( PyInterp_base* _py_interp,
320 SALOME_PYQT_Module* _obj,
322 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
327 virtual void execute()
329 myObj->guiEvent( myId );
334 SALOME_PYQT_Module* myObj;
337 // Posting the request
338 PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
342 * Desktop activation slot. Used for notifying about changing of the active study.
344 void SALOME_PYQT_Module::onDesktopActivated()
346 // StudyChangedReq: request class for internal studyChanged() operation
347 class StudyChangedReq : public PyInterp_Request
350 StudyChangedReq( SUIT_Study* _study,
351 SALOME_PYQT_Module* _obj )
352 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
357 virtual void execute()
359 myObj->studyChanged( myStudy );
364 SALOME_PYQT_Module* myObj;
367 // Posting the request
368 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( application()->activeStudy(), this ) );
372 Context popup menu request.
373 Called when user activates popup menu in some window (view, object browser, etc).
375 void SALOME_PYQT_Module::contextMenuPopup( const QString& theContext, QPopupMenu* thePopupMenu, QString& /*title*/ )
377 MESSAGE( "SALOME_PYQT_Module::contextMenuPopup : " << theContext.latin1() );
378 // perform synchronous request to Python event dispatcher
379 class PopupMenuEvent : public PyInterp_LockRequest
382 PopupMenuEvent( PyInterp_base* _py_interp,
383 SALOME_PYQT_Module* _obj,
384 const QString& _context,
386 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
387 myContext( _context ),
392 virtual void execute()
394 myObj->contextMenu( myContext, myPopup );
398 SALOME_PYQT_Module* myObj;
403 // Posting the request only if dispatcher is not busy!
404 // Executing the request synchronously
405 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
406 PyInterp_Dispatcher::Get()->Exec( new PopupMenuEvent( myInterp, this, theContext, thePopupMenu ) );
410 * Defines the dockable window associated with the module.
411 * To fill the list of windows the correspondind Python module's windows()
412 * method is called from SALOME_PYQT_Module::init() method.
413 * By default, ObjectBrowser, PythonConsole and LogWindow are provided.
415 void SALOME_PYQT_Module::windows( QMap<int, int>& mappa ) const
417 // First clear the output parameters
418 QMap<int, int>::ConstIterator it;
419 for ( it = myWindowsMap.begin(); it != myWindowsMap.end(); ++it ) {
420 mappa[ it.key() ] = it.data();
425 * Defines the compatible views which should be opened on module activation.
426 * To fill the list of views the correspondind Python module's views()
427 * method is called from SALOME_PYQT_Module::init() method.
428 * By default, the list is empty.
430 void SALOME_PYQT_Module::viewManagers( QStringList& listik ) const
432 for ( QStringList::ConstIterator it = myViewMgrList.begin(); it != myViewMgrList.end(); ++it ) {
433 listik.append( *it );
438 * Performs internal initialization
439 * - initializes/gets the Python interpreter (one per study)
440 * - imports the Python module
441 * - passes the workspace widget to the Python module
442 * - calls Python module's initialize() method
443 * - calls Python module's windows() method
444 * - calls Python module's views() method
446 void SALOME_PYQT_Module::init( CAM_Application* app )
448 // reset interpreter to NULL
452 SalomeApp_Application* anApp = dynamic_cast<SalomeApp_Application*>( app );
456 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() );
459 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
461 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
462 initInterp( aStudyId );
466 // import Python GUI module
471 #ifdef __CALL_OLD_METHODS__
472 // call Python module's setWorkspace() method
474 #endif // __CALL_OLD_METHODS__
476 // then call Python module's initialize() method
477 // ... first get python lock
478 PyLockWrapper aLock = myInterp->GetLockWrapper();
479 // ... (the Python module is already imported)
480 // ... finally call Python module's initialize() method
481 PyObjWrapper res( PyObject_CallMethod( myModule, "initialize", "" ) );
483 // VSR: this method may not be implemented in Python module
487 // get the windows list from the Python module by calling windows() method
488 // ... first put default values
489 myWindowsMap.insert( SalomeApp_Application::WT_ObjectBrowser, Qt::DockLeft );
490 myWindowsMap.insert( SalomeApp_Application::WT_PyConsole, Qt::DockBottom );
491 // VSR: LogWindow is not yet implemented
492 // myWindowsMap.insert( SalomeApp_Application::WT_LogWindow, Qt::DockBottom );
494 PyObjWrapper res1( PyObject_CallMethod( myModule, "windows", "" ) );
496 // VSR: this method may not be implemented in Python module
500 myWindowsMap.clear();
501 if ( PyDict_Check( res1 ) ) {
505 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
506 // parse the return value
507 // it should be a map: {integer:integer}
509 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
510 aKey = PyInt_AsLong( key );
511 aValue = PyInt_AsLong( value );
512 myWindowsMap[ aKey ] = aValue;
517 // get the windows list from the Python module by calling views() method
518 PyObjWrapper res2( PyObject_CallMethod( myModule, "views", "" ) );
520 // VSR: this method may not be implemented in Python module
524 // parse the return value
525 // result can be one string...
526 if ( PyString_Check( res2 ) ) {
527 myViewMgrList.append( PyString_AsString( res2 ) );
529 // ... or list of strings
530 else if ( PyList_Check( res2 ) ) {
531 int size = PyList_Size( res2 );
532 for ( int i = 0; i < size; i++ ) {
533 PyObject* value = PyList_GetItem( res2, i );
534 if( value && PyString_Check( value ) ) {
535 myViewMgrList.append( PyString_AsString( value ) );
543 * Performs internal activation:
544 * - initializes/gets the Python interpreter (one per study)
545 * - imports the Python GUI module
546 * - calls Python module's setSettings() method (obsolete function, used for compatibility with old code)
547 * or activate() method (for new modules)
549 void SALOME_PYQT_Module::activate( SUIT_Study* theStudy )
552 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
553 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
555 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
556 initInterp( aStudyId );
560 // import Python GUI module
566 PyLockWrapper aLock = myInterp->GetLockWrapper();
568 #ifdef __CALL_OLD_METHODS__
569 // call Python module's setSettings() method (obsolete)
570 PyObjWrapper res( PyObject_CallMethod( myModule, "setSettings", "" ) );
572 // VSR: this method may not be implemented in Python module
575 #endif // __CALL_OLD_METHODS__
577 // call Python module's activate() method (for the new modules)
578 PyObjWrapper res1( PyObject_CallMethod( myModule, "activate", "" ) );
580 // VSR: this method may not be implemented in Python module
586 * Performs internal deactivation:
587 * - calls Python module's deactivate() method
589 void SALOME_PYQT_Module::deactivate( SUIT_Study* theStudy )
591 // check if the subinterpreter is initialized and Python module is imported
592 if ( !myInterp || !myModule ) {
593 // Error! Python subinterpreter should be initialized and module should be imported first!
596 // then call Python module's deactivate() method
597 PyObjWrapper res( PyObject_CallMethod( myModule, "deactivate", "" ) );
599 // VSR: this method may not be implemented in Python module
605 * Called when active the study is actived (user brings its desktop to top)
606 * - initializes/gets the Python interpreter (one per study)
607 * - imports the Python GUI module
608 * - calls Python module's activeStudyChanged() method
610 void SALOME_PYQT_Module::studyChanged( SUIT_Study* theStudy )
613 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
614 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
616 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
617 initInterp( aStudyId );
621 // import Python GUI module
627 PyLockWrapper aLock = myInterp->GetLockWrapper();
629 // call Python module's activeStudyChanged() method
630 PyObjWrapper res( PyObject_CallMethod( myModule, "activeStudyChanged", "i", aStudyId ) );
632 // VSR: this method may not be implemented in Python module
638 * Get module engine, returns nil var if engine is not found in LifeCycleCORBA
640 Engines::Component_var SALOME_PYQT_Module::getEngine() const
642 Engines::Component_var comp = getApp()->lcc()->FindOrLoad_Component( "FactoryServerPy", name( "" ) );
647 * Get module engine IOR, returns empty string if engine is not found in LifeCycleCORBA
649 QString SALOME_PYQT_Module::engineIOR() const
651 if ( !CORBA::is_nil( getEngine() ) )
652 return QString( getApp()->orb()->object_to_string( getEngine() ) );
653 return QString( "" );
657 * Processes context popup menu request
658 * - calls Python module's definePopup(...) method (obsolete function, used for compatibility with old code)
659 * to define the popup menu context
660 * - parses XML resourses file (if exists) and fills the popup menu with the items)
661 * - calls Python module's customPopup(...) method (obsolete function, used for compatibility with old code)
662 * to allow module to customize the popup menu
663 * - for new modules calls createPopupMenu() function to allow the modules to build the popup menu
664 * by using insertItem(...) Qt functions.
666 void SALOME_PYQT_Module::contextMenu( const QString& theContext, QPopupMenu* thePopupMenu )
668 // Python interpreter should be initialized and Python module should be
670 if ( !myInterp || !myModule )
673 QString aContext( theContext ), aObject( "" ), aParent( "" );
674 #ifdef __CALL_OLD_METHODS__
675 // call definePopup() Python module's function
676 // this is obsolete function, used only for compatibility reasons
677 PyObjWrapper res(PyObject_CallMethod( myModule,
682 aParent.latin1() ) );
684 // VSR: this method may not be implemented in Python module
688 // parse return value
690 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
696 #endif // __CALL_OLD_METHODS__
698 // first try to create menu via XML parser:
699 // we create popup menus without help of QtxPopupMgr
701 myXmlHandler->createPopup( thePopupMenu, aContext, aParent, aObject );
703 PyObjWrapper sipPopup( sipMapCppToSelf( thePopupMenu, sipClass_QPopupMenu ) );
705 // then call Python module's createPopupMenu() method (for new modules)
706 PyObjWrapper res1( PyObject_CallMethod( myModule,
710 aContext.latin1() ) );
712 // VSR: this method may not be implemented in Python module
716 #ifdef __CALL_OLD_METHODS__
717 // call customPopup() Python module's function
718 // this is obsolete function, used only for compatibility reasons
719 PyObjWrapper res2( PyObject_CallMethod( myModule,
725 aParent.latin1() ) );
727 // VSR: this method may not be implemented in Python module
730 #endif // __CALL_OLD_METHODS__
734 * Processes GUI event
735 * - calls Python module's OnGUIEvent() method
737 void SALOME_PYQT_Module::guiEvent( const int theId )
739 // Python interpreter should be initialized and Python module should be
741 if ( !myInterp || !myModule )
744 PyObjWrapper res( PyObject_CallMethod( myModule, "OnGUIEvent", "i", theId ) );
752 * Initialises python subinterpreter (one per study)
754 void SALOME_PYQT_Module::initInterp( int theStudyId )
758 // Error! Study Id must not be 0!
762 // try to find the subinterpreter
763 if( myInterpMap.find( theStudyId ) != myInterpMap.end() ) {
765 myInterp = myInterpMap[ theStudyId ];
768 // not found - create a new one!
769 ///////////////////////////////////////////////////////////////////
770 // Attention: the creation of Python interpretor must be protected
771 // by a C++ Lock because of C threads
772 ///////////////////////////////////////////////////////////////////
773 myInterp = new SALOME_PYQT_PyInterp();
774 myInterp->initialize();
775 myInterpMap[ theStudyId ] = myInterp;
777 // import 'salome' module and call 'salome_init' method;
778 // do it only once on interpreter creation
779 // ... first get python lock
780 PyLockWrapper aLock = myInterp->GetLockWrapper();
781 // ... then import a module
782 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
788 // ... then call a method
789 PyObjWrapper aRes( PyObject_CallMethod( aMod, "salome_init", "" ) );
798 * Imports Python GUI module and remember the reference to the module
799 * !!! initInterp() should be called first!!!
801 void SALOME_PYQT_Module::importModule()
803 // check if the subinterpreter is initialized
805 // Error! Python subinterpreter should be initialized first!
809 // import Python GUI module and puts it in <myModule> attribute
810 // ... first get python lock
811 PyLockWrapper aLock = myInterp->GetLockWrapper();
812 // ... then import a module
813 QString aMod = QString( name("") ) + "GUI";
814 myModule = PyImport_ImportModule( (char*)( aMod.latin1() ) );
823 * Calls <module>.setWorkSpace() method with PyQt QWidget object to use with
825 * !!! initInterp() and importModule() should be called first!!!
827 void SALOME_PYQT_Module::setWorkSpace()
829 // check if the subinterpreter is initialized and Python module is imported
830 if ( !myInterp || !myModule ) {
831 // Error! Python subinterpreter should be initialized and module should be imported first!
835 // call setWorkspace() method
836 // ... first get python lock
837 PyLockWrapper aLock = myInterp->GetLockWrapper();
839 // ... then try to import SalomePyQt module. If it's not possible don't go on.
840 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
846 #ifdef __CALL_OLD_METHODS__
847 // ... then get workspace object
848 STD_MDIDesktop* aDesktop = dynamic_cast<STD_MDIDesktop*>( getApp()->desktop() );
849 if ( aDesktop && aDesktop->workspace() ) {
850 QWidget* aWorkspace = aDesktop->workspace();
851 PyObjWrapper pyws( sipMapCppToSelfSubClass( aWorkspace, sipClass_QWidget ) );
852 // ... and finally call Python module's setWorkspace() method (obsolete)
853 PyObjWrapper res( PyObject_CallMethod( myModule, "setWorkSpace", "O", pyws.get() ) );
855 // VSR: this method may not be implemented in Python module
860 #endif // __CALL_OLD_METHODS__
864 * Adds an action into private action list [internal usage]
866 void SALOME_PYQT_Module::addAction( const PyQtGUIAction type, QAction* action )
869 case PYQT_ACTION_MENU:
870 myMenuActionList.append( action );
872 case PYQT_ACTION_TOOLBAL:
873 myToolbarActionList.append( action );
875 case PYQT_ACTION_POPUP:
876 myPopupActionList.append( action );
881 //=============================================================================
882 // SALOME_PYQT_XmlHandler class implementation
883 //=============================================================================
885 // gets an tag name for the dom element [ static ]
886 // returns an empty string if the element does not have tag name
887 static QString tagName( const QDomElement& element ) {
888 return element.tagName().stripWhiteSpace();
891 // gets an attribute by it's name for the dom element [ static ]
892 // returns an empty string if the element does not have such attribute
893 static QString attribute( const QDomElement& element, const QString& attName ) {
894 return element.attribute( attName ).stripWhiteSpace();
897 // checks the given value for the boolean value [ static ]
898 // returns TRUE if string is "true", "yes" or "1"
899 static bool checkBool( const QString& value ) {
900 return ( value == "true" || value == "yes" || value == "1" );
903 // checks the given value for the integer value [ static ]
904 // returns -1 if item is empty or presents and invalid number
905 static int checkInt( const QString& value )
907 return value.isEmpty() ? -1 : value.toInt();
913 SALOME_PYQT_XmlHandler::SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName )
916 QFile aFile( fileName );
917 if ( !aFile.open( IO_ReadOnly ) )
919 if ( !myDoc.setContent( &aFile ) ) {
927 Called by SALOME_PYQT_Module::initialize() in order to create actions
928 (menus, toolbars, popup menus)
930 void SALOME_PYQT_XmlHandler::createActions()
932 // get document element
933 QDomElement aDocElem = myDoc.documentElement();
935 // get main menu actions
936 QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
937 for ( int i = 0; i < aMenuList.count(); i++ ) {
938 QDomNode n = aMenuList.item( i );
942 // create toolbars actions
943 QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
944 for ( int i = 0; i < aToolsList.count(); i++ ) {
945 QDomNode n = aToolsList.item( i );
953 void SALOME_PYQT_XmlHandler::createPopup( QPopupMenu* menu,
954 const QString& context,
955 const QString& parent,
956 const QString& object )
958 // get document element
959 QDomElement aDocElem = myDoc.documentElement();
961 // get popup menus actions
962 QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
963 for ( int i = 0; i < aPopupList.count(); i++ ) {
964 QDomNode n = aPopupList.item( i );
965 if ( !n.isNull() && n.isElement() ) {
966 QDomElement e = n.toElement();
967 QString lab = attribute( e, "label-id" );
968 QString ctx = attribute( e, "context-id" );
969 QString prt = attribute( e, "parent-id" );
970 QString obj = attribute( e, "object-id" );
971 if ( ctx == context && prt == parent && obj == object ) {
972 insertPopupItems( n, menu );
980 Create main menu with child actions
982 void SALOME_PYQT_XmlHandler::createMenu( QDomNode& parentNode, const int parentMenuId )
987 if ( parentNode.isNull() )
990 QDomElement parentElement = parentNode.toElement();
991 if ( !parentElement.isNull() ) {
992 QString plabel = attribute( parentElement, "label-id" );
993 int pid = checkInt( attribute( parentElement, "item-id" ) );
994 int ppos = checkInt( attribute( parentElement, "pos-id" ) );
995 if ( !plabel.isEmpty() ) {
997 int menuId = myModule->createMenu( plabel, // label
998 parentMenuId, // parent menu ID, should be -1 for main menu
1002 QDomNode node = parentNode.firstChild();
1003 while ( !node.isNull() ) {
1004 if ( node.isElement() ) {
1005 QDomElement elem = node.toElement();
1006 QString aTagName = tagName( elem );
1007 if ( aTagName == "popup-item" ) {
1008 int id = checkInt( attribute( elem, "item-id" ) );
1009 int pos = checkInt( attribute( elem, "pos-id" ) );
1010 QString label = attribute( elem, "label-id" );
1011 QString icon = attribute( elem, "icon-id" );
1012 QString tooltip = attribute( elem, "tooltip-id" );
1013 QString accel = attribute( elem, "accel-id" );
1014 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
1015 QString execute = attribute( elem, "execute-action" ); // not used
1018 if ( !icon.isEmpty() ) {
1019 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1020 if ( !pixmap.isNull() )
1021 anIcon = QIconSet( pixmap );
1024 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1025 // also check if the action with given ID is already created
1026 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_MENU + id ) ) {
1027 // little trick to have several actions with same ID for menus and toolbars
1028 id = SALOME_PYQT_Module::PYQT_ACTION_MENU + id;
1029 // create menu action
1030 QAction* action = myModule->createAction( id, // ID
1034 tooltip, // status-bar text
1035 QKeySequence( accel ), // keyboard accelerator
1036 myModule->getApp()->desktop(), // desktop
1037 toggle, // toogled action
1038 myModule, // receiver
1039 SLOT( onGUIEvent() ) ); // slot
1040 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_MENU, action );
1041 myModule->createMenu( action, menuId, -1, 100, pos );
1044 else if ( aTagName == "submenu" ) {
1046 createMenu( node, menuId );
1048 else if ( aTagName == "separator" ) {
1049 // create menu separator
1050 int pos = checkInt( attribute( elem, "pos-id" ) );
1051 QAction* action = myModule->separator();
1052 myModule->createMenu( action, menuId, -1, 100, pos );
1055 node = node.nextSibling();
1062 Create a toolbar with child actions
1064 void SALOME_PYQT_XmlHandler::createToolBar( QDomNode& parentNode )
1069 if ( parentNode.isNull() )
1072 QDomElement parentElement = parentNode.toElement();
1073 if ( !parentElement.isNull() ) {
1074 QString aLabel = attribute( parentElement, "label-id" );
1075 if ( !aLabel.isEmpty() ) {
1077 int tbId = myModule->createTool( aLabel );
1078 QDomNode node = parentNode.firstChild();
1079 while ( !node.isNull() ) {
1080 if ( node.isElement() ) {
1081 QDomElement elem = node.toElement();
1082 QString aTagName = tagName( elem );
1083 if ( aTagName == "toolbutton-item" ) {
1084 int id = checkInt( attribute( elem, "item-id" ) );
1085 int pos = checkInt( attribute( elem, "pos-id" ) );
1086 QString label = attribute( elem, "label-id" );
1087 QString icon = attribute( elem, "icon-id" );
1088 QString tooltip = attribute( elem, "tooltip-id" );
1089 QString accel = attribute( elem, "accel-id" );
1090 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
1091 QString execute = attribute( elem, "execute-action" ); // not used
1094 if ( !icon.isEmpty() ) {
1095 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1096 if ( !pixmap.isNull() )
1097 anIcon = QIconSet( pixmap );
1100 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1101 // also check if the action with given ID is already created
1102 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id ) ) {
1103 // little trick to have several actions with same ID for menus and toolbars
1104 id = SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id;
1105 // create toolbar action
1106 QAction* action = myModule->createAction( id, // ID
1110 tooltip, // status-bar text
1111 QKeySequence( accel ), // keyboard accelerator
1112 myModule->getApp()->desktop(), // desktop
1113 toggle, // toogled action
1114 myModule, // receiver
1115 SLOT( onGUIEvent() ) ); // slot
1116 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL, action );
1117 myModule->createTool( action, tbId, -1, pos );
1120 else if ( aTagName == "separatorTB" ) {
1121 // create toolbar separator
1122 int pos = checkInt( attribute( elem, "pos-id" ) );
1123 QAction* action = myModule->separator();
1124 myModule->createTool( action, tbId, -1, pos );
1127 node = node.nextSibling();
1133 void SALOME_PYQT_XmlHandler::insertPopupItems( QDomNode& parentNode, QPopupMenu* menu )
1138 if ( parentNode.isNull() )
1141 // we create popup menus without help of QtxPopupMgr
1142 QDomNode node = parentNode.firstChild();
1143 while ( !node.isNull() ) {
1144 if ( node.isElement() ) {
1145 QDomElement elem = node.toElement();
1146 QString aTagName = tagName( elem );
1147 if ( aTagName == "popup-item" ) {
1148 // insert a command item
1149 int id = checkInt( attribute( elem, "item-id" ) );
1150 int pos = checkInt( attribute( elem, "pos-id" ) );
1151 QString label = attribute( elem, "label-id" );
1152 QString icon = attribute( elem, "icon-id" );
1153 QString tooltip = attribute( elem, "tooltip-id" ); // not used
1154 QString accel = attribute( elem, "accel-id" );
1155 bool toggle = checkBool( attribute( elem, "toggle-id" ) ); // not used
1156 QString execute = attribute( elem, "execute-action" ); // not used
1159 if ( !icon.isEmpty() ) {
1160 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1161 if ( !pixmap.isNull() )
1162 anIcon = QIconSet( pixmap );
1165 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1166 // also check if the action with given ID is already created
1168 menu->insertItem( anIcon, label, myModule, SLOT( onGUIEvent() ), QKeySequence( accel ), id, pos );
1171 else if ( aTagName == "submenu" ) {
1173 int id = checkInt( attribute( elem, "item-id" ) );
1174 int pos = checkInt( attribute( elem, "pos-id" ) );
1175 QString label = attribute( elem, "label-id" );
1176 QString icon = attribute( elem, "icon-id" );
1179 if ( !icon.isEmpty() ) {
1180 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1181 if ( !pixmap.isNull() )
1182 anIcon = QIconSet( pixmap );
1185 QPopupMenu* newPopup = new QPopupMenu( menu, label );
1186 menu->insertItem( anIcon, label, newPopup, id, pos );
1187 insertPopupItems( node, newPopup );
1189 else if ( aTagName == "separator" ) {
1190 // create menu separator
1191 int pos = checkInt( attribute( elem, "pos-id" ) );
1192 menu->insertSeparator( pos );
1195 node = node.nextSibling();