Salome HOME
From Community Forum: Save/Load incorrectly works if there are several Python modules...
[modules/gui.git] / src / SALOME_PYQT / SALOME_PYQT_GUI / SALOME_PYQT_Module.cxx
index 9d6a53d79e76991c87d99d001da0865e9cd35cff..4b44353888ab88c3b17cdc9f75d47a7099c192e3 100644 (file)
-//=============================================================================
-// File      : SALOME_PYQT_Module.cxx
-// Created   : 25/04/05
-// Author    : Vadim SANDLER
-// Project   : SALOME
-// Copyright : 2003-2005 CEA/DEN, EDF R&D
-// $Header   : $
-//=============================================================================
+// Copyright (C) 2007-2020  CEA/DEN, EDF R&D, OPEN CASCADE
+//
+// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File   : SALOME_PYQT_Module.cxx
+// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
 
 #include "SALOME_PYQT_Module.h"
-
-#include "PyInterp_Dispatcher.h"
-#include "SUIT_ResourceMgr.h"
-#include "STD_MDIDesktop.h"
+#include "SALOME_PYQT_PyModule.h"
 #include "SalomeApp_Application.h"
-#include "SalomeApp_Study.h"
-#include "SalomeApp_DataModel.h"
-
-#include <qfile.h>
-#include <qdom.h>
-#include <qworkspace.h>
-#include <qpopupmenu.h>
 
-#if QT_VERSION > 0x030005
-#include "sipAPISalomePyQtGUI.h"
-#else
-#include "sipSalomePyQtGUIDeclSalomePyQtGUI.h"
-#endif
+#include <SALOME_LifeCycleCORBA.hxx>
+#include <Container_init_python.hxx>
 
-#include <sipqtQWidget.h>
-#include <sipqtQPopupMenu.h>
+#include <QCoreApplication>
 
-using namespace std;
-
-#ifdef _DEBUG_
-static int MYDEBUG = 0;
-#else
-static int MYDEBUG = 0;
-#endif
-
-// Default name of the module, should be replaced at the moment 
-// of module creation
-#define __DEFAULT_NAME__ "SALOME_PYQT_Module"
-// Comment this define to block invoking of obsolete Python module's
-// methods like setSetting(), definePopup(), etc.
-#define __CALL_OLD_METHODS__
-
-//=============================================================================
-// General rule for Python requests created by SALOME_PYQT_Module:
+//
+// NB: Python requests.
+// General rule for Python requests created by Python-based GUI modules
+// (SALOME_PYQT_Module and other ones):
 // all requests should be executed SYNCHRONOUSLY within the main GUI thread.
 // However, it is obligatory that ANY Python call is wrapped with a request object,
 // so that ALL Python API calls are serialized with PyInterp_Dispatcher.
