Salome HOME
Merge V9_dev branch into master
[modules/gui.git] / src / SALOME_PYQT / SALOME_PYQT_GUILight / SALOME_PYQT_PyModule.cxx
index a9d6ad43fcc96f304471a5dc915c8be272ce1c43..ce5f82caf5df1fc2dc56a52627c726091e380a8c 100644 (file)
@@ -1,9 +1,9 @@
-// Copyright (C) 2007-2013  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
 //
 // 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
 //
 // 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.
+// 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
 //
 // This library is distributed in the hope that it will be useful,
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -65,6 +65,9 @@ const int DEFAULT_GROUP = 40;
 */
 QMutex myInitMutex;
 
 */
 QMutex myInitMutex;
 
+/*! DEBUG mode */
+const bool DEBUG = false;
+
 /*!
   \var IsCallOldMethods
   \brief Allow calling obsolete callback methods.
 /*!
   \var IsCallOldMethods
   \brief Allow calling obsolete callback methods.
@@ -75,7 +78,7 @@ QMutex myInitMutex;
   etc. is blocked.
 
   CALL_OLD_METHODS macro can be defined, for example, by adding 
   etc. is blocked.
 
   CALL_OLD_METHODS macro can be defined, for example, by adding 
-  -DCALL_OLD_METHODS compilation option to the Makefile.
+  -DCALL_OLD_METHODS compilation option to the CMakeLists.txt.
 */
 #ifdef CALL_OLD_METHODS
 const bool IsCallOldMethods = true;
 */
 #ifdef CALL_OLD_METHODS
 const bool IsCallOldMethods = true;
@@ -165,15 +168,18 @@ public:
   FuncMsg( const QString& funcName )
   {
     myName = funcName;
   FuncMsg( const QString& funcName )
   {
     myName = funcName;
-    MESSAGE( qPrintable( myName ) << " [ begin ]" );
+    if ( DEBUG )
+      MESSAGE( qPrintable( myName ) << " [ begin ]" );
   }
   ~FuncMsg()
   {
   }
   ~FuncMsg()
   {
-    MESSAGE( qPrintable( myName ) << " [ end ]" );
+    if ( DEBUG )
+      MESSAGE( qPrintable( myName ) << " [ end ]" );
   }
   void message( const QString& msg )
   {
   }
   void message( const QString& msg )
   {
-    MESSAGE( qPrintable( myName ) << " : " << qPrintable( msg ) );
+    if ( DEBUG )
+      MESSAGE( qPrintable( myName ) << " : " << qPrintable( msg ) );
   }
 private:
   QString myName;
   }
 private:
   QString myName;
