]> SALOME platform Git repositories - modules/gui.git/blob - src/SALOME_PYQT/SALOME_PYQT_GUI/SALOME_PYQT_Module.cxx
Salome HOME
Join modifications from branch BR_3_1_0deb
[modules/gui.git] / src / SALOME_PYQT / SALOME_PYQT_GUI / SALOME_PYQT_Module.cxx
1 // Copyright (C) 2005  OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D
2 // 
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either 
6 // version 2.1 of the License.
7 // 
8 // This library is distributed in the hope that it will be useful 
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public  
14 // License along with this library; if not, write to the Free Software 
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/
18 //
19 //=============================================================================
20 // File      : SALOME_PYQT_Module.cxx
21 // Created   : 25/04/05
22 // Author    : Vadim SANDLER
23 // Project   : SALOME
24 // Copyright : 2003-2005 CEA/DEN, EDF R&D
25 // $Header   : $
26 //=============================================================================
27
28 #include "SALOME_PYQT_Module.h"
29
30 #include "PyInterp_Dispatcher.h"
31 #include "SUIT_ResourceMgr.h"
32 #include "STD_MDIDesktop.h"
33 #include "STD_TabDesktop.h"
34 #include "SalomeApp_Application.h"
35 #include "SalomeApp_Study.h"
36
37 #include "QtxWorkstack.h"
38 #include <SALOME_LifeCycleCORBA.hxx>
39 #include <Container_init_python.hxx>
40
41 #include <qfile.h>
42 #include <qdom.h>
43 #include <qworkspace.h>
44 #include <qpopupmenu.h>
45
46 #include "SALOME_PYQT_SipDefs.h"
47 #if defined(SIP_VERS_v4_old) || defined(SIP_VERS_v4_new)
48 #include "sipAPISalomePyQtGUI.h"
49 #else
50 #include "sipSalomePyQtGUIDeclSalomePyQtGUI.h"
51 #endif
52
53 #include <sipqtQWidget.h>
54 #include <sipqtQPopupMenu.h>
55
56 #include <CORBA.h>
57
58 using namespace std;
59
60 ///////////////////////////////////////////////////////////////////////////////
61 // Default name of the module, replaced at the moment of module creation
62 #define __DEFAULT_NAME__ "SALOME_PYQT_Module"
63
64 ///////////////////////////////////////////////////////////////////////////////
65 // If __CALL_OLD_METHODS__ macro is not defined the invoking of obsolete Python 
66 // module's methods like setSetting(), definePopup(), etc. is blocked.
67 // This macro is defined by default (in Makefile)
68 #ifdef __CALL_OLD_METHODS__
69 const bool IsCallOldMethods = true;
70 #else 
71 const bool IsCallOldMethods = false;
72 #endif
73
74 ///////////////////////////////////////////////////////////////////////////////
75 // NB: Python requests.
76 // General rule for Python requests created by SALOME_PYQT_Module:
77 // all requests should be executed SYNCHRONOUSLY within the main GUI thread.
78 // However, it is obligatory that ANY Python call is wrapped with a request object,
79 // so that ALL Python API calls are serialized with PyInterp_Dispatcher.
80 ///////////////////////////////////////////////////////////////////////////////
81
82 //=============================================================================
83 // The class for parsing of the XML resource files.
84 // Used for backward compatibility with existing Python modules.
85 //=============================================================================
86 class SALOME_PYQT_XmlHandler
87 {
88 public:
89   SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName );
90   void createActions();
91   void createPopup  ( QPopupMenu*    menu, 
92                       const QString& context, 
93                       const QString& parent, 
94                       const QString& object );
95
96 protected:
97   void createToolBar   ( QDomNode& parentNode );
98   void createMenu      ( QDomNode& parentNode, 
99                          const int parentMenuId = -1 );
100
101   void insertPopupItems( QDomNode&   parentNode, 
102                          QPopupMenu* menu );
103
104 private:
105   SALOME_PYQT_Module* myModule;
106   QDomDocument        myDoc;
107 };
108
109 //=============================================================================
110 // SALOME_PYQT_Module class implementation (implements CAM_Module API for
111 // all Python-based SALOME module
112 //=============================================================================
113
114 // While the SalomePyQtGUI library is not imported in Python it's initialization function
115 // should be called manually (and only once) in order to initialize global sip data
116 // and to get C API from sip : sipBuildResult for example
117 #if defined(SIP_STATIC_MODULE)
118 extern "C" void initSalomePyQtGUI();
119 #else
120 PyMODINIT_FUNC initSalomePyQtGUI();
121 #endif
122
123 /*!
124  * This function creates an instance of SALOME_PYQT_Module object by request
125  * of and application object when the module is loaded.
126  */
127 extern "C" {
128   SALOME_PYQT_EXPORT CAM_Module* createModule() {
129     static bool alreadyInitialized = false;
130     if ( !alreadyInitialized ) {
131       // call only once (see above) !
132       PyEval_RestoreThread( KERNEL_PYTHON::_gtstate );
133       initSalomePyQtGUI(); 
134       PyEval_ReleaseThread( KERNEL_PYTHON::_gtstate );
135       alreadyInitialized = !alreadyInitialized;
136     }
137     return new SALOME_PYQT_Module();
138   }
139 }
140
141 /*! 
142  * Static variables definition
143  */
144 SALOME_PYQT_Module::InterpMap SALOME_PYQT_Module::myInterpMap;
145 SALOME_PYQT_Module* SALOME_PYQT_Module::myInitModule = 0;
146
147 /*!
148  * Little trick : provide an access to being activated Python module from outside;
149  * needed by the SalomePyQt library :(
150 */
151 SALOME_PYQT_Module* SALOME_PYQT_Module::getInitModule() 
152
153   return myInitModule; 
154 }
155
156 /*!
157  * Constructor
158  */
159 SALOME_PYQT_Module::SALOME_PYQT_Module() :
160        SalomeApp_Module( __DEFAULT_NAME__ ), myModule( 0 ), myXmlHandler ( 0 )
161 {
162   myMenuActionList.setAutoDelete( false );
163   myPopupActionList.setAutoDelete( false );
164   myToolbarActionList.setAutoDelete( false );
165 }
166
167 /*!
168  * Destructor
169  */
170 SALOME_PYQT_Module::~SALOME_PYQT_Module()
171 {
172   myMenuActionList.clear();
173   myPopupActionList.clear();
174   myToolbarActionList.clear();
175   if ( myXmlHandler )
176     delete myXmlHandler;
177 }
178
179 /*!
180  * Initialization of the module.
181  * Inherited from CAM_Module.
182  *
183  * This method is used for creation of the menus, toolbars and other staff.
184  * There are two ways:
185  * - for obsolete modules this method first tries to read <module>_<language>.xml 
186  *   resource file which contains a menu, toolbars and popup menus description.
187  * - new modules can create menus by by calling the corresponding methods of SalomePyQt
188  *   Python API in the Python module's initialize() method which is called from here.
189  * NOTE: if postponed modules loading is not used, the active study might be not defined
190  * yet at this stage, so initialize() method should not perform any study-based initialization.
191  */
192 void SALOME_PYQT_Module::initialize( CAM_Application* app )
193 {
194   SalomeApp_Module::initialize( app );
195
196   // Try to get XML resource file name
197   SUIT_ResourceMgr* aResMgr = getApp()->resourceMgr();
198   QString aLang = aResMgr->stringValue( "language", "language", QString::null );
199   if ( aLang.isEmpty() ) aLang = QString( "en" );
200   QString aName = name( "" );
201   QString aFileName = aName + "_" + aLang + ".xml";
202   aFileName = aResMgr->path( "resources", aName, aFileName );
203  
204   // parse XML file if it is found and create actions
205   if ( !myXmlHandler && !aFileName.isEmpty() ) {
206     myXmlHandler = new SALOME_PYQT_XmlHandler( this, aFileName );
207     myXmlHandler->createActions();
208   }
209
210   // perform internal initialization and call module's initialize() method
211   // InitializeReq: request class for internal init() operation
212   class InitializeReq : public PyInterp_Request
213   {
214   public:
215     InitializeReq( CAM_Application*    _app,
216                    SALOME_PYQT_Module* _obj ) 
217       : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
218         myApp( _app ),
219         myObj( _obj ) {}
220     
221   protected:
222     virtual void execute()
223     {
224       myObj->init( myApp );
225     }
226
227   private:
228     CAM_Application*    myApp;
229     SALOME_PYQT_Module* myObj;
230   };
231
232   // Posting the request
233   PyInterp_Dispatcher::Get()->Exec( new InitializeReq( app, this ) );
234 }
235
236 /*!
237  * Activation of the module.
238  * Inherited from CAM_Module.
239  */
240 bool SALOME_PYQT_Module::activateModule( SUIT_Study* theStudy )
241 {
242   MESSAGE( "SALOME_PYQT_Module::activateModule" );
243
244   bool res = SalomeApp_Module::activateModule( theStudy );
245   
246   if ( !res )
247     return res;
248
249   // ActivateReq: request class for internal activate() operation
250   class ActivateReq : public PyInterp_Request
251   {
252   public:
253     ActivateReq( SUIT_Study*         _study, 
254                  SALOME_PYQT_Module* _obj ) 
255       : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
256         myStudy ( _study ),
257         myObj   ( _obj   ) {}
258     
259   protected:
260     virtual void execute()
261     {
262       myObj->activate( myStudy );
263     }
264
265   private:
266     SUIT_Study*         myStudy;
267     SALOME_PYQT_Module* myObj;
268   };
269
270   // Posting the request
271   PyInterp_Dispatcher::Get()->Exec( new ActivateReq( theStudy, this ) );
272
273   // activate menus, toolbars, etc
274   setMenuShown( true );
275   setToolShown( true );
276
277   return true;
278 }
279
280 /*!
281  * Deactivation of the module.
282  * Inherited from CAM_Module.
283  */
284 bool SALOME_PYQT_Module::deactivateModule( SUIT_Study* theStudy )
285 {
286   MESSAGE( "SALOME_PYQT_Module::deactivateModule" );
287
288   bool res = SalomeApp_Module::deactivateModule( theStudy );
289
290   // deactivate menus, toolbars, etc
291   setMenuShown( false );
292   setToolShown( false );
293
294   // DeactivateReq: request class for internal deactivate() operation
295   class DeactivateReq : public PyInterp_LockRequest
296   {
297   public:
298     DeactivateReq( PyInterp_base*      _py_interp, 
299                    SUIT_Study*         _study, 
300                    SALOME_PYQT_Module* _obj ) 
301       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
302         myStudy ( _study ),
303         myObj   ( _obj   ) {}
304     
305   protected:
306     virtual void execute()
307     {
308       myObj->deactivate( myStudy );
309     }
310
311   private:
312     SUIT_Study*         myStudy;
313     SALOME_PYQT_Module* myObj;
314   };
315
316   // Posting the request
317   PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, theStudy, this ) );
318
319   return res;
320 }
321
322 /*!
323  * Processes GUI action (from main menu, toolbar or context popup menu)
324  */
325 void SALOME_PYQT_Module::onGUIEvent()
326 {
327   // get sender action
328   const QObject* obj = sender();
329   if ( !obj || !obj->inherits( "QAction" ) )
330     return;
331   QAction* action = (QAction*)obj;
332
333   // get action ID
334   int id = actionId( action );
335   MESSAGE( "SALOME_PYQT_Module::onGUIEvent: id = " << id );
336
337   // perform synchronous request to Python event dispatcher
338   class GUIEvent : public PyInterp_LockRequest
339   {
340   public:
341     GUIEvent( PyInterp_base*      _py_interp, 
342               SALOME_PYQT_Module* _obj,
343               int                 _id ) 
344       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
345         myId    ( _id  ),
346         myObj   ( _obj ) {}
347     
348   protected:
349     virtual void execute()
350     {
351       myObj->guiEvent( myId );
352     }
353
354   private:
355     int                 myId;
356     SALOME_PYQT_Module* myObj;
357   };
358
359   // Posting the request
360   PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
361 }
362
363 /*!
364  * Processes GUI action (from context popup menu, only for XML-based actions!)
365  */
366 void SALOME_PYQT_Module::onGUIEvent( int id ) 
367 {
368   // perform synchronous request to Python event dispatcher
369   class GUIEvent : public PyInterp_LockRequest
370   {
371   public:
372     GUIEvent( PyInterp_base*      _py_interp, 
373               SALOME_PYQT_Module* _obj,
374               int                 _id ) 
375       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
376         myId    ( _id  ),
377         myObj   ( _obj ) {}
378     
379   protected:
380     virtual void execute()
381     {
382       myObj->guiEvent( myId );
383     }
384
385   private:
386     int                 myId;
387     SALOME_PYQT_Module* myObj;
388   };
389
390   // Posting the request
391   PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
392 }
393
394 /*! 
395   Context popup menu request.
396   Called when user activates popup menu in some window (view, object browser, etc).
397   */
398 void SALOME_PYQT_Module::contextMenuPopup( const QString& theContext, QPopupMenu* thePopupMenu, QString& /*title*/ )
399 {
400   MESSAGE( "SALOME_PYQT_Module::contextMenuPopup : " << theContext.latin1() );
401   // perform synchronous request to Python event dispatcher
402   class PopupMenuEvent : public PyInterp_LockRequest
403   {
404   public:
405     PopupMenuEvent( PyInterp_base*     _py_interp, 
406                     SALOME_PYQT_Module* _obj,
407                     const QString&      _context, 
408                     QPopupMenu*        _popup ) 
409       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
410         myContext( _context ), 
411         myPopup  ( _popup  ),
412         myObj    ( _obj )   {}
413     
414   protected:
415     virtual void execute()
416     {
417       myObj->contextMenu( myContext, myPopup );
418     }
419
420   private:
421     SALOME_PYQT_Module* myObj;
422     QString             myContext;
423     QPopupMenu*         myPopup;
424   };
425
426   // Posting the request only if dispatcher is not busy!
427   // Executing the request synchronously
428   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
429     PyInterp_Dispatcher::Get()->Exec( new PopupMenuEvent( myInterp, this, theContext, thePopupMenu ) );
430 }
431
432 /*!
433  * Defines the dockable window associated with the module.
434  * To fill the list of windows the correspondind Python module's windows() 
435  * method is called from SALOME_PYQT_Module::init() method.
436  * By default, ObjectBrowser, PythonConsole and LogWindow are provided.
437  */
438 void SALOME_PYQT_Module::windows( QMap<int, int>& mappa ) const
439 {
440   // First clear the output parameters 
441   QMap<int, int>::ConstIterator it;
442   for ( it = myWindowsMap.begin(); it != myWindowsMap.end(); ++it ) {
443     mappa[ it.key() ] = it.data();
444   }
445 }
446
447 /*!
448  * Defines the compatible views which should be opened on module activation.
449  * To fill the list of views the correspondind Python module's views() 
450  * method is called from SALOME_PYQT_Module::init() method.
451  * By default, the list is empty.
452  */
453 void SALOME_PYQT_Module::viewManagers( QStringList& listik ) const
454 {
455   for ( QStringList::ConstIterator it = myViewMgrList.begin(); it != myViewMgrList.end(); ++it ) {
456     listik.append( *it );
457   }
458 }
459
460 /*!
461  * Performs internal initialization
462  * - initializes/gets the Python interpreter (one per study)
463  * - imports the Python module
464  * - passes the workspace widget to the Python module
465  * - calls Python module's initialize() method
466  * - calls Python module's windows() method
467  * - calls Python module's views() method
468  */
469 void SALOME_PYQT_Module::init( CAM_Application* app )
470 {
471   // reset interpreter to NULL
472   myInterp = NULL;
473
474   // get study Id
475   SalomeApp_Application* anApp = dynamic_cast<SalomeApp_Application*>( app );
476   if ( !anApp )
477     return;
478
479   SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() );
480   if ( !aStudy )
481     return;
482   int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
483
484   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
485   initInterp( aStudyId );
486   if ( !myInterp ) 
487     return; // Error 
488
489   // import Python GUI module
490   importModule();
491   if ( !myModule )
492     return; // Error 
493  
494   myInitModule = this;
495
496   if ( IsCallOldMethods ) { // __CALL_OLD_METHODS__
497     // call Python module's setWorkspace() method
498     setWorkSpace();
499   }                         //__CALL_OLD_METHODS__
500
501   // then call Python module's initialize() method
502   // ... first get python lock
503   PyLockWrapper aLock = myInterp->GetLockWrapper();
504   // ... (the Python module is already imported)
505   // ... finally call Python module's initialize() method
506   if(PyObject_HasAttrString(myModule , "initialize")){
507     PyObjWrapper res( PyObject_CallMethod( myModule, "initialize", "" ) );
508     if( !res ) {
509       PyErr_Print();
510     }
511   }
512   
513   // get the windows list from the Python module by calling windows() method
514   // ... first put default values
515   myWindowsMap.insert( SalomeApp_Application::WT_ObjectBrowser, Qt::DockLeft );
516   myWindowsMap.insert( SalomeApp_Application::WT_PyConsole,     Qt::DockBottom );
517   // VSR: LogWindow is not yet implemented
518   // myWindowsMap.insert( SalomeApp_Application::WT_LogWindow,     Qt::DockBottom );
519
520   if(PyObject_HasAttrString(myModule , "windows")){
521     PyObjWrapper res1( PyObject_CallMethod( myModule, "windows", "" ) );
522     if( !res1 ) {
523       PyErr_Print();
524     }
525     else {
526       myWindowsMap.clear();
527       if ( PyDict_Check( res1 ) ) {
528         PyObject* key;
529         PyObject* value;
530         int pos = 0;
531         while ( PyDict_Next( res1, &pos, &key, &value ) ) {
532           // parse the return value
533           // it should be a map: {integer:integer}
534           int aKey, aValue;
535           if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
536             aKey   = PyInt_AsLong( key );
537             aValue = PyInt_AsLong( value );
538             myWindowsMap[ aKey ] = aValue;
539           }
540         }
541       }
542     }
543   }
544   // get the windows list from the Python module by calling views() method
545   if(PyObject_HasAttrString(myModule , "views")){
546     PyObjWrapper res2( PyObject_CallMethod( myModule, "views", "" ) );
547     if( !res2 ) {
548       PyErr_Print();
549     }
550     else {
551       // parse the return value
552       // result can be one string...
553       if ( PyString_Check( res2 ) ) {
554         myViewMgrList.append( PyString_AsString( res2 ) );
555       }
556       // ... or list of strings
557       else if ( PyList_Check( res2 ) ) {
558         int size = PyList_Size( res2 );
559         for ( int i = 0; i < size; i++ ) {
560           PyObject* value = PyList_GetItem( res2, i );
561           if( value && PyString_Check( value ) ) {
562             myViewMgrList.append( PyString_AsString( value ) );
563           }
564         }
565       }
566     }
567   }
568   myInitModule = 0;
569 }
570
571 /*!
572  * Performs internal activation: 
573  * - initializes/gets the Python interpreter (one per study)
574  * - imports the Python GUI module
575  * - calls Python module's setSettings() method (obsolete function, used for compatibility with old code)
576  *   or activate() method (for new modules)
577  */
578 void SALOME_PYQT_Module::activate( SUIT_Study* theStudy )
579 {
580   // get study Id
581   SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
582   int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
583
584   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
585   initInterp( aStudyId );
586   if ( !myInterp ) 
587     return; // Error 
588
589   // import Python GUI module
590   importModule();
591   if ( !myModule )
592     return; // Error 
593  
594   // get python lock
595   PyLockWrapper aLock = myInterp->GetLockWrapper();
596
597   if ( IsCallOldMethods ) { //__CALL_OLD_METHODS__
598     // call Python module's setSettings() method (obsolete)
599     if(PyObject_HasAttrString(myModule , "setSettings")){
600       PyObjWrapper res( PyObject_CallMethod( myModule, "setSettings", "" ) );
601       if( !res ) {
602         PyErr_Print();
603       }
604     }
605   }                         //__CALL_OLD_METHODS__
606
607   // call Python module's activate() method (for the new modules)
608   if(PyObject_HasAttrString(myModule , "activate")){
609     PyObjWrapper res1( PyObject_CallMethod( myModule, "activate", "" ) );
610     if( !res1 ) {
611       PyErr_Print();
612     }
613   }
614 }
615
616 /*!
617  * Performs internal deactivation: 
618  * - calls Python module's deactivate() method
619  */
620 void SALOME_PYQT_Module::deactivate( SUIT_Study* theStudy )
621 {
622   // check if the subinterpreter is initialized and Python module is imported
623   if ( !myInterp || !myModule ) {
624     // Error! Python subinterpreter should be initialized and module should be imported first!
625     return;
626   }
627   // then call Python module's deactivate() method
628   if(PyObject_HasAttrString(myModule , "deactivate")){
629     PyObjWrapper res( PyObject_CallMethod( myModule, "deactivate", "" ) );
630     if( !res ) {
631       PyErr_Print();
632     }
633   }
634 }
635
636 /*!
637  * Called when active the study is actived (user brings its desktop to top)
638  * - initializes/gets the Python interpreter (one per study)
639  * - imports the Python GUI module
640  * - calls Python module's activeStudyChanged() method
641  */
642 void SALOME_PYQT_Module::studyChanged( SUIT_Study* theStudy )
643 {
644   // get study Id
645   SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
646   int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
647
648   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
649   initInterp( aStudyId );
650   if ( !myInterp ) 
651     return; // Error 
652
653   // import Python GUI module
654   importModule();
655   if ( !myModule )
656     return; // Error 
657  
658   // get python lock
659   PyLockWrapper aLock = myInterp->GetLockWrapper();
660
661   // call Python module's activeStudyChanged() method
662   if(PyObject_HasAttrString(myModule , "activeStudyChanged")){
663     PyObjWrapper res( PyObject_CallMethod( myModule, "activeStudyChanged", "i", aStudyId ) );
664     if( !res ) {
665       PyErr_Print();
666     }
667   }
668 }
669
670 /*!
671  * Get module engine, returns nil var if engine is not found in LifeCycleCORBA
672  */
673 Engines::Component_var SALOME_PYQT_Module::getEngine() const
674 {
675   Engines::Component_var comp;
676   // temporary solution
677   try {
678     comp = getApp()->lcc()->FindOrLoad_Component( "FactoryServerPy", name( "" ) );
679   }
680   catch (CORBA::Exception&) {
681   }
682   return comp;
683 }
684
685 /*!
686  * Get module engine IOR, returns empty string if engine is not found in LifeCycleCORBA
687  */
688 QString SALOME_PYQT_Module::engineIOR() const
689 {
690   if ( !CORBA::is_nil( getEngine() ) )
691     return QString( getApp()->orb()->object_to_string( getEngine() ) );
692   return QString( "" );
693 }
694
695 /*! 
696  * Called when study desktop is activated.
697  * Used for notifying about changing of the active study.
698  */
699 void SALOME_PYQT_Module::studyActivated()
700 {
701   // StudyChangedReq: request class for internal studyChanged() operation
702   class StudyChangedReq : public PyInterp_Request
703   {
704   public:
705     StudyChangedReq( SUIT_Study*         _study, 
706                      SALOME_PYQT_Module* _obj ) 
707       : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
708         myStudy ( _study ),
709         myObj   ( _obj   ) {}
710     
711   protected:
712     virtual void execute()
713     {
714       myObj->studyChanged( myStudy );
715     }
716
717   private:
718     SUIT_Study*         myStudy;
719     SALOME_PYQT_Module* myObj;
720   };
721
722   // Posting the request
723   PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( application()->activeStudy(), this ) );
724 }
725
726 /*!
727  * Processes context popup menu request
728  * - calls Python module's definePopup(...) method (obsolete function, used for compatibility with old code)
729  *   to define the popup menu context
730  * - parses XML resourses file (if exists) and fills the popup menu with the items)
731  * - calls Python module's customPopup(...) method (obsolete function, used for compatibility with old code)
732  *   to allow module to customize the popup menu
733  * - for new modules calls createPopupMenu() function to allow the modules to build the popup menu
734  *   by using insertItem(...) Qt functions.
735  */
736 void SALOME_PYQT_Module::contextMenu( const QString& theContext, QPopupMenu* thePopupMenu )
737 {
738   // Python interpreter should be initialized and Python module should be
739   // import first
740   if ( !myInterp || !myModule )
741     return;
742   
743   QString aContext( theContext ), aObject( "" ), aParent( "" );
744  
745   if ( IsCallOldMethods && PyObject_HasAttrString(myModule , "definePopup") ) { //__CALL_OLD_METHODS__
746     // call definePopup() Python module's function
747     // this is obsolete function, used only for compatibility reasons
748     PyObjWrapper res(PyObject_CallMethod( myModule, 
749                                           "definePopup", 
750                                           "sss",
751                                           aContext.latin1(), 
752                                           aObject.latin1(), 
753                                           aParent.latin1() ) );
754     if( !res ) {
755       PyErr_Print();
756     }
757     else {
758       // parse return value
759       char *co, *ob, *pa;
760       if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
761         aContext = co;
762         aObject  = ob;
763         aParent  = pa;
764       }
765     }
766   }                        //__CALL_OLD_METHODS__
767
768   // first try to create menu via XML parser:
769   // we create popup menus without help of QtxPopupMgr
770   if ( myXmlHandler )
771     myXmlHandler->createPopup( thePopupMenu, aContext, aParent, aObject );
772
773   PyObjWrapper sipPopup( sipBuildResult( 0, "M", thePopupMenu, sipClass_QPopupMenu ) );
774
775   // then call Python module's createPopupMenu() method (for new modules)
776   if ( PyObject_HasAttrString(myModule , "createPopupMenu") ) { 
777     PyObjWrapper res1( PyObject_CallMethod( myModule,
778                                           "createPopupMenu",
779                                           "Os",
780                                           sipPopup.get(),
781                                           aContext.latin1() ) );
782     if( !res1 ) {
783       PyErr_Print();
784     }
785   }
786
787   if ( IsCallOldMethods && PyObject_HasAttrString(myModule , "customPopup") ) { //__CALL_OLD_METHODS__
788     // call customPopup() Python module's function
789     // this is obsolete function, used only for compatibility reasons
790     PyObjWrapper res2( PyObject_CallMethod( myModule,
791                                             "customPopup",
792                                             "Osss",
793                                             sipPopup.get(),
794                                             aContext.latin1(), 
795                                             aObject.latin1(), 
796                                             aParent.latin1() ) );
797     if( !res2 ) {
798       PyErr_Print();
799     }
800   }                        //__CALL_OLD_METHODS__
801 }
802
803 /*!
804  * Processes GUI event
805  * - calls Python module's OnGUIEvent() method
806  */ 
807 void SALOME_PYQT_Module::guiEvent( const int theId )
808 {
809   // Python interpreter should be initialized and Python module should be
810   // import first
811   if ( !myInterp || !myModule )
812     return;
813   
814   if ( PyObject_HasAttrString(myModule , "OnGUIEvent") ) { 
815     PyObjWrapper res( PyObject_CallMethod( myModule, "OnGUIEvent", "i", theId ) );
816     if( !res ) {
817       PyErr_Print();
818     }
819   }
820 }
821
822 /*!
823  *  Initialises python subinterpreter (one per study)
824  */
825 void SALOME_PYQT_Module::initInterp( int theStudyId )
826 {
827   // check study Id
828   if ( !theStudyId ) {
829     // Error! Study Id must not be 0!
830     myInterp = NULL;
831     return;
832   }
833   // try to find the subinterpreter
834   if( myInterpMap.find( theStudyId ) != myInterpMap.end() ) {
835     // found!
836     myInterp = myInterpMap[ theStudyId ];
837     return;
838   }
839   // not found - create a new one!
840   ///////////////////////////////////////////////////////////////////
841   // Attention: the creation of Python interpretor must be protected 
842   // by a C++ Lock because of C threads
843   ///////////////////////////////////////////////////////////////////
844   myInterp = new SALOME_PYQT_PyInterp();
845   myInterp->initialize();
846   myInterpMap[ theStudyId ] = myInterp;
847    
848   // import 'salome' module and call 'salome_init' method;
849   // do it only once on interpreter creation
850   // ... first get python lock
851   PyLockWrapper aLock = myInterp->GetLockWrapper();
852   // ... then import a module
853   PyObjWrapper aMod = PyImport_ImportModule( "salome" );
854   if( !aMod ) {
855     // Error!
856     PyErr_Print();
857     return;
858   }
859   // ... then call a method
860   PyObjWrapper aRes( PyObject_CallMethod( aMod, "salome_init", "" ) );
861   if( !aRes ) {
862     // Error!
863     PyErr_Print();
864     return;
865   }
866 }
867
868 /*!
869  *  Imports Python GUI module and remember the reference to the module
870  *  !!! initInterp() should be called first!!!
871  */
872 void SALOME_PYQT_Module::importModule()
873 {
874   // check if the subinterpreter is initialized
875   if ( !myInterp ) {
876     // Error! Python subinterpreter should be initialized first!
877     myModule = 0;
878     return;
879   }
880   // import Python GUI module and puts it in <myModule> attribute
881   // ... first get python lock
882   PyLockWrapper aLock = myInterp->GetLockWrapper();
883   // ... then import a module
884   QString aMod = QString( name("") ) + "GUI";
885   myModule = PyImport_ImportModule( (char*)( aMod.latin1() ) );
886   if( !myModule ) {
887     // Error!
888     PyErr_Print();
889     return;
890   }
891 }
892
893 /*!
894  *  Calls <module>.setWorkSpace() method with PyQt QWidget object to use with
895  *  interpreter.
896  *  !!! initInterp() and importModule() should be called first!!!
897  */
898 void SALOME_PYQT_Module::setWorkSpace()
899 {
900   // check if the subinterpreter is initialized and Python module is imported
901   if ( !myInterp || !myModule ) {
902     // Error! Python subinterpreter should be initialized and module should be imported first!
903     return;
904   }
905
906   // call setWorkspace() method
907   // ... first get python lock
908   PyLockWrapper aLock = myInterp->GetLockWrapper();
909
910   // ... then try to import SalomePyQt module. If it's not possible don't go on.
911   PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
912   if( !aQtModule ) {
913     // Error!
914     PyErr_Print();
915     return;
916   }  
917
918   if ( IsCallOldMethods ) { //__CALL_OLD_METHODS__
919     // ... then get workspace object
920     QWidget* aWorkspace = 0;
921     if ( getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
922       STD_MDIDesktop* aDesktop = dynamic_cast<STD_MDIDesktop*>( getApp()->desktop() );
923       if ( aDesktop )
924         aWorkspace = aDesktop->workspace();
925     }
926     else if ( getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
927       STD_TabDesktop* aDesktop = dynamic_cast<STD_TabDesktop*>( getApp()->desktop() );
928       if ( aDesktop )
929         aWorkspace = aDesktop->workstack();
930     }
931     PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget ) );
932     // ... and finally call Python module's setWorkspace() method (obsolete)
933     if ( PyObject_HasAttrString(myModule , "setWorkSpace") ) { 
934       PyObjWrapper res( PyObject_CallMethod( myModule, "setWorkSpace", "O", pyws.get() ) );
935       if( !res ) {
936         PyErr_Print();
937       }
938     }
939   }                         //__CALL_OLD_METHODS__
940 }
941
942 /*!
943  * Adds an action into private action list [internal usage]
944  */
945 void SALOME_PYQT_Module::addAction( const PyQtGUIAction type, QAction* action )
946 {
947   switch ( type ) {
948   case PYQT_ACTION_MENU:
949     myMenuActionList.append( action );
950     break;
951   case PYQT_ACTION_TOOLBAL:
952     myToolbarActionList.append( action );
953     break;
954   case PYQT_ACTION_POPUP:
955     myPopupActionList.append( action );
956     break;
957   }
958 }
959
960
961 /*!
962  * The next methods just call the parent implementation.
963  * This is done to open protected methods from CAM_Module class.
964 */
965 int SALOME_PYQT_Module::createTool( const QString& name )
966 {
967   return SalomeApp_Module::createTool( name );
968 }
969 int SALOME_PYQT_Module::createTool( const int id, const int tBar, const int idx )
970 {
971   return SalomeApp_Module::createTool( id, tBar, idx );
972 }
973 int SALOME_PYQT_Module::createTool( const int id, const QString& tBar, const int idx )
974 {
975   return SalomeApp_Module::createTool( id, tBar, idx );
976 }
977 int SALOME_PYQT_Module::createTool( QAction* a, const int tBar, const int id, const int idx )
978 {
979   return SalomeApp_Module::createTool( a, tBar, id, idx );
980 }
981 int SALOME_PYQT_Module::createTool( QAction* a, const QString& tBar, const int id, const int idx )
982 {
983   return SalomeApp_Module::createTool( a, tBar, id, idx );
984 }
985 int SALOME_PYQT_Module::createMenu( const QString& subMenu, const int menu, const int id, const int group, const int idx )
986 {
987   return SalomeApp_Module::createMenu( subMenu, menu, id, group, idx );
988 }
989 int SALOME_PYQT_Module::createMenu( const QString& subMenu, const QString& menu, const int id, const int group, const int idx )
990 {
991   return SalomeApp_Module::createMenu( subMenu, menu, id, group, idx );
992 }
993 int SALOME_PYQT_Module::createMenu( const int id, const int menu, const int group, const int idx )
994 {
995   return SalomeApp_Module::createMenu( id, menu, group, idx );
996 }
997 int SALOME_PYQT_Module::createMenu( const int id, const QString& menu, const int group, const int idx )
998 {
999   return SalomeApp_Module::createMenu( id, menu, group, idx );
1000 }
1001 int SALOME_PYQT_Module::createMenu( QAction* a, const int menu, const int id, const int group, const int idx )
1002 {
1003   return SalomeApp_Module::createMenu( a, menu, id, group, idx );
1004 }
1005 int SALOME_PYQT_Module::createMenu( QAction* a, const QString& menu, const int id, const int group, const int idx )
1006 {
1007   return SalomeApp_Module::createMenu( a, menu, id, group, idx );
1008 }
1009 QAction* SALOME_PYQT_Module::createSeparator()
1010 {
1011   return SalomeApp_Module::separator();
1012 }
1013 QAction* SALOME_PYQT_Module::action( const int id ) const
1014 {
1015   QAction* a = SalomeApp_Module::action( id );
1016   if ( !a ) // try own action map for menu items
1017     a = SalomeApp_Module::action( id + PYQT_ACTION_MENU );
1018   if ( !a ) // try own action map for toolbar items
1019     a = SalomeApp_Module::action( id + PYQT_ACTION_TOOLBAL );
1020   if ( !a ) // try own action map for popup items
1021     a = SalomeApp_Module::action( id + PYQT_ACTION_POPUP );
1022   return a;
1023 }
1024 int SALOME_PYQT_Module::actionId( const QAction* a ) const
1025 {
1026   int id = SalomeApp_Module::actionId( a );
1027   if ( myMenuActionList.contains( a ) )    // check own action map for menu items
1028     id -= PYQT_ACTION_MENU;
1029   if ( myToolbarActionList.contains( a ) ) // check own action map for toolbar items
1030     id -= PYQT_ACTION_TOOLBAL;
1031   if ( myPopupActionList.contains( a ) )   // check own action map for popup items
1032     id -= PYQT_ACTION_POPUP;
1033   return id;
1034 }
1035 QAction* SALOME_PYQT_Module::createAction( const int id, const QString& text, const QString& icon, 
1036                                            const QString& menu, const QString& tip, const int key,
1037                                            const bool toggle )
1038 {
1039   QIconSet anIcon;
1040   if ( !icon.isEmpty() ) {
1041     QPixmap pixmap  = getApp()->resourceMgr()->loadPixmap( name(""), tr( icon ) );
1042     if ( !pixmap.isNull() )
1043       anIcon = QIconSet( pixmap );
1044   }
1045   return SalomeApp_Module::createAction( id, text, anIcon, menu, tip, key, getApp()->desktop(), toggle, this, SLOT( onGUIEvent() ) );
1046 }
1047
1048
1049 //=============================================================================
1050 // SALOME_PYQT_XmlHandler class implementation
1051 //=============================================================================
1052
1053 // gets an tag name for the dom element [ static ]
1054 // returns an empty string if the element does not have tag name
1055 static QString tagName( const QDomElement& element ) {
1056  return element.tagName().stripWhiteSpace();
1057 }
1058
1059 // gets an attribute by it's name for the dom element [ static ]
1060 // returns an empty string if the element does not have such attribute
1061 static QString attribute( const QDomElement& element, const QString& attName ) {
1062   return element.attribute( attName ).stripWhiteSpace();
1063 }
1064
1065 // checks the given value for the boolean value [ static ]
1066 // returns TRUE if string is "true", "yes" or "1"
1067 static bool checkBool( const QString& value ) {
1068   return ( value == "true" || value == "yes" || value == "1" );
1069 }
1070
1071 // checks the given value for the integer value [ static ]
1072 // returns -1 if item is empty or presents and invalid number
1073 static int checkInt( const QString& value ) 
1074 {
1075   return value.isEmpty() ? -1 : value.toInt();
1076 }
1077
1078 /*!
1079  * Constructor
1080  */
1081 SALOME_PYQT_XmlHandler::SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName ) 
1082      : myModule( module )
1083 {
1084   QFile aFile( fileName );
1085   if ( !aFile.open( IO_ReadOnly ) )
1086     return;
1087   if ( !myDoc.setContent( &aFile ) ) {
1088       aFile.close();
1089       return;
1090   }
1091   aFile.close();
1092 }
1093
1094 /*!
1095   Called by SALOME_PYQT_Module::initialize() in order to create actions 
1096   (menus, toolbars, popup menus)
1097  */
1098 void SALOME_PYQT_XmlHandler::createActions()
1099 {
1100   // get document element
1101   QDomElement aDocElem = myDoc.documentElement();
1102
1103   // get main menu actions
1104   QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
1105   for ( int i = 0; i < aMenuList.count(); i++ ) {
1106     QDomNode n = aMenuList.item( i );
1107     createMenu( n );
1108   }
1109
1110   // create toolbars actions
1111   QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
1112   for ( int i = 0; i < aToolsList.count(); i++ ) {
1113     QDomNode n = aToolsList.item( i );
1114     createToolBar( n );
1115   }
1116 }
1117
1118 /*!
1119  *  Creates popup menu
1120  */
1121 void SALOME_PYQT_XmlHandler::createPopup( QPopupMenu*    menu, 
1122                                           const QString& context, 
1123                                           const QString& parent, 
1124                                           const QString& object )
1125 {
1126   // get document element
1127   QDomElement aDocElem = myDoc.documentElement();
1128
1129   // get popup menus actions
1130   QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
1131   for ( int i = 0; i < aPopupList.count(); i++ ) {
1132     QDomNode n = aPopupList.item( i );
1133     if ( !n.isNull() && n.isElement() ) {
1134       QDomElement e = n.toElement();
1135       QString lab = attribute( e, "label-id"   );
1136       QString ctx = attribute( e, "context-id" );
1137       QString prt = attribute( e, "parent-id"  );
1138       QString obj = attribute( e, "object-id"  );
1139       if ( ctx == context && prt == parent && obj == object )  {
1140         insertPopupItems( n, menu );
1141         break;
1142       }
1143     }
1144   }
1145 }
1146
1147 /*!
1148   Create main menu with child actions
1149  */
1150 void SALOME_PYQT_XmlHandler::createMenu( QDomNode& parentNode, const int parentMenuId )
1151 {
1152   if ( !myModule )
1153     return;
1154   
1155   if ( parentNode.isNull() )
1156     return;
1157
1158   QDomElement parentElement = parentNode.toElement(); 
1159   if ( !parentElement.isNull() ) {
1160     QString plabel = attribute( parentElement, "label-id" );
1161     int     pid    = checkInt( attribute( parentElement, "item-id" ) );
1162     int     ppos   = checkInt( attribute( parentElement, "pos-id" ) );
1163     if ( !plabel.isEmpty() ) {
1164       // create menu
1165       int menuId = myModule->createMenu( plabel,         // label
1166                                          parentMenuId,   // parent menu ID, should be -1 for main menu
1167                                          pid,            // ID
1168                                          80,             // group ID
1169                                          ppos );         // position
1170       QDomNode node = parentNode.firstChild();
1171       while ( !node.isNull() ) {
1172         if ( node.isElement() ) {
1173           QDomElement elem = node.toElement();
1174           QString aTagName = tagName( elem );
1175           if ( aTagName == "popup-item" ) {
1176             int     id      = checkInt( attribute( elem, "item-id" ) );
1177             int     pos     = checkInt( attribute( elem, "pos-id" ) );
1178             QString label   = attribute( elem, "label-id" );
1179             QString icon    = attribute( elem, "icon-id" );
1180             QString tooltip = attribute( elem, "tooltip-id" );
1181             QString accel   = attribute( elem, "accel-id" );
1182             bool    toggle  = checkBool( attribute( elem, "toggle-id" ) );
1183             ////QString execute = attribute( elem, "execute-action" );               // not used
1184
1185             // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1186             // also check if the action with given ID is already created
1187             if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_MENU + id ) ) {
1188               // little trick to have several actions with same ID for menus and toolbars
1189               id = SALOME_PYQT_Module::PYQT_ACTION_MENU + id;
1190               // create menu action
1191               QAction* action = myModule->createAction( id,                               // ID
1192                                                         tooltip,                          // tooltip
1193                                                         icon,                             // icon
1194                                                         label,                            // menu text
1195                                                         tooltip,                          // status-bar text
1196                                                         QKeySequence( accel ),            // keyboard accelerator
1197                                                         toggle );                         // toogled action
1198               myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_MENU, action );
1199               myModule->createMenu( action, menuId, -1, 80, pos );
1200             }
1201           }
1202           else if ( aTagName == "submenu" ) {
1203             // create sub-menu
1204             createMenu( node, menuId );
1205           }
1206           else if ( aTagName == "separator" ) {
1207             // create menu separator
1208             int     pos     = checkInt( attribute( elem, "pos-id" ) );
1209             QAction* action = myModule->createSeparator();
1210             myModule->createMenu( action, menuId, -1, 80, pos );
1211           }
1212         }
1213         node = node.nextSibling();
1214       }
1215     }
1216   }
1217 }
1218
1219 /*!
1220   Create a toolbar with child actions
1221  */
1222 void SALOME_PYQT_XmlHandler::createToolBar( QDomNode& parentNode )
1223 {
1224   if ( !myModule )
1225     return;
1226   
1227   if ( parentNode.isNull() )
1228     return;
1229
1230   QDomElement parentElement = parentNode.toElement(); 
1231   if ( !parentElement.isNull() ) {
1232     QString aLabel = attribute( parentElement, "label-id" );
1233     if ( !aLabel.isEmpty() ) {
1234       // create toolbar
1235       int tbId = myModule->createTool( aLabel );
1236       QDomNode node = parentNode.firstChild();
1237       while ( !node.isNull() ) {
1238         if ( node.isElement() ) {
1239           QDomElement elem = node.toElement();
1240           QString aTagName = tagName( elem );
1241           if ( aTagName == "toolbutton-item" ) {
1242             int     id      = checkInt( attribute( elem, "item-id" ) );
1243             int     pos     = checkInt( attribute( elem, "pos-id" ) );
1244             QString label   = attribute( elem, "label-id" );
1245             QString icon    = attribute( elem, "icon-id" );
1246             QString tooltip = attribute( elem, "tooltip-id" );
1247             QString accel   = attribute( elem, "accel-id" );
1248             bool    toggle  = checkBool( attribute( elem, "toggle-id" ) );
1249             ////QString execute = attribute( elem, "execute-action" );               // not used
1250
1251             // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1252             // also check if the action with given ID is already created
1253             if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id ) ) {
1254               // little trick to have several actions with same ID for menus and toolbars
1255               id = SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id;
1256               // create toolbar action
1257               QAction* action = myModule->createAction( id,                               // ID
1258                                                         tooltip,                          // tooltip
1259                                                         icon,                             // icon
1260                                                         label,                            // menu text
1261                                                         tooltip,                          // status-bar text
1262                                                         QKeySequence( accel ),            // keyboard accelerator
1263                                                         toggle );                         // toogled action
1264               myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL, action );
1265               myModule->createTool( action, tbId, -1, pos );
1266             }
1267           }
1268           else if ( aTagName == "separatorTB" ) {
1269             // create toolbar separator
1270             int     pos     = checkInt( attribute( elem, "pos-id" ) );
1271             QAction* action = myModule->createSeparator();
1272             myModule->createTool( action, tbId, -1, pos );
1273           }
1274         }
1275         node = node.nextSibling();
1276       }
1277     }
1278   }      
1279 }
1280
1281 void SALOME_PYQT_XmlHandler::insertPopupItems( QDomNode& parentNode, QPopupMenu* menu )
1282 {
1283   if ( !myModule )
1284     return;
1285   
1286   if ( parentNode.isNull() )
1287     return;
1288
1289   // we create popup menus without help of QtxPopupMgr
1290   QDomNode node = parentNode.firstChild();
1291   while ( !node.isNull() ) {
1292     if ( node.isElement() ) {
1293       QDomElement elem = node.toElement();
1294       QString aTagName = tagName( elem );
1295       if ( aTagName == "popup-item" ) {
1296         // insert a command item
1297         int     id      = checkInt( attribute( elem, "item-id" ) );
1298         int     pos     = checkInt( attribute( elem, "pos-id" ) );
1299         QString label   = attribute( elem, "label-id" );
1300         QString icon    = attribute( elem, "icon-id" );
1301         /////QString tooltip = attribute( elem, "tooltip-id" );                   // not used
1302         QString accel   = attribute( elem, "accel-id" );
1303         /////bool    toggle  = checkBool( attribute( elem, "toggle-id" ) );       // not used
1304         /////QString execute = attribute( elem, "execute-action" );               // not used
1305
1306         QIconSet anIcon;
1307         if ( !icon.isEmpty() ) {
1308           QPixmap pixmap  = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1309           if ( !pixmap.isNull() )
1310             anIcon = QIconSet( pixmap );
1311         }
1312         
1313         // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1314         // also check if the action with given ID is already created
1315         if ( id != -1 ) {
1316           menu->insertItem( anIcon, label, myModule, SLOT( onGUIEvent(int) ), QKeySequence( accel ), id, pos );
1317         }
1318       }
1319       else if ( aTagName == "submenu" ) {
1320         // create sub-menu
1321         int     id    = checkInt( attribute( elem, "item-id" ) );
1322         int     pos   = checkInt( attribute( elem, "pos-id" ) );
1323         QString label = attribute( elem, "label-id" );
1324         QString icon    = attribute( elem, "icon-id" );
1325
1326         QIconSet anIcon;
1327         if ( !icon.isEmpty() ) {
1328           QPixmap pixmap  = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1329           if ( !pixmap.isNull() )
1330             anIcon = QIconSet( pixmap );
1331         }
1332
1333         QPopupMenu* newPopup = new QPopupMenu( menu, label );
1334         menu->insertItem( anIcon, label, newPopup, id, pos );
1335         insertPopupItems( node, newPopup );
1336       }
1337       else if ( aTagName == "separator" ) {
1338         // create menu separator
1339         int     pos     = checkInt( attribute( elem, "pos-id" ) );
1340         menu->insertSeparator( pos );
1341       }
1342     }
1343     node = node.nextSibling();
1344   }
1345 }