-//=============================================================================
-
-
-//=============================================================================
-// The default PyQt module data model.
-// Reuses common data model from SalomeApp.
-//=============================================================================
-class SALOME_PYQT_DataModel: public SalomeApp_DataModel
-{
-public:
-  SALOME_PYQT_DataModel( CAM_Module* theModule ) : SalomeApp_DataModel( theModule ) {}
-  bool isModified() const { return false; }
-  bool isSaved()  const   { return false; }
-};
-
-//=============================================================================
-// The class for parsing of the XML resource files.
-// Used for backward compatibility with existing Python modules.
-//=============================================================================
-class SALOME_PYQT_XmlHandler
-{
-public:
-  SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName );
-  void createActions();
-  void createPopup  ( QPopupMenu*    menu, 
-                     const QString& context, 
-                     const QString& parent, 
-                     const QString& object );
-
-protected:
-  void createToolBar   ( QDomNode& parentNode );
-  void createMenu      ( QDomNode& parentNode, 
-                        const int parentMenuId = -1 );
-
-  void insertPopupItems( QDomNode&   parentNode, 
-                        QPopupMenu* menu );
-
-private:
-  SALOME_PYQT_Module* myModule;
-  QDomDocument        myDoc;
-};
-
-//=============================================================================
-// SALOME_PYQT_Module class implementation (implements CAM_Module API for
-// all Python-based SALOME module
-//=============================================================================
-
-/*!
- * This function creates an instance of SALOME_PYQT_Module object by request
- * of and application object when the module is loaded.
- */
-extern "C" {
-  SALOME_PYQT_EXPORT CAM_Module* createModule() {
-    return new SALOME_PYQT_Module();
-  }
-}
-
-/*! 
- * Static variables definition
- */
-SALOME_PYQT_Module::InterpMap SALOME_PYQT_Module::myInterpMap;
-
-/*!
- * Constructor
- */
-SALOME_PYQT_Module::SALOME_PYQT_Module() :
-       SalomeApp_Module( __DEFAULT_NAME__ ), myModule( 0 ), myXmlHandler ( 0 )
-{
-  myMenuActionList.setAutoDelete( false );
-  myPopupActionList.setAutoDelete( false );
-  myToolbarActionList.setAutoDelete( false );
-}
-
-/*!
- * Destructor
- */
-SALOME_PYQT_Module::~SALOME_PYQT_Module()
-{
-  myMenuActionList.clear();
-  myPopupActionList.clear();
-  myToolbarActionList.clear();
-  if ( myXmlHandler )
-    delete myXmlHandler;
-}
-
-/*!
- * Creates data model for the module.
- * Reimplemented from CAM_Module.
- */
-CAM_DataModel* SALOME_PYQT_Module::createDataModel()
-{
-  // VSR: this is a temporary solution : 
-  // should reuse default data model from SalomeApp
-  return new SALOME_PYQT_DataModel( this );
-}
-
-/*!
- * Initialization of the module.
- * Inherited from CAM_Module.
- *
- * This method is used for creation of the menus, toolbars and other staff.
- * There are two ways:
- * - for obsolete modules this method first tries to read <module>_<language>.xml 
- *   resource file which contains a menu, toolbars and popup menus description.
- * - new modules can create menus by by calling the corresponding methods of SalomePyQt
- *   Python API in the Python module's initialize() method which is called from here.
- * NOTE: if postponed modules loading is not used, the active study might be not defined
- * yet at this stage, so initialize() method should not perform any study-based initialization.
- */
-void SALOME_PYQT_Module::initialize( CAM_Application* app )
-{
-  SalomeApp_Module::initialize( app );
-
-  // Try to get XML resource file name
-  SUIT_ResourceMgr* aResMgr = getApp()->resourceMgr();
-  QString aLang = aResMgr->stringValue( "language", "language", QString::null );
-  if ( aLang.isEmpty() ) aLang = QString( "en" );
-  QString aName = name( "" );
-  QString aFileName = aName + "_" + aLang + ".xml";
-  aFileName = aResMgr->path( "resources", aName, aFileName );
-  // parse XML file if it is found and create actions
-  if ( !myXmlHandler && !aFileName.isEmpty() ) {
-    myXmlHandler = new SALOME_PYQT_XmlHandler( this, aFileName );
-    myXmlHandler->createActions();
-  }
-
-  // perform internal initialization and call module's initialize() method
-  // InitializeReq: request class for internal init() operation
-  class InitializeReq : public PyInterp_Request
-  {
-  public:
-    InitializeReq( CAM_Application*    _app,
-                  SALOME_PYQT_Module* _obj ) 
-      : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
-        myApp( _app ),
-        myObj( _obj ) {}
-    
-  protected:
-    virtual void execute()
-    {
-      myObj->init( myApp );
-    }
-
-  private:
-    CAM_Application*    myApp;
-    SALOME_PYQT_Module* myObj;
-  };
-
-  // Posting the request
-  PyInterp_Dispatcher::Get()->Exec( new InitializeReq( app, this ) );
-}
+//
+// NB: Library initialization
+// Since the SalomePyQtGUI library is not imported in Python it's initialization function
+// should be called manually (and only once) in order to initialize global sip data
+// and to get C API from sip : sipBuildResult for example
+//
+
+#define INIT_FUNCTION PyInit_SalomePyQtGUILight
+#if defined(SIP_STATIC_MODULE)
+extern "C" void INIT_FUNCTION();
+#else
+PyMODINIT_FUNC INIT_FUNCTION();
+#endif
 
 /*!
- * Activation of the module.
- * Inherited from CAM_Module.
- */
-void SALOME_PYQT_Module::activateModule( SUIT_Study* theStudy )
-{
-  MESSAGE( "SALOME_PYQT_Module::activateModule" );
-
-  SalomeApp_Module::activateModule( theStudy );
+  \fn CAM_Module* createModule()
+  \brief Module factory function.
+  \internal
+  
+  Creates an instance of SALOME_PYQT_Module object by request
+  of an application when the module is loaded and initialized.
 
-  // activate menus, toolbars, etc
-  setMenuShown( true );
-  setToolShown( true );
+  \return new module object
+*/
 
-  // ActivateReq: request class for internal activate() operation
-  class ActivateReq : public PyInterp_Request
+extern "C" {
+  SALOME_PYQT_EXPORT CAM_Module* createModule()
   {
-  public:
-    ActivateReq( SUIT_Study*         _study, 
-                SALOME_PYQT_Module* _obj ) 
-      : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
-        myStudy ( _study ),
-        myObj   ( _obj   ) {}
-    
-  protected:
-    virtual void execute()
-    {
-      myObj->activate( myStudy );
-    }
+    QCoreApplication* app = QCoreApplication::instance();
+    bool alreadyInitialized = app && app->property( "salome_pyqt_gui_light_initialized" ).toBool();
 
-  private:
-    SUIT_Study*         myStudy;
-    SALOME_PYQT_Module* myObj;
-  };
-
-  // Posting the request
-  PyInterp_Dispatcher::Get()->Exec( new ActivateReq( theStudy, this ) );
-
-  // connect desktop activation signal
-  connect( application()->desktop(), SIGNAL( activated() ), this, SLOT( onDesktopActivated() ) );
-}
-
-/*!
- * Deactivation of the module.
- * Inherited from CAM_Module.
- */
-void SALOME_PYQT_Module::deactivateModule( SUIT_Study* theStudy )
-{
-  MESSAGE( "SALOME_PYQT_Module::deactivateModule" );
-
-  SalomeApp_Module::deactivateModule( theStudy );
-
-  // deactivate menus, toolbars, etc
-  setMenuShown( false );
-  setToolShown( false );
-
-  // DeactivateReq: request class for internal deactivate() operation
-  class DeactivateReq : public PyInterp_LockRequest
-  {
-  public:
-    DeactivateReq( PyInterp_base*      _py_interp, 
-                  SUIT_Study*         _study, 
-                  SALOME_PYQT_Module* _obj ) 
-      : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
-        myStudy ( _study ),
-        myObj   ( _obj   ) {}
-    
-  protected:
-    virtual void execute()
-    {
-      myObj->deactivate( myStudy );
+    if ( !alreadyInitialized ) {
+      PyLockWrapper lck; // GIL acquisition
+      INIT_FUNCTION();
+      if ( app ) app->setProperty( "salome_pyqt_gui_light_initialized", true );
     }
 
-  private:
-    SUIT_Study*         myStudy;
-    SALOME_PYQT_Module* myObj;
-  };
-
-  // Posting the request
-  PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, theStudy, this ) );
-
-  // disconnect desktop activation signal
-  disconnect( application()->desktop(), SIGNAL( activated() ), this, SLOT( onDesktopActivated() ) );
-}
-
-/*!
- * Processes GUI action (from main menu, toolbar or context popup menu)
- */
-void SALOME_PYQT_Module::onGUIEvent()
-{
-  // get sender action
-  const QObject* obj = sender();
-  if ( !obj || !obj->inherits( "QAction" ) )
-    return;
-  QAction* action = (QAction*)obj;
-
-  // get action ID
-  int id = actionId( action );
-  if ( myMenuActionList.contains( action ) )
-    id -= PYQT_ACTION_MENU;
-  if ( myToolbarActionList.contains( action ) )
-    id -= PYQT_ACTION_TOOLBAL;
-  if ( myPopupActionList.contains( action ) )
-    id -= PYQT_ACTION_POPUP;
-  MESSAGE( "SALOME_PYQT_Module::onGUIEvent: id = " << id );
-
-  // perform synchronous request to Python event dispatcher
-  class GUIEvent : public PyInterp_LockRequest
-  {
-  public:
-    GUIEvent( PyInterp_base*      _py_interp, 
-             SALOME_PYQT_Module* _obj,
-             int                 _id ) 
-      : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
-        myId    ( _id  ),
-        myObj   ( _obj ) {}
-    
-  protected:
-    virtual void execute()
-    {
-      myObj->guiEvent( myId );
-    }
-
-  private:
-    int                 myId;
-    SALOME_PYQT_Module* myObj;
-  };
-
-  // Posting the request
-  PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
+    return new SALOME_PYQT_Module();
+  }
 }
 
 /*!
- * Desktop activation slot. Used for notifying about changing of the active study.
- */
-void SALOME_PYQT_Module::onDesktopActivated()
-{
-  // StudyChangedReq: request class for internal studyChanged() operation
-  class StudyChangedReq : public PyInterp_Request
-  {
-  public:
-    StudyChangedReq( SUIT_Study*         _study, 
-                    SALOME_PYQT_Module* _obj ) 
-      : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
-        myStudy ( _study ),
-        myObj   ( _obj   ) {}
-    
-  protected:
-    virtual void execute()
-    {
-      myObj->studyChanged( myStudy );
-    }
-
-  private:
-    SUIT_Study*         myStudy;
-    SALOME_PYQT_Module* myObj;
-  };
-
-  // Posting the request
-  PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( application()->activeStudy(), this ) );
-}
-
-/*! 
-  Context popup menu request.
-  Called when user activates popup menu in some window (view, object browser, etc).
-  */
-void SALOME_PYQT_Module::contextMenuPopup( const QString& theContext, QPopupMenu* thePopupMenu, QString& /*title*/ )
-{
-  MESSAGE( "SALOME_PYQT_Module::contextMenuPopup : " << theContext.latin1() );
-  // perform synchronous request to Python event dispatcher
-  class PopupMenuEvent : public PyInterp_LockRequest
-  {
-  public:
-    PopupMenuEvent( PyInterp_base*     _py_interp, 
-                   SALOME_PYQT_Module* _obj,
-                   const QString&      _context, 
-                   QPopupMenu*        _popup ) 
-      : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
-        myContext( _context ), 
-        myPopup  ( _popup  ),
-        myObj    ( _obj )   {}
-    
-  protected:
-    virtual void execute()
-    {
-      myObj->contextMenu( myContext, myPopup );
-    }
-
-  private:
-    SALOME_PYQT_Module* myObj;
-    QString             myContext;
-    QPopupMenu*         myPopup;
-  };
-
-  // Posting the request only if dispatcher is not busy!
-  // Executing the request synchronously
-  if ( !PyInterp_Dispatcher::Get()->IsBusy() )
-    PyInterp_Dispatcher::Get()->Exec( new PopupMenuEvent( myInterp, this, theContext, thePopupMenu ) );
-}
+  \class SALOME_PYQT_Module
+  \brief This class implements GUI module for CORBA engine-based Python SALOME modules.
+*/
 
 /*!
- * Defines the dockable window associated with the module.
- * To fill the list of windows the correspondind Python module's windows() 
- * method is called from SALOME_PYQT_Module::init() method.
- * By default, ObjectBrowser, PythonConsole and LogWindow are provided.
- */
-void SALOME_PYQT_Module::windows( QMap<int, int>& mappa ) const
+  \brief Constructor
+*/
+SALOME_PYQT_Module::SALOME_PYQT_Module()
+  : SalomeApp_Module( "noname" ) // name is set explicitly at the module initialization
 {
-  // First clear the output parameters 
-  QMap<int, int>::ConstIterator it;
-  for ( it = myWindowsMap.begin(); it != myWindowsMap.end(); ++it ) {
-    mappa[ it.key() ] = it.data();
-  }
+  // initialize helper
+  myHelper = new PyModuleHelper( this );
 }
 
 /*!
- * Defines the compatible views which should be opened on module activation.
- * To fill the list of views the correspondind Python module's views() 
- * method is called from SALOME_PYQT_Module::init() method.
- * By default, the list is empty.
- */
-void SALOME_PYQT_Module::viewManagers( QStringList& listik ) const
+  \brief Destructor
+*/
+SALOME_PYQT_Module::~SALOME_PYQT_Module()
 {
-  for ( QStringList::ConstIterator it = myViewMgrList.begin(); it != myViewMgrList.end(); ++it ) {
-    listik.append( *it );
-  }
+  // as myHelper is a QObject, it should be deleted automatically
 }
 
 /*!
- * Performs internal initialization
- * - initializes/gets the Python interpreter (one per study)
- * - imports the Python module
- * - passes the workspace widget to the Python module
- * - calls Python module's initialize() method
- * - calls Python module's windows() method
- * - calls Python module's views() method
- */
-void SALOME_PYQT_Module::init( CAM_Application* app )
-{
-  // reset interpreter to NULL
-  myInterp = NULL;
-
-  // get study Id
-  SalomeApp_Application* anApp = dynamic_cast<SalomeApp_Application*>( app );
-  if ( !anApp )
-    return;
-
-  SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() );
-  if ( !aStudy )
-    return;
-  int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
-
-  // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
-  initInterp( aStudyId );
-  if ( !myInterp ) 
-    return; // Error 
-
-  // import Python GUI module
-  importModule();
-  if ( !myModule )
-    return; // Error 
-#ifdef __CALL_OLD_METHODS__
-  // call Python module's setWorkspace() method
-  setWorkSpace();
-#endif // __CALL_OLD_METHODS__
-
-  // then call Python module's initialize() method
-  // ... first get python lock
-  PyLockWrapper aLock = myInterp->GetLockWrapper();
-  // ... (the Python module is already imported)
-  // ... finally call Python module's initialize() method
-  PyObjWrapper res( PyObject_CallMethod( myModule, "initialize", "" ) );
-  if( !res ) {
-    // VSR: this method may not be implemented in Python module
-    // PyErr_Print();
-  }
+  \brief Get module engine IOR
   
