Back to index

salome-gui  6.5.0
SALOME_PYQT_ModuleLight.cxx
Go to the documentation of this file.
00001 // Copyright (C) 2007-2012  CEA/DEN, EDF R&D, OPEN CASCADE
00002 //
00003 // This library is free software; you can redistribute it and/or
00004 // modify it under the terms of the GNU Lesser General Public
00005 // License as published by the Free Software Foundation; either
00006 // version 2.1 of the License.
00007 //
00008 // This library is distributed in the hope that it will be useful,
00009 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011 // Lesser General Public License for more details.
00012 //
00013 // You should have received a copy of the GNU Lesser General Public
00014 // License along with this library; if not, write to the Free Software
00015 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
00016 //
00017 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
00018 //
00019 
00020 // File   : SALOME_PYQT_Module.cxx
00021 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
00022 //
00023 #include "SALOME_PYQT_PyInterp.h"
00024 #include <SUITApp_init_python.hxx>
00025 #include <PyInterp_Interp.h>
00026 #include <PyConsole_Interp.h>
00027 #include <PyConsole_Console.h>
00028 #include <PyInterp_Dispatcher.h>
00029 
00030 #include "SALOME_PYQT_ModuleLight.h"
00031 #include "SALOME_PYQT_DataModelLight.h"
00032 
00033 #ifndef GUI_DISABLE_CORBA
00034 #include <Container_init_python.hxx>
00035 #endif
00036 
00037 #include <SUIT_ResourceMgr.h>
00038 #include <SUIT_DataObjectIterator.h>
00039 #include <SUIT_Desktop.h>
00040 #include <SUIT_ViewModel.h>
00041 #include <SUIT_ViewWindow.h>
00042 #include <SUIT_ViewManager.h>
00043 #include <STD_MDIDesktop.h>
00044 #include <STD_TabDesktop.h>
00045 #include <LightApp_Preferences.h>
00046 #include <LightApp_Application.h>
00047 #include <LightApp_Study.h>
00048 
00049 #include <QtxWorkstack.h>
00050 #include <QtxWorkspace.h>
00051 #include <QtxActionGroup.h>
00052 #include <QtxActionMenuMgr.h>
00053 #include <QtxActionToolMgr.h>
00054 
00055 #include <QFile>
00056 #include <QDomDocument>
00057 #include <QDomNode>
00058 #include <QDomElement>
00059 #include <QMenuBar>
00060 #include <QMenu>
00061 #include <QAction>
00062 
00063 #include "sipAPISalomePyQtGUILight.h"
00064 
00065 #include <sip.h>
00066 #if SIP_VERSION < 0x040700
00067 #include "sipQtGuiQWidget.h"
00068 #include "sipQtGuiQMenu.h"
00069 #endif
00070 
00071 #include <utilities.h>
00072 
00078 const char* DEFAULT_NAME  = "SALOME_PYQT_ModuleLight";
00079 
00084 const int DEFAULT_GROUP = 40;
00085 
00098 #ifdef CALL_OLD_METHODS
00099 const bool IsCallOldMethods = true;
00100 #else
00101 const bool IsCallOldMethods = false;
00102 #endif
00103 
00104 /* Py_ssize_t for old Pythons */
00105 /* This code is as recommended by: */
00106 /* http://www.python.org/dev/peps/pep-0353/#conversion-guidelines */
00107 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
00108 typedef int Py_ssize_t;
00109 # define PY_SSIZE_T_MAX INT_MAX
00110 # define PY_SSIZE_T_MIN INT_MIN
00111 #endif
00112 
00113 //
00114 // NB: Python requests.
00115 // General rule for Python requests created by SALOME_PYQT_Module:
00116 // all requests should be executed SYNCHRONOUSLY within the main GUI thread.
00117 // However, it is obligatory that ANY Python call is wrapped with a request object,
00118 // so that ALL Python API calls are serialized with PyInterp_Dispatcher.
00119 //
00120 
00131 class SALOME_PYQT_ModuleLight::XmlHandler
00132 {
00133 public:
00134   XmlHandler( SALOME_PYQT_ModuleLight* module, const QString& fileName );
00135   void createActions();
00136   void createPopup  ( QMenu*         menu,
00137                       const QString& context,
00138                       const QString& parent,
00139                       const QString& object );
00140   void activateMenus( bool );
00141 
00142 protected:
00143   void createToolBar   ( QDomNode&   parentNode );
00144   void createMenu      ( QDomNode&   parentNode,
00145                          const int   parentMenuId = -1,
00146                          QMenu*      parentPopup = 0 );
00147 
00148   void insertPopupItems( QDomNode&   parentNode,
00149                          QMenu*      menu );
00150 
00151 private:
00152   SALOME_PYQT_ModuleLight* myModule;
00153   QDomDocument             myDoc;
00154   QList<int>               myMenuItems;
00155 };
00156 
00157 //
00158 // NB: Library initialization
00159 // Since the SalomePyQtGUILight library is not imported in Python it's initialization function
00160 // should be called manually (and only once) in order to initialize global sip data
00161 // and to get C API from sip : sipBuildResult for example
00162 //
00163 
00164 #define INIT_FUNCTION initSalomePyQtGUILight
00165 #if defined(SIP_STATIC_MODULE)
00166 extern "C" void INIT_FUNCTION();
00167 #else
00168 PyMODINIT_FUNC INIT_FUNCTION();
00169 #endif
00170 
00182 extern "C" {
00183   SALOME_PYQT_LIGHT_EXPORT CAM_Module* createModule() {
00184 
00185     static bool alreadyInitialized = false;
00186     if ( !alreadyInitialized ) {
00187       // call only once (see comment above) !
00188       static PyThreadState *gtstate = 0;
00189 #ifndef GUI_DISABLE_CORBA
00190       if(SUIT_PYTHON::initialized)
00191         gtstate = SUIT_PYTHON::_gtstate;
00192       else
00193         gtstate = KERNEL_PYTHON::_gtstate;
00194 #else
00195       gtstate = SUIT_PYTHON::_gtstate;
00196 #endif
00197       PyEval_RestoreThread( gtstate );
00198       INIT_FUNCTION();
00199       PyEval_ReleaseThread( gtstate );
00200       alreadyInitialized = !alreadyInitialized;
00201     }
00202     return new SALOME_PYQT_ModuleLight();
00203   }
00204 }
00205 
00212 class FuncMsg
00213 {
00214 public:
00215   FuncMsg( const QString& funcName )
00216   {
00217     myName = funcName;
00218     MESSAGE( myName.toLatin1().constData() << " [ begin ]" );
00219   }
00220   ~FuncMsg()
00221   {
00222     MESSAGE( myName.toLatin1().constData() << " [ end ]" );
00223   }
00224   void message( const QString& msg )
00225   {
00226     MESSAGE( myName.toLatin1().constData() << " : " << msg.toLatin1().constData() );
00227   }
00228 private:
00229   QString myName;
00230 };
00231 
00238 //
00239 // Static variables definition
00240 //
00241 SALOME_PYQT_ModuleLight::InterpMap SALOME_PYQT_ModuleLight::myInterpMap;
00242 SALOME_PYQT_ModuleLight* SALOME_PYQT_ModuleLight::myInitModule = 0;
00243 
00258 SALOME_PYQT_ModuleLight* SALOME_PYQT_ModuleLight::getInitModule()
00259 {
00260   return myInitModule;
00261 }
00262 
00266 SALOME_PYQT_ModuleLight::SALOME_PYQT_ModuleLight()
00267 : LightApp_Module( DEFAULT_NAME ),
00268   myInterp( 0 ),
00269   myModule( 0 ), 
00270   myXmlHandler ( 0 ),
00271   myLastActivateStatus( true )
00272 {
00273 }
00274 
00278 SALOME_PYQT_ModuleLight::~SALOME_PYQT_ModuleLight()
00279 {
00280   if ( myXmlHandler )
00281     delete myXmlHandler;
00282   if ( myInterp && myModule ) {
00283     PyLockWrapper aLock = myInterp->GetLockWrapper();
00284     Py_XDECREF(myModule);
00285   }
00286 }
00287 
00313 void SALOME_PYQT_ModuleLight::initialize( CAM_Application* app )
00314 {
00315   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::initialize()" );
00316 
00317   // call base implementation
00318   LightApp_Module::initialize( app );
00319 
00320   // try to get XML resource file name
00321   SUIT_ResourceMgr* aResMgr = getApp()->resourceMgr();
00322   if ( !myXmlHandler && aResMgr ) {
00323     // get current language
00324     QString aLang = aResMgr->stringValue( "language", "language", QString() );
00325     if ( aLang.isEmpty() ) 
00326       aLang = "en";
00327     // define resource file name
00328     QString aFileName = name() + "_" + aLang + ".xml";
00329     aFileName = aResMgr->path( "resources", name(), aFileName );
00330     // create XML handler instance
00331     if ( !aFileName.isEmpty() && QFile::exists( aFileName ) )
00332       myXmlHandler = new SALOME_PYQT_ModuleLight::XmlHandler( this, aFileName );
00333     // create menus & toolbars from XML file if required
00334     if ( myXmlHandler )
00335       myXmlHandler->createActions();
00336   }
00337 
00338   // perform internal initialization and call module's initialize() funtion
00339   // InitializeReq: request class for internal init() operation
00340   class InitializeReq : public PyInterp_Request
00341   {
00342   public:
00343     InitializeReq( CAM_Application*    _app,
00344                    SALOME_PYQT_ModuleLight* _obj )
00345       : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
00346         myApp( _app ),
00347         myObj( _obj ) {}
00348 
00349   protected:
00350     virtual void execute()
00351     {
00352       myObj->init( myApp );
00353     }
00354 
00355   private:
00356     CAM_Application*    myApp;
00357     SALOME_PYQT_ModuleLight* myObj;
00358   };
00359 
00360   // post request
00361   PyInterp_Dispatcher::Get()->Exec( new InitializeReq( app, this ) );
00362 }
00363 
00377 bool SALOME_PYQT_ModuleLight::activateModule( SUIT_Study* theStudy )
00378 {
00379   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::activateModule()" );
00380 
00381   // call base implementation
00382   bool res = LightApp_Module::activateModule( theStudy );
00383 
00384   if ( !res )
00385     return res;
00386 
00387   // internal activation
00388   return activateModuleInternal( theStudy );
00389 }
00390 
00400 bool SALOME_PYQT_ModuleLight::activateModuleInternal( SUIT_Study* theStudy )
00401 {
00402   // reset the activation status to the default value
00403   myLastActivateStatus = true;
00404 
00405   // perform internal activation
00406   // ActivateReq: request class for internal activate() operation
00407   class ActivateReq : public PyInterp_Request
00408   {
00409   public:
00410     ActivateReq( SUIT_Study*         _study,
00411                  SALOME_PYQT_ModuleLight* _obj )
00412       : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
00413         myStudy ( _study ),
00414         myObj   ( _obj   ) {}
00415 
00416   protected:
00417     virtual void execute()
00418     {
00419       myObj->activate( myStudy );
00420     }
00421 
00422   private:
00423     SUIT_Study*         myStudy;
00424     SALOME_PYQT_ModuleLight* myObj;
00425   };
00426 
00427   // post request
00428   PyInterp_Dispatcher::Get()->Exec( new ActivateReq( theStudy, this ) );
00429 
00430   // check activation status (set by activate())
00431   if ( !lastActivationStatus() )
00432     return false;
00433 
00434   // activate menus, toolbars, etc
00435   if ( myXmlHandler ) myXmlHandler->activateMenus( true );
00436   setMenuShown( true );
00437   setToolShown( true );
00438 
00439   // connect preferences changing signal
00440   connect( getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
00441            this,     SLOT(   preferenceChanged( const QString&, const QString&, const QString& ) ) );
00442 
00443   // perform custom activation actions
00444   // CustomizeReq: request class for internal customize() operation
00445   class CustomizeReq : public PyInterp_Request
00446   {
00447   public:
00448     CustomizeReq( SUIT_Study*         _study,
00449                   SALOME_PYQT_ModuleLight* _obj )
00450       : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
00451         myStudy ( _study ),
00452         myObj   ( _obj   ) {}
00453 
00454   protected:
00455     virtual void execute()
00456     {
00457       myObj->customize( myStudy );
00458     }
00459 
00460   private:
00461     SUIT_Study*         myStudy;
00462     SALOME_PYQT_ModuleLight* myObj;
00463   };
00464 
00465   // post request
00466   PyInterp_Dispatcher::Get()->Exec( new CustomizeReq( theStudy, this ) );
00467 
00468   return true;
00469 }
00470 
00481 bool SALOME_PYQT_ModuleLight::deactivateModule( SUIT_Study* theStudy )
00482 {
00483   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::deactivateModule()" );
00484 
00485   // disconnect preferences changing signal
00486   disconnect( getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
00487               this,     SLOT(   preferenceChanged( const QString&, const QString&, const QString& ) ) );
00488 
00489   // perform internal deactivation
00490   // DeactivateReq: request class for internal deactivate() operation
00491   class DeactivateReq : public PyInterp_LockRequest
00492   {
00493   public:
00494     DeactivateReq( PyInterp_Interp*    _py_interp,
00495                    SUIT_Study*         _study,
00496                    SALOME_PYQT_ModuleLight* _obj )
00497       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
00498         myStudy ( _study ),
00499         myObj   ( _obj   ) {}
00500 
00501   protected:
00502     virtual void execute()
00503     {
00504       myObj->deactivate( myStudy );
00505     }
00506 
00507   private:
00508     SUIT_Study*         myStudy;
00509     SALOME_PYQT_ModuleLight* myObj;
00510   };
00511 
00512   // post request
00513   PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, theStudy, this ) );
00514 
00515   // deactivate menus, toolbars, etc
00516   if ( myXmlHandler ) myXmlHandler->activateMenus( false );
00517   setMenuShown( false );
00518   setToolShown( false );
00519 
00520   // call base implementation
00521   return LightApp_Module::deactivateModule( theStudy );
00522 }
00523 
00529 bool SALOME_PYQT_ModuleLight::lastActivationStatus() const
00530 {
00531   return myLastActivateStatus;
00532 }
00533 
00543 void SALOME_PYQT_ModuleLight::preferenceChanged( const QString& module, 
00544                                             const QString& section, 
00545                                             const QString& setting )
00546 {
00547   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::preferenceChanged()" );
00548 
00549   // perform synchronous request to Python event dispatcher
00550   class Event : public PyInterp_LockRequest
00551   {
00552   public:
00553     Event( PyInterp_Interp*    _py_interp,
00554            SALOME_PYQT_ModuleLight* _obj,
00555            const QString&      _section,
00556            const QString&      _setting )
00557       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
00558         myObj    ( _obj ),
00559         mySection( _section ),
00560         mySetting( _setting ) {}
00561 
00562   protected:
00563     virtual void execute()
00564     {
00565       myObj->prefChanged( mySection, mySetting );
00566     }
00567 
00568   private:
00569     SALOME_PYQT_ModuleLight* myObj;
00570     QString mySection, mySetting;
00571   };
00572 
00573   if ( module != moduleName() ) {
00574     // module's own preferences are processed by preferencesChanged() method
00575     // ...
00576     // post the request only if dispatcher is not busy!
00577     // execute request synchronously
00578     if ( !PyInterp_Dispatcher::Get()->IsBusy() )
00579       PyInterp_Dispatcher::Get()->Exec( new Event( myInterp, this, section, setting ) );
00580   }
00581 }
00582 
00589 void SALOME_PYQT_ModuleLight::studyActivated()
00590 {
00591   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::studyActivated()" );
00592 
00593   // StudyChangedReq: request class for internal studyChanged() operation
00594   class StudyChangedReq : public PyInterp_Request
00595   {
00596   public:
00597     StudyChangedReq( SUIT_Study*         _study,
00598                      SALOME_PYQT_ModuleLight* _obj )
00599       : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
00600         myStudy ( _study ),
00601         myObj   ( _obj   ) {}
00602 
00603   protected:
00604     virtual void execute()
00605     {
00606       myObj->studyChanged( myStudy );
00607     }
00608 
00609   private:
00610     SUIT_Study*         myStudy;
00611     SALOME_PYQT_ModuleLight* myObj;
00612   };
00613 
00614   // post request
00615   PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( application()->activeStudy(), this ) );
00616 }
00617 
00622 void SALOME_PYQT_ModuleLight::onGUIEvent()
00623 {
00624   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::onGUIEvent()" );
00625 
00626   // get sender action
00627   QAction* action = qobject_cast<QAction*>( sender() );
00628   if ( !action )
00629     return;
00630 
00631   // get action ID
00632   int id = actionId( action );
00633   fmsg.message( QString( "action id = %1" ).arg( id ) );
00634 
00635   // perform synchronous request to Python event dispatcher
00636   class GUIEvent : public PyInterp_LockRequest
00637   {
00638   public:
00639     GUIEvent( PyInterp_Interp*    _py_interp,
00640               SALOME_PYQT_ModuleLight* _obj,
00641               int                 _id )
00642       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
00643         myId    ( _id  ),
00644         myObj   ( _obj ) {}
00645 
00646   protected:
00647     virtual void execute()
00648     {
00649       myObj->guiEvent( myId );
00650     }
00651 
00652   private:
00653     int                 myId;
00654     SALOME_PYQT_ModuleLight* myObj;
00655   };
00656 
00657   // post request
00658   PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
00659 }
00660 
00671 void SALOME_PYQT_ModuleLight::contextMenuPopup( const QString& theContext, 
00672                                            QMenu*         thePopupMenu, 
00673                                            QString&       /*title*/ )
00674 {
00675   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::contextMenuPopup()" );
00676   fmsg.message( QString( "context: %1" ).arg( theContext ) );
00677 
00678   // perform synchronous request to Python event dispatcher
00679   class PopupMenuEvent : public PyInterp_LockRequest
00680   {
00681   public:
00682     PopupMenuEvent( PyInterp_Interp*    _py_interp,
00683                     SALOME_PYQT_ModuleLight* _obj,
00684                     const QString&      _context,
00685                     QMenu*        _popup )
00686       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
00687         myContext( _context ),
00688         myPopup  ( _popup  ),
00689         myObj    ( _obj )   {}
00690 
00691   protected:
00692     virtual void execute()
00693     {
00694       myObj->contextMenu( myContext, myPopup );
00695     }
00696 
00697   private:
00698     SALOME_PYQT_ModuleLight* myObj;
00699     QString             myContext;
00700     QMenu*         myPopup;
00701   };
00702 
00703   // post request only if dispatcher is not busy!
00704   // execute request synchronously
00705   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
00706     PyInterp_Dispatcher::Get()->Exec( new PopupMenuEvent( myInterp, this, theContext, thePopupMenu ) );
00707 }
00708 
00714 void SALOME_PYQT_ModuleLight::createPreferences()
00715 {
00716   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::createPreferences()" );
00717 
00718   // perform synchronous request to Python event dispatcher
00719   class Event : public PyInterp_LockRequest
00720   {
00721   public:
00722     Event( PyInterp_Interp*    _py_interp,
00723            SALOME_PYQT_ModuleLight* _obj )
00724       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
00725         myObj    ( _obj )   {}
00726 
00727   protected:
00728     virtual void execute()
00729     {
00730       myObj->initPreferences();
00731     }
00732 
00733   private:
00734     SALOME_PYQT_ModuleLight* myObj;
00735   };
00736 
00737   // post request only if dispatcher is not busy!
00738   // execute request synchronously
00739   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
00740     PyInterp_Dispatcher::Get()->Exec( new Event( myInterp, this ) );
00741 }
00742 
00765 void SALOME_PYQT_ModuleLight::windows( QMap<int, int>& mappa ) const
00766 {
00767   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::windows()" );
00768 
00769   mappa = myWindowsMap;
00770 }
00771 
00784 void SALOME_PYQT_ModuleLight::viewManagers( QStringList& lst ) const
00785 {
00786   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::viewManagers()" );
00787 
00788   lst = myViewMgrList;
00789 }
00790 
00799 void SALOME_PYQT_ModuleLight::preferencesChanged( const QString& section, const QString& setting )
00800 {
00801   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::preferencesChanged()" );
00802 
00803   // perform synchronous request to Python event dispatcher
00804   class Event : public PyInterp_LockRequest
00805   {
00806   public:
00807     Event( PyInterp_Interp*    _py_interp,
00808            SALOME_PYQT_ModuleLight* _obj,
00809            const QString&      _section,
00810            const QString&      _setting )
00811       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
00812         myObj    ( _obj ),
00813         mySection( _section ),
00814         mySetting( _setting ) {}
00815 
00816   protected:
00817     virtual void execute()
00818     {
00819       myObj->prefChanged( mySection, mySetting );
00820     }
00821 
00822   private:
00823     SALOME_PYQT_ModuleLight* myObj;
00824     QString mySection, mySetting;
00825   };
00826 
00827   // post request only if dispatcher is not busy!
00828   // execut request synchronously
00829   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
00830     PyInterp_Dispatcher::Get()->Exec( new Event( myInterp, this, section, setting ) );
00831 }
00832 
00846 void SALOME_PYQT_ModuleLight::init( CAM_Application* app )
00847 {
00848   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::init()" );
00849 
00850   // reset interpreter to NULL
00851   myInterp = NULL;
00852 
00853   // get study Id
00854   LightApp_Application* anApp = dynamic_cast<LightApp_Application*>( app );
00855   if ( !anApp )
00856     return;
00857   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
00858   if ( !aStudy )
00859     return;
00860   int aStudyId = aStudy ? aStudy->id() : 0;
00861 
00862   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
00863   initInterp( aStudyId );
00864   if ( !myInterp )
00865     return; // Error
00866 
00867   // import Python GUI module
00868   importModule();
00869   if ( !myModule )
00870     return; // Error
00871 
00872   // this module is being activated now!
00873   myInitModule = this;
00874 
00875   // then call Python module's initialize() method
00876   // ... first get python lock
00877   PyLockWrapper aLock = myInterp->GetLockWrapper();
00878   // ... (the Python module is already imported)
00879   // ... finally call Python module's initialize() method
00880   if ( PyObject_HasAttrString( myModule, (char*)"initialize" ) ) {
00881     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"initialize", (char*)"" ) );
00882     if ( !res ) {
00883       PyErr_Print();
00884     }
00885   }
00886 
00887   // get required dockable windows list from the Python module 
00888   // by calling windows() method
00889   // ... first put default values
00890   myWindowsMap.insert( LightApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
00891   myWindowsMap.insert( LightApp_Application::WT_PyConsole,     Qt::BottomDockWidgetArea );
00892   myWindowsMap.insert( LightApp_Application::WT_LogWindow,     Qt::BottomDockWidgetArea );
00893 
00894   if ( PyObject_HasAttrString( myModule , (char*)"windows" ) ) {
00895     PyObjWrapper res1( PyObject_CallMethod( myModule, (char*)"windows", (char*)"" ) );
00896     if ( !res1 ) {
00897       PyErr_Print();
00898     }
00899     else {
00900       myWindowsMap.clear();
00901       if ( PyDict_Check( res1 ) ) {
00902         PyObject* key;
00903         PyObject* value;
00904         Py_ssize_t pos = 0;
00905         while ( PyDict_Next( res1, &pos, &key, &value ) ) {
00906           // parse the return value
00907           // it should be a map: {integer:integer}
00908           int aKey, aValue;
00909           if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
00910             aKey   = PyInt_AsLong( key );
00911             aValue = PyInt_AsLong( value );
00912             myWindowsMap[ aKey ] = aValue;
00913           }
00914         }
00915       }
00916     }
00917   }
00918 
00919   // get compatible view windows types from the Python module 
00920   // by calling views() method
00921   if ( PyObject_HasAttrString( myModule , (char*)"views" ) ) {
00922     PyObjWrapper res2( PyObject_CallMethod( myModule, (char*)"views", (char*)"" ) );
00923     if ( !res2 ) {
00924       PyErr_Print();
00925     }
00926     else {
00927       // parse the return value
00928       // result can be one string...
00929       if ( PyString_Check( res2 ) ) {
00930         myViewMgrList.append( PyString_AsString( res2 ) );
00931       }
00932       // ... or list of strings
00933       else if ( PyList_Check( res2 ) ) {
00934         int size = PyList_Size( res2 );
00935         for ( int i = 0; i < size; i++ ) {
00936           PyObject* value = PyList_GetItem( res2, i );
00937           if( value && PyString_Check( value ) ) {
00938             myViewMgrList.append( PyString_AsString( value ) );
00939           }
00940         }
00941       }
00942     }
00943   }
00944   // module is already activated!
00945   myInitModule = 0;
00946 }
00947 
00958 void SALOME_PYQT_ModuleLight::activate( SUIT_Study* theStudy )
00959 {
00960   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::activate()" );
00961 
00962   // get study Id
00963   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
00964   int aStudyId = aStudy ? aStudy->id() : 0;
00965 
00966   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
00967   initInterp( aStudyId );
00968   if ( !myInterp ) {
00969     myLastActivateStatus = false;
00970     return; // Error
00971   }
00972 
00973   // import Python GUI module
00974   importModule();
00975   if ( !myModule ) {
00976     myLastActivateStatus = false;
00977     return; // Error
00978   }
00979 
00980   // get python lock
00981   PyLockWrapper aLock = myInterp->GetLockWrapper();
00982 
00983   // call Python module's activate() method (for the new modules)
00984   if ( PyObject_HasAttrString( myModule , (char*)"activate" ) ) {
00985     PyObject* res1 = PyObject_CallMethod( myModule, (char*)"activate", (char*)"" );
00986     if ( !res1 || !PyBool_Check( res1 ) ) {
00987       PyErr_Print();
00988       // always true for old modules (no return value)
00989       myLastActivateStatus = true;
00990     }
00991     else {
00992       // detect return status
00993       myLastActivateStatus = PyObject_IsTrue( res1 );
00994     }
00995   } 
00996   
00997   // Connect the SUIT_Desktop signal windowActivated() to this->onActiveViewChanged()
00998   SUIT_Desktop* aDesk = theStudy->application()->desktop();
00999   if ( aDesk )
01000   {
01001     connect( aDesk, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
01002              this,  SLOT( onActiveViewChanged( SUIT_ViewWindow* ) ) );
01003     // If a active window exists send activeViewChanged
01004     // If a getActiveView() in SalomePyQt available we no longer need this 
01005     SUIT_ViewWindow* aView = aDesk->activeWindow();
01006     if ( aView ) 
01007       activeViewChanged( aView );
01008     
01009     // get all view currently opened in the study and connect their signals  to 
01010     // the corresponding slots of the class.
01011     QList<SUIT_ViewWindow*> wndList = aDesk->windows();
01012     SUIT_ViewWindow* wnd;
01013     foreach ( wnd, wndList )
01014       connectView( wnd );
01015   }
01016 }
01017 
01029 void SALOME_PYQT_ModuleLight::customize( SUIT_Study* theStudy )
01030 {
01031   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::customize()" );
01032 
01033   // get study Id
01034   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
01035   int aStudyId = aStudy ? aStudy->id() : 0;
01036 
01037   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
01038   initInterp( aStudyId );
01039   if ( !myInterp )
01040     return; // Error
01041 
01042   // import Python GUI module
01043   importModule();
01044   if ( !myModule )
01045     return; // Error
01046 
01047   if ( IsCallOldMethods ) {
01048     // call Python module's setWorkspace() method
01049     setWorkSpace();
01050   }
01051 
01052   // get python lock
01053   PyLockWrapper aLock = myInterp->GetLockWrapper();
01054 
01055   if ( IsCallOldMethods ) {
01056     // call Python module's setSettings() method (obsolete)
01057     if ( PyObject_HasAttrString( myModule , (char*)"setSettings" ) ) {
01058       PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"setSettings", (char*)"" ) );
01059       if( !res ) {
01060         PyErr_Print();
01061       }
01062     }
01063   }
01064 }
01065 
01074 void SALOME_PYQT_ModuleLight::deactivate( SUIT_Study* theStudy )
01075 {
01076   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::deactivate()" );
01077 
01078   // check if the subinterpreter is initialized and Python module is imported
01079   if ( !myInterp || !myModule ) {
01080     // Error! Python subinterpreter should be initialized and module should be imported first!
01081     return;
01082   }
01083   // then call Python module's deactivate() method
01084   if ( PyObject_HasAttrString( myModule , (char*)"deactivate" ) ) {
01085     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"deactivate", (char*)"" ) );
01086     if( !res ) {
01087       PyErr_Print();
01088     }
01089   }
01090   
01091   // Disconnect the SUIT_Desktop signal windowActivated()
01092   SUIT_Desktop* aDesk = theStudy->application()->desktop();
01093   if ( aDesk )
01094   {
01095     disconnect( aDesk, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
01096                 this,  SLOT( onActiveViewChanged( SUIT_ViewWindow* ) ) );      
01097   }
01098 }
01099 
01111 void SALOME_PYQT_ModuleLight::studyChanged( SUIT_Study* theStudy )
01112 {
01113   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::studyChanged()" );
01114 
01115   // get study Id
01116   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
01117   int aStudyId = aStudy ? aStudy->id() : 0;
01118 
01119   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
01120   initInterp( aStudyId );
01121   if ( !myInterp )
01122     return; // Error
01123 
01124   // import Python GUI module
01125   importModule();
01126   if ( !myModule )
01127     return; // Error
01128 
01129   if ( IsCallOldMethods ) {
01130     // call Python module's setWorkspace() method
01131     setWorkSpace();
01132   }
01133 
01134   // get python lock
01135   PyLockWrapper aLock = myInterp->GetLockWrapper();
01136 
01137   // call Python module's activeStudyChanged() method
01138   if ( PyObject_HasAttrString( myModule, (char*)"activeStudyChanged" ) ) {
01139     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"activeStudyChanged", (char*)"i", aStudyId ) );
01140     if( !res ) {
01141       PyErr_Print();
01142     }
01143   }
01144 }
01145 
01161 void SALOME_PYQT_ModuleLight::contextMenu( const QString& theContext, QMenu* thePopupMenu )
01162 {
01163   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::contextMenu()" );
01164 
01165   // Python interpreter should be initialized and Python module should be
01166   // import first
01167   if ( !myInterp || !myModule )
01168     return;
01169 
01170   QString aContext( "" ), aObject( "" ), aParent( theContext );
01171 
01172   if ( IsCallOldMethods && PyObject_HasAttrString( myModule, (char*)"definePopup" ) ) {
01173     // call definePopup() Python module's function
01174     // this is obsolete function, used only for compatibility reasons
01175     PyObjWrapper res( PyObject_CallMethod( myModule,
01176                                            (char*)"definePopup",
01177                                            (char*)"sss",
01178                                            theContext.toLatin1().constData(),
01179                                            aObject.toLatin1().constData(),
01180                                            aParent.toLatin1().constData() ) );
01181     if( !res ) {
01182       PyErr_Print();
01183     }
01184     else {
01185       // parse return value
01186       char *co, *ob, *pa;
01187       if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
01188         aContext = co;
01189         aObject  = ob;
01190         aParent  = pa;
01191       }
01192     }
01193   } // if ( IsCallOldMethods ... )
01194 
01195   // first try to create menu via XML parser:
01196   // we create popup menus without help of QtxPopupMgr
01197   if ( myXmlHandler )
01198     myXmlHandler->createPopup( thePopupMenu, aContext, aParent, aObject );
01199 
01200 #if SIP_VERSION < 0x040800
01201   PyObjWrapper sipPopup( sipBuildResult( 0, "M", thePopupMenu, sipClass_QMenu) );
01202 #else
01203   PyObjWrapper sipPopup( sipBuildResult( 0, "D", thePopupMenu, sipType_QMenu, NULL) );
01204 #endif
01205 
01206   // then call Python module's createPopupMenu() method (for new modules)
01207   if ( PyObject_HasAttrString( myModule, (char*)"createPopupMenu" ) ) {
01208     PyObjWrapper res1( PyObject_CallMethod( myModule,
01209                                             (char*)"createPopupMenu",
01210                                             (char*)"Os",
01211                                             sipPopup.get(),
01212                                             theContext.toLatin1().constData() ) );
01213     if( !res1 ) {
01214       PyErr_Print();
01215     }
01216   }
01217 
01218   if ( IsCallOldMethods && PyObject_HasAttrString( myModule, (char*)"customPopup" ) ) {
01219     // call customPopup() Python module's function
01220     // this is obsolete function, used only for compatibility reasons
01221     PyObjWrapper res2( PyObject_CallMethod( myModule,
01222                                             (char*)"customPopup",
01223                                             (char*)"Osss",
01224                                             sipPopup.get(),
01225                                             aContext.toLatin1().constData(),
01226                                             aObject.toLatin1().constData(),
01227                                             aParent.toLatin1().constData() ) );
01228     if( !res2 ) {
01229       PyErr_Print();
01230     }
01231   }
01232 }
01233 
01242 void SALOME_PYQT_ModuleLight::guiEvent( const int theId )
01243 {
01244   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::guiEvent()" );
01245 
01246   // Python interpreter should be initialized and Python module should be
01247   // import first
01248   if ( !myInterp || !myModule )
01249     return;
01250 
01251   if ( PyObject_HasAttrString( myModule, (char*)"OnGUIEvent" ) ) {
01252     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"OnGUIEvent", (char*)"i", theId ) );
01253     if( !res ) {
01254       PyErr_Print();
01255     }
01256   }
01257 }
01258 
01265 void SALOME_PYQT_ModuleLight::initPreferences()
01266 {
01267   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::initPreferences()" );
01268 
01269   // Python interpreter should be initialized and Python module should be
01270   // import first
01271   if ( !myInterp || !myModule )
01272     return;
01273 
01274   // temporary set myInitModule because createPreferences() method
01275   // might be called during the module intialization process
01276   myInitModule = this;
01277 
01278   if ( PyObject_HasAttrString( myModule, (char*)"createPreferences" ) ) {
01279     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"createPreferences", (char*)"" ) );
01280     if( !res ) {
01281       PyErr_Print();
01282     }
01283   }
01284 
01285   myInitModule = 0;
01286 }
01287 
01292 void SALOME_PYQT_ModuleLight::initInterp( int theStudyId )
01293 {
01294   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::initInterp()" );
01295 
01296   // check study Id
01297   if ( !theStudyId ) {
01298     // Error! Study Id must not be 0!
01299     myInterp = NULL;
01300     return;
01301   }
01302   // try to find the subinterpreter
01303   if( myInterpMap.contains( theStudyId ) ) {
01304     // found!
01305     myInterp = myInterpMap[ theStudyId ];
01306     return;
01307   }
01308 
01309   myInterp = new SALOME_PYQT_PyInterp();
01310   if(!myInterp)
01311     return;
01312   
01313   myInterp->initialize();
01314   myInterpMap[ theStudyId ] = myInterp;
01315   
01316 #ifndef GUI_DISABLE_CORBA
01317   if(!SUIT_PYTHON::initialized) {
01318     // import 'salome' module and call 'salome_init' method;
01319     // do it only once on interpreter creation
01320     // ... first get python lock
01321     PyLockWrapper aLock = myInterp->GetLockWrapper();
01322     // ... then import a module
01323     PyObjWrapper aMod = PyImport_ImportModule( "salome" );
01324     if( !aMod ) {
01325       // Error!
01326       PyErr_Print();
01327       return;
01328     }
01329     // ... then call a method
01330     int embedded = 1;
01331     PyObjWrapper aRes( PyObject_CallMethod( aMod, (char*)"salome_init", (char*)"ii", theStudyId, embedded ) );
01332     if( !aRes ) {
01333       // Error!
01334       PyErr_Print();
01335       return;
01336     }
01337   }
01338 #endif 
01339 }
01340 
01346 void SALOME_PYQT_ModuleLight::importModule()
01347 {
01348   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::importModule()" );
01349 
01350   // check if the subinterpreter is initialized
01351   if ( !myInterp ) {
01352     // Error! Python subinterpreter should be initialized first!
01353     myModule = 0;
01354     return;
01355   }
01356   // import Python GUI module and puts it in <myModule> attribute
01357   // ... first get python lock
01358   PyLockWrapper aLock = myInterp->GetLockWrapper();
01359   // ... then import a module
01360   QString aMod = name() + "GUI";
01361   try {
01362     myModule = PyImport_ImportModule( aMod.toLatin1().data() );
01363   }
01364   catch (...) {
01365   }
01366   if( !myModule ) {
01367     // Error!
01368     PyErr_Print();
01369     return;
01370   }
01371 }
01372 
01381 void SALOME_PYQT_ModuleLight::setWorkSpace()
01382 {
01383   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::setWorkSpace()" );
01384 
01385   // check if the subinterpreter is initialized and Python module is imported
01386   if ( !myInterp || !myModule ) {
01387     // Error! Python subinterpreter should be initialized and module should be imported first!
01388     return;
01389   }
01390 
01391   // call setWorkspace() method
01392   // ... first get python lock
01393   PyLockWrapper aLock = myInterp->GetLockWrapper();
01394 
01395   // ... then try to import SalomePyQt module. If it's not possible don't go on.
01396   PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
01397   if( !aQtModule ) {
01398     // Error!
01399     PyErr_Print();
01400     return;
01401   }
01402 
01403   if ( IsCallOldMethods ) {
01404     // ... then get workspace object
01405     QWidget* aWorkspace = 0;
01406     if ( getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
01407       STD_MDIDesktop* aDesktop = dynamic_cast<STD_MDIDesktop*>( getApp()->desktop() );
01408       if ( aDesktop )
01409         aWorkspace = aDesktop->workspace();
01410     }
01411     else if ( getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
01412       STD_TabDesktop* aDesktop = dynamic_cast<STD_TabDesktop*>( getApp()->desktop() );
01413       if ( aDesktop )
01414         aWorkspace = aDesktop->workstack();
01415     }
01416 #if SIP_VERSION < 0x040800
01417     PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget) );
01418 #else
01419     PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
01420 #endif
01421     // ... and finally call Python module's setWorkspace() method (obsolete)
01422     if ( PyObject_HasAttrString( myModule, (char*)"setWorkSpace" ) ) {
01423       PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
01424       if( !res ) {
01425         PyErr_Print();
01426       }
01427     }
01428   }
01429 }
01430 
01440 void SALOME_PYQT_ModuleLight::prefChanged( const QString& section, const QString& setting )
01441 {
01442   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::prefChanged()" );
01443 
01444   // Python interpreter should be initialized and Python module should be
01445   // import first
01446   if ( !myInterp || !myModule )
01447     return;
01448 
01449   if ( PyObject_HasAttrString( myModule, (char*)"preferenceChanged" ) ) {
01450     PyObjWrapper res( PyObject_CallMethod( myModule,
01451                                            (char*)"preferenceChanged", 
01452                                            (char*)"ss", 
01453                                            section.toLatin1().constData(), 
01454                                            setting.toLatin1().constData() ) );
01455     if( !res ) {
01456       PyErr_Print();
01457     }
01458   }
01459 }
01460 
01465 int SALOME_PYQT_ModuleLight::defaultMenuGroup()
01466 {
01467   return DEFAULT_GROUP; 
01468 }
01469 
01470 //
01471 // The next methods call the parent implementation.
01472 // This is done to open protected methods from CAM_Module class.
01473 //
01474 
01480 int SALOME_PYQT_ModuleLight::createTool( const QString& name )
01481 {
01482   return LightApp_Module::createTool( name );
01483 }
01484 
01492 int SALOME_PYQT_ModuleLight::createTool( const int id, const int tBar, const int idx )
01493 {
01494   return LightApp_Module::createTool( id, tBar, idx );
01495 }
01496 
01504 int SALOME_PYQT_ModuleLight::createTool( const int id, const QString& tBar, const int idx )
01505 {
01506   return LightApp_Module::createTool( id, tBar, idx );
01507 }
01508 
01517 int SALOME_PYQT_ModuleLight::createTool( QAction* a, const int tBar, const int id, const int idx )
01518 {
01519   return LightApp_Module::createTool( a, tBar, id, idx );
01520 }
01521 
01530 int SALOME_PYQT_ModuleLight::createTool( QAction* a, const QString& tBar, const int id, const int idx )
01531 {
01532   return LightApp_Module::createTool( a, tBar, id, idx );
01533 }
01534 
01544 int SALOME_PYQT_ModuleLight::createMenu( const QString& subMenu, const int menu, const int id, const int group, const int idx )
01545 {
01546   return LightApp_Module::createMenu( subMenu, menu, id, group, idx );
01547 }
01548 
01558 int SALOME_PYQT_ModuleLight::createMenu( const QString& subMenu, const QString& menu, const int id, const int group, const int idx )
01559 {
01560   return LightApp_Module::createMenu( subMenu, menu, id, group, idx );
01561 }
01562 
01571 int SALOME_PYQT_ModuleLight::createMenu( const int id, const int menu, const int group, const int idx )
01572 {
01573   return LightApp_Module::createMenu( id, menu, group, idx );
01574 }
01575 
01584 int SALOME_PYQT_ModuleLight::createMenu( const int id, const QString& menu, const int group, const int idx )
01585 {
01586   return LightApp_Module::createMenu( id, menu, group, idx );
01587 }
01588 
01597 int SALOME_PYQT_ModuleLight::createMenu( QAction* a, const int menu, const int id, const int group, const int idx )
01598 {
01599   return LightApp_Module::createMenu( a, menu, id, group, idx );
01600 }
01601 
01610 int SALOME_PYQT_ModuleLight::createMenu( QAction* a, const QString& menu, const int id, const int group, const int idx )
01611 {
01612   return LightApp_Module::createMenu( a, menu, id, group, idx );
01613 }
01614 
01619 QAction* SALOME_PYQT_ModuleLight::separator()
01620 {
01621   return LightApp_Module::separator();
01622 }
01623 
01628 QAction* SALOME_PYQT_ModuleLight::action( const int id ) const
01629 {
01630   QAction* a = LightApp_Module::action( id );
01631   if ( !a ) {
01632     // try menu
01633     QMenu* m = menuMgr()->findMenu( id );
01634     if ( m ) a = m->menuAction();
01635   }
01636   return a;
01637 }
01638 
01643 int SALOME_PYQT_ModuleLight::actionId( const QAction* a ) const
01644 {
01645   return LightApp_Module::actionId( a );
01646 }
01647 
01663 QAction* SALOME_PYQT_ModuleLight::createAction( const int id, const QString& text, const QString& icon,
01664                                            const QString& menu, const QString& tip, const int key,
01665                                            const bool toggle, QObject* parent )
01666 {
01667   QIcon anIcon = loadIcon( icon );
01668   QAction* a = action( id );
01669   if ( a ) {
01670     if ( a->toolTip().isEmpty()   && !text.isEmpty() )  a->setToolTip( text );
01671     if ( a->text().isEmpty()      && !menu.isEmpty() )  a->setText( menu );
01672     if ( a->icon().isNull()       && !anIcon.isNull() ) a->setIcon( anIcon );
01673     if ( a->statusTip().isEmpty() && !tip.isEmpty() )   a->setStatusTip( tip );
01674     if ( a->shortcut().isEmpty()  && key )              a->setShortcut( key );
01675     if ( a->isCheckable() != toggle )                   a->setCheckable( toggle );
01676     disconnect( a, SIGNAL( triggered( bool ) ), this, SLOT( onGUIEvent() ) );
01677     connect(    a, SIGNAL( triggered( bool ) ), this, SLOT( onGUIEvent() ) );
01678   }
01679   else {
01680     a = LightApp_Module::createAction( id, 
01681                                         text, 
01682                                         anIcon, 
01683                                         menu, 
01684                                         tip, 
01685                                         key, 
01686                                         parent ? parent : this, 
01687                                         toggle, 
01688                                         this, 
01689                                         SLOT( onGUIEvent() ) );
01690   }
01691   return a;
01692 }
01693 
01709 QtxActionGroup* SALOME_PYQT_ModuleLight::createActionGroup(const int id, const bool exclusive)
01710 {
01711   QtxActionGroup* a = qobject_cast<QtxActionGroup*>( action( id ) );
01712   if ( !a ) {
01713     a = new QtxActionGroup( this );
01714     LightApp_Module::registerAction( id, a );
01715   }
01716   a->setExclusive( exclusive );
01717   return a;
01718 }
01719 
01725 QIcon SALOME_PYQT_ModuleLight::loadIcon( const QString& fileName )
01726 {
01727   QIcon anIcon;
01728   if ( !fileName.isEmpty() ) {
01729     QPixmap pixmap = getApp()->resourceMgr()->loadPixmap( name(), tr( fileName.toLatin1() ) );
01730     if ( !pixmap.isNull() )
01731       anIcon = QIcon( pixmap );
01732   }
01733   return anIcon;
01734 }
01735 
01742 int SALOME_PYQT_ModuleLight::addGlobalPreference( const QString& label )
01743 {
01744   LightApp_Preferences* pref = preferences();
01745   if ( !pref )
01746     return -1;
01747 
01748   return pref->addPreference( label, -1 );
01749 }
01750 
01756 int SALOME_PYQT_ModuleLight::addPreference( const QString& label )
01757 {
01758   return LightApp_Module::addPreference( label );
01759 }
01760                                        
01770 int SALOME_PYQT_ModuleLight::addPreference( const QString& label, 
01771                                        const int pId, const int type,
01772                                        const QString& section,
01773                                        const QString& param )
01774 {
01775   return LightApp_Module::addPreference( label, pId, type, section, param );
01776 }
01777 
01784 QVariant SALOME_PYQT_ModuleLight::preferenceProperty( const int id, 
01785                                                  const QString& prop ) const
01786 {
01787   QVariant v = LightApp_Module::preferenceProperty( id, prop );
01788   return v;
01789 }
01790 
01797 void SALOME_PYQT_ModuleLight::setPreferenceProperty( const int id, 
01798                                                 const QString& prop, 
01799                                                 const QVariant& var )
01800 {
01801   LightApp_Module::setPreferenceProperty( id, prop, var );
01802 }
01803 
01804 
01809 void SALOME_PYQT_ModuleLight::onActiveViewChanged( SUIT_ViewWindow* pview )
01810 {
01811   class ActiveViewChange : public PyInterp_LockRequest
01812   {
01813   public:
01814     ActiveViewChange( PyInterp_Interp* _py_interp, SALOME_PYQT_ModuleLight* _obj, const SUIT_ViewWindow* _pview )
01815       : PyInterp_LockRequest( _py_interp, 0, true ),
01816         myObj(_obj),myView(_pview) {}
01817 
01818   protected:
01819     virtual void execute()
01820     {
01821       myObj->activeViewChanged( myView );
01822     }
01823 
01824   private:
01825     SALOME_PYQT_ModuleLight* myObj;
01826     const SUIT_ViewWindow * myView;
01827   };
01828   
01829   PyInterp_Dispatcher::Get()->Exec( new ActiveViewChange( myInterp, this, pview ) ); 
01830 }
01831 
01836 void SALOME_PYQT_ModuleLight::activeViewChanged( const SUIT_ViewWindow* pview )
01837 {
01838   if ( !myInterp || !myModule ) 
01839     return;
01840   
01841   // Do not use SUIT_ViewWindow::closing() signal here. View manager reacts on 
01842   // this signal and deletes view. So our slot does not works if it is connected 
01843   // on this signal. SUIT_ViewManager::deleteView(SUIT_ViewWindow*) is used here
01844   
01845   connectView( pview );
01846 
01847   if ( PyObject_HasAttrString( myModule, (char*)"activeViewChanged" ) ) 
01848   {
01849     if ( !pview ) 
01850       return;   
01851 
01852     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"activeViewChanged", (char*)"i" , pview->getId() ) );
01853     if( !res )
01854       PyErr_Print();
01855   }
01856 }
01857 
01862 void SALOME_PYQT_ModuleLight::onViewCloned( SUIT_ViewWindow* pview )
01863 {
01864   class ViewClone : public PyInterp_LockRequest
01865   {
01866   public:
01867     ViewClone( PyInterp_Interp* _py_interp, SALOME_PYQT_ModuleLight* _obj, const SUIT_ViewWindow* _pview )
01868       : PyInterp_LockRequest( _py_interp, 0, true ),
01869         myObj(_obj), myView(_pview) {}
01870 
01871   protected:
01872     virtual void execute()
01873     {
01874       myObj->viewCloned( myView );
01875     }
01876 
01877   private:
01878     SALOME_PYQT_ModuleLight* myObj;    
01879     const SUIT_ViewWindow* myView;
01880   };
01881   
01882   PyInterp_Dispatcher::Get()->Exec( new ViewClone( myInterp, this, pview ) );
01883 }
01884 
01889 void SALOME_PYQT_ModuleLight::viewCloned( const SUIT_ViewWindow* pview )
01890 {
01891   if ( !myInterp || !myModule || !pview ) 
01892     return;  
01893 
01894   if ( PyObject_HasAttrString( myModule, (char*)"viewCloned" ) ) 
01895   {
01896     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"viewCloned", (char*)"i", pview->getId() ) );
01897     if( !res )
01898       PyErr_Print();
01899   }
01900 }
01901 
01906 void SALOME_PYQT_ModuleLight::onViewTryClose( SUIT_ViewWindow* pview )
01907 {
01908   class ViewTryClose : public PyInterp_LockRequest
01909   {
01910   public:
01911     ViewTryClose( PyInterp_Interp* _py_interp, SALOME_PYQT_ModuleLight* _obj, const SUIT_ViewWindow* _pview )
01912       : PyInterp_LockRequest( _py_interp, 0, true ),
01913         myObj(_obj),myView(_pview) {}
01914 
01915   protected:
01916     virtual void execute()
01917     {
01918       myObj->viewTryClose( myView );
01919     }
01920 
01921   private:
01922     SALOME_PYQT_ModuleLight* myObj;
01923     const SUIT_ViewWindow * myView;    
01924   };
01925 
01926   PyInterp_Dispatcher::Get()->Exec( new ViewTryClose( myInterp, this, pview ) );
01927 }
01928 
01933 void SALOME_PYQT_ModuleLight::viewTryClose( const SUIT_ViewWindow* pview )
01934 {
01935   if ( !myInterp || !myModule ) 
01936     return;  
01937 
01938   if ( PyObject_HasAttrString( myModule, (char*)"viewTryClose" ) ) 
01939   {
01940     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"viewTryClose", (char*)"i", pview->getId() ) );
01941     if ( !res )
01942     {
01943       PyErr_Print();
01944     }
01945   }
01946 }
01947 
01952 void SALOME_PYQT_ModuleLight::onViewClosed( SUIT_ViewWindow* pview )
01953 {
01954   class ViewClose : public PyInterp_LockRequest
01955   {
01956   public:
01957     ViewClose( PyInterp_Interp* _py_interp, SALOME_PYQT_ModuleLight* _obj, const SUIT_ViewWindow* _pview )
01958       : PyInterp_LockRequest( _py_interp, 0, true ),
01959         myObj(_obj),myView(_pview) {}
01960 
01961   protected:
01962     virtual void execute()
01963     {
01964       myObj->viewClosed( myView );
01965     }
01966 
01967   private:
01968     SALOME_PYQT_ModuleLight* myObj;
01969     const SUIT_ViewWindow * myView;    
01970   };
01971 
01972   PyInterp_Dispatcher::Get()->Exec( new ViewClose( myInterp, this, pview ) );
01973 }
01974 
01979 void SALOME_PYQT_ModuleLight::viewClosed( const SUIT_ViewWindow* pview )
01980 {
01981   if ( !myInterp || !myModule ) 
01982     return;  
01983 
01984   if ( PyObject_HasAttrString( myModule, (char*)"viewClosed" ) ) 
01985   {
01986     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"viewClosed", (char*)"i", pview->getId() ) );
01987     if ( !res )
01988     {
01989       PyErr_Print();
01990     }
01991   }
01992 }
01993 
01998 void SALOME_PYQT_ModuleLight::connectView( const SUIT_ViewWindow* pview )
01999 {
02000   SUIT_ViewManager* viewMgr = pview->getViewManager();
02001   SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
02002       
02003   if ( viewMgr )
02004   {
02005     disconnect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
02006               this, SLOT( onViewTryClose( SUIT_ViewWindow* ) ) );
02007     disconnect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
02008               this, SLOT( onViewClosed( SUIT_ViewWindow* ) ) );
02009   
02010     connect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
02011             this, SLOT( onViewTryClose( SUIT_ViewWindow* ) ) );
02012     connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
02013              this, SLOT( onViewClosed( SUIT_ViewWindow* ) ) );
02014   }
02015   
02016   // Connect cloneView() signal of an OCC View
02017   if ( pview->inherits( "OCCViewer_ViewWindow" ) )
02018   {
02019     disconnect( pview, SIGNAL( viewCloned( SUIT_ViewWindow* ) ), 
02020                 this, SLOT( onViewCloned( SUIT_ViewWindow* ) ) );
02021     connect( pview, SIGNAL( viewCloned( SUIT_ViewWindow* ) ), 
02022              this, SLOT( onViewCloned( SUIT_ViewWindow* ) ) );
02023   }
02024   // Connect cloneView() signal of Plot2d View manager
02025   else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) )
02026   {
02027     disconnect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ), 
02028                 this, SLOT( onViewCloned( SUIT_ViewWindow* ) ) );
02029     connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ), 
02030              this, SLOT( onViewCloned( SUIT_ViewWindow* ) ) );
02031   }
02032 }
02033 
02040 static QString tagName( const QDomElement& element )
02041 {
02042   return element.tagName().trimmed();
02043 }
02044 
02052 static QString attribute( const QDomElement& element, const QString& attName )
02053 {
02054   return element.attribute( attName ).trimmed();
02055 }
02056 
02073 static bool checkBool( const QString& value, const int check = -1 )
02074 {
02075   QString v = value.toLower();
02076   if ( ( v == "true"  || v == "yes" || v == "1" ) && ( check != 0 ) )
02077     return true;
02078   if ( ( v == "false" || v == "no"  || v == "0" ) && ( check == 0 ) )
02079     return true;
02080   return false;
02081 }
02082 
02092 static int checkInt( const QString& value, const int def = -1, const int shift = -1 )
02093 {
02094   bool bOk;
02095   int val = value.toInt( &bOk );
02096   if ( !bOk ) val = def;
02097   if ( shift > 0 && bOk && val < 0 )
02098     val += shift;
02099   return val;
02100 }
02101 
02108 SALOME_PYQT_ModuleLight::XmlHandler::XmlHandler( SALOME_PYQT_ModuleLight* module, 
02109                                             const QString&      fileName )
02110 : myModule( module )
02111 {
02112   if ( fileName.isEmpty() ) 
02113     return;
02114   QFile aFile( fileName );
02115   if ( !aFile.open( QIODevice::ReadOnly ) )
02116     return;
02117   myDoc.setContent( &aFile );
02118   aFile.close();
02119 }
02120 
02128 void SALOME_PYQT_ModuleLight::XmlHandler::createActions()
02129 {
02130   // get document element
02131   QDomElement aDocElem = myDoc.documentElement();
02132 
02133   // create main menu actions
02134   QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
02135   for ( int i = 0; i < aMenuList.count(); i++ ) {
02136     QDomNode n = aMenuList.item( i );
02137     createMenu( n );
02138   }
02139 
02140   // create toolbars actions
02141   QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
02142   for ( int i = 0; i < aToolsList.count(); i++ ) {
02143     QDomNode n = aToolsList.item( i );
02144     createToolBar( n );
02145   }
02146 }
02147 
02156 void SALOME_PYQT_ModuleLight::XmlHandler::createPopup( QMenu*         menu,
02157                                                   const QString& context,
02158                                                   const QString& parent,
02159                                                   const QString& object )
02160 {
02161   // get document element
02162   QDomElement aDocElem = myDoc.documentElement();
02163 
02164   // get popup menus actions
02165   QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
02166   for ( int i = 0; i < aPopupList.count(); i++ ) {
02167     QDomNode n = aPopupList.item( i );
02168     if ( !n.isNull() && n.isElement() ) {
02169       QDomElement e = n.toElement();
02170       // QString lab = attribute( e, "label-id" ); // not used // 
02171       QString ctx = attribute( e, "context-id" );
02172       QString prt = attribute( e, "parent-id"  );
02173       QString obj = attribute( e, "object-id"  );
02174       if ( ctx == context && prt == parent && obj == object )  {
02175         insertPopupItems( n, menu );
02176         break;
02177       }
02178     }
02179   }
02180 }
02181 
02187 void SALOME_PYQT_ModuleLight::XmlHandler::activateMenus( bool enable )
02188 {
02189   if ( !myModule )
02190     return;
02191 
02192   QtxActionMenuMgr* mgr = myModule->menuMgr();
02193   int id;
02194   foreach( id, myMenuItems ) mgr->setEmptyEnabled( id, enable );
02195 }
02196 
02204 void SALOME_PYQT_ModuleLight::XmlHandler::createMenu( QDomNode& parentNode, 
02205                                                  const int parentMenuId, 
02206                                                  QMenu*    parentPopup )
02207 {
02208   if ( !myModule || parentNode.isNull() )
02209     return;
02210   
02211   QDomElement parentElement = parentNode.toElement();
02212   if ( !parentElement.isNull() ) {
02213     QString plabel = attribute( parentElement, "label-id" );
02214     int     pid    = checkInt( attribute( parentElement, "item-id" ) );
02215     int     ppos   = checkInt( attribute( parentElement, "pos-id" ) );
02216     int     group  = checkInt( attribute( parentElement, "group-id" ), 
02217                                myModule->defaultMenuGroup() );
02218     if ( !plabel.isEmpty() ) {
02219       QMenu* popup = 0;
02220       int menuId = -1;
02221       // create menu
02222       menuId = myModule->createMenu( plabel,         // label
02223                                      parentMenuId,   // parent menu ID, -1 for top-level menu
02224                                      pid,            // ID
02225                                      group,          // group ID
02226                                      ppos );         // position
02227       myMenuItems.append( menuId );
02228       QDomNode node = parentNode.firstChild();
02229       while ( !node.isNull() ) {
02230         if ( node.isElement() ) {
02231           QDomElement elem = node.toElement();
02232           QString aTagName = tagName( elem );
02233           if ( aTagName == "popup-item" ) {
02234             int     id      = checkInt( attribute( elem, "item-id" ) );
02235             int     pos     = checkInt( attribute( elem, "pos-id" ) );
02236             int     group   = checkInt( attribute( elem, "group-id" ), 
02237                                         myModule->defaultMenuGroup() );
02238             QString label   = attribute( elem, "label-id" );
02239             QString icon    = attribute( elem, "icon-id" );
02240             QString tooltip = attribute( elem, "tooltip-id" );
02241             QString accel   = attribute( elem, "accel-id" );
02242             bool    toggle  = checkBool( attribute( elem, "toggle-id" ) );
02243 
02244             // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
02245             // also check if the action with given ID is already created
02246             if ( id != -1 ) {
02247               // create menu action
02248               QAction* action = myModule->createAction( id,                     // ID
02249                                                         tooltip,                // tooltip
02250                                                         icon,                   // icon
02251                                                         label,                  // menu text
02252                                                         tooltip,                // status-bar text
02253                                                         QKeySequence( accel ),  // keyboard accelerator
02254                                                         toggle );               // toogled action
02255               myModule->createMenu( action,   // action
02256                                     menuId,   // parent menu ID
02257                                     id,       // ID (same as for createAction())
02258                                     group,    // group ID
02259                                     pos );    // position
02260             }
02261           }
02262           else if ( aTagName == "submenu" ) {
02263             // create sub-menu
02264             createMenu( node, menuId, popup );
02265           }
02266           else if ( aTagName == "separator" ) {
02267             // create menu separator
02268             int id    = checkInt( attribute( elem, "item-id" ) ); // separator can have ID
02269             int pos   = checkInt( attribute( elem, "pos-id" ) );
02270             int group = checkInt( attribute( elem, "group-id" ), 
02271                                   myModule->defaultMenuGroup() );
02272             QAction* action = myModule->separator();
02273             myModule->createMenu( action,  // separator action
02274                                   menuId,  // parent menu ID
02275                                   id,      // ID
02276                                   group,   // group ID
02277                                   pos );   // position
02278           }
02279         }
02280         node = node.nextSibling();
02281       }
02282     }
02283   }
02284 }
02285 
02290 void SALOME_PYQT_ModuleLight::XmlHandler::createToolBar( QDomNode& parentNode )
02291 {
02292   if ( !myModule || parentNode.isNull() )
02293     return;
02294 
02295   QDomElement parentElement = parentNode.toElement();
02296   if ( !parentElement.isNull() ) {
02297     QString aLabel = attribute( parentElement, "label-id" );
02298     if ( !aLabel.isEmpty() ) {
02299       // create toolbar
02300       int tbId = myModule->createTool( aLabel );
02301       QDomNode node = parentNode.firstChild();
02302       while ( !node.isNull() ) {
02303         if ( node.isElement() ) {
02304           QDomElement elem = node.toElement();
02305           QString aTagName = tagName( elem );
02306           if ( aTagName == "toolbutton-item" ) {
02307             int     id      = checkInt( attribute( elem, "item-id" ) );
02308             int     pos     = checkInt( attribute( elem, "pos-id" ) );
02309             QString label   = attribute( elem, "label-id" );
02310             QString icon    = attribute( elem, "icon-id" );
02311             QString tooltip = attribute( elem, "tooltip-id" );
02312             QString accel   = attribute( elem, "accel-id" );
02313             bool    toggle  = checkBool( attribute( elem, "toggle-id" ) );
02314 
02315             // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
02316             // also check if the action with given ID is already created
02317             if ( id != -1 ) {
02318               // create toolbar action
02319               QAction* action = myModule->createAction( id,                    // ID
02320                                                         tooltip,               // tooltip
02321                                                         icon,                  // icon
02322                                                         label,                 // menu text
02323                                                         tooltip,               // status-bar text
02324                                                         QKeySequence( accel ), // keyboard accelerator
02325                                                         toggle );              // toogled action
02326               myModule->createTool( action, tbId, -1, pos );
02327             }
02328           }
02329           else if ( aTagName == "separatorTB" || aTagName == "separator" ) {
02330             // create toolbar separator
02331             int pos = checkInt( attribute( elem, "pos-id" ) );
02332             QAction* action = myModule->separator();
02333             myModule->createTool( action, tbId, -1, pos );
02334           }
02335         }
02336         node = node.nextSibling();
02337       }
02338     }
02339   }
02340 }
02341 
02347 void SALOME_PYQT_ModuleLight::XmlHandler::insertPopupItems( QDomNode& parentNode, QMenu* menu )
02348 {
02349   if ( !myModule && parentNode.isNull() )
02350     return;
02351 
02352   // we create popup menus without help of QtxPopupMgr
02353   QDomNode node = parentNode.firstChild();
02354   while ( !node.isNull() ) { 
02355     if ( node.isElement() ) {
02356       QDomElement elem = node.toElement();
02357       QString aTagName = tagName( elem );
02358       QList<QAction*> actions = menu->actions();
02359       if ( aTagName == "popup-item" ) {
02360         // insert a command item
02361         int     id      = checkInt( attribute( elem, "item-id" ) );
02362         int     pos     = checkInt( attribute( elem, "pos-id" ) );
02363         QString label   = attribute( elem, "label-id" );
02364         QString icon    = attribute( elem, "icon-id" );
02365         QString tooltip = attribute( elem, "tooltip-id" );
02366         QString accel   = attribute( elem, "accel-id" );
02367         bool    toggle  = checkBool( attribute( elem, "toggle-id" ) );
02368 
02369         // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
02370         // also check if the action with given ID is already created
02371         if ( id != -1 ) {
02372           QAction* action = myModule->createAction( id,                     // ID
02373                                                     tooltip,                // tooltip
02374                                                     icon,                   // icon
02375                                                     label,                  // menu text
02376                                                     tooltip,                // status-bar text
02377                                                     QKeySequence( accel ),  // keyboard accelerator
02378                                                     toggle );               // toogled action
02379           QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
02380           menu->insertAction( before, action );
02381         }
02382       }
02383       else if ( aTagName == "submenu" ) {
02384         // create sub-menu
02386         int     pos   = checkInt( attribute( elem, "pos-id" ) );
02387         QString label = attribute( elem, "label-id" );
02388         QString icon  = attribute( elem, "icon-id" );
02389 
02390         QIcon anIcon;
02391         if ( !icon.isEmpty() ) {
02392           QPixmap pixmap  = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(), icon );
02393           if ( !pixmap.isNull() )
02394             anIcon = QIcon( pixmap );
02395         }
02396 
02397         QMenu* newPopup = menu->addMenu( anIcon, label );
02398         QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
02399         menu->insertMenu( before, newPopup );
02400         insertPopupItems( node, newPopup );
02401       }
02402       else if ( aTagName == "separator" ) {
02403         // create menu separator
02404         int pos = checkInt( attribute( elem, "pos-id" ) );
02405         QAction* action = myModule->separator();
02406         QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
02407         menu->insertAction( before, action );
02408       }
02409     }
02410     node = node.nextSibling();
02411   }
02412 }
02413 
02414 /*
02415  * Save study request.
02416  * Called when user save study.
02417  */
02418 void SALOME_PYQT_ModuleLight::save(QStringList& theListOfFiles)
02419 {
02420   MESSAGE("SALOME_PYQT_ModuleLight::save()")
02421   // perform synchronous request to Python event dispatcher
02422   class SaveEvent: public PyInterp_LockRequest
02423   {
02424   public:     
02425     SaveEvent(PyInterp_Interp*           _py_interp,
02426               SALOME_PYQT_ModuleLight*       _obj,
02427               QStringList&                   _files_list)
02428       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
02429         myObj( _obj ) ,
02430         myFilesList(_files_list) {}
02431   protected:
02432     virtual void execute()
02433     {
02434       myObj->saveEvent(myFilesList);
02435     }
02436   private:
02437     SALOME_PYQT_ModuleLight* myObj;
02438     QStringList&        myFilesList;
02439   };
02440   
02441   // Posting the request only if dispatcher is not busy!
02442   // Executing the request synchronously
02443   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
02444     PyInterp_Dispatcher::Get()->Exec( new SaveEvent( myInterp, this, theListOfFiles ) );
02445 }
02446 
02447 void SALOME_PYQT_ModuleLight::saveEvent(QStringList& theListOfFiles)
02448 {
02449   MESSAGE("SALOME_PYQT_ModuleLight::saveEvent()");
02450   QStringList::Iterator it = theListOfFiles.begin();
02451   // Python interpreter should be initialized and Python module should be
02452   // import first
02453   if ( !myInterp || !myModule || (it == theListOfFiles.end()))
02454     return;
02455 
02456   if ( PyObject_HasAttrString(myModule, (char*)"saveFiles") ) {
02457     // temporary set myInitModule because saveEvent() method
02458     // might be called by the framework when this module is inactive,
02459     // but still it should be possible to access this module's data
02460     // from Python
02461     myInitModule = this;
02462 
02463     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"saveFiles",
02464                                            (char*)"s", (*it).toLatin1().constData()));
02465 
02466     myInitModule = 0;
02467 
02468     if( !res ) {
02469       PyErr_Print();
02470     }
02471     else{
02472       // parse the return value
02473       // result can be one string...
02474       if ( PyString_Check( res ) ) {
02475         QString astr = PyString_AsString( res );
02476         //SCRUTE(astr);
02477         theListOfFiles.append(astr);
02478       }
02479       //also result can be a list...
02480       else if ( PyList_Check( res ) ) {
02481         int size = PyList_Size( res );
02482         for ( int i = 0; i < size; i++ ) {
02483           PyObject* value = PyList_GetItem( res, i );
02484           if( value && PyString_Check( value ) ) {
02485             theListOfFiles.append( PyString_AsString( value ) );
02486           }
02487         }
02488       }
02489     }
02490   }
02491 }
02492 
02493 /*
02494  * Python dump request.
02495  * Called when user activates dump study operation.
02496  */
02497 void SALOME_PYQT_ModuleLight::dumpPython(QStringList& theListOfFiles)
02498 {
02499   MESSAGE("SALOME_PYQT_ModuleLight::dumpPython()")
02500   // perform synchronous request to Python event dispatcher
02501   class DumpEvent: public PyInterp_LockRequest
02502   {
02503   public:     
02504     DumpEvent(PyInterp_Interp*          _py_interp,
02505               SALOME_PYQT_ModuleLight*  _obj,
02506               QStringList&              _files_list)
02507       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
02508         myObj( _obj ) ,
02509         myFilesList(_files_list) {}
02510   protected:
02511     virtual void execute()
02512     {
02513       myObj->dumpEvent(myFilesList);
02514     }
02515   private:
02516     SALOME_PYQT_ModuleLight* myObj;
02517     QStringList&             myFilesList;
02518   };
02519   
02520   // Posting the request only if dispatcher is not busy!
02521   // Executing the request synchronously
02522   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
02523     PyInterp_Dispatcher::Get()->Exec( new DumpEvent( myInterp, this, theListOfFiles ) );
02524 }
02525 
02526 void SALOME_PYQT_ModuleLight::dumpEvent(QStringList& theListOfFiles)
02527 {
02528   MESSAGE("SALOME_PYQT_ModuleLight::dumpEvent()");
02529   QStringList::Iterator it = theListOfFiles.begin();
02530   // Python interpreter should be initialized and Python module should be
02531   // import first
02532   if ( !myInterp || !myModule || (it == theListOfFiles.end()))
02533     return;
02534 
02535   if ( PyObject_HasAttrString(myModule, (char*)"dumpStudy") ) {
02536     // temporary set myInitModule because dumpEvent() method
02537     // might be called by the framework when this module is inactive,
02538     // but still it should be possible to access this module's data
02539     // from Python
02540     myInitModule = this;
02541 
02542     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"dumpStudy",
02543                                            (char*)"s", (*it).toLatin1().constData()));
02544 
02545     myInitModule = 0;
02546 
02547     if( !res ) {
02548       PyErr_Print();
02549     }
02550     else{
02551       // parse the return value
02552       // result can be one string...
02553       if ( PyString_Check( res ) ) {
02554         QString astr = PyString_AsString( res );
02555         //SCRUTE(astr);
02556         theListOfFiles.append(astr);
02557       }
02558       //also result can be a list...
02559       else if ( PyList_Check( res ) ) {
02560         int size = PyList_Size( res );
02561         for ( int i = 0; i < size; i++ ) {
02562           PyObject* value = PyList_GetItem( res, i );
02563           if( value && PyString_Check( value ) ) {
02564             theListOfFiles.append( PyString_AsString( value ) );
02565           }
02566         }
02567       }
02568     }
02569   }
02570 }
02571 
02572 /*
02573  * Open study request.
02574  * Called when user open study.
02575  */
02576 bool SALOME_PYQT_ModuleLight::open(QStringList theListOfFiles)
02577 {
02578   MESSAGE("SALOME_PYQT_ModuleLight::open()");
02579   // perform synchronous request to Python event dispatcher
02580   bool opened = false;
02581   class OpenEvent: public PyInterp_LockRequest
02582   {
02583   public:
02584     OpenEvent(PyInterp_Interp*              _py_interp,
02585               SALOME_PYQT_ModuleLight*      _obj,
02586               QStringList                   _files_list,
02587               bool&                         _opened)
02588       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
02589         myObj( _obj ) ,
02590         myFilesList(_files_list),
02591         myOpened(_opened) {}
02592   protected:
02593     virtual void execute()
02594     {
02595       myObj->openEvent(myFilesList,myOpened);
02596     }
02597 
02598   private:
02599     SALOME_PYQT_ModuleLight* myObj;
02600     QStringList        myFilesList;
02601     bool& myOpened;
02602   };
02603   
02604   // Posting the request only if dispatcher is not busy!
02605   // Executing the request synchronously
02606   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
02607     PyInterp_Dispatcher::Get()->Exec( new OpenEvent( myInterp, this, theListOfFiles, opened) );
02608   return opened;
02609 }
02610 
02611 
02612 void SALOME_PYQT_ModuleLight::openEvent(QStringList theListOfFiles, bool &opened)
02613 {
02614   MESSAGE("SALOME_PYQT_ModuleLight::openEvent()");
02615   // Python interpreter should be initialized and Python module should be
02616   // import first
02617   if ( !myInterp || !myModule || theListOfFiles.isEmpty())
02618     return;
02619   QStringList* theList = new QStringList(theListOfFiles);
02620 
02621 #if SIP_VERSION < 0x040800
02622   PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
02623 #else
02624   PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
02625 #endif
02626   if ( PyObject_HasAttrString(myModule , (char*)"openFiles") ) {
02627     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"openFiles",
02628                                            (char*)"O", sipList.get()));
02629     if( !res || !PyBool_Check( res )) {
02630       PyErr_Print();
02631       opened = false;
02632     }
02633     else{
02634       opened = PyObject_IsTrue( res );
02635       
02636     }
02637   }
02638 }
02639 
02640 /*
02641  * Create new empty Data Object and return its entry
02642  */
02643 QString SALOME_PYQT_ModuleLight::createObject(const QString& parent)
02644 {
02645   SALOME_PYQT_DataObjectLight* obj=0;
02646   if(!parent.isEmpty())
02647   {
02648     SALOME_PYQT_DataObjectLight* parentObj = findObject(parent);
02649     if(parentObj)
02650     {
02651       obj = new SALOME_PYQT_DataObjectLight(parentObj);
02652     }
02653   }
02654   else
02655   {
02656       SALOME_PYQT_DataModelLight* dm =
02657         dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel());
02658       if(dm)
02659       {
02660         obj = new SALOME_PYQT_DataObjectLight(dm->getRoot());
02661       }
02662   }  
02663   if (obj)
02664     return obj->entry();
02665   else
02666     return QString::null;
02667 }
02668 
02669 /*
02670  * Create new Data Object with name, icon and tooltip
02671  * and return its entry
02672  */
02673 QString SALOME_PYQT_ModuleLight::createObject(const QString& aname,
02674                                               const QString& iconname,
02675                                               const QString& tooltip,
02676                                               const QString& parent)
02677 {
02678   QString entry = createObject(parent);
02679   SALOME_PYQT_DataObjectLight* obj = findObject(entry);
02680 
02681   if(obj)
02682   {
02683     obj->setName(aname);
02684     obj->setToolTip(tooltip);
02685     obj->setIcon(iconname);
02686     return obj->entry();
02687   }
02688   else
02689     return QString::null;
02690 }
02691 
02692 /*
02693  * Find object by entry
02694  */
02695 SALOME_PYQT_DataObjectLight* SALOME_PYQT_ModuleLight::findObject(const QString& entry)
02696 {
02697   SALOME_PYQT_DataObjectLight* obj = 0;
02698   SALOME_PYQT_DataModelLight* dm =
02699     dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel());
02700   if(!entry.isEmpty() && dm ){
02701     for ( SUIT_DataObjectIterator it( dm->getRoot(), SUIT_DataObjectIterator::DepthLeft ); it.current(); ++it ) { 
02702       SALOME_PYQT_DataObjectLight* curentobj =
02703         dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.current() );
02704       
02705       if(curentobj && curentobj->entry() == entry) {
02706         obj = curentobj;
02707         return obj;
02708       }
02709     }
02710   }
02711   return obj;
02712 }
02713 
02714 /*
02715  * Set Name for object
02716  */
02717 void SALOME_PYQT_ModuleLight::setName(const QString& obj, const QString& name)
02718 {
02719   SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
02720   if(dataObj) {
02721     dataObj->setName(name);
02722   }
02723 }
02724 
02725 /*
02726  * Set Icon for object
02727  */
02728 void SALOME_PYQT_ModuleLight::setIcon(const QString& obj, const QString& iconname)
02729 {
02730   SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
02731   if(dataObj) {
02732     dataObj->setIcon(iconname);
02733   }
02734 }
02735 
02736 /*
02737  * Return Name of object
02738  */
02739 QString SALOME_PYQT_ModuleLight::getName(const QString& obj)
02740 {
02741   SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
02742   if(dataObj) {
02743     return dataObj->name();
02744   }
02745   return QString::null;
02746 }
02747 
02748 /*
02749  * Return Tool Tip of object
02750  */
02751 QString SALOME_PYQT_ModuleLight::getToolTip(const QString& obj)
02752 {
02753   SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
02754   if(dataObj) {
02755     return dataObj->toolTip();
02756   }
02757   return QString::null;
02758 }
02759 
02760 
02761 /*
02762  * Set Tool Tip for object
02763  */
02764 void SALOME_PYQT_ModuleLight::setToolTip(const QString& obj, const QString& tooltip)
02765 {
02766   SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
02767   if(dataObj) {
02768     dataObj->setToolTip(tooltip);
02769   }
02770 }
02771 
02772 /*
02773  * Return color of object
02774  */
02775 QColor SALOME_PYQT_ModuleLight::getColor(const QString& obj)
02776 {
02777   SALOME_PYQT_DataObjectLight* dataObj = findObject( obj );
02778   if( dataObj ) {
02779     return dataObj->color( SUIT_DataObject::Foreground );
02780   }
02781   return QColor();
02782 }
02783 
02784 /*
02785  * Set color for object
02786  */
02787 void SALOME_PYQT_ModuleLight::setColor(const QString& obj, const QColor& color)
02788 {
02789   SALOME_PYQT_DataObjectLight* dataObj = findObject( obj );
02790   if( dataObj ) {
02791     dataObj->setColor( color );
02792   }
02793 }
02794 
02795 /*
02796  * Return entry of the referenced object (if any)
02797  */
02798 QString SALOME_PYQT_ModuleLight::getReference(const QString& obj)
02799 {
02800   SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
02801   if(dataObj) {
02802     return dataObj->refEntry();
02803   }
02804   return QString::null;
02805 }
02806 
02807 
02808 /*
02809  * Set entry of the referenced object
02810  */
02811 void SALOME_PYQT_ModuleLight::setReference(const QString& obj, const QString& refEntry)
02812 {
02813   SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
02814   if(dataObj) {
02815     dataObj->setRefEntry(refEntry);
02816   }
02817 }
02818 
02819 /*
02820  * Remove object by entry
02821  */
02822 void SALOME_PYQT_ModuleLight::removeObject(const QString& obj)
02823 {
02824   SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
02825   if(dataObj) {
02826     dataObj->parent()->removeChild(dataObj);
02827   }
02828 }
02829 
02830 
02831 /*
02832  * Remove chields from object 
02833  */
02834 void SALOME_PYQT_ModuleLight::removeChild(const QString& obj)
02835 {
02836   MESSAGE("SALOME_PYQT_ModuleLight::removeChild()");
02837   DataObjectList lst;
02838   if(!obj.isEmpty())
02839     {
02840     SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
02841     if(dataObj)
02842     {
02843       dataObj->children(lst);
02844       QListIterator<SUIT_DataObject*> it( lst );
02845       while( it.hasNext() )
02846       {
02847         SALOME_PYQT_DataObjectLight* sobj = dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.next() );
02848         dataObj->removeChild(sobj);
02849       }
02850     }
02851   }
02852   else
02853   {
02854     SALOME_PYQT_DataModelLight* dm =
02855       dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel());
02856     if(dm)
02857     {
02858       dm->getRoot()->children(lst);
02859       QListIterator<SUIT_DataObject*> it( lst );
02860       while(it.hasNext() )
02861       {
02862         SALOME_PYQT_DataObjectLight* sobj = dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.next() );
02863         dm->getRoot()->removeChild(sobj);
02864       }
02865     }
02866   }
02867 }
02868 
02869 QStringList SALOME_PYQT_ModuleLight::getChildren(const QString& obj, const bool rec)
02870 {
02871   DataObjectList lst;
02872   QStringList entryList;
02873   if(!obj.isEmpty())
02874   {
02875     SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
02876     if(dataObj)
02877     {
02878       dataObj->children(lst,rec);
02879       QListIterator<SUIT_DataObject*> it( lst );
02880       while(it.hasNext())
02881       {
02882         SALOME_PYQT_DataObjectLight* sobj = dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.next() );
02883         entryList.append(sobj->entry());
02884       }
02885     }
02886   }
02887   else
02888   {
02889     SALOME_PYQT_DataModelLight* dm =
02890       dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel());
02891     if(dm)
02892     {
02893       dm->getRoot()->children(lst);
02894       QListIterator<SUIT_DataObject*> it( lst );
02895       while( it.hasNext() )
02896       {
02897         SALOME_PYQT_DataObjectLight* sobj = dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.next() );
02898         entryList.append(sobj->entry());
02899       }
02900     }
02901   }
02902   return entryList;
02903 }
02904 
02908 CAM_DataModel* SALOME_PYQT_ModuleLight::createDataModel()
02909 {
02910   MESSAGE( "SALOME_PYQT_ModuleLight::createDataModel()" );
02911   return new SALOME_PYQT_DataModelLight(this);
02912 }
02913 
02917 PyObject* SALOME_PYQT_ModuleLight::getPythonModule()
02918 {
02919   return myModule;
02920 }
02921 
02922 bool SALOME_PYQT_ModuleLight::isDraggable( const SUIT_DataObject* what ) const
02923 {
02924   MESSAGE("SALOME_PYQT_ModuleLight::isDraggable()");
02925   // perform synchronous request to Python event dispatcher
02926   bool draggable = false;
02927   class IsDraggableEvent: public PyInterp_LockRequest
02928   {
02929   public:
02930     IsDraggableEvent(PyInterp_Interp*          _py_interp,
02931                    SALOME_PYQT_ModuleLight*  _obj,
02932                    LightApp_DataObject*      _data_object,
02933                    bool&                     _is_draggable )
02934       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
02935         myObj( _obj ) ,
02936         myDataObject( _data_object ),
02937         myIsDraggable( _is_draggable ) {}
02938   protected:
02939     virtual void execute()
02940     {
02941       myIsDraggable = myObj->isDraggableEvent( myDataObject );
02942     }
02943 
02944   private:
02945     SALOME_PYQT_ModuleLight* myObj;
02946     LightApp_DataObject*     myDataObject;
02947     bool&                    myIsDraggable;
02948   };
02949 
02950   const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( what );
02951                                                        
02952   // Posting the request only if dispatcher is not busy!
02953   // Executing the request synchronously
02954   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
02955     PyInterp_Dispatcher::Get()->Exec( new IsDraggableEvent( myInterp,
02956                                                      const_cast<SALOME_PYQT_ModuleLight*>( this ),
02957                                                      const_cast<LightApp_DataObject*>( data_object ),
02958                                                      draggable ) );
02959   return draggable;
02960 }
02961 
02962 bool SALOME_PYQT_ModuleLight::isDraggableEvent( LightApp_DataObject* what )
02963 {
02964   MESSAGE("SALOME_PYQT_ModuleLight::isDraggableEvent()");
02965 
02966   bool draggable = false;
02967 
02968   // Python interpreter should be initialized and Python module should be
02969   // import first
02970   if ( !myInterp || !myModule || !what )
02971     return draggable;
02972 
02973   if ( PyObject_HasAttrString(myModule , (char*)"isDraggable") ) {
02974     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"isDraggable",
02975                                       (char*)"s", what->entry().toLatin1().constData() ) );
02976     if( !res || !PyBool_Check( res )) {
02977       PyErr_Print();
02978       draggable = false;
02979     }
02980     else{
02981       draggable = PyObject_IsTrue( res );
02982     }
02983   }
02984 
02985   return draggable;
02986 }
02987 
02988 bool SALOME_PYQT_ModuleLight::isDropAccepted( const SUIT_DataObject* where ) const
02989 {
02990   MESSAGE("SALOME_PYQT_ModuleLight::isDropAccepted()");
02991   // perform synchronous request to Python event dispatcher
02992   bool dropAccepted = false;
02993   class IsDropAcceptedEvent: public PyInterp_LockRequest
02994   {
02995   public:
02996     IsDropAcceptedEvent(PyInterp_Interp*          _py_interp,
02997                      SALOME_PYQT_ModuleLight*  _obj,
02998                      LightApp_DataObject*      _data_object,
02999                      bool&                     _is_drop_accepted )
03000       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
03001         myObj( _obj ) ,
03002         myDataObject( _data_object ),
03003         myIsDropAccepted( _is_drop_accepted ) {}
03004   protected:
03005     virtual void execute()
03006     {
03007       myIsDropAccepted = myObj->isDropAcceptedEvent( myDataObject );
03008     }
03009 
03010   private:
03011     SALOME_PYQT_ModuleLight* myObj;
03012     LightApp_DataObject*     myDataObject;
03013     bool&                    myIsDropAccepted;
03014   };
03015   
03016   const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( where );
03017                                                        
03018   // Posting the request only if dispatcher is not busy!
03019   // Executing the request synchronously
03020   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
03021     PyInterp_Dispatcher::Get()->Exec( new IsDropAcceptedEvent( myInterp,
03022                                                         const_cast<SALOME_PYQT_ModuleLight*>( this ),
03023                                                         const_cast<LightApp_DataObject*>( data_object ),
03024                                                         dropAccepted ) );
03025   return dropAccepted;
03026 }
03027 
03028 bool SALOME_PYQT_ModuleLight::isDropAcceptedEvent( LightApp_DataObject* where )
03029 {
03030   MESSAGE("SALOME_PYQT_ModuleLight::isDropAcceptedEvent()");
03031 
03032   bool dropAccepted = false;
03033 
03034   // Python interpreter should be initialized and Python module should be
03035   // import first
03036   if ( !myInterp || !myModule || !where )
03037     return dropAccepted;
03038 
03039   if ( PyObject_HasAttrString(myModule , (char*)"isDropAccepted") ) {
03040     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"isDropAccepted",
03041                                       (char*)"s", where->entry().toLatin1().constData() ) );
03042     if( !res || !PyBool_Check( res )) {
03043       PyErr_Print();
03044       dropAccepted = false;
03045     }
03046     else{
03047       dropAccepted = PyObject_IsTrue( res );
03048     }
03049   }
03050 
03051   return dropAccepted;
03052 }
03053 
03054 void SALOME_PYQT_ModuleLight::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
03055                                       const int row, Qt::DropAction action )
03056 {
03057   MESSAGE("SALOME_PYQT_ModuleLight::dropObjects()");
03058   // perform synchronous request to Python event dispatcher
03059   class DropObjectsEvent: public PyInterp_LockRequest
03060   {
03061   public:
03062     DropObjectsEvent(PyInterp_Interp*          _py_interp,
03063                    SALOME_PYQT_ModuleLight*  _obj,
03064                    const DataObjectList&     _what,
03065                    SUIT_DataObject*          _where,
03066                    const int                 _row,
03067                    Qt::DropAction            _action )
03068       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
03069         myObj( _obj ) ,
03070         myWhat( _what ),
03071        myWhere( _where ),
03072        myRow ( _row ),
03073        myAction ( _action ){}
03074   protected:
03075     virtual void execute()
03076     {
03077       myObj->dropObjectsEvent( myWhat, myWhere, myRow, myAction );
03078     }
03079 
03080   private:
03081     SALOME_PYQT_ModuleLight* myObj;
03082     DataObjectList           myWhat;
03083     SUIT_DataObject*         myWhere;
03084     int                      myRow;
03085     Qt::DropAction           myAction;
03086   };
03087   
03088   // Posting the request only if dispatcher is not busy!
03089   // Executing the request synchronously
03090   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
03091     PyInterp_Dispatcher::Get()->Exec( new DropObjectsEvent( myInterp, this, what, where, row, action ) );
03092 }
03093 
03094 void SALOME_PYQT_ModuleLight::dropObjectsEvent( const DataObjectList& what, SUIT_DataObject* where,
03095                                           const int row, Qt::DropAction action )
03096 {
03097   MESSAGE("SALOME_PYQT_ModuleLight::dropObjectsEvent()");
03098   // Python interpreter should be initialized and Python module should be
03099   // import first
03100   if ( !myInterp || !myModule || what.isEmpty() || !where )
03101     return;
03102 
03103   QStringList* theList = new QStringList();
03104 
03105   LightApp_DataObject* whereObject = dynamic_cast<LightApp_DataObject*>( where );
03106   if ( !whereObject ) return;
03107   
03108   for ( int i = 0; i < what.count(); i++ ) {
03109     LightApp_DataObject* dataObject = dynamic_cast<LightApp_DataObject*>( what[i] );
03110     if ( dataObject ) theList->append( dataObject->entry() );
03111   }
03112 
03113 #if SIP_VERSION < 0x040800
03114   PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
03115 #else
03116   PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
03117 #endif
03118   if ( PyObject_HasAttrString(myModule, (char*)"dropObjects") ) {
03119     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"dropObjects", (char*)"Osii",
03120                                       sipList.get(),
03121                                       whereObject->entry().toLatin1().constData(),
03122                                       row, action ) );
03123     
03124     if( !res ) {
03125       PyErr_Print();
03126     }
03127   }
03128 }