@@ -476,9 +482,10 @@ void PyModuleHelper::XmlHandler::createToolBar( QDomNode& parentNode )
   QDomElement parentElement = parentNode.toElement();
   if ( !parentElement.isNull() ) {
     QString aLabel = attribute( parentElement, "label-id" );
   QDomElement parentElement = parentNode.toElement();
   if ( !parentElement.isNull() ) {
     QString aLabel = attribute( parentElement, "label-id" );
+    QString aName  = attribute( parentElement, "name-id" );
     if ( !aLabel.isEmpty() ) {
       // create toolbar
     if ( !aLabel.isEmpty() ) {
       // create toolbar
-      int tbId = module()->createTool( aLabel );
+      int tbId = module()->createTool( aLabel, aName );
       QDomNode node = parentNode.firstChild();
       while ( !node.isNull() ) {
         if ( node.isElement() ) {
       QDomNode node = parentNode.firstChild();
       while ( !node.isNull() ) {
         if ( node.isElement() ) {
@@ -602,7 +609,6 @@ void PyModuleHelper::XmlHandler::insertPopupItems( QDomNode& parentNode, QMenu*
   SALOME GUI modules.
 */
 
   SALOME GUI modules.
 */
 
-PyModuleHelper::InterpMap PyModuleHelper::myInterpMap;
 LightApp_Module*          PyModuleHelper::myInitModule = 0;
 
 /*!
 LightApp_Module*          PyModuleHelper::myInitModule = 0;
 
 /*!
@@ -1095,7 +1101,7 @@ void PyModuleHelper::studyActivated( SUIT_Study* study )
   public:
     StudyChangedReq( PyModuleHelper* _helper,
                      SUIT_Study*     _study )
   public:
     StudyChangedReq( PyModuleHelper* _helper,
                      SUIT_Study*     _study )
-      : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
+      : PyInterp_Request(0, true ), // this request should be processed synchronously (sync == true)
         myHelper( _helper ), 
         myStudy ( _study )
     {}
         myHelper( _helper ), 
         myStudy ( _study )
     {}
@@ -1155,6 +1161,44 @@ void PyModuleHelper::actionActivated()
   PyInterp_Dispatcher::Get()->Exec( new ActionReq( myInterp, this, myModule->actionId( action ) ) );
 }
 
   PyInterp_Dispatcher::Get()->Exec( new ActionReq( myInterp, this, myModule->actionId( action ) ) );
 }
 
+/*!
+  \brief update selection from other views or modules.
+
+  Called when selection is modified outside.
+*/
+void PyModuleHelper::selectionUpdated(const QStringList& entries)
+{
+  FuncMsg fmsg( "PyModuleHelper::selectionUpdated()" );
+  MESSAGE("selectionUpdated");
+
+  // perform synchronous request to Python event dispatcher
+  class SelectionReq : public PyInterp_LockRequest
+  {
+  public:
+    SelectionReq( PyInterp_Interp* _py_interp,
+                  PyModuleHelper*  _helper,
+                  const QStringList& _entries )
+      : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
+        myHelper( _helper ),
+        myEntries( _entries  )
+    {
+      MESSAGE("SelectionReq");
+    }
+  protected:
+    virtual void execute()
+    {
+      MESSAGE("execute");
+      myHelper->internalSelectionUpdated( myEntries );
+    }
+  private:
+    PyModuleHelper* myHelper;
+    const QStringList& myEntries;
+  };
+
+  // post request
+  PyInterp_Dispatcher::Get()->Exec( new SelectionReq( myInterp, this, entries ) );
+}
+
 /*!
   \brief Process context popup menu request.
   
 /*!
   \brief Process context popup menu request.
   
@@ -1367,8 +1411,9 @@ void PyModuleHelper::cloneView( SUIT_ViewWindow* view )
 /*!
   \brief Save module data. Called when user saves study.
   \param files output list of files where module stores data
 /*!
   \brief Save module data. Called when user saves study.
   \param files output list of files where module stores data
+  \param url study URL
 */
 */
-void PyModuleHelper::save( QStringList& files )
+void PyModuleHelper::save( QStringList& files, const QString& url )
 {
   FuncMsg fmsg( "PyModuleHelper::save()" );
 
 {
   FuncMsg fmsg( "PyModuleHelper::save()" );
 
@@ -1384,33 +1429,38 @@ void PyModuleHelper::save( QStringList& files )
   public:     
     SaveReq( PyInterp_Interp* _py_interp,
              PyModuleHelper*  _helper,
   public:     
     SaveReq( PyInterp_Interp* _py_interp,
              PyModuleHelper*  _helper,
-             QStringList&     _files )
+             QStringList&     _files,
+             const QString&   _url )
       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
         myHelper( _helper ) ,
       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
         myHelper( _helper ) ,
-        myFiles( _files )
+        myFiles( _files ),
+        myUrl( _url )
     {}
   protected:
     virtual void execute()
     {
     {}
   protected:
     virtual void execute()
     {
-      myHelper->internalSave( myFiles );
+      myHelper->internalSave( myFiles, myUrl );
     }
   private:
     PyModuleHelper* myHelper;
     QStringList&    myFiles;
     }
   private:
     PyModuleHelper* myHelper;
     QStringList&    myFiles;
+    QString         myUrl;
   };
   
   // Posting the request only if dispatcher is not busy!
   // Executing the request synchronously
   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
   };
   
   // Posting the request only if dispatcher is not busy!
   // Executing the request synchronously
   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
-    PyInterp_Dispatcher::Get()->Exec( new SaveReq( myInterp, this, files ) );
+    PyInterp_Dispatcher::Get()->Exec( new SaveReq( myInterp, this, files, url ) );
 }
 
 /*
 }
 
 /*
- \brief Load module data. Called when user opens study 
- and activates module.
- \param files list of files where module data is stored
+  \brief Load module data. Called when user opens study 
+  and activates module.
+  \param files list of files where module data is stored
+  \param url study URL
+  \return \c true if loading has been finished successfully or \c false otherwise
 */
 */
-bool PyModuleHelper::load( const QStringList& files )
+bool PyModuleHelper::load( const QStringList& files, const QString& url )
 {
   FuncMsg fmsg( "PyModuleHelper::load()" );
 
 {
   FuncMsg fmsg( "PyModuleHelper::load()" );
 
@@ -1422,27 +1472,30 @@ bool PyModuleHelper::load( const QStringList& files )
     LoadReq( PyInterp_Interp* _py_interp,
              PyModuleHelper*  _helper,
              QStringList      _files,
     LoadReq( PyInterp_Interp* _py_interp,
              PyModuleHelper*  _helper,
              QStringList      _files,
+             const QString&   _url,
              bool&            _loaded )
       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
         myHelper( _helper ) ,
         myFiles( _files ),
              bool&            _loaded )
       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
         myHelper( _helper ) ,
         myFiles( _files ),
+        myUrl( _url ),
         myLoaded( _loaded )
     {}
   protected:
     virtual void execute()
     {
         myLoaded( _loaded )
     {}
   protected:
     virtual void execute()
     {
-      myHelper->internalLoad( myFiles, myLoaded );
+      myHelper->internalLoad( myFiles, myUrl, myLoaded );
     }
   private:
     PyModuleHelper* myHelper;
     QStringList     myFiles;
     }
   private:
     PyModuleHelper* myHelper;
     QStringList     myFiles;
+    QString         myUrl;
     bool&           myLoaded;
   };
   
   // Posting the request only if dispatcher is not busy!
   // Executing the request synchronously
   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
     bool&           myLoaded;
   };
   
   // Posting the request only if dispatcher is not busy!
   // Executing the request synchronously
   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
-    PyInterp_Dispatcher::Get()->Exec( new LoadReq( myInterp, this, files, loaded ) );
+    PyInterp_Dispatcher::Get()->Exec( new LoadReq( myInterp, this, files, url, loaded ) );
 
   return loaded;
 }
 
   return loaded;
 }
@@ -1677,31 +1730,15 @@ QString PyModuleHelper::engineIOR() const
 /*!
   \brief Initialize python subinterpreter (one per study).
   \internal
 /*!
   \brief Initialize python subinterpreter (one per study).
   \internal
-  \param studyId study ID
 */
 */
-void PyModuleHelper::initInterp( int studyId )
+void PyModuleHelper::initInterp()
 {
   FuncMsg fmsg( "--- PyModuleHelper::initInterp()" );
 
 {
   FuncMsg fmsg( "--- PyModuleHelper::initInterp()" );
 
-  // check study Id
-  if ( !studyId ) {
-    // Error! Study Id must not be 0!
-    myInterp = 0;
-    return;
-  }
-
   QMutexLocker ml( &myInitMutex );
 
   QMutexLocker ml( &myInitMutex );
 
-  // try to find the subinterpreter
-  if ( myInterpMap.contains( studyId ) ) {
-    // found!
-    myInterp = myInterpMap[ studyId ];
-    return;
-  }
-
   myInterp = new SALOME_PYQT_PyInterp();
   myInterp->initialize();
   myInterp = new SALOME_PYQT_PyInterp();
   myInterp->initialize();
-  myInterpMap[ studyId ] = myInterp;
   
 #ifndef GUI_DISABLE_CORBA
   if ( !SUIT_PYTHON::initialized ) {
   
 #ifndef GUI_DISABLE_CORBA
   if ( !SUIT_PYTHON::initialized ) {
@@ -1718,7 +1755,7 @@ void PyModuleHelper::initInterp( int studyId )
     }
     // ... then call a method
     int embedded = 1;
     }
     // ... then call a method
     int embedded = 1;
-    PyObjWrapper aRes( PyObject_CallMethod( aMod, (char*)"salome_init", (char*)"ii", studyId, embedded ) );
+    PyObjWrapper aRes( PyObject_CallMethod( aMod, (char*)"salome_init", (char*)"i", embedded ) );
     if ( !aRes ) {
       // Error!
       PyErr_Print();
     if ( !aRes ) {
       // Error!
       PyErr_Print();
@@ -1809,11 +1846,12 @@ void PyModuleHelper::setWorkSpace()
     if ( d )
       aWorkspace = d->workstack();
   }
     if ( d )
       aWorkspace = d->workstack();
   }
-#if SIP_VERSION < 0x040800
-  PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget) );
-#else
-  PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
+#if SIP_VERSION >= 0x041300
+  static const sipTypeDef *sipType_QWidget = 0;
+  if (!sipType_QWidget)
+    sipType_QWidget = sipFindType("QWidget");
 #endif
 #endif
+  PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
   // ... and finally call Python module's setWorkSpace() method (obsolete)
   if ( PyObject_HasAttrString( myPyModule, (char*)"setWorkSpace" ) ) {
     PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
   // ... and finally call Python module's setWorkSpace() method (obsolete)
   if ( PyObject_HasAttrString( myPyModule, (char*)"setWorkSpace" ) ) {
     PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
@@ -1851,10 +1889,9 @@ void PyModuleHelper::internalInitialize( CAM_Application* app )
   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
   if ( !aStudy )
     return;
   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
   if ( !aStudy )
     return;
-  int aStudyId = aStudy ? aStudy->id() : 0;
 
   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
 
   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
-  initInterp( aStudyId );
+  initInterp();
   if ( !myInterp )
     return; // Error
 
   if ( !myInterp )
     return; // Error
 
@@ -1898,9 +1935,9 @@ void PyModuleHelper::internalInitialize( CAM_Application* app )
           // parse the return value
           // it should be a map: {integer:integer}
           int aKey, aValue;
           // 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 );
+          if( key && PyLong_Check( key ) && value && PyLong_Check( value ) ) {
+            aKey   = PyLong_AsLong( key );
+            aValue = PyLong_AsLong( value );
             myWindowsMap[ aKey ] = aValue;
           }
         }
             myWindowsMap[ aKey ] = aValue;
           }
         }
@@ -1918,16 +1955,16 @@ void PyModuleHelper::internalInitialize( CAM_Application* app )
     else {
       // parse the return value
       // result can be one string...
     else {
       // parse the return value
       // result can be one string...
-      if ( PyString_Check( res2 ) ) {
-        myViewMgrList.append( PyString_AsString( res2 ) );
+      if ( PyUnicode_Check( res2 ) ) {
+        myViewMgrList.append( PyUnicode_AsUTF8( res2 ) );
       }
       // ... 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 );
       }
       // ... 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 ) );
+          if( value && PyUnicode_Check( value ) ) {
+            myViewMgrList.append( PyUnicode_AsUTF8( value ) );
           }
         }
       }
           }
         }
       }
@@ -1952,10 +1989,11 @@ void PyModuleHelper::internalActivate( SUIT_Study* study )
 
   // get study Id
   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
 
   // get study Id
   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
-  int aStudyId = aStudy ? aStudy->id() : 0;
+  if ( !aStudy )
+    return;
 
   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
 
   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
-  initInterp( aStudyId );
+  initInterp();
   if ( !myInterp ) {
     myLastActivateStatus = false;
     return; // Error
   if ( !myInterp ) {
     myLastActivateStatus = false;
     return; // Error
@@ -2004,10 +2042,11 @@ void PyModuleHelper::internalCustomize( SUIT_Study* study )
 
   // get study Id
   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
 
   // get study Id
   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
-  int aStudyId = aStudy ? aStudy->id() : 0;
+  if ( !aStudy )
+    return;
 
   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
 
   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
-  initInterp( aStudyId );
+  initInterp();
   if ( !myInterp ) {
     myLastActivateStatus = false;
     return; // Error
   if ( !myInterp ) {
     myLastActivateStatus = false;
     return; // Error
@@ -2080,7 +2119,8 @@ void PyModuleHelper::internalClosedStudy( SUIT_Study* theStudy )
   // Get study Id
   // get study Id
   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
   // Get study Id
   // get study Id
   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
-  int aStudyId = aStudy ? aStudy->id() : 0;
+  if ( !aStudy )
+    return;
 
   // check that Python subinterpreter is initialized and Python module is imported
   if ( !myInterp || !myPyModule ) {
 
   // check that Python subinterpreter is initialized and Python module is imported
   if ( !myInterp || !myPyModule ) {
@@ -2089,7 +2129,7 @@ void PyModuleHelper::internalClosedStudy( SUIT_Study* theStudy )
   }
   // then call Python module's deactivate() method
   if ( PyObject_HasAttrString( myPyModule , (char*)"closeStudy" ) ) {
   }
   // then call Python module's deactivate() method
   if ( PyObject_HasAttrString( myPyModule , (char*)"closeStudy" ) ) {
-    PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"closeStudy", (char*)"i", aStudyId ) );
+    PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"closeStudy", (char*)"i" ) );
     if( !res ) {
       PyErr_Print();
     }
     if( !res ) {
       PyErr_Print();
     }
@@ -2148,12 +2188,11 @@ void PyModuleHelper::internalStudyChanged( SUIT_Study* study )
 
   // get study Id
   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
 
   // get study Id
   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
-  int id = aStudy ? aStudy->id() : 0;
-
-  fmsg.message( QString( "study id = %1" ).arg( id ) );
+  if ( !aStudy )
+    return;
 
   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
 
   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
-  initInterp( id );
+  initInterp();
   if ( !myInterp )
     return; // Error
 
   if ( !myInterp )
     return; // Error
 
@@ -2170,7 +2209,7 @@ void PyModuleHelper::internalStudyChanged( SUIT_Study* study )
 
   // call Python module's activeStudyChanged() method
   if ( PyObject_HasAttrString( myPyModule, (char*)"activeStudyChanged" ) ) {
 
   // call Python module's activeStudyChanged() method
   if ( PyObject_HasAttrString( myPyModule, (char*)"activeStudyChanged" ) ) {
-    PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeStudyChanged", (char*)"i", id ) );
+    PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeStudyChanged", (char*)"i" ) );
     if( !res ) {
       PyErr_Print();
     }
     if( !res ) {
       PyErr_Print();
     }
@@ -2204,6 +2243,44 @@ void PyModuleHelper::internalActionActivated( int id )
   }
 }
 
   }
 }
 
+/*!
+  \brief update selection from other views or modules
+  \internal
+
+  Performs the following actions:
+  - calls Python module's onSelectionpdated(entries) method
+
+  \param list of entries
+*/
+void PyModuleHelper::internalSelectionUpdated(const QStringList& entries)
+{
+  FuncMsg fmsg("--- PyModuleHelper::internalSelectionUpdated()");
+  MESSAGE("internalSelectionUpdated");
+
+  // Python interpreter should be initialized and Python module should be imported first
+  if (!myInterp || !myPyModule)
+    return; // Error
+
+  QStringList* theList = new QStringList(entries);
+
+#if SIP_VERSION >= 0x041300
+  static const sipTypeDef *sipType_QStringList = 0;
+  if (!sipType_QStringList)
+    sipType_QStringList = sipFindType("QStringList");
+#endif
+  PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
+  if (PyObject_HasAttrString(myPyModule, (char*) "onSelectionUpdated"))
+    {
+      MESSAGE("call onSelectionUpdated");
+      PyObjWrapper res(PyObject_CallMethod(myPyModule, (char*) "onSelectionUpdated", (char*) "O", sipList.get()));
+
+      if (!res)
+        {
+          PyErr_Print();
+        }
+    }
+}
+
 /*!
   \brief Context popup menu handling callback function
   \internal
 /*!
   \brief Context popup menu handling callback function
   \internal
@@ -2260,11 +2337,12 @@ void PyModuleHelper::internalContextMenu( const QString& context, QMenu* menu )
   if ( myXmlHandler )
     myXmlHandler->createPopup( menu, aContext, aParent, aObject );
 
   if ( myXmlHandler )
     myXmlHandler->createPopup( menu, aContext, aParent, aObject );
 
-#if SIP_VERSION < 0x040800
-  PyObjWrapper sipPopup( sipBuildResult( 0, "M", menu, sipClass_QMenu ) );
-#else
-  PyObjWrapper sipPopup( sipBuildResult( 0, "D", menu, sipType_QMenu, NULL ) );
+#if SIP_VERSION >= 0x041300
+  static const sipTypeDef *sipType_QMenu = 0;
+  if (!sipType_QMenu)
+    sipType_QMenu = sipFindType("QMenu");
 #endif
 #endif
+  PyObjWrapper sipPopup( sipBuildResult( 0, "D", menu, sipType_QMenu, NULL ) );
 
   // then call Python module's createPopupMenu() method (for new modules)
   if ( PyObject_HasAttrString( myPyModule, (char*)"createPopupMenu" ) ) {
 
   // then call Python module's createPopupMenu() method (for new modules)
   if ( PyObject_HasAttrString( myPyModule, (char*)"createPopupMenu" ) ) {
@@ -2414,8 +2492,9 @@ void PyModuleHelper::internalCloneView( SUIT_ViewWindow* view )
   \brief Module data saving callback function.
   \internal
   \param files output list of files where module stores data
   \brief Module data saving callback function.
   \internal
   \param files output list of files where module stores data
+  \param url study URL
 */
 */
-void PyModuleHelper::internalSave( QStringList& files )
+void PyModuleHelper::internalSave( QStringList& files, const QString& url )
 {
   FuncMsg fmsg( "--- PyModuleHelper::internalSave()" );
 
 {
   FuncMsg fmsg( "--- PyModuleHelper::internalSave()" );
 
@@ -2427,17 +2506,24 @@ void PyModuleHelper::internalSave( QStringList& files )
 
   if ( PyObject_HasAttrString(myPyModule, (char*)"saveFiles") ) {
 
 
   if ( PyObject_HasAttrString(myPyModule, (char*)"saveFiles") ) {
 
+    // try with two parameters (new syntax)
     PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"saveFiles",
     PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"saveFiles",
-                                           (char*)"s", files.first().toLatin1().constData() ) );
-
+                                           (char*)"ss",
+                                           files.first().toLatin1().constData(),
+                                           url.toLatin1().constData() ) );
+    if ( !res )
+      // try with single parameter (old syntax)
+      res = PyObject_CallMethod( myPyModule, (char*)"saveFiles",
+                                 (char*)"s", files.first().toLatin1().constData() );
+    
     if ( !res ) {
       PyErr_Print();
     }
     else {
       // parse the return value
       // result can be one string...
     if ( !res ) {
       PyErr_Print();
     }
     else {
       // parse the return value
       // result can be one string...
-      if ( PyString_Check( res ) ) {
-        QString astr = PyString_AsString( res );
+      if ( PyUnicode_Check( res ) ) {
+        QString astr = PyUnicode_AsUTF8( res );
         files.append( astr );
       }
       //also result can be a list...
         files.append( astr );
       }
       //also result can be a list...
@@ -2445,8 +2531,8 @@ void PyModuleHelper::internalSave( QStringList& files )
         int size = PyList_Size( res );
         for ( int i = 0; i < size; i++ ) {
           PyObject* value = PyList_GetItem( res, i );
         int size = PyList_Size( res );
         for ( int i = 0; i < size; i++ ) {
           PyObject* value = PyList_GetItem( res, i );
-          if ( value && PyString_Check( value ) ) {
-            files.append( PyString_AsString( value ) );
+          if ( value && PyUnicode_Check( value ) ) {
+            files.append( PyUnicode_AsUTF8( value ) );
           }
         }
       }
           }
         }
       }
@@ -2458,9 +2544,10 @@ void PyModuleHelper::internalSave( QStringList& files )
   \brief Module data loading callback function.
   \internal
   \param files list of files where module data is stored
   \brief Module data loading callback function.
   \internal
   \param files list of files where module data is stored
+  \param url study URL
   \param opened output success flag
 */
   \param opened output success flag
 */
-void PyModuleHelper::internalLoad( const QStringList& files, bool& opened )
+void PyModuleHelper::internalLoad( const QStringList& files, const QString& url, bool& opened )
 {
   FuncMsg fmsg( "--- PyModuleHelper::internalLoad()" );
 
 {
   FuncMsg fmsg( "--- PyModuleHelper::internalLoad()" );
 
@@ -2471,19 +2558,29 @@ void PyModuleHelper::internalLoad( const QStringList& files, bool& opened )
 
   QStringList* theList = new QStringList( files );
 
 
   QStringList* theList = new QStringList( files );
 
-#if SIP_VERSION < 0x040800
-  PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList ) );
-#else
-  PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
+#if SIP_VERSION >= 0x041300
+  static const sipTypeDef *sipType_QStringList = 0;
+  if (!sipType_QStringList)
+    sipType_QStringList = sipFindType("QStringList");
 #endif
 #endif
+  PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
   if ( PyObject_HasAttrString(myPyModule , (char*)"openFiles") ) {
   if ( PyObject_HasAttrString(myPyModule , (char*)"openFiles") ) {
+
+    // try with two parameters (new syntax)
     PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"openFiles",
     PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"openFiles",
-                                           (char*)"O", sipList.get()));
-    if( !res || !PyBool_Check( res )) {
+                                           (char*)"Os", sipList.get(),
+                                           url.toLatin1().constData() ) );
+
+    if ( !res )
+      // try with single parameter (old syntax)
+      res = PyObject_CallMethod( myPyModule, (char*)"openFiles",
+                                 (char*)"O", sipList.get() );
+
+    if ( !res || !PyBool_Check( res ) ) {
       PyErr_Print();
       opened = false;
     }
       PyErr_Print();
       opened = false;
     }