-  // get the windows list from the Python module by calling windows() method
-  // ... first put default values
-  myWindowsMap.insert( SalomeApp_Application::WT_ObjectBrowser, Qt::DockLeft );
-  myWindowsMap.insert( SalomeApp_Application::WT_PyConsole,     Qt::DockBottom );
-  // VSR: LogWindow is not yet implemented
-  // myWindowsMap.insert( SalomeApp_Application::WT_LogWindow,     Qt::DockBottom );
-
-  PyObjWrapper res1( PyObject_CallMethod( myModule, "windows", "" ) );
-  if( !res1 ) {
-    // VSR: this method may not be implemented in Python module
-    // PyErr_Print();
-  }
-  else {
-    myWindowsMap.clear();
-    if ( PyDict_Check( res1 ) ) {
-      PyObject* key;
-      PyObject* value;
-      int pos = 0;
-      while ( PyDict_Next( res1, &pos, &key, &value ) ) {
-       // parse the return value
-       // it should be a map: {integer:integer}
-       int aKey, aValue;
-       if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
-         aKey   = PyInt_AsLong( key );
-         aValue = PyInt_AsLong( value );
-         myWindowsMap[ aKey ] = aValue;
-       }
-      }
-    }
-  }
-  // get the windows list from the Python module by calling views() method
-  PyObjWrapper res2( PyObject_CallMethod( myModule, "views", "" ) );
-  if( !res2 ) {
-    // VSR: this method may not be implemented in Python module
-    // PyErr_Print();
-  }
-  else {
-    // parse the return value
-    // result can be one string...
-    if ( PyString_Check( res2 ) ) {
-      myViewMgrList.append( PyString_AsString( res2 ) );
+  This function tries to get engine IOR from the Python module using engineIOR() function.
+  That function can load module engine using appropriate container if required.
+  If this function is not available in Python module, the default implementation
+  is used which loads engine to the default FactoryServer container.
+*/
+QString SALOME_PYQT_Module::engineIOR() const
+{
+  // first call helper to get IOR from Python module
+  QString ior = myHelper->engineIOR();
+
+  // if IOR is still not specified, try default implementation
+  // which loads engine to the default FactoryServer container.
+  if ( ior.isEmpty() ) {
+    Engines::EngineComponent_var comp;
+    // temporary solution
+    try {
+      comp = getApp()->lcc()->FindOrLoad_Component( "FactoryServer", name().toLatin1() );
     }
-    // ... or list of strings
-    else if ( PyList_Check( res2 ) ) {
-      int size = PyList_Size( res2 );
-      for ( int i = 0; i < size; i++ ) {
-       PyObject* value = PyList_GetItem( res2, i );
-       if( value && PyString_Check( value ) ) {
-         myViewMgrList.append( PyString_AsString( value ) );
-       }
-      }
+    catch (CORBA::Exception&) {
     }
+    if ( !CORBA::is_nil( comp ) )
+      ior = QString( getApp()->orb()->object_to_string( comp.in() ) );
   }
-}
-
-/*!
- * Performs internal activation: 
- * - initializes/gets the Python interpreter (one per study)
- * - imports the Python GUI module
- * - calls Python module's setSettings() method (obsolete function, used for compatibility with old code)
- *   or activate() method (for new modules)
- */
-void SALOME_PYQT_Module::activate( SUIT_Study* theStudy )
-{
-  // get study Id
-  SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
-  int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
-
-  // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
-  initInterp( aStudyId );
-  if ( !myInterp ) 
-    return; // Error 
-
-  // import Python GUI module
-  importModule();
-  if ( !myModule )
-    return; // Error 
-  // get python lock
-  PyLockWrapper aLock = myInterp->GetLockWrapper();
-
-#ifdef __CALL_OLD_METHODS__
-  // call Python module's setSettings() method (obsolete)
-  PyObjWrapper res( PyObject_CallMethod( myModule, "setSettings", "" ) );
-  if( !res ) {
-    // VSR: this method may not be implemented in Python module
-    // PyErr_Print();
-  }
-#endif // __CALL_OLD_METHODS__
 
-  // call Python module's activate() method (for the new modules)
-  PyObjWrapper res1( PyObject_CallMethod( myModule, "activate", "" ) );
-  if( !res1 ) {
-    // VSR: this method may not be implemented in Python module
-    // PyErr_Print();
-  }
+  return ior;
 }
 
 /*!
- * Performs internal deactivation: 
- * - calls Python module's deactivate() method
- */
-void SALOME_PYQT_Module::deactivate( SUIT_Study* theStudy )
-{
-  // check if the subinterpreter is initialized and Python module is imported
-  if ( !myInterp || !myModule ) {
-    // Error! Python subinterpreter should be initialized and module should be imported first!
-    return;
-  }
-  // then call Python module's deactivate() method
-  PyObjWrapper res( PyObject_CallMethod( myModule, "deactivate", "" ) );
-  if( !res ) {
-    // VSR: this method may not be implemented in Python module
-    // PyErr_Print();
-  }
-}
-
-/*!
- * Called when active the study is actived (user brings its desktop to top)
- * - initializes/gets the Python interpreter (one per study)
- * - imports the Python GUI module
- * - calls Python module's activeStudyChanged() method
- */
-void SALOME_PYQT_Module::studyChanged( SUIT_Study* theStudy )
+  \brief Initialization of the module.
+  \param app parent application object
+  \sa PyModuleHelper::initialize()
+*/
+void SALOME_PYQT_Module::initialize( CAM_Application* app )
 {
-  // get study Id
-  SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
-  int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
-
-  // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
-  initInterp( aStudyId );
-  if ( !myInterp ) 
-    return; // Error 
-
-  // import Python GUI module
-  importModule();
-  if ( !myModule )
-    return; // Error 
-  // get python lock
-  PyLockWrapper aLock = myInterp->GetLockWrapper();
+  // call base implementation
+  SalomeApp_Module::initialize( app );
 
-  // call Python module's activeStudyChanged() method
-  PyObjWrapper res( PyObject_CallMethod( myModule, "activeStudyChanged", "i", aStudyId ) );
-  if( !res ) {
-    // VSR: this method may not be implemented in Python module
-    // PyErr_Print();
-  }
+  // ... then call helper
+  myHelper->initialize( app );
 }
 
 /*!
- * Processes context popup menu request
- * - calls Python module's definePopup(...) method (obsolete function, used for compatibility with old code)
- *   to define the popup menu context
- * - parses XML resourses file (if exists) and fills the popup menu with the items)
- * - calls Python module's customPopup(...) method (obsolete function, used for compatibility with old code)
- *   to allow module to customize the popup menu
- * - for new modules calls createPopupMenu() function to allow the modules to build the popup menu
- *   by using insertItem(...) Qt functions.
- */
-void SALOME_PYQT_Module::contextMenu( const QString& theContext, QPopupMenu* thePopupMenu )
+  \brief Activation of the module.
+  \param study parent study
+  \return \c true if activation is successful and \c false otherwise
+  \sa PyModuleHelper::activate()
+*/
+bool SALOME_PYQT_Module::activateModule( SUIT_Study* study )
 {
-  // Python interpreter should be initialized and Python module should be
-  // import first
-  if ( !myInterp || !myModule )
-    return;
-  
-  QString aContext( theContext ), aObject( "" ), aParent( "" );
-#ifdef __CALL_OLD_METHODS__
-  // call definePopup() Python module's function
-  // this is obsolete function, used only for compatibility reasons
-  PyObjWrapper res(PyObject_CallMethod( myModule, 
-                                       "definePopup", 
-                                       "sss",
-                                       aContext.latin1(), 
-                                       aObject.latin1(), 
-                                       aParent.latin1() ) );
-  if( !res ) {
-    // VSR: this method may not be implemented in Python module
-    // PyErr_Print();
-  }
-  else {
-    // parse return value
-    char *co, *ob, *pa;
-    if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
-      aContext = co;
-      aObject  = ob;
-      aParent  = pa;
-    }
-  }
-#endif // __CALL_OLD_METHODS__
-
-  // first try to create menu via XML parser:
-  // we create popup menus without help of QtxPopupMgr
-  if ( !myXmlHandler )
-    myXmlHandler->createPopup( thePopupMenu, aContext, aParent, aObject );
-
-  PyObjWrapper sipPopup( sipMapCppToSelf( thePopupMenu, sipClass_QPopupMenu ) );
-
-  // then call Python module's createPopupMenu() method (for new modules)
-  PyObjWrapper res1( PyObject_CallMethod( myModule,
-                                         "createPopupMenu",
-                                         "Os",
-                                         sipPopup.get(),
-                                         aContext.latin1() ) );
-  if( !res1 ) {
-    // VSR: this method may not be implemented in Python module
-    // PyErr_Print();
-  }
-
-#ifdef __CALL_OLD_METHODS__
-  // call customPopup() Python module's function
-  // this is obsolete function, used only for compatibility reasons
-  PyObjWrapper res2( PyObject_CallMethod( myModule,
-                                         "customPopup",
-                                         "Osss",
-                                         sipPopup.get(),
-                                         aContext.latin1(), 
-                                         aObject.latin1(), 
-                                         aParent.latin1() ) );
-  if( !res2 ) {
-    // VSR: this method may not be implemented in Python module
-    // PyErr_Print();
-  }
-#endif // __CALL_OLD_METHODS__
+  // call base implementation and then helper
+  return SalomeApp_Module::activateModule( study ) && myHelper->activate( study );
 }
 
 /*!
- * Processes GUI event
- * - calls Python module's OnGUIEvent() method
- */ 
-void SALOME_PYQT_Module::guiEvent( const int theId )
+  \brief Deactivation of the module.
+  \param study parent study
+  \return \c true if deactivation is successful and \c false otherwise
+  \sa PyModuleHelper::deactivate()
+*/
+bool SALOME_PYQT_Module::deactivateModule( SUIT_Study* study )
 {
-  // Python interpreter should be initialized and Python module should be
-  // import first
-  if ( !myInterp || !myModule )
-    return;
-  
-  PyObjWrapper res( PyObject_CallMethod( myModule, "OnGUIEvent", "i", theId ) );
-  if( !res ) {
-    // Error!
-    PyErr_Print();
-  }
+  // call helper
+  bool res = myHelper->deactivate( study );
+    
+  // ... then call base implementation
+  return SalomeApp_Module::deactivateModule( study ) && res;
 }
 
 /*!
- *  Initialises python subinterpreter (one per study)
- */
-void SALOME_PYQT_Module::initInterp( int theStudyId )
+  \brief Get the dockable windows associated with the module.
+  \param winMap output map of dockable windows in form { <window_type> : <dock_area> }
+  \sa PyModuleHelper::windows()
+*/
+void SALOME_PYQT_Module::windows( QMap<int, int>& winMap ) const
 {
-  // check study Id
-  if ( !theStudyId ) {
-    // Error! Study Id must not be 0!
-    myInterp = NULL;
-    return;
-  }
-  // try to find the subinterpreter
-  if( myInterpMap.find( theStudyId ) != myInterpMap.end() ) {
-    // found!
-    myInterp = myInterpMap[ theStudyId ];
-    return;
-  }
-  // not found - create a new one!
-  ///////////////////////////////////////////////////////////////////
-  // Attention: the creation of Python interpretor must be protected 
-  // by a C++ Lock because of C threads
-  ///////////////////////////////////////////////////////////////////
-  myInterp = new SALOME_PYQT_PyInterp();
-  myInterp->initialize();
-  myInterpMap[ theStudyId ] = myInterp;
-   
-  // import 'salome' module and call 'salome_init' method;
-  // do it only once on interpreter creation
-  // ... first get python lock
-  PyLockWrapper aLock = myInterp->GetLockWrapper();
-  // ... then import a module
-  PyObjWrapper aMod = PyImport_ImportModule( "salome" );
-  if( !aMod ) {
-    // Error!
-    PyErr_Print();
-    return;
-  }
-  // ... then call a method
-  PyObjWrapper aRes( PyObject_CallMethod( aMod, "salome_init", "" ) );
-  if( !aRes ) {
-    // Error!
-    PyErr_Print();
-    return;
-  }
+  // get list of dockable windows from helper
+  winMap = myHelper->windows();
 }
 
 /*!
- *  Imports Python GUI module and remember the reference to the module
- *  !!! initInterp() should be called first!!!
- */
-void SALOME_PYQT_Module::importModule()
+  \brief Define the compatible view windows associated with the module.
+  \param viewList output list of view windows types
+  \sa PyModuleHelper::viewManagers()
+*/
+void SALOME_PYQT_Module::viewManagers( QStringList& viewList ) const
 {
-  // check if the subinterpreter is initialized
-  if ( !myInterp ) {
-    // Error! Python subinterpreter should be initialized first!
-    myModule = 0;
-    return;
-  }
-  // import Python GUI module and puts it in <myModule> attribute
-  // ... first get python lock
-  PyLockWrapper aLock = myInterp->GetLockWrapper();
-  // ... then import a module
-  QString aMod = QString( name("") ) + "GUI";
-  myModule = PyImport_ImportModule( (char*)( aMod.latin1() ) );
-  if( !myModule ) {
-    // Error!
-    PyErr_Print();
-    return;
-  }
+  // get list of view types from helper
+  viewList = myHelper->viewManagers();
 }
 
 /*!
- *  Calls <module>.setWorkSpace() method with PyQt QWidget object to use with
- *  interpreter.
- *  !!! initInterp() and importModule() should be called first!!!
- */
-void SALOME_PYQT_Module::setWorkSpace()
+  \brief Process study activation.
+  \sa PyModuleHelper::studyActivated()
+*/
+void SALOME_PYQT_Module::studyActivated()
 {
-  // check if the subinterpreter is initialized and Python module is imported
-  if ( !myInterp || !myModule ) {
-    // Error! Python subinterpreter should be initialized and module should be imported first!
-    return;
-  }
-
-  // call setWorkspace() method
-  // ... first get python lock
-  PyLockWrapper aLock = myInterp->GetLockWrapper();
-
-  // ... then try to import SalomePyQt module. If it's not possible don't go on.
-  PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
-  if( !aQtModule ) {
-    // Error!
-    PyErr_Print();
-    return;
-  }  
-#ifdef __CALL_OLD_METHODS__
-  // ... then get workspace object
-  STD_MDIDesktop* aDesktop = dynamic_cast<STD_MDIDesktop*>( getApp()->desktop() );
-  if ( aDesktop && aDesktop->workspace() ) {
-    QWidget* aWorkspace = aDesktop->workspace();
-    PyObjWrapper pyws( sipMapCppToSelfSubClass( aWorkspace, sipClass_QWidget ) );
-    // ... and finally call Python module's setWorkspace() method (obsolete)
-    PyObjWrapper res( PyObject_CallMethod( myModule, "setWorkSpace", "O", pyws.get() ) );
-    if( !res ) {
-      // VSR: this method may not be implemented in Python module
-      // PyErr_Print();
-      return;
-    }
-  }
-#endif // __CALL_OLD_METHODS__
+  // call helper
+  myHelper->studyActivated( application()->activeStudy() );
 }
 
 /*!
- * Adds an action into private action list [internal usage]
- */
-void SALOME_PYQT_Module::addAction( const PyQtGUIAction type, QAction* action )
+  \brief Process context popup menu request.
+  \param context popup menu context (e.g. "ObjectBrowser")
+  \param menu popup menu
+  \param title popup menu title (not used)
+  \sa PyModuleHelper::contextMenu()
+*/
+void SALOME_PYQT_Module::contextMenuPopup( const QString& context, 
+                                          QMenu*         menu, 
+                                          QString&       /*title*/ )
 {
-  switch ( type ) {
-  case PYQT_ACTION_MENU:
-    myMenuActionList.append( action );
-    break;
-  case PYQT_ACTION_TOOLBAL:
-    myToolbarActionList.append( action );
-    break;
-  case PYQT_ACTION_POPUP:
-    myPopupActionList.append( action );
-    break;
-  }
+  // call helper
+  myHelper->contextMenu( context, menu );
 }
 
-//=============================================================================
-// SALOME_PYQT_XmlHandler class implementation
-//=============================================================================
-
-// gets an tag name for the dom element [ static ]
-// returns an empty string if the element does not have tag name
-static QString tagName( const QDomElement& element ) {
- return element.tagName().stripWhiteSpace();
-}
-
-// gets an attribute by it's name for the dom element [ static ]
-// returns an empty string if the element does not have such attribute
-static QString attribute( const QDomElement& element, const QString& attName ) {
-  return element.attribute( attName ).stripWhiteSpace();
-}
-
-// checks the given value for the boolean value [ static ]
-// returns TRUE if string is "true", "yes" or "1"
-static bool checkBool( const QString& value ) {
-  return ( value == "true" || value == "yes" || value == "1" );
-}
-
-// checks the given value for the integer value [ static ]
-// returns -1 if item is empty or presents and invalid number
-static int checkInt( const QString& value ) 
+/*!
+  \brief Export preferences for the Python module.
+  \sa PyModuleHelper::createPreferences()
+*/
+void SALOME_PYQT_Module::createPreferences()
 {
-  return value.isEmpty() ? -1 : value.toInt();
+  // call helper
+  myHelper->createPreferences();
 }
 
 /*!
- * Constructor
- */
-SALOME_PYQT_XmlHandler::SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName ) 
-     : myModule( module )
+  \brief Process module's preferences changing.
+  \param section preference resources section
+  \param parameter preference resources parameter name
+  \sa PyModuleHelper::preferencesChanged()
+*/
+void SALOME_PYQT_Module::preferencesChanged( const QString& section, const QString& parameter )
 {
-  QFile aFile( fileName );
-  if ( !aFile.open( IO_ReadOnly ) )
-    return;
-  if ( !myDoc.setContent( &aFile ) ) {
-      aFile.close();
-      return;
-  }
-  aFile.close();
+  // call helper
+  myHelper->preferencesChanged( section, parameter );
 }
 
 /*!
-  Called by SALOME_PYQT_Module::initialize() in order to create actions 
-  (menus, toolbars, popup menus)
- */
-void SALOME_PYQT_XmlHandler::createActions()
+  \brief Called when study is closed
+  \param study study being closed
+  \sa PyModuleHelper::studyClosed()
+*/
+void SALOME_PYQT_Module::studyClosed( SUIT_Study* study )
 {
-  // get document element
-  QDomElement aDocElem = myDoc.documentElement();
-
-  // get main menu actions
-  QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
-  for ( int i = 0; i < aMenuList.count(); i++ ) {
-    QDomNode n = aMenuList.item( i );
-    createMenu( n );
-  }
-
-  // create toolbars actions
-  QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
-  for ( int i = 0; i < aToolsList.count(); i++ ) {
-    QDomNode n = aToolsList.item( i );
-    createToolBar( n );
-  }
+  // call helper
+  myHelper->modelClosed( study );
+  SalomeApp_Module::studyClosed( study );
 }
 
 /*!
- *  Creates popup menu
- */
-void SALOME_PYQT_XmlHandler::createPopup( QPopupMenu*    menu, 
-                                         const QString& context, 
-                                         const QString& parent, 
-                                         const QString& object )
+  \brief Test if object \a what can be dragged by the user.
+  \param what data object being tested
+  \return \c true if object can be dragged or \c false otherwise
+  \sa PyModuleHelper::isDraggable()
+*/
+bool SALOME_PYQT_Module::isDraggable( const SUIT_DataObject* what ) const
 {
-  // get document element
-  QDomElement aDocElem = myDoc.documentElement();
-
-  // get popup menus actions
-  QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
-  for ( int i = 0; i < aPopupList.count(); i++ ) {
-    QDomNode n = aPopupList.item( i );
-    if ( !n.isNull() && n.isElement() ) {
-      QDomElement e = n.toElement();
-      QString lab = attribute( e, "label-id"   );
-      QString ctx = attribute( e, "context-id" );
-      QString prt = attribute( e, "parent-id"  );
-      QString obj = attribute( e, "object-id"  );
-      if ( ctx == context && prt == parent && obj == object )  {
-       insertPopupItems( n, menu );
-       break;
-      }
-    }
-  }
+  // call helper
+  return myHelper->isDraggable( what );
 }
 
 /*!
-  Create main menu with child actions
- */
-void SALOME_PYQT_XmlHandler::createMenu( QDomNode& parentNode, const int parentMenuId )
+  \brief Test if drop operation can be done on the \a where object.
+  \param where data object being tested
+  \return \c true if if drop operation is supported by object or \c false otherwise
+  \sa PyModuleHelper::isDropAccepted()
+*/
+bool SALOME_PYQT_Module::isDropAccepted( const SUIT_DataObject* where ) const
 {
-  if ( !myModule )
-    return;
-  
-  if ( parentNode.isNull() )
-    return;
-
-  QDomElement parentElement = parentNode.toElement(); 
-  if ( !parentElement.isNull() ) {
-    QString plabel = attribute( parentElement, "label-id" );
-    int     pid    = checkInt( attribute( parentElement, "item-id" ) );
-    int     ppos   = checkInt( attribute( parentElement, "pos-id" ) );
-    if ( !plabel.isEmpty() ) {
-      // create menu
-      int menuId = myModule->createMenu( plabel,         // label
-                                        parentMenuId,   // parent menu ID, should be -1 for main menu
-                                        pid,            // ID
-                                        100,            // group ID
-                                        ppos );         // position
-      QDomNode node = parentNode.firstChild();
-      while ( !node.isNull() ) {
-       if ( node.isElement() ) {
-         QDomElement elem = node.toElement();
-         QString aTagName = tagName( elem );
-         if ( aTagName == "popup-item" ) {
-           int     id      = checkInt( attribute( elem, "item-id" ) );
-           int     pos     = checkInt( attribute( elem, "pos-id" ) );
-           QString label   = attribute( elem, "label-id" );
-           QString icon    = attribute( elem, "icon-id" );
-           QString tooltip = attribute( elem, "tooltip-id" );
-           QString accel   = attribute( elem, "accel-id" );
-           bool    toggle  = checkBool( attribute( elem, "toggle-id" ) );
-           QString execute = attribute( elem, "execute-action" );               // not used
-
-           QPixmap pixmap  = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
-           QIconSet anIcon;
-           if ( !pixmap.isNull() )
-             anIcon = QIconSet( pixmap );
-
-           // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
-           // also check if the action with given ID is already created
-           if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_MENU + id ) ) {
-             // little trick to have several actions with same ID for menus and toolbars
-             id = SALOME_PYQT_Module::PYQT_ACTION_MENU + id;
-             // create menu action
-             QAction* action = myModule->createAction( id,                               // ID
-                                                       tooltip,                          // tooltip
-                                                       anIcon,                           // icon
-                                                       label,                            // menu text
-                                                       tooltip,                          // status-bar text
-                                                       QKeySequence( accel ),            // keyboard accelerator
-                                                       myModule->getApp()->desktop(),    // desktop
-                                                       toggle,                           // toogled action
-                                                       myModule,                         // receiver
-                                                       SLOT( onGUIEvent() ) );           // slot
-             myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_MENU, action );
-             myModule->createMenu( action, menuId, -1, 100, pos );
-           }
-         }
-         else if ( aTagName == "submenu" ) {
-           // create sub-menu
-           createMenu( node, menuId );
-         }
-         else if ( aTagName == "separator" ) {
-           // create menu separator
-           int     pos     = checkInt( attribute( elem, "pos-id" ) );
-           QAction* action = myModule->separator();
-           myModule->createMenu( action, menuId, -1, 100, pos );
-         }
-       }
-       node = node.nextSibling();
-      }
-    }
-  }
+  // call helper
+  return myHelper->isDropAccepted( where );
 }
 
 /*!
-  Create a toolbar with child actions
- */
-void SALOME_PYQT_XmlHandler::createToolBar( QDomNode& parentNode )
+  \brief Perform drop operation
+  \param what list of data objects being dropped
+  \param where target data object for drop operation
+  \param row line (child item index) where drop operation is performed to
+  \param action current drop action (copy or move)
+  \sa PyModuleHelper::dropObjects()
+*/
+void SALOME_PYQT_Module::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
+                                     const int row, Qt::DropAction action )
 {
-  if ( !myModule )
-    return;
-  
-  if ( parentNode.isNull() )
-    return;
-
-  QDomElement parentElement = parentNode.toElement(); 
-  if ( !parentElement.isNull() ) {
-    QString aLabel = attribute( parentElement, "label-id" );
-    if ( !aLabel.isEmpty() ) {
-      // create toolbar
-      int tbId = myModule->createTool( aLabel );
-      QDomNode node = parentNode.firstChild();
-      while ( !node.isNull() ) {
-       if ( node.isElement() ) {
-         QDomElement elem = node.toElement();
-         QString aTagName = tagName( elem );
-         if ( aTagName == "toolbutton-item" ) {
-           int     id      = checkInt( attribute( elem, "item-id" ) );
-           int     pos     = checkInt( attribute( elem, "pos-id" ) );
-           QString label   = attribute( elem, "label-id" );
-           QString icon    = attribute( elem, "icon-id" );
-           QString tooltip = attribute( elem, "tooltip-id" );
-           QString accel   = attribute( elem, "accel-id" );
-           bool    toggle  = checkBool( attribute( elem, "toggle-id" ) );
-           QString execute = attribute( elem, "execute-action" );               // not used
-
-           QPixmap pixmap  = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
-           QIconSet anIcon;
-           if ( !pixmap.isNull() )
-             anIcon = QIconSet( pixmap );
-
-           // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
-           // also check if the action with given ID is already created
-           if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id ) ) {
-             // little trick to have several actions with same ID for menus and toolbars
-             id = SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id;
-             // create toolbar action
-             QAction* action = myModule->createAction( id,                               // ID
-                                                       tooltip,                          // tooltip
-                                                       anIcon,                           // icon
-                                                       label,                            // menu text
-                                                       tooltip,                          // status-bar text
-                                                       QKeySequence( accel ),            // keyboard accelerator
-                                                       myModule->getApp()->desktop(),    // desktop
-                                                       toggle,                           // toogled action
-                                                       myModule,                         // receiver
-                                                       SLOT( onGUIEvent() ) );           // slot
-             myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL, action );
-             myModule->createTool( action, tbId, -1, pos );
-           }
-         }
-         else if ( aTagName == "separatorTB" ) {
-           // create toolbar separator
-           int     pos     = checkInt( attribute( elem, "pos-id" ) );
-           QAction* action = myModule->separator();
-           myModule->createTool( action, tbId, -1, pos );
-         }
-       }
-       node = node.nextSibling();
-      }
-    }
-  }      
+  // call helper
+  myHelper->dropObjects( what, where, row, action );
 }
 
-void SALOME_PYQT_XmlHandler::insertPopupItems( QDomNode& parentNode, QPopupMenu* menu )
-{
-  if ( !myModule )
-    return;
-  
-  if ( parentNode.isNull() )
-    return;
-
-  // we create popup menus without help of QtxPopupMgr
-  QDomNode node = parentNode.firstChild();
-  while ( !node.isNull() ) {
-    if ( node.isElement() ) {
-      QDomElement elem = node.toElement();
-      QString aTagName = tagName( elem );
-      if ( aTagName == "popup-item" ) {
-       // insert a command item
-       int     id      = checkInt( attribute( elem, "item-id" ) );
-       int     pos     = checkInt( attribute( elem, "pos-id" ) );
-       QString label   = attribute( elem, "label-id" );
-       QString icon    = attribute( elem, "icon-id" );
-       QString tooltip = attribute( elem, "tooltip-id" );                   // not used
-       QString accel   = attribute( elem, "accel-id" );
-       bool    toggle  = checkBool( attribute( elem, "toggle-id" ) );       // not used
-       QString execute = attribute( elem, "execute-action" );               // not used
-
-       QPixmap pixmap  = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
-       QIconSet anIcon;
-       if ( !pixmap.isNull() )
-         anIcon = QIconSet( pixmap );
-       
-       // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
-       // also check if the action with given ID is already created
-       if ( id != -1 ) {
-         menu->insertItem( anIcon, label, myModule, SLOT( onGUIEvent() ), QKeySequence( accel ), id, pos );
-       }
-      }
-      else if ( aTagName == "submenu" ) {
-       // create sub-menu
-       int     id    = checkInt( attribute( elem, "item-id" ) );
-       int     pos   = checkInt( attribute( elem, "pos-id" ) );
-       QString label = attribute( elem, "label-id" );
-       QString icon    = attribute( elem, "icon-id" );
-
-       QPixmap pixmap  = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
-       QIconSet anIcon;
-       if ( !pixmap.isNull() )
-         anIcon = QIconSet( pixmap );
-
-       QPopupMenu* newPopup = new QPopupMenu( menu, label );
-       menu->insertItem( anIcon, label, newPopup, id, pos );
-       insertPopupItems( node, newPopup );
-      }
-      else if ( aTagName == "separator" ) {
-       // create menu separator
-       int     pos     = checkInt( attribute( elem, "pos-id" ) );
-       menu->insertSeparator( pos );
-      }
-    }
-    node = node.nextSibling();
-  }
-}