-    else{
+    else {
       opened = PyObject_IsTrue( res );
     }
   }
       opened = PyObject_IsTrue( res );
     }
   }
@@ -2514,8 +2611,8 @@ void PyModuleHelper::internalDumpPython( QStringList& files )
     else {
       // parse the return value
       // result can be one string...
     else {
       // parse the return value
       // result can be one string...
-      if ( PyString_Check( res ) ) {
-        QString astr = PyString_AsString( res );
+      if ( PyUnicode_Check( res ) ) {
+        QString astr = PyUnicode_AsUTF8( res );
         //SCRUTE(astr);
         files.append(astr);
       }
         //SCRUTE(astr);
         files.append(astr);
       }
@@ -2524,8 +2621,8 @@ void PyModuleHelper::internalDumpPython( QStringList& files )
         int size = PyList_Size( res );
         for ( int i = 0; i < size; i++ ) {
           PyObject* value = PyList_GetItem( res, i );
         int size = PyList_Size( res );
         for ( int i = 0; i < size; i++ ) {
           PyObject* value = PyList_GetItem( res, i );
-          if( value && PyString_Check( value ) ) {
-            files.append( PyString_AsString( value ) );
+          if( value && PyUnicode_Check( value ) ) {
+            files.append( PyUnicode_AsUTF8( value ) );
           }
         }
       }
           }
         }
       }
@@ -2625,11 +2722,12 @@ void PyModuleHelper::internalDropObjects( const DataObjectList& what, SUIT_DataO
     if ( dataObject ) theList->append( dataObject->entry() );
   }
 
     if ( dataObject ) theList->append( dataObject->entry() );
   }
 
-#if SIP_VERSION < 0x040800
-  PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
-#else
-  PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
+#if SIP_VERSION >= 0x041300
+  static const sipTypeDef *sipType_QStringList = 0;
+  if (!sipType_QStringList)
+    sipType_QStringList = sipFindType("QStringList");
 #endif
 #endif
+  PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
   if ( PyObject_HasAttrString(myPyModule, (char*)"dropObjects") ) {
       PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dropObjects", (char*)"Osii",
                         sipList.get(),
   if ( PyObject_HasAttrString(myPyModule, (char*)"dropObjects") ) {
       PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dropObjects", (char*)"Osii",
                         sipList.get(),
@@ -2667,8 +2765,8 @@ QString PyModuleHelper::internalEngineIOR() const
       }
       else {
         // parse the return value, result chould be string
       }
       else {
         // parse the return value, result chould be string
-        if ( PyString_Check( res ) ) {
-          ior = PyString_AsString( res );
+        if ( PyUnicode_Check( res ) ) {
+          ior = PyUnicode_AsUTF8( res );
         }
       }
     }
         }
       }
     }
@@ -2708,3 +2806,65 @@ void PyModuleHelper::connectView( SUIT_ViewWindow* view )
              Qt::UniqueConnection );
   }
 }
              Qt::UniqueConnection );
   }
 }
+
+
+
+void PyModuleHelper::internalOBClickedPython( const QString& theObj, int theColumn)
+{
+  FuncMsg fmsg( "--- PyModuleHelper::internalOBClickedPython()" );
+
+  // Python interpreter should be initialized and Python module should be
+  // import first
+  if ( !myInterp || !myPyModule )
+    return; // Error
+
+  if ( PyObject_HasAttrString( myPyModule, (char*)"onObjectBrowserClicked" ) ) {
+    PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"onObjectBrowserClicked", (char*)"si", theObj.toLatin1().constData(), theColumn ) );
+    if( !res ) {
+      PyErr_Print();
+    }
+  }
+}
+
+
+
+void PyModuleHelper::onObjectBrowserClicked(SUIT_DataObject* theObj, int theColumn)
+{
+  FuncMsg fmsg( "PyModuleHelper::onObjectBrowserClicked()" );
+
+  // temporary set myInitModule because dumpPython() method
+  // might be called by the framework when this module is inactive,
+  // but still it should be possible to access this module's data
+  // from Python
+  InitLocker lock( myModule );
+
+  class PythonReq: public PyInterp_LockRequest
+  {
+  public:     
+    PythonReq( PyInterp_Interp* _py_interp,
+               PyModuleHelper*  _helper,
+               const QString& _entry,
+               int     _column )
+      : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
+        myHelper( _helper ) ,
+        myEntry( _entry ),
+        myColumn( _column )
+    {}
+  protected:
+    virtual void execute()
+    {
+      myHelper->internalOBClickedPython( myEntry, myColumn );
+    }
+  private:
+    PyModuleHelper* myHelper;
+    int    myColumn;
+    QString myEntry;
+  };
+  
+  // Posting the request only if dispatcher is not busy!
+  // Executing the request synchronously
+  const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( theObj );
+  if ( (!PyInterp_Dispatcher::Get()->IsBusy()) && data_object )
+    PyInterp_Dispatcher::Get()->Exec( new PythonReq( myInterp, this, data_object->entry(), theColumn ) );
+}
+