Salome HOME
b9a6aa3d279efefe1b271827ebb12022079871f9
[modules/gui.git] / src / SALOME_PYQT / SALOME_PYQT_GUILight / SALOME_PYQT_ModuleLight.cxx
1 // Copyright (C) 2007-2012  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 // File   : SALOME_PYQT_Module.cxx
21 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
22 //
23 #include "SALOME_PYQT_PyInterp.h"
24 #include <SUITApp_init_python.hxx>
25 #include <PyInterp_Interp.h>
26 #include <PyConsole_Interp.h>
27 #include <PyConsole_Console.h>
28 #include <PyInterp_Dispatcher.h>
29
30 #include "SALOME_PYQT_ModuleLight.h"
31 #include "SALOME_PYQT_DataModelLight.h"
32
33 #ifndef GUI_DISABLE_CORBA
34 #include <Container_init_python.hxx>
35 #endif
36
37 #include <SUIT_ResourceMgr.h>
38 #include <SUIT_DataObjectIterator.h>
39 #include <SUIT_Desktop.h>
40 #include <SUIT_ViewModel.h>
41 #include <SUIT_ViewWindow.h>
42 #include <SUIT_ViewManager.h>
43 #include <STD_MDIDesktop.h>
44 #include <STD_TabDesktop.h>
45 #include <LightApp_Preferences.h>
46 #include <LightApp_Application.h>
47 #include <LightApp_Study.h>
48
49 #include <QtxWorkstack.h>
50 #include <QtxWorkspace.h>
51 #include <QtxActionGroup.h>
52 #include <QtxActionMenuMgr.h>
53 #include <QtxActionToolMgr.h>
54
55 #include <QFile>
56 #include <QDomDocument>
57 #include <QDomNode>
58 #include <QDomElement>
59 #include <QMenuBar>
60 #include <QMenu>
61 #include <QAction>
62
63 #include "sipAPISalomePyQtGUILight.h"
64
65 #include <sip.h>
66 #if SIP_VERSION < 0x040700
67 #include "sipQtGuiQWidget.h"
68 #include "sipQtGuiQMenu.h"
69 #endif
70
71 #include <utilities.h>
72
73 /*!
74   \brief Default name of the module, replaced at the moment
75   of module creation.
76   \internal
77 */
78 const char* DEFAULT_NAME  = "SALOME_PYQT_ModuleLight";
79
80 /*!
81   \brief Default menu group number.
82   \internal
83 */
84 const int DEFAULT_GROUP = 40;
85
86 /*!
87   \var IsCallOldMethods
88   \brief Allow calling obsolete callback methods.
89   \internal
90   
91   If the macro CALL_OLD_METHODS is not defined, the invoking
92   of obsolete Python module's methods like setSetting(), definePopup(), 
93   etc. is blocked.
94
95   CALL_OLD_METHODS macro can be defined for example by adding 
96   -DCALL_OLD_METHODS compilation option to the Makefile.
97 */
98 #ifdef CALL_OLD_METHODS
99 const bool IsCallOldMethods = true;
100 #else
101 const bool IsCallOldMethods = false;
102 #endif
103
104 /* Py_ssize_t for old Pythons */
105 /* This code is as recommended by: */
106 /* http://www.python.org/dev/peps/pep-0353/#conversion-guidelines */
107 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
108 typedef int Py_ssize_t;
109 # define PY_SSIZE_T_MAX INT_MAX
110 # define PY_SSIZE_T_MIN INT_MIN
111 #endif
112
113 //
114 // NB: Python requests.
115 // General rule for Python requests created by SALOME_PYQT_Module:
116 // all requests should be executed SYNCHRONOUSLY within the main GUI thread.
117 // However, it is obligatory that ANY Python call is wrapped with a request object,
118 // so that ALL Python API calls are serialized with PyInterp_Dispatcher.
119 //
120
121 /*!
122   \class SALOME_PYQT_Module::XmlHandler
123   \brief XML resource files parser.
124   \internal
125
126   This class is used to provide backward compatibility with
127   existing Python modules in which obsolete menu definition system
128   (via XML files) is used.
129 */
130
131 class SALOME_PYQT_ModuleLight::XmlHandler
132 {
133 public:
134   XmlHandler( SALOME_PYQT_ModuleLight* module, const QString& fileName );
135   void createActions();
136   void createPopup  ( QMenu*         menu,
137                       const QString& context,
138                       const QString& parent,
139                       const QString& object );
140   void activateMenus( bool );
141
142 protected:
143   void createToolBar   ( QDomNode&   parentNode );
144   void createMenu      ( QDomNode&   parentNode,
145                          const int   parentMenuId = -1,
146                          QMenu*      parentPopup = 0 );
147
148   void insertPopupItems( QDomNode&   parentNode,
149                          QMenu*      menu );
150
151 private:
152   SALOME_PYQT_ModuleLight* myModule;
153   QDomDocument             myDoc;
154   QList<int>               myMenuItems;
155 };
156
157 //
158 // NB: Library initialization
159 // Since the SalomePyQtGUILight library is not imported in Python it's initialization function
160 // should be called manually (and only once) in order to initialize global sip data
161 // and to get C API from sip : sipBuildResult for example
162 //
163
164 #define INIT_FUNCTION initSalomePyQtGUILight
165 #if defined(SIP_STATIC_MODULE)
166 extern "C" void INIT_FUNCTION();
167 #else
168 PyMODINIT_FUNC INIT_FUNCTION();
169 #endif
170
171 /*!
172   \fn CAM_Module* createModule()
173   \brief Module factory function.
174   \internal
175   
176   Creates an instance of SALOME_PYQT_Module object by request
177   of an application when the module is loaded and initialized.
178
179   \return new module object
180 */
181
182 extern "C" {
183   SALOME_PYQT_LIGHT_EXPORT CAM_Module* createModule() {
184
185     static bool alreadyInitialized = false;
186     if ( !alreadyInitialized ) {
187       // call only once (see comment above) !
188       static PyThreadState *gtstate = 0;
189 #ifndef GUI_DISABLE_CORBA
190       if(SUIT_PYTHON::initialized)
191         gtstate = SUIT_PYTHON::_gtstate;
192       else
193         gtstate = KERNEL_PYTHON::_gtstate;
194 #else
195       gtstate = SUIT_PYTHON::_gtstate;
196 #endif
197       PyEval_RestoreThread( gtstate );
198       INIT_FUNCTION();
199       PyEval_ReleaseThread( gtstate );
200       alreadyInitialized = !alreadyInitialized;
201     }
202     return new SALOME_PYQT_ModuleLight();
203   }
204 }
205
206 /*!
207   \class FuncMsg
208   \brief Function call in/out tracer.
209   \internal
210 */
211
212 class FuncMsg
213 {
214 public:
215   FuncMsg( const QString& funcName )
216   {
217     myName = funcName;
218     MESSAGE( myName.toLatin1().constData() << " [ begin ]" );
219   }
220   ~FuncMsg()
221   {
222     MESSAGE( myName.toLatin1().constData() << " [ end ]" );
223   }
224   void message( const QString& msg )
225   {
226     MESSAGE( myName.toLatin1().constData() << " : " << msg.toLatin1().constData() );
227   }
228 private:
229   QString myName;
230 };
231
232 /*!
233   \class SALOME_PYQT_ModuleLight
234   \brief This class implements module API for all the Python-based 
235   SALOME modules.
236 */
237
238 //
239 // Static variables definition
240 //
241 SALOME_PYQT_ModuleLight::InterpMap SALOME_PYQT_ModuleLight::myInterpMap;
242 SALOME_PYQT_ModuleLight* SALOME_PYQT_ModuleLight::myInitModule = 0;
243
244 /*!
245   \brief Get the module being initialized.
246   
247   This is a little trick :) needed to provide an access from Python
248   (SalomePyQt) to the module being currently activated. The problem
249   that during the process of module initialization (initialize() 
250   function) it is not yet available via application->activeModule()
251   call.
252   
253   This method returns valid pointer only if called in scope of
254   initialize() function.
255
256   \return the module being currently initialized
257 */
258 SALOME_PYQT_ModuleLight* SALOME_PYQT_ModuleLight::getInitModule()
259 {
260   return myInitModule;
261 }
262
263 /*!
264   \brief Constructor
265 */
266 SALOME_PYQT_ModuleLight::SALOME_PYQT_ModuleLight()
267 : LightApp_Module( DEFAULT_NAME ),
268   myInterp( 0 ),
269   myModule( 0 ), 
270   myXmlHandler ( 0 ),
271   myLastActivateStatus( true )
272 {
273 }
274
275 /*!
276   \brief Destructor
277 */
278 SALOME_PYQT_ModuleLight::~SALOME_PYQT_ModuleLight()
279 {
280   if ( myXmlHandler )
281     delete myXmlHandler;
282   if ( myInterp && myModule ) {
283     PyLockWrapper aLock = myInterp->GetLockWrapper();
284     Py_XDECREF(myModule);
285   }
286 }
287
288 /*!
289   \brief Initialization of the module.
290   
291   This method can be used for creation of the menus, toolbars and 
292   other such staff.
293   
294   There are two ways to do this:
295   - for obsolete modules this method first tries to read
296   <module>_<language>.xml resource file which contains a menu,
297   toolbars and popup menus description;
298   - new modules can create menus by direct calling of the
299   corresponding methods of SalomePyQt Python API in the Python
300   module's initialize() method which is called from here.
301
302   NOTE: SALOME supports two modes of modules loading:
303   - immediate (all the modules are created and initialized 
304   immediately when the application object is created;
305   - postponed modules loading (used currently); in this mode
306   the module is loaded only be request.
307   If postponed modules loading is not used, the active
308   study might be not yet defined at this stage, so initialize()
309   method should not perform any study-based initialization.
310
311   \param app parent application object
312 */
313 void SALOME_PYQT_ModuleLight::initialize( CAM_Application* app )
314 {
315   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::initialize()" );
316
317   // call base implementation
318   LightApp_Module::initialize( app );
319
320   // try to get XML resource file name
321   SUIT_ResourceMgr* aResMgr = getApp()->resourceMgr();
322   if ( !myXmlHandler && aResMgr ) {
323     // get current language
324     QString aLang = aResMgr->stringValue( "language", "language", QString() );
325     if ( aLang.isEmpty() ) 
326       aLang = "en";
327     // define resource file name
328     QString aFileName = name() + "_" + aLang + ".xml";
329     aFileName = aResMgr->path( "resources", name(), aFileName );
330     // create XML handler instance
331     if ( !aFileName.isEmpty() && QFile::exists( aFileName ) )
332       myXmlHandler = new SALOME_PYQT_ModuleLight::XmlHandler( this, aFileName );
333     // create menus & toolbars from XML file if required
334     if ( myXmlHandler )
335       myXmlHandler->createActions();
336   }
337
338   // perform internal initialization and call module's initialize() funtion
339   // InitializeReq: request class for internal init() operation
340   class InitializeReq : public PyInterp_Request
341   {
342   public:
343     InitializeReq( CAM_Application*    _app,
344                    SALOME_PYQT_ModuleLight* _obj )
345       : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
346         myApp( _app ),
347         myObj( _obj ) {}
348
349   protected:
350     virtual void execute()
351     {
352       myObj->init( myApp );
353     }
354
355   private:
356     CAM_Application*    myApp;
357     SALOME_PYQT_ModuleLight* myObj;
358   };
359
360   // post request
361   PyInterp_Dispatcher::Get()->Exec( new InitializeReq( app, this ) );
362 }
363
364 /*!
365   \brief Activation of the module.
366
367   This function is usually used in order to show the module's 
368   specific menus and toolbars, update actions state and perform
369   other such actions required when the module is activated.
370   
371   Note, that returning \c false in this function prevents the 
372   module activation.
373
374   \param theStudy parent study
375   \return \c true if activation is successful and \c false otherwise
376 */
377 bool SALOME_PYQT_ModuleLight::activateModule( SUIT_Study* theStudy )
378 {
379   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::activateModule()" );
380
381   // call base implementation
382   bool res = LightApp_Module::activateModule( theStudy );
383
384   if ( !res )
385     return res;
386
387   // internal activation
388   return activateModuleInternal( theStudy );
389 }
390
391 /*!
392   \brief Perform internal activation of the module.
393
394   The only goal of this function is to extract common functionality
395   for LightApp_Module and SalomeApp_module classes requried by the
396   specific architecture aspects of "light" / "full" SALOME modes.
397
398   \sa activateModule()
399 */
400 bool SALOME_PYQT_ModuleLight::activateModuleInternal( SUIT_Study* theStudy )
401 {
402   // reset the activation status to the default value
403   myLastActivateStatus = true;
404
405   // perform internal activation
406   // ActivateReq: request class for internal activate() operation
407   class ActivateReq : public PyInterp_Request
408   {
409   public:
410     ActivateReq( SUIT_Study*         _study,
411                  SALOME_PYQT_ModuleLight* _obj )
412       : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
413         myStudy ( _study ),
414         myObj   ( _obj   ) {}
415
416   protected:
417     virtual void execute()
418     {
419       myObj->activate( myStudy );
420     }
421
422   private:
423     SUIT_Study*         myStudy;
424     SALOME_PYQT_ModuleLight* myObj;
425   };
426
427   // post request
428   PyInterp_Dispatcher::Get()->Exec( new ActivateReq( theStudy, this ) );
429
430   // check activation status (set by activate())
431   if ( !lastActivationStatus() )
432     return false;
433
434   // activate menus, toolbars, etc
435   if ( myXmlHandler ) myXmlHandler->activateMenus( true );
436   setMenuShown( true );
437   setToolShown( true );
438
439   // connect preferences changing signal
440   connect( getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
441            this,     SLOT(   preferenceChanged( const QString&, const QString&, const QString& ) ) );
442
443   // perform custom activation actions
444   // CustomizeReq: request class for internal customize() operation
445   class CustomizeReq : public PyInterp_Request
446   {
447   public:
448     CustomizeReq( SUIT_Study*         _study,
449                   SALOME_PYQT_ModuleLight* _obj )
450       : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
451         myStudy ( _study ),
452         myObj   ( _obj   ) {}
453
454   protected:
455     virtual void execute()
456     {
457       myObj->customize( myStudy );
458     }
459
460   private:
461     SUIT_Study*         myStudy;
462     SALOME_PYQT_ModuleLight* myObj;
463   };
464
465   // post request
466   PyInterp_Dispatcher::Get()->Exec( new CustomizeReq( theStudy, this ) );
467
468   return true;
469 }
470
471 /*!
472   \brief Deactivation of the module.
473
474   This function is usually used in order to hide the module's 
475   specific menus and toolbars and perform other such actions
476   required when the module is deactivated.
477
478   \param theStudy parent study
479   \return \c true if deactivation is successful and \c false otherwise
480 */
481 bool SALOME_PYQT_ModuleLight::deactivateModule( SUIT_Study* theStudy )
482 {
483   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::deactivateModule()" );
484
485   // disconnect preferences changing signal
486   disconnect( getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
487               this,     SLOT(   preferenceChanged( const QString&, const QString&, const QString& ) ) );
488
489   // perform internal deactivation
490   // DeactivateReq: request class for internal deactivate() operation
491   class DeactivateReq : public PyInterp_LockRequest
492   {
493   public:
494     DeactivateReq( PyInterp_Interp*    _py_interp,
495                    SUIT_Study*         _study,
496                    SALOME_PYQT_ModuleLight* _obj )
497       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
498         myStudy ( _study ),
499         myObj   ( _obj   ) {}
500
501   protected:
502     virtual void execute()
503     {
504       myObj->deactivate( myStudy );
505     }
506
507   private:
508     SUIT_Study*         myStudy;
509     SALOME_PYQT_ModuleLight* myObj;
510   };
511
512   // post request
513   PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, theStudy, this ) );
514
515   // deactivate menus, toolbars, etc
516   if ( myXmlHandler ) myXmlHandler->activateMenus( false );
517   setMenuShown( false );
518   setToolShown( false );
519
520   // call base implementation
521   return LightApp_Module::deactivateModule( theStudy );
522 }
523
524 /*!
525  \brief Get last activation status.
526  \return status of last module activation operation
527  \sa activateModule()
528 */
529 bool SALOME_PYQT_ModuleLight::lastActivationStatus() const
530 {
531   return myLastActivateStatus;
532 }
533
534 /*!
535   \breif Process application preferences changing.
536
537   Called when any application setting is changed.
538
539   \param module preference module
540   \param section preference resource file section
541   \param setting preference resource name
542 */
543 void SALOME_PYQT_ModuleLight::preferenceChanged( const QString& module, 
544                                             const QString& section, 
545                                             const QString& setting )
546 {
547   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::preferenceChanged()" );
548
549   // perform synchronous request to Python event dispatcher
550   class Event : public PyInterp_LockRequest
551   {
552   public:
553     Event( PyInterp_Interp*    _py_interp,
554            SALOME_PYQT_ModuleLight* _obj,
555            const QString&      _section,
556            const QString&      _setting )
557       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
558         myObj    ( _obj ),
559         mySection( _section ),
560         mySetting( _setting ) {}
561
562   protected:
563     virtual void execute()
564     {
565       myObj->prefChanged( mySection, mySetting );
566     }
567
568   private:
569     SALOME_PYQT_ModuleLight* myObj;
570     QString mySection, mySetting;
571   };
572
573   if ( module != moduleName() ) {
574     // module's own preferences are processed by preferencesChanged() method
575     // ...
576     // post the request only if dispatcher is not busy!
577     // execute request synchronously
578     if ( !PyInterp_Dispatcher::Get()->IsBusy() )
579       PyInterp_Dispatcher::Get()->Exec( new Event( myInterp, this, section, setting ) );
580   }
581 }
582
583 /*!
584   \brief Process study activation.
585   
586   Called when study desktop is activated. Used for notifying the Python
587   module about changing of the active study.
588 */
589 void SALOME_PYQT_ModuleLight::studyActivated()
590 {
591   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::studyActivated()" );
592
593   // StudyChangedReq: request class for internal studyChanged() operation
594   class StudyChangedReq : public PyInterp_Request
595   {
596   public:
597     StudyChangedReq( SUIT_Study*         _study,
598                      SALOME_PYQT_ModuleLight* _obj )
599       : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
600         myStudy ( _study ),
601         myObj   ( _obj   ) {}
602
603   protected:
604     virtual void execute()
605     {
606       myObj->studyChanged( myStudy );
607     }
608
609   private:
610     SUIT_Study*         myStudy;
611     SALOME_PYQT_ModuleLight* myObj;
612   };
613
614   // post request
615   PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( application()->activeStudy(), this ) );
616 }
617
618 /*!
619   \brief Process GUI action (from main menu, toolbar or 
620   context popup menu action).
621 */
622 void SALOME_PYQT_ModuleLight::onGUIEvent()
623 {
624   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::onGUIEvent()" );
625
626   // get sender action
627   QAction* action = qobject_cast<QAction*>( sender() );
628   if ( !action )
629     return;
630
631   // get action ID
632   int id = actionId( action );
633   fmsg.message( QString( "action id = %1" ).arg( id ) );
634
635   // perform synchronous request to Python event dispatcher
636   class GUIEvent : public PyInterp_LockRequest
637   {
638   public:
639     GUIEvent( PyInterp_Interp*    _py_interp,
640               SALOME_PYQT_ModuleLight* _obj,
641               int                 _id )
642       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
643         myId    ( _id  ),
644         myObj   ( _obj ) {}
645
646   protected:
647     virtual void execute()
648     {
649       myObj->guiEvent( myId );
650     }
651
652   private:
653     int                 myId;
654     SALOME_PYQT_ModuleLight* myObj;
655   };
656
657   // post request
658   PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
659 }
660
661 /*!
662   \brief Process context popup menu request.
663   
664   Called when user activates popup menu in some window
665   (view, object browser, etc).
666
667   \param theContext popup menu context (e.g. "ObjectBrowser")
668   \param thePopupMenu popup menu
669   \param title popup menu title (not used)
670 */
671 void SALOME_PYQT_ModuleLight::contextMenuPopup( const QString& theContext, 
672                                            QMenu*         thePopupMenu, 
673                                            QString&       /*title*/ )
674 {
675   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::contextMenuPopup()" );
676   fmsg.message( QString( "context: %1" ).arg( theContext ) );
677
678   // perform synchronous request to Python event dispatcher
679   class PopupMenuEvent : public PyInterp_LockRequest
680   {
681   public:
682     PopupMenuEvent( PyInterp_Interp*    _py_interp,
683                     SALOME_PYQT_ModuleLight* _obj,
684                     const QString&      _context,
685                     QMenu*        _popup )
686       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
687         myContext( _context ),
688         myPopup  ( _popup  ),
689         myObj    ( _obj )   {}
690
691   protected:
692     virtual void execute()
693     {
694       myObj->contextMenu( myContext, myPopup );
695     }
696
697   private:
698     SALOME_PYQT_ModuleLight* myObj;
699     QString             myContext;
700     QMenu*         myPopup;
701   };
702
703   // post request only if dispatcher is not busy!
704   // execute request synchronously
705   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
706     PyInterp_Dispatcher::Get()->Exec( new PopupMenuEvent( myInterp, this, theContext, thePopupMenu ) );
707 }
708
709 /*!
710   \brief Export preferences for the Python module.
711   
712   Called only once when the first instance of the module is created.
713 */
714 void SALOME_PYQT_ModuleLight::createPreferences()
715 {
716   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::createPreferences()" );
717
718   // perform synchronous request to Python event dispatcher
719   class Event : public PyInterp_LockRequest
720   {
721   public:
722     Event( PyInterp_Interp*    _py_interp,
723            SALOME_PYQT_ModuleLight* _obj )
724       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
725         myObj    ( _obj )   {}
726
727   protected:
728     virtual void execute()
729     {
730       myObj->initPreferences();
731     }
732
733   private:
734     SALOME_PYQT_ModuleLight* myObj;
735   };
736
737   // post request only if dispatcher is not busy!
738   // execute request synchronously
739   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
740     PyInterp_Dispatcher::Get()->Exec( new Event( myInterp, this ) );
741 }
742
743 /*!
744   \brief Define the dockable windows associated with the module.
745   
746   To fill the list of windows the correspondind Python module's windows()
747   method is called from SALOME_PYQT_ModuleLight::init() method.
748
749   By default, ObjectBrowser, PythonConsole and LogWindow windows are 
750   associated to the module.
751
752   Allowed dockable windows:
753   - LightApp_Application::WT_ObjectBrowser : object browser
754   - LightApp_Application::WT_PyConsole : python console
755   - LightApp_Application::WT_LogWindow : log messages output window
756
757   Dock area is defined by Qt::DockWidgetArea enumeration:
758   - Qt::TopDockWidgetArea : top dock area
759   - Qt::BottomDockWidgetArea : bottom dock area
760   - Qt::LeftDockWidgetArea : left dock area
761   - Qt::RightDockWidgetArea : right dock area
762
763   \param mappa map of dockable windows: { <window_type> : <dock_area> }
764 */
765 void SALOME_PYQT_ModuleLight::windows( QMap<int, int>& mappa ) const
766 {
767   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::windows()" );
768
769   mappa = myWindowsMap;
770 }
771
772 /*!
773   \brief Define the compatible view windows associated with the module.
774
775   The associated view windows are opened automatically when the module
776   is activated.
777
778   To fill the list of views the correspondind Python module's views()
779   method is called from SALOME_PYQT_ModuleLight::init() method.
780   By default, the list is empty.
781
782   \param listik list of view windows types
783 */
784 void SALOME_PYQT_ModuleLight::viewManagers( QStringList& lst ) const
785 {
786   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::viewManagers()" );
787
788   lst = myViewMgrList;
789 }
790
791 /*!
792   \brief Process module's preferences changing.
793
794   Called when the module's preferences are changed.
795   
796   \param section setting section
797   \param setting setting name
798 */
799 void SALOME_PYQT_ModuleLight::preferencesChanged( const QString& section, const QString& setting )
800 {
801   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::preferencesChanged()" );
802
803   // perform synchronous request to Python event dispatcher
804   class Event : public PyInterp_LockRequest
805   {
806   public:
807     Event( PyInterp_Interp*    _py_interp,
808            SALOME_PYQT_ModuleLight* _obj,
809            const QString&      _section,
810            const QString&      _setting )
811       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
812         myObj    ( _obj ),
813         mySection( _section ),
814         mySetting( _setting ) {}
815
816   protected:
817     virtual void execute()
818     {
819       myObj->prefChanged( mySection, mySetting );
820     }
821
822   private:
823     SALOME_PYQT_ModuleLight* myObj;
824     QString mySection, mySetting;
825   };
826
827   // post request only if dispatcher is not busy!
828   // execut request synchronously
829   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
830     PyInterp_Dispatcher::Get()->Exec( new Event( myInterp, this, section, setting ) );
831 }
832
833 /*!
834   \brief Internal module initialization:
835
836   Performs the following actions:
837   - initialize or get the Python interpreter (one per study)
838   - import the Python module
839   - pass the workspace widget to the Python module
840   - call Python module's initialize() method
841   - call Python module's windows() method
842   - call Python module's views() method
843
844   \param app parent application object
845 */
846 void SALOME_PYQT_ModuleLight::init( CAM_Application* app )
847 {
848   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::init()" );
849
850   // reset interpreter to NULL
851   myInterp = NULL;
852
853   // get study Id
854   LightApp_Application* anApp = dynamic_cast<LightApp_Application*>( app );
855   if ( !anApp )
856     return;
857   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
858   if ( !aStudy )
859     return;
860   int aStudyId = aStudy ? aStudy->id() : 0;
861
862   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
863   initInterp( aStudyId );
864   if ( !myInterp )
865     return; // Error
866
867   // import Python GUI module
868   importModule();
869   if ( !myModule )
870     return; // Error
871
872   // this module is being activated now!
873   myInitModule = this;
874
875   // then call Python module's initialize() method
876   // ... first get python lock
877   PyLockWrapper aLock = myInterp->GetLockWrapper();
878   // ... (the Python module is already imported)
879   // ... finally call Python module's initialize() method
880   if ( PyObject_HasAttrString( myModule, (char*)"initialize" ) ) {
881     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"initialize", (char*)"" ) );
882     if ( !res ) {
883       PyErr_Print();
884     }
885   }
886
887   // get required dockable windows list from the Python module 
888   // by calling windows() method
889   // ... first put default values
890   myWindowsMap.insert( LightApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
891   myWindowsMap.insert( LightApp_Application::WT_PyConsole,     Qt::BottomDockWidgetArea );
892   myWindowsMap.insert( LightApp_Application::WT_LogWindow,     Qt::BottomDockWidgetArea );
893
894   if ( PyObject_HasAttrString( myModule , (char*)"windows" ) ) {
895     PyObjWrapper res1( PyObject_CallMethod( myModule, (char*)"windows", (char*)"" ) );
896     if ( !res1 ) {
897       PyErr_Print();
898     }
899     else {
900       myWindowsMap.clear();
901       if ( PyDict_Check( res1 ) ) {
902         PyObject* key;
903         PyObject* value;
904         Py_ssize_t pos = 0;
905         while ( PyDict_Next( res1, &pos, &key, &value ) ) {
906           // parse the return value
907           // it should be a map: {integer:integer}
908           int aKey, aValue;
909           if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
910             aKey   = PyInt_AsLong( key );
911             aValue = PyInt_AsLong( value );
912             myWindowsMap[ aKey ] = aValue;
913           }
914         }
915       }
916     }
917   }
918
919   // get compatible view windows types from the Python module 
920   // by calling views() method
921   if ( PyObject_HasAttrString( myModule , (char*)"views" ) ) {
922     PyObjWrapper res2( PyObject_CallMethod( myModule, (char*)"views", (char*)"" ) );
923     if ( !res2 ) {
924       PyErr_Print();
925     }
926     else {
927       // parse the return value
928       // result can be one string...
929       if ( PyString_Check( res2 ) ) {
930         myViewMgrList.append( PyString_AsString( res2 ) );
931       }
932       // ... or list of strings
933       else if ( PyList_Check( res2 ) ) {
934         int size = PyList_Size( res2 );
935         for ( int i = 0; i < size; i++ ) {
936           PyObject* value = PyList_GetItem( res2, i );
937           if( value && PyString_Check( value ) ) {
938             myViewMgrList.append( PyString_AsString( value ) );
939           }
940         }
941       }
942     }
943   }
944   // module is already activated!
945   myInitModule = 0;
946 }
947
948 /*!
949   \brief Internal activation:
950
951   Performs the following actions:
952   - initialize or get the Python interpreter (one per study)
953   - import the Python GUI module
954   - call Python module's activate() method
955
956   \param theStudy parent study object
957 */
958 void SALOME_PYQT_ModuleLight::activate( SUIT_Study* theStudy )
959 {
960   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::activate()" );
961
962   // get study Id
963   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
964   int aStudyId = aStudy ? aStudy->id() : 0;
965
966   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
967   initInterp( aStudyId );
968   if ( !myInterp ) {
969     myLastActivateStatus = false;
970     return; // Error
971   }
972
973   // import Python GUI module
974   importModule();
975   if ( !myModule ) {
976     myLastActivateStatus = false;
977     return; // Error
978   }
979
980   // get python lock
981   PyLockWrapper aLock = myInterp->GetLockWrapper();
982
983   // call Python module's activate() method (for the new modules)
984   if ( PyObject_HasAttrString( myModule , (char*)"activate" ) ) {
985     PyObject* res1 = PyObject_CallMethod( myModule, (char*)"activate", (char*)"" );
986     if ( !res1 || !PyBool_Check( res1 ) ) {
987       PyErr_Print();
988       // always true for old modules (no return value)
989       myLastActivateStatus = true;
990     }
991     else {
992       // detect return status
993       myLastActivateStatus = PyObject_IsTrue( res1 );
994     }
995   } 
996   
997   // Connect the SUIT_Desktop signal windowActivated() to this->onActiveViewChanged()
998   SUIT_Desktop* aDesk = theStudy->application()->desktop();
999   if ( aDesk )
1000   {
1001     connect( aDesk, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
1002              this,  SLOT( onActiveViewChanged( SUIT_ViewWindow* ) ) );
1003     // If a active window exists send activeViewChanged
1004     // If a getActiveView() in SalomePyQt available we no longer need this 
1005     SUIT_ViewWindow* aView = aDesk->activeWindow();
1006     if ( aView ) 
1007       activeViewChanged( aView );
1008     
1009     // get all view currently opened in the study and connect their signals  to 
1010     // the corresponding slots of the class.
1011     QList<SUIT_ViewWindow*> wndList = aDesk->windows();
1012     SUIT_ViewWindow* wnd;
1013     foreach ( wnd, wndList )
1014       connectView( wnd );
1015   }
1016 }
1017
1018 /*!
1019   \brief Additional customization after module is activated:
1020
1021   Performs the following actions:
1022   - get the Python interpreter (one per study)
1023   - import the Python GUI module
1024   - call Python module's setSettings() method (obsolete function, 
1025   used for compatibility with old code)
1026
1027   \param theStudy parent study object
1028 */
1029 void SALOME_PYQT_ModuleLight::customize( SUIT_Study* theStudy )
1030 {
1031   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::customize()" );
1032
1033   // get study Id
1034   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
1035   int aStudyId = aStudy ? aStudy->id() : 0;
1036
1037   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1038   initInterp( aStudyId );
1039   if ( !myInterp )
1040     return; // Error
1041
1042   // import Python GUI module
1043   importModule();
1044   if ( !myModule )
1045     return; // Error
1046
1047   if ( IsCallOldMethods ) {
1048     // call Python module's setWorkspace() method
1049     setWorkSpace();
1050   }
1051
1052   // get python lock
1053   PyLockWrapper aLock = myInterp->GetLockWrapper();
1054
1055   if ( IsCallOldMethods ) {
1056     // call Python module's setSettings() method (obsolete)
1057     if ( PyObject_HasAttrString( myModule , (char*)"setSettings" ) ) {
1058       PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"setSettings", (char*)"" ) );
1059       if( !res ) {
1060         PyErr_Print();
1061       }
1062     }
1063   }
1064 }
1065
1066 /*!
1067   \brief Internal deactivation:
1068
1069   Performs the following actions:
1070   - call Python module's deactivate() method
1071
1072   \param theStudy parent study object
1073 */
1074 void SALOME_PYQT_ModuleLight::deactivate( SUIT_Study* theStudy )
1075 {
1076   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::deactivate()" );
1077
1078   // check if the subinterpreter is initialized and Python module is imported
1079   if ( !myInterp || !myModule ) {
1080     // Error! Python subinterpreter should be initialized and module should be imported first!
1081     return;
1082   }
1083   // then call Python module's deactivate() method
1084   if ( PyObject_HasAttrString( myModule , (char*)"deactivate" ) ) {
1085     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"deactivate", (char*)"" ) );
1086     if( !res ) {
1087       PyErr_Print();
1088     }
1089   }
1090   
1091   // Disconnect the SUIT_Desktop signal windowActivated()
1092   SUIT_Desktop* aDesk = theStudy->application()->desktop();
1093   if ( aDesk )
1094   {
1095     disconnect( aDesk, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
1096                 this,  SLOT( onActiveViewChanged( SUIT_ViewWindow* ) ) );      
1097   }
1098 }
1099
1100 /*!
1101   \brief Perform internal actions when active study is changed.
1102
1103   Called when active the study is actived (user brings its 
1104   desktop to top):
1105   - initialize or get the Python interpreter (one per study)
1106   - import the Python GUI module
1107   - call Python module's activeStudyChanged() method
1108
1109   \param theStudy study being activated
1110 */
1111 void SALOME_PYQT_ModuleLight::studyChanged( SUIT_Study* theStudy )
1112 {
1113   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::studyChanged()" );
1114
1115   // get study Id
1116   LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
1117   int aStudyId = aStudy ? aStudy->id() : 0;
1118
1119   // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1120   initInterp( aStudyId );
1121   if ( !myInterp )
1122     return; // Error
1123
1124   // import Python GUI module
1125   importModule();
1126   if ( !myModule )
1127     return; // Error
1128
1129   if ( IsCallOldMethods ) {
1130     // call Python module's setWorkspace() method
1131     setWorkSpace();
1132   }
1133
1134   // get python lock
1135   PyLockWrapper aLock = myInterp->GetLockWrapper();
1136
1137   // call Python module's activeStudyChanged() method
1138   if ( PyObject_HasAttrString( myModule, (char*)"activeStudyChanged" ) ) {
1139     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"activeStudyChanged", (char*)"i", aStudyId ) );
1140     if( !res ) {
1141       PyErr_Print();
1142     }
1143   }
1144 }
1145
1146 /*!
1147   \brief Process (internally) context popup menu request.
1148
1149   Performs the following actions:
1150   - calls Python module's definePopup(...) method (obsolete function, 
1151   used for compatibility with old code) to define the popup menu context
1152   - parses XML resourses file (if exists) and fills the popup menu with the items)
1153   - calls Python module's customPopup(...) method (obsolete function, 
1154   used for compatibility with old code) to allow module to customize the popup menu
1155   - for new modules calls createPopupMenu() function to allow the 
1156   modules to build the popup menu by using insertItem(...) Qt functions.
1157
1158   \param theContext popup menu context
1159   \param thePopupMenu popup menu
1160 */
1161 void SALOME_PYQT_ModuleLight::contextMenu( const QString& theContext, QMenu* thePopupMenu )
1162 {
1163   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::contextMenu()" );
1164
1165   // Python interpreter should be initialized and Python module should be
1166   // import first
1167   if ( !myInterp || !myModule )
1168     return;
1169
1170   QString aContext( "" ), aObject( "" ), aParent( theContext );
1171
1172   if ( IsCallOldMethods && PyObject_HasAttrString( myModule, (char*)"definePopup" ) ) {
1173     // call definePopup() Python module's function
1174     // this is obsolete function, used only for compatibility reasons
1175     PyObjWrapper res( PyObject_CallMethod( myModule,
1176                                            (char*)"definePopup",
1177                                            (char*)"sss",
1178                                            theContext.toLatin1().constData(),
1179                                            aObject.toLatin1().constData(),
1180                                            aParent.toLatin1().constData() ) );
1181     if( !res ) {
1182       PyErr_Print();
1183     }
1184     else {
1185       // parse return value
1186       char *co, *ob, *pa;
1187       if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
1188         aContext = co;
1189         aObject  = ob;
1190         aParent  = pa;
1191       }
1192     }
1193   } // if ( IsCallOldMethods ... )
1194
1195   // first try to create menu via XML parser:
1196   // we create popup menus without help of QtxPopupMgr
1197   if ( myXmlHandler )
1198     myXmlHandler->createPopup( thePopupMenu, aContext, aParent, aObject );
1199
1200 #if SIP_VERSION < 0x040800
1201   PyObjWrapper sipPopup( sipBuildResult( 0, "M", thePopupMenu, sipClass_QMenu) );
1202 #else
1203   PyObjWrapper sipPopup( sipBuildResult( 0, "D", thePopupMenu, sipType_QMenu, NULL) );
1204 #endif
1205
1206   // then call Python module's createPopupMenu() method (for new modules)
1207   if ( PyObject_HasAttrString( myModule, (char*)"createPopupMenu" ) ) {
1208     PyObjWrapper res1( PyObject_CallMethod( myModule,
1209                                             (char*)"createPopupMenu",
1210                                             (char*)"Os",
1211                                             sipPopup.get(),
1212                                             theContext.toLatin1().constData() ) );
1213     if( !res1 ) {
1214       PyErr_Print();
1215     }
1216   }
1217
1218   if ( IsCallOldMethods && PyObject_HasAttrString( myModule, (char*)"customPopup" ) ) {
1219     // call customPopup() Python module's function
1220     // this is obsolete function, used only for compatibility reasons
1221     PyObjWrapper res2( PyObject_CallMethod( myModule,
1222                                             (char*)"customPopup",
1223                                             (char*)"Osss",
1224                                             sipPopup.get(),
1225                                             aContext.toLatin1().constData(),
1226                                             aObject.toLatin1().constData(),
1227                                             aParent.toLatin1().constData() ) );
1228     if( !res2 ) {
1229       PyErr_Print();
1230     }
1231   }
1232 }
1233
1234 /*!
1235   \brief Internal GUI event handling.
1236
1237   Performs the following actions:
1238   - calls Python module's OnGUIEvent() method
1239
1240   \param theId GUI action ID
1241 */
1242 void SALOME_PYQT_ModuleLight::guiEvent( const int theId )
1243 {
1244   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::guiEvent()" );
1245
1246   // Python interpreter should be initialized and Python module should be
1247   // import first
1248   if ( !myInterp || !myModule )
1249     return;
1250
1251   if ( PyObject_HasAttrString( myModule, (char*)"OnGUIEvent" ) ) {
1252     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"OnGUIEvent", (char*)"i", theId ) );
1253     if( !res ) {
1254       PyErr_Print();
1255     }
1256   }
1257 }
1258
1259 /*!
1260   \brief Initialize (internally) preferences for the module.
1261
1262   Performs the following actions:
1263   - calls Python module's createPreferences() method
1264 */
1265 void SALOME_PYQT_ModuleLight::initPreferences()
1266 {
1267   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::initPreferences()" );
1268
1269   // Python interpreter should be initialized and Python module should be
1270   // import first
1271   if ( !myInterp || !myModule )
1272     return;
1273
1274   // temporary set myInitModule because createPreferences() method
1275   // might be called during the module intialization process
1276   myInitModule = this;
1277
1278   if ( PyObject_HasAttrString( myModule, (char*)"createPreferences" ) ) {
1279     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"createPreferences", (char*)"" ) );
1280     if( !res ) {
1281       PyErr_Print();
1282     }
1283   }
1284
1285   myInitModule = 0;
1286 }
1287
1288 /*!
1289   \brief Initialize python subinterpreter (one per study).
1290   \param theStudyId study ID
1291 */
1292 void SALOME_PYQT_ModuleLight::initInterp( int theStudyId )
1293 {
1294   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::initInterp()" );
1295
1296   // check study Id
1297   if ( !theStudyId ) {
1298     // Error! Study Id must not be 0!
1299     myInterp = NULL;
1300     return;
1301   }
1302   // try to find the subinterpreter
1303   if( myInterpMap.contains( theStudyId ) ) {
1304     // found!
1305     myInterp = myInterpMap[ theStudyId ];
1306     return;
1307   }
1308
1309   myInterp = new SALOME_PYQT_PyInterp();
1310   if(!myInterp)
1311     return;
1312   
1313   myInterp->initialize();
1314   myInterpMap[ theStudyId ] = myInterp;
1315   
1316 #ifndef GUI_DISABLE_CORBA
1317   if(!SUIT_PYTHON::initialized) {
1318     // import 'salome' module and call 'salome_init' method;
1319     // do it only once on interpreter creation
1320     // ... first get python lock
1321     PyLockWrapper aLock = myInterp->GetLockWrapper();
1322     // ... then import a module
1323     PyObjWrapper aMod = PyImport_ImportModule( "salome" );
1324     if( !aMod ) {
1325       // Error!
1326       PyErr_Print();
1327       return;
1328     }
1329     // ... then call a method
1330     int embedded = 1;
1331     PyObjWrapper aRes( PyObject_CallMethod( aMod, (char*)"salome_init", (char*)"ii", theStudyId, embedded ) );
1332     if( !aRes ) {
1333       // Error!
1334       PyErr_Print();
1335       return;
1336     }
1337   }
1338 #endif 
1339 }
1340
1341 /*!
1342   \brief Import Python GUI module and remember the reference to the module.
1343
1344   Attention! initInterp() should be called first!!!
1345 */
1346 void SALOME_PYQT_ModuleLight::importModule()
1347 {
1348   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::importModule()" );
1349
1350   // check if the subinterpreter is initialized
1351   if ( !myInterp ) {
1352     // Error! Python subinterpreter should be initialized first!
1353     myModule = 0;
1354     return;
1355   }
1356   // import Python GUI module and puts it in <myModule> attribute
1357   // ... first get python lock
1358   PyLockWrapper aLock = myInterp->GetLockWrapper();
1359   // ... then import a module
1360   QString aMod = name() + "GUI";
1361   try {
1362     myModule = PyImport_ImportModule( aMod.toLatin1().data() );
1363   }
1364   catch (...) {
1365   }
1366   if( !myModule ) {
1367     // Error!
1368     PyErr_Print();
1369     return;
1370   }
1371 }
1372
1373 /*!
1374   \brief Set study workspace to the Python module.
1375
1376   Calls setWorkSpace() method of the Pythohn module with 
1377   PyQt QWidget object to use with interpreter.
1378
1379   Attention! initInterp() and importModule() should be called first!!!
1380 */
1381 void SALOME_PYQT_ModuleLight::setWorkSpace()
1382 {
1383   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::setWorkSpace()" );
1384
1385   // check if the subinterpreter is initialized and Python module is imported
1386   if ( !myInterp || !myModule ) {
1387     // Error! Python subinterpreter should be initialized and module should be imported first!
1388     return;
1389   }
1390
1391   // call setWorkspace() method
1392   // ... first get python lock
1393   PyLockWrapper aLock = myInterp->GetLockWrapper();
1394
1395   // ... then try to import SalomePyQt module. If it's not possible don't go on.
1396   PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
1397   if( !aQtModule ) {
1398     // Error!
1399     PyErr_Print();
1400     return;
1401   }
1402
1403   if ( IsCallOldMethods ) {
1404     // ... then get workspace object
1405     QWidget* aWorkspace = 0;
1406     if ( getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
1407       STD_MDIDesktop* aDesktop = dynamic_cast<STD_MDIDesktop*>( getApp()->desktop() );
1408       if ( aDesktop )
1409         aWorkspace = aDesktop->workspace();
1410     }
1411     else if ( getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
1412       STD_TabDesktop* aDesktop = dynamic_cast<STD_TabDesktop*>( getApp()->desktop() );
1413       if ( aDesktop )
1414         aWorkspace = aDesktop->workstack();
1415     }
1416 #if SIP_VERSION < 0x040800
1417     PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget) );
1418 #else
1419     PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
1420 #endif
1421     // ... and finally call Python module's setWorkspace() method (obsolete)
1422     if ( PyObject_HasAttrString( myModule, (char*)"setWorkSpace" ) ) {
1423       PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
1424       if( !res ) {
1425         PyErr_Print();
1426       }
1427     }
1428   }
1429 }
1430
1431 /*!
1432   \brief Preference changing callback function (internal).
1433
1434   Performs the following actions:
1435   - call Python module's preferenceChanged() method
1436
1437   \param section setting section name
1438   \param setting setting name
1439 */
1440 void SALOME_PYQT_ModuleLight::prefChanged( const QString& section, const QString& setting )
1441 {
1442   FuncMsg fmsg( "SALOME_PYQT_ModuleLight::prefChanged()" );
1443
1444   // Python interpreter should be initialized and Python module should be
1445   // import first
1446   if ( !myInterp || !myModule )
1447     return;
1448
1449   if ( PyObject_HasAttrString( myModule, (char*)"preferenceChanged" ) ) {
1450     PyObjWrapper res( PyObject_CallMethod( myModule,
1451                                            (char*)"preferenceChanged", 
1452                                            (char*)"ss", 
1453                                            section.toLatin1().constData(), 
1454                                            setting.toLatin1().constData() ) );
1455     if( !res ) {
1456       PyErr_Print();
1457     }
1458   }
1459 }
1460
1461 /*!
1462   \brief Get default menu group identifier
1463   \return menu group ID (40 by default)
1464 */
1465 int SALOME_PYQT_ModuleLight::defaultMenuGroup()
1466 {
1467   return DEFAULT_GROUP; 
1468 }
1469
1470 //
1471 // The next methods call the parent implementation.
1472 // This is done to open protected methods from CAM_Module class.
1473 //
1474
1475 /*!
1476   \brief Create toolbar with specified \a name.
1477   \param name toolbar name
1478   \return toolbar ID or -1 if toolbar creation is failed
1479 */
1480 int SALOME_PYQT_ModuleLight::createTool( const QString& name )
1481 {
1482   return LightApp_Module::createTool( name );
1483 }
1484
1485 /*! 
1486   \brief Insert action with specified \a id to the toolbar.
1487   \param id action ID
1488   \param tBar toolbar ID
1489   \param idx required index in the toolbar
1490   \return action ID or -1 if action could not be added
1491 */
1492 int SALOME_PYQT_ModuleLight::createTool( const int id, const int tBar, const int idx )
1493 {
1494   return LightApp_Module::createTool( id, tBar, idx );
1495 }
1496
1497 /*!
1498   \brief Insert action with specified \a id to the toolbar.
1499   \param id action ID
1500   \param tBar toolbar name
1501   \param idx required index in the toolbar
1502   \return action ID or -1 if action could not be added
1503 */
1504 int SALOME_PYQT_ModuleLight::createTool( const int id, const QString& tBar, const int idx )
1505 {
1506   return LightApp_Module::createTool( id, tBar, idx );
1507 }
1508
1509 /*!
1510   \brief Insert action to the toolbar.
1511   \param a action
1512   \param tBar toolbar ID
1513   \param id required action ID
1514   \param idx required index in the toolbar
1515   \return action ID or -1 if action could not be added
1516 */
1517 int SALOME_PYQT_ModuleLight::createTool( QAction* a, const int tBar, const int id, const int idx )
1518 {
1519   return LightApp_Module::createTool( a, tBar, id, idx );
1520 }
1521
1522 /*!
1523   \brief Insert action to the toolbar.
1524   \param a action
1525   \param tBar toolbar name
1526   \param id required action ID
1527   \param idx required index in the toolbar
1528   \return action ID or -1 if action could not be added
1529 */
1530 int SALOME_PYQT_ModuleLight::createTool( QAction* a, const QString& tBar, const int id, const int idx )
1531 {
1532   return LightApp_Module::createTool( a, tBar, id, idx );
1533 }
1534
1535 /*!
1536   \brief Create main menu.
1537   \param subMenu menu name
1538   \param menu parent menu ID
1539   \param id required menu ID
1540   \param group menu group ID
1541   \param idx required index in the menu
1542   \return menu ID or -1 if menu could not be added
1543 */
1544 int SALOME_PYQT_ModuleLight::createMenu( const QString& subMenu, const int menu, const int id, const int group, const int idx )
1545 {
1546   return LightApp_Module::createMenu( subMenu, menu, id, group, idx );
1547 }
1548
1549 /*!
1550   \brief Create main menu.
1551   \param subMenu menu name
1552   \param menu parent menu name (list of menu names separated by "|")
1553   \param id required menu ID
1554   \param group menu group ID
1555   \param idx required index in the menu
1556   \return menu ID or -1 if menu could not be added
1557 */
1558 int SALOME_PYQT_ModuleLight::createMenu( const QString& subMenu, const QString& menu, const int id, const int group, const int idx )
1559 {
1560   return LightApp_Module::createMenu( subMenu, menu, id, group, idx );
1561 }
1562
1563 /*!
1564   \brief Insert action to the main menu.
1565   \param id action ID
1566   \param menu parent menu ID
1567   \param group menu group ID
1568   \param idx required index in the menu
1569   \return action ID or -1 if action could not be added
1570 */
1571 int SALOME_PYQT_ModuleLight::createMenu( const int id, const int menu, const int group, const int idx )
1572 {
1573   return LightApp_Module::createMenu( id, menu, group, idx );
1574 }
1575
1576 /*!
1577   \brief Insert action to the main menu.
1578   \param id action ID
1579   \param menu parent menu name (list of menu names separated by "|")
1580   \param group menu group ID
1581   \param idx required index in the menu
1582   \return action ID or -1 if action could not be added
1583 */
1584 int SALOME_PYQT_ModuleLight::createMenu( const int id, const QString& menu, const int group, const int idx )
1585 {
1586   return LightApp_Module::createMenu( id, menu, group, idx );
1587 }
1588
1589 /*!
1590   \brief Insert action to the main menu.
1591   \param a action
1592   \param menu parent menu ID
1593   \param group menu group ID
1594   \param idx required index in the menu
1595   \return action ID or -1 if action could not be added
1596 */
1597 int SALOME_PYQT_ModuleLight::createMenu( QAction* a, const int menu, const int id, const int group, const int idx )
1598 {
1599   return LightApp_Module::createMenu( a, menu, id, group, idx );
1600 }
1601
1602 /*!
1603   \brief Insert action to the main menu.
1604   \param a action
1605   \param menu parent menu name (list of menu names separated by "|")
1606   \param group menu group ID
1607   \param idx required index in the menu
1608   \return action ID or -1 if action could not be added
1609 */
1610 int SALOME_PYQT_ModuleLight::createMenu( QAction* a, const QString& menu, const int id, const int group, const int idx )
1611 {
1612   return LightApp_Module::createMenu( a, menu, id, group, idx );
1613 }
1614
1615 /*!
1616   \brief Create separator action which can be used in the menu or toolbar.
1617   \return new separator action
1618 */
1619 QAction* SALOME_PYQT_ModuleLight::separator()
1620 {
1621   return LightApp_Module::separator();
1622 }
1623
1624 /*!
1625   \brief Get action by specified \a id.
1626   \return action or 0 if it is not found
1627 */
1628 QAction* SALOME_PYQT_ModuleLight::action( const int id ) const
1629 {
1630   QAction* a = LightApp_Module::action( id );
1631   if ( !a ) {
1632     // try menu
1633     QMenu* m = menuMgr()->findMenu( id );
1634     if ( m ) a = m->menuAction();
1635   }
1636   return a;
1637 }
1638
1639 /*!
1640   \brief Get action identifier.
1641   \return action ID or -1 if action is not registered
1642 */
1643 int SALOME_PYQT_ModuleLight::actionId( const QAction* a ) const
1644 {
1645   return LightApp_Module::actionId( a );
1646 }
1647
1648 /*!
1649   \brief Create new action.
1650   
1651   If the action with specified identifier already registered
1652   it is not created, but its attributes are only modified.
1653
1654   \param id action ID
1655   \param text tooltip text
1656   \param icon icon
1657   \param menu menu text
1658   \param tip status tip
1659   \param key keyboard shortcut
1660   \param toggle \c true for checkable action
1661   \return created action
1662 */
1663 QAction* SALOME_PYQT_ModuleLight::createAction( const int id, const QString& text, const QString& icon,
1664                                            const QString& menu, const QString& tip, const int key,
1665                                            const bool toggle, QObject* parent )
1666 {
1667   QIcon anIcon = loadIcon( icon );
1668   QAction* a = action( id );
1669   if ( a ) {
1670     if ( a->toolTip().isEmpty()   && !text.isEmpty() )  a->setToolTip( text );
1671     if ( a->text().isEmpty()      && !menu.isEmpty() )  a->setText( menu );
1672     if ( a->icon().isNull()       && !anIcon.isNull() ) a->setIcon( anIcon );
1673     if ( a->statusTip().isEmpty() && !tip.isEmpty() )   a->setStatusTip( tip );
1674     if ( a->shortcut().isEmpty()  && key )              a->setShortcut( key );
1675     if ( a->isCheckable() != toggle )                   a->setCheckable( toggle );
1676     disconnect( a, SIGNAL( triggered( bool ) ), this, SLOT( onGUIEvent() ) );
1677     connect(    a, SIGNAL( triggered( bool ) ), this, SLOT( onGUIEvent() ) );
1678   }
1679   else {
1680     a = LightApp_Module::createAction( id, 
1681                                         text, 
1682                                         anIcon, 
1683                                         menu, 
1684                                         tip, 
1685                                         key, 
1686                                         parent ? parent : this, 
1687                                         toggle, 
1688                                         this, 
1689                                         SLOT( onGUIEvent() ) );
1690   }
1691   return a;
1692 }
1693
1694 /*!
1695   \brief Create new action group.
1696   
1697   If the action with specified identifier already registered
1698   it is not created, but its attributes are only modified.
1699
1700   \param id action ID
1701   \param text tooltip text
1702   \param icon icon
1703   \param menu menu text
1704   \param tip status tip
1705   \param key keyboard shortcut
1706   \param toggle \c true for checkable action
1707   \return created action
1708 */
1709 QtxActionGroup* SALOME_PYQT_ModuleLight::createActionGroup(const int id, const bool exclusive)
1710 {
1711   QtxActionGroup* a = qobject_cast<QtxActionGroup*>( action( id ) );
1712   if ( !a ) {
1713     a = new QtxActionGroup( this );
1714     LightApp_Module::registerAction( id, a );
1715   }
1716   a->setExclusive( exclusive );
1717   return a;
1718 }
1719
1720 /*! 
1721   \brief Load icon from resource file.
1722   \param fileName icon file name
1723   \return icon (null icon if loading failed)
1724 */
1725 QIcon SALOME_PYQT_ModuleLight::loadIcon( const QString& fileName )
1726 {
1727   QIcon anIcon;
1728   if ( !fileName.isEmpty() ) {
1729     QPixmap pixmap = getApp()->resourceMgr()->loadPixmap( name(), tr( fileName.toLatin1() ) );
1730     if ( !pixmap.isNull() )
1731       anIcon = QIcon( pixmap );
1732   }
1733   return anIcon;
1734 }
1735
1736 /*!
1737   \brief Add global application preference (for example, 
1738   application specific section).
1739   \param label preference name
1740   \return preference ID
1741 */
1742 int SALOME_PYQT_ModuleLight::addGlobalPreference( const QString& label )
1743 {
1744   LightApp_Preferences* pref = preferences();
1745   if ( !pref )
1746     return -1;
1747
1748   return pref->addPreference( label, -1 );
1749 }
1750
1751 /*!
1752   \brief Add preference.
1753   \param label preference name
1754   \return preference ID
1755 */
1756 int SALOME_PYQT_ModuleLight::addPreference( const QString& label )
1757 {
1758   return LightApp_Module::addPreference( label );
1759 }
1760                                        
1761 /*!
1762   \brief Add preference.
1763   \param label preference name
1764   \param pId parent preference ID
1765   \param type preference type
1766   \param section resource file section name
1767   \param param resource file setting name
1768   \return preference ID
1769 */
1770 int SALOME_PYQT_ModuleLight::addPreference( const QString& label, 
1771                                        const int pId, const int type,
1772                                        const QString& section,
1773                                        const QString& param )
1774 {
1775   return LightApp_Module::addPreference( label, pId, type, section, param );
1776 }
1777
1778 /*!
1779   \brief Get the preference property.
1780   \param id preference ID
1781   \param prop property name
1782   \return property value (invalid QVariant() if property is not found)
1783 */
1784 QVariant SALOME_PYQT_ModuleLight::preferenceProperty( const int id, 
1785                                                  const QString& prop ) const
1786 {
1787   QVariant v = LightApp_Module::preferenceProperty( id, prop );
1788   return v;
1789 }
1790
1791 /*!
1792   \brief Set the preference property.
1793   \param id preference ID
1794   \param prop property name
1795   \param var property value
1796 */
1797 void SALOME_PYQT_ModuleLight::setPreferenceProperty( const int id, 
1798                                                 const QString& prop, 
1799                                                 const QVariant& var )
1800 {
1801   LightApp_Module::setPreferenceProperty( id, prop, var );
1802 }
1803
1804
1805 /*!
1806   \brief Signal handler windowActivated(SUIT_ViewWindow*) of SUIT_Desktop
1807   \param pview view being activated
1808 */
1809 void SALOME_PYQT_ModuleLight::onActiveViewChanged( SUIT_ViewWindow* pview )
1810 {
1811   class ActiveViewChange : public PyInterp_LockRequest
1812   {
1813   public:
1814     ActiveViewChange( PyInterp_Interp* _py_interp, SALOME_PYQT_ModuleLight* _obj, const SUIT_ViewWindow* _pview )
1815       : PyInterp_LockRequest( _py_interp, 0, true ),
1816         myObj(_obj),myView(_pview) {}
1817
1818   protected:
1819     virtual void execute()
1820     {
1821       myObj->activeViewChanged( myView );
1822     }
1823
1824   private:
1825     SALOME_PYQT_ModuleLight* myObj;
1826     const SUIT_ViewWindow * myView;
1827   };
1828   
1829   PyInterp_Dispatcher::Get()->Exec( new ActiveViewChange( myInterp, this, pview ) ); 
1830 }
1831
1832 /*!
1833   \brief Processes the view changing, calls Python module's activeViewChanged() method 
1834   \param pview view being activated
1835 */
1836 void SALOME_PYQT_ModuleLight::activeViewChanged( const SUIT_ViewWindow* pview )
1837 {
1838   if ( !myInterp || !myModule ) 
1839     return;
1840   
1841   // Do not use SUIT_ViewWindow::closing() signal here. View manager reacts on 
1842   // this signal and deletes view. So our slot does not works if it is connected 
1843   // on this signal. SUIT_ViewManager::deleteView(SUIT_ViewWindow*) is used here
1844   
1845   connectView( pview );
1846
1847   if ( PyObject_HasAttrString( myModule, (char*)"activeViewChanged" ) ) 
1848   {
1849     if ( !pview ) 
1850       return;   
1851
1852     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"activeViewChanged", (char*)"i" , pview->getId() ) );
1853     if( !res )
1854       PyErr_Print();
1855   }
1856 }
1857
1858 /*!
1859   \brief Signal handler cloneView() of OCCViewer_ViewWindow
1860   \param pview view being cloned
1861 */
1862 void SALOME_PYQT_ModuleLight::onViewCloned( SUIT_ViewWindow* pview )
1863 {
1864   class ViewClone : public PyInterp_LockRequest
1865   {
1866   public:
1867     ViewClone( PyInterp_Interp* _py_interp, SALOME_PYQT_ModuleLight* _obj, const SUIT_ViewWindow* _pview )
1868       : PyInterp_LockRequest( _py_interp, 0, true ),
1869         myObj(_obj), myView(_pview) {}
1870
1871   protected:
1872     virtual void execute()
1873     {
1874       myObj->viewCloned( myView );
1875     }
1876
1877   private:
1878     SALOME_PYQT_ModuleLight* myObj;    
1879     const SUIT_ViewWindow* myView;
1880   };
1881   
1882   PyInterp_Dispatcher::Get()->Exec( new ViewClone( myInterp, this, pview ) );
1883 }
1884
1885 /*!
1886   \brief Processes the view cloning, calls Python module's activeViewCloned() method
1887   \param pview view being cloned
1888 */
1889 void SALOME_PYQT_ModuleLight::viewCloned( const SUIT_ViewWindow* pview )
1890 {
1891   if ( !myInterp || !myModule || !pview ) 
1892     return;  
1893
1894   if ( PyObject_HasAttrString( myModule, (char*)"viewCloned" ) ) 
1895   {
1896     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"viewCloned", (char*)"i", pview->getId() ) );
1897     if( !res )
1898       PyErr_Print();
1899   }
1900 }
1901
1902 /*!
1903   \brief Signal handler tryClose(SUIT_ViewWindow*) of a view
1904   \param pview view being closed
1905 */
1906 void SALOME_PYQT_ModuleLight::onViewTryClose( SUIT_ViewWindow* pview )
1907 {
1908   class ViewTryClose : public PyInterp_LockRequest
1909   {
1910   public:
1911     ViewTryClose( PyInterp_Interp* _py_interp, SALOME_PYQT_ModuleLight* _obj, const SUIT_ViewWindow* _pview )
1912       : PyInterp_LockRequest( _py_interp, 0, true ),
1913         myObj(_obj),myView(_pview) {}
1914
1915   protected:
1916     virtual void execute()
1917     {
1918       myObj->viewTryClose( myView );
1919     }
1920
1921   private:
1922     SALOME_PYQT_ModuleLight* myObj;
1923     const SUIT_ViewWindow * myView;    
1924   };
1925
1926   PyInterp_Dispatcher::Get()->Exec( new ViewTryClose( myInterp, this, pview ) );
1927 }
1928
1929 /*!
1930   \brief Processes the view closing attempt, calls Python module's viewTryClose() method
1931   \param pview view user tries to close
1932 */
1933 void SALOME_PYQT_ModuleLight::viewTryClose( const SUIT_ViewWindow* pview )
1934 {
1935   if ( !myInterp || !myModule ) 
1936     return;  
1937
1938   if ( PyObject_HasAttrString( myModule, (char*)"viewTryClose" ) ) 
1939   {
1940     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"viewTryClose", (char*)"i", pview->getId() ) );
1941     if ( !res )
1942     {
1943       PyErr_Print();
1944     }
1945   }
1946 }
1947
1948 /*!
1949   \brief Signal handler closing(SUIT_ViewWindow*) of a view
1950   \param pview view being closed
1951 */
1952 void SALOME_PYQT_ModuleLight::onViewClosed( SUIT_ViewWindow* pview )
1953 {
1954   class ViewClose : public PyInterp_LockRequest
1955   {
1956   public:
1957     ViewClose( PyInterp_Interp* _py_interp, SALOME_PYQT_ModuleLight* _obj, const SUIT_ViewWindow* _pview )
1958       : PyInterp_LockRequest( _py_interp, 0, true ),
1959         myObj(_obj),myView(_pview) {}
1960
1961   protected:
1962     virtual void execute()
1963     {
1964       myObj->viewClosed( myView );
1965     }
1966
1967   private:
1968     SALOME_PYQT_ModuleLight* myObj;
1969     const SUIT_ViewWindow * myView;    
1970   };
1971
1972   PyInterp_Dispatcher::Get()->Exec( new ViewClose( myInterp, this, pview ) );
1973 }
1974
1975 /*!
1976   \brief Processes the view closing, calls Python module's viewClosed() method
1977   \param pview view being closed
1978 */
1979 void SALOME_PYQT_ModuleLight::viewClosed( const SUIT_ViewWindow* pview )
1980 {
1981   if ( !myInterp || !myModule ) 
1982     return;  
1983
1984   if ( PyObject_HasAttrString( myModule, (char*)"viewClosed" ) ) 
1985   {
1986     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"viewClosed", (char*)"i", pview->getId() ) );
1987     if ( !res )
1988     {
1989       PyErr_Print();
1990     }
1991   }
1992 }
1993
1994 /*!
1995   \brief Connects or disconnects signals about activating and cloning view on the module slots
1996   \param pview view which is connected/disconnected
1997 */
1998 void SALOME_PYQT_ModuleLight::connectView( const SUIT_ViewWindow* pview )
1999 {
2000   SUIT_ViewManager* viewMgr = pview->getViewManager();
2001   SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
2002       
2003   if ( viewMgr )
2004   {
2005     disconnect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
2006                 this, SLOT( onViewTryClose( SUIT_ViewWindow* ) ) );
2007     disconnect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
2008                 this, SLOT( onViewClosed( SUIT_ViewWindow* ) ) );
2009   
2010     connect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
2011              this, SLOT( onViewTryClose( SUIT_ViewWindow* ) ) );
2012     connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
2013              this, SLOT( onViewClosed( SUIT_ViewWindow* ) ) );
2014   }
2015   
2016   // Connect cloneView() signal of an OCC View
2017   if ( pview->inherits( "OCCViewer_ViewWindow" ) )
2018   {
2019     disconnect( pview, SIGNAL( viewCloned( SUIT_ViewWindow* ) ), 
2020                 this, SLOT( onViewCloned( SUIT_ViewWindow* ) ) );
2021     connect( pview, SIGNAL( viewCloned( SUIT_ViewWindow* ) ), 
2022              this, SLOT( onViewCloned( SUIT_ViewWindow* ) ) );
2023   }
2024   // Connect cloneView() signal of Plot2d View manager
2025   else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) )
2026   {
2027     disconnect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ), 
2028                 this, SLOT( onViewCloned( SUIT_ViewWindow* ) ) );
2029     connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ), 
2030              this, SLOT( onViewCloned( SUIT_ViewWindow* ) ) );
2031   }
2032 }
2033
2034 /*!
2035   \brief Get tag name for the DOM element.
2036   \param element DOM element
2037   \return empty string if the element does not have tag name
2038   \internal
2039 */
2040 static QString tagName( const QDomElement& element )
2041 {
2042   return element.tagName().trimmed();
2043 }
2044
2045 /*!
2046   \brief Get DOM element's attribute by its name.
2047   \param element DOM element
2048   \param attName attribute name
2049   \return empty string if the element does not have such attribute
2050   \internal
2051 */
2052 static QString attribute( const QDomElement& element, const QString& attName )
2053 {
2054   return element.attribute( attName ).trimmed();
2055 }
2056
2057 /*!
2058   \brief Inspect specified string for the boolean value.
2059   
2060   This function returns \c true if string represents boolean value: 
2061   - "true", "yes" or "1" for \c true
2062   - "false", "no" or "0" for \c false
2063   Second parameter allows to specify what boolean value is expected:
2064   - 1: \c true
2065   - 0: \c false
2066   - other value is not taken into account (return represented value)
2067
2068   \param value inspected string
2069   \param check expected boolean value
2070   \return boolean value represented by the string (\a check is not 1 or 0)
2071           or \c true if value correspond to the specified \a check
2072 */
2073 static bool checkBool( const QString& value, const int check = -1 )
2074 {
2075   QString v = value.toLower();
2076   if ( ( v == "true"  || v == "yes" || v == "1" ) && ( check != 0 ) )
2077     return true;
2078   if ( ( v == "false" || v == "no"  || v == "0" ) && ( check == 0 ) )
2079     return true;
2080   return false;
2081 }
2082
2083 /*!
2084   \brief Inspect specified string for the integer value.
2085   
2086   This function returns returns -1 if item is empty or represents
2087   an invalid number.
2088   \param value inspected string
2089   \param def default value
2090   \param shift shift value (it is added to the integer value to produce shifted result)
2091 */
2092 static int checkInt( const QString& value, const int def = -1, const int shift = -1 )
2093 {
2094   bool bOk;
2095   int val = value.toInt( &bOk );
2096   if ( !bOk ) val = def;
2097   if ( shift > 0 && bOk && val < 0 )
2098     val += shift;
2099   return val;
2100 }
2101
2102 /*!
2103   \brief Constructor
2104   \internal
2105   \param module parent module pointer
2106   \param fileName XML file path
2107 */
2108 SALOME_PYQT_ModuleLight::XmlHandler::XmlHandler( SALOME_PYQT_ModuleLight* module, 
2109                                             const QString&      fileName )
2110 : myModule( module )
2111 {
2112   if ( fileName.isEmpty() ) 
2113     return;
2114   QFile aFile( fileName );
2115   if ( !aFile.open( QIODevice::ReadOnly ) )
2116     return;
2117   myDoc.setContent( &aFile );
2118   aFile.close();
2119 }
2120
2121 /*!
2122   \brief Parse XML file and create actions.
2123   \internal
2124   
2125   Called by SALOME_PYQT_ModuleLight::activate() in order to create actions
2126   (menus, toolbars).
2127 */
2128 void SALOME_PYQT_ModuleLight::XmlHandler::createActions()
2129 {
2130   // get document element
2131   QDomElement aDocElem = myDoc.documentElement();
2132
2133   // create main menu actions
2134   QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
2135   for ( int i = 0; i < aMenuList.count(); i++ ) {
2136     QDomNode n = aMenuList.item( i );
2137     createMenu( n );
2138   }
2139
2140   // create toolbars actions
2141   QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
2142   for ( int i = 0; i < aToolsList.count(); i++ ) {
2143     QDomNode n = aToolsList.item( i );
2144     createToolBar( n );
2145   }
2146 }
2147
2148 /*!
2149   \brief Create popup menu.
2150   \internal
2151   \param menu popup menu
2152   \param context popup menu context
2153   \param context popup menu parent object name
2154   \param context popup menu object name
2155 */
2156 void SALOME_PYQT_ModuleLight::XmlHandler::createPopup( QMenu*         menu,
2157                                                   const QString& context,
2158                                                   const QString& parent,
2159                                                   const QString& object )
2160 {
2161   // get document element
2162   QDomElement aDocElem = myDoc.documentElement();
2163
2164   // get popup menus actions
2165   QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
2166   for ( int i = 0; i < aPopupList.count(); i++ ) {
2167     QDomNode n = aPopupList.item( i );
2168     if ( !n.isNull() && n.isElement() ) {
2169       QDomElement e = n.toElement();
2170       // QString lab = attribute( e, "label-id" ); // not used // 
2171       QString ctx = attribute( e, "context-id" );
2172       QString prt = attribute( e, "parent-id"  );
2173       QString obj = attribute( e, "object-id"  );
2174       if ( ctx == context && prt == parent && obj == object )  {
2175         insertPopupItems( n, menu );
2176         break;
2177       }
2178     }
2179   }
2180 }
2181
2182 /*!
2183   \brief Activate menus
2184   \internal
2185   \param enable if \c true menus are activated, otherwise menus are deactivated
2186 */
2187 void SALOME_PYQT_ModuleLight::XmlHandler::activateMenus( bool enable )
2188 {
2189   if ( !myModule )
2190     return;
2191
2192   QtxActionMenuMgr* mgr = myModule->menuMgr();
2193   int id;
2194   foreach( id, myMenuItems ) mgr->setEmptyEnabled( id, enable );
2195 }
2196
2197 /*!
2198   \brief Create main menu item and insert actions to it.
2199   \internal
2200   \param parentNode XML node with menu description
2201   \param parentMenuId parent menu ID (-1 for top-level menu)
2202   \param parentPopup parent popup menu (0 for top-level menu)
2203 */
2204 void SALOME_PYQT_ModuleLight::XmlHandler::createMenu( QDomNode& parentNode, 
2205                                                  const int parentMenuId, 
2206                                                  QMenu*    parentPopup )
2207 {
2208   if ( !myModule || parentNode.isNull() )
2209     return;
2210   
2211   QDomElement parentElement = parentNode.toElement();
2212   if ( !parentElement.isNull() ) {
2213     QString plabel = attribute( parentElement, "label-id" );
2214     int     pid    = checkInt( attribute( parentElement, "item-id" ) );
2215     int     ppos   = checkInt( attribute( parentElement, "pos-id" ) );
2216     int     group  = checkInt( attribute( parentElement, "group-id" ), 
2217                                myModule->defaultMenuGroup() );
2218     if ( !plabel.isEmpty() ) {
2219       QMenu* popup = 0;
2220       int menuId = -1;
2221       // create menu
2222       menuId = myModule->createMenu( plabel,         // label
2223                                      parentMenuId,   // parent menu ID, -1 for top-level menu
2224                                      pid,            // ID
2225                                      group,          // group ID
2226                                      ppos );         // position
2227       myMenuItems.append( menuId );
2228       QDomNode node = parentNode.firstChild();
2229       while ( !node.isNull() ) {
2230         if ( node.isElement() ) {
2231           QDomElement elem = node.toElement();
2232           QString aTagName = tagName( elem );
2233           if ( aTagName == "popup-item" ) {
2234             int     id      = checkInt( attribute( elem, "item-id" ) );
2235             int     pos     = checkInt( attribute( elem, "pos-id" ) );
2236             int     group   = checkInt( attribute( elem, "group-id" ), 
2237                                         myModule->defaultMenuGroup() );
2238             QString label   = attribute( elem, "label-id" );
2239             QString icon    = attribute( elem, "icon-id" );
2240             QString tooltip = attribute( elem, "tooltip-id" );
2241             QString accel   = attribute( elem, "accel-id" );
2242             bool    toggle  = checkBool( attribute( elem, "toggle-id" ) );
2243
2244             // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
2245             // also check if the action with given ID is already created
2246             if ( id != -1 ) {
2247               // create menu action
2248               QAction* action = myModule->createAction( id,                     // ID
2249                                                         tooltip,                // tooltip
2250                                                         icon,                   // icon
2251                                                         label,                  // menu text
2252                                                         tooltip,                // status-bar text
2253                                                         QKeySequence( accel ),  // keyboard accelerator
2254                                                         toggle );               // toogled action
2255               myModule->createMenu( action,   // action
2256                                     menuId,   // parent menu ID
2257                                     id,       // ID (same as for createAction())
2258                                     group,    // group ID
2259                                     pos );    // position
2260             }
2261           }
2262           else if ( aTagName == "submenu" ) {
2263             // create sub-menu
2264             createMenu( node, menuId, popup );
2265           }
2266           else if ( aTagName == "separator" ) {
2267             // create menu separator
2268             int id    = checkInt( attribute( elem, "item-id" ) ); // separator can have ID
2269             int pos   = checkInt( attribute( elem, "pos-id" ) );
2270             int group = checkInt( attribute( elem, "group-id" ), 
2271                                   myModule->defaultMenuGroup() );
2272             QAction* action = myModule->separator();
2273             myModule->createMenu( action,  // separator action
2274                                   menuId,  // parent menu ID
2275                                   id,      // ID
2276                                   group,   // group ID
2277                                   pos );   // position
2278           }
2279         }
2280         node = node.nextSibling();
2281       }
2282     }
2283   }
2284 }
2285
2286 /*!
2287   \brief Create a toolbar and insert actions to it.
2288   \param parentNode XML node with toolbar description
2289 */
2290 void SALOME_PYQT_ModuleLight::XmlHandler::createToolBar( QDomNode& parentNode )
2291 {
2292   if ( !myModule || parentNode.isNull() )
2293     return;
2294
2295   QDomElement parentElement = parentNode.toElement();
2296   if ( !parentElement.isNull() ) {
2297     QString aLabel = attribute( parentElement, "label-id" );
2298     if ( !aLabel.isEmpty() ) {
2299       // create toolbar
2300       int tbId = myModule->createTool( aLabel );
2301       QDomNode node = parentNode.firstChild();
2302       while ( !node.isNull() ) {
2303         if ( node.isElement() ) {
2304           QDomElement elem = node.toElement();
2305           QString aTagName = tagName( elem );
2306           if ( aTagName == "toolbutton-item" ) {
2307             int     id      = checkInt( attribute( elem, "item-id" ) );
2308             int     pos     = checkInt( attribute( elem, "pos-id" ) );
2309             QString label   = attribute( elem, "label-id" );
2310             QString icon    = attribute( elem, "icon-id" );
2311             QString tooltip = attribute( elem, "tooltip-id" );
2312             QString accel   = attribute( elem, "accel-id" );
2313             bool    toggle  = checkBool( attribute( elem, "toggle-id" ) );
2314
2315             // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
2316             // also check if the action with given ID is already created
2317             if ( id != -1 ) {
2318               // create toolbar action
2319               QAction* action = myModule->createAction( id,                    // ID
2320                                                         tooltip,               // tooltip
2321                                                         icon,                  // icon
2322                                                         label,                 // menu text
2323                                                         tooltip,               // status-bar text
2324                                                         QKeySequence( accel ), // keyboard accelerator
2325                                                         toggle );              // toogled action
2326               myModule->createTool( action, tbId, -1, pos );
2327             }
2328           }
2329           else if ( aTagName == "separatorTB" || aTagName == "separator" ) {
2330             // create toolbar separator
2331             int pos = checkInt( attribute( elem, "pos-id" ) );
2332             QAction* action = myModule->separator();
2333             myModule->createTool( action, tbId, -1, pos );
2334           }
2335         }
2336         node = node.nextSibling();
2337       }
2338     }
2339   }
2340 }
2341
2342 /*!
2343   \brief Fill popup menu with the items.
2344   \param parentNode XML node with popup menu description
2345   \param menu popup menu
2346 */
2347 void SALOME_PYQT_ModuleLight::XmlHandler::insertPopupItems( QDomNode& parentNode, QMenu* menu )
2348 {
2349   if ( !myModule && parentNode.isNull() )
2350     return;
2351
2352   // we create popup menus without help of QtxPopupMgr
2353   QDomNode node = parentNode.firstChild();
2354   while ( !node.isNull() ) { 
2355     if ( node.isElement() ) {
2356       QDomElement elem = node.toElement();
2357       QString aTagName = tagName( elem );
2358       QList<QAction*> actions = menu->actions();
2359       if ( aTagName == "popup-item" ) {
2360         // insert a command item
2361         int     id      = checkInt( attribute( elem, "item-id" ) );
2362         int     pos     = checkInt( attribute( elem, "pos-id" ) );
2363         QString label   = attribute( elem, "label-id" );
2364         QString icon    = attribute( elem, "icon-id" );
2365         QString tooltip = attribute( elem, "tooltip-id" );
2366         QString accel   = attribute( elem, "accel-id" );
2367         bool    toggle  = checkBool( attribute( elem, "toggle-id" ) );
2368
2369         // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
2370         // also check if the action with given ID is already created
2371         if ( id != -1 ) {
2372           QAction* action = myModule->createAction( id,                     // ID
2373                                                     tooltip,                // tooltip
2374                                                     icon,                   // icon
2375                                                     label,                  // menu text
2376                                                     tooltip,                // status-bar text
2377                                                     QKeySequence( accel ),  // keyboard accelerator
2378                                                     toggle );               // toogled action
2379           QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
2380           menu->insertAction( before, action );
2381         }
2382       }
2383       else if ( aTagName == "submenu" ) {
2384         // create sub-menu
2385         ////int     id    = checkInt( attribute( elem, "item-id" ) ); // not used //
2386         int     pos   = checkInt( attribute( elem, "pos-id" ) );
2387         QString label = attribute( elem, "label-id" );
2388         QString icon  = attribute( elem, "icon-id" );
2389
2390         QIcon anIcon;
2391         if ( !icon.isEmpty() ) {
2392           QPixmap pixmap  = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(), icon );
2393           if ( !pixmap.isNull() )
2394             anIcon = QIcon( pixmap );
2395         }
2396
2397         QMenu* newPopup = menu->addMenu( anIcon, label );
2398         QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
2399         menu->insertMenu( before, newPopup );
2400         insertPopupItems( node, newPopup );
2401       }
2402       else if ( aTagName == "separator" ) {
2403         // create menu separator
2404         int pos = checkInt( attribute( elem, "pos-id" ) );
2405         QAction* action = myModule->separator();
2406         QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
2407         menu->insertAction( before, action );
2408       }
2409     }
2410     node = node.nextSibling();
2411   }
2412 }
2413
2414 /*
2415  * Save study request.
2416  * Called when user save study.
2417  */
2418 void SALOME_PYQT_ModuleLight::save(QStringList& theListOfFiles)
2419 {
2420   MESSAGE("SALOME_PYQT_ModuleLight::save()")
2421   // perform synchronous request to Python event dispatcher
2422   class SaveEvent: public PyInterp_LockRequest
2423   {
2424   public:     
2425     SaveEvent(PyInterp_Interp*           _py_interp,
2426               SALOME_PYQT_ModuleLight*       _obj,
2427               QStringList&                   _files_list)
2428       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2429         myObj( _obj ) ,
2430         myFilesList(_files_list) {}
2431   protected:
2432     virtual void execute()
2433     {
2434       myObj->saveEvent(myFilesList);
2435     }
2436   private:
2437     SALOME_PYQT_ModuleLight* myObj;
2438     QStringList&        myFilesList;
2439   };
2440   
2441   // Posting the request only if dispatcher is not busy!
2442   // Executing the request synchronously
2443   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
2444     PyInterp_Dispatcher::Get()->Exec( new SaveEvent( myInterp, this, theListOfFiles ) );
2445 }
2446
2447 void SALOME_PYQT_ModuleLight::saveEvent(QStringList& theListOfFiles)
2448 {
2449   MESSAGE("SALOME_PYQT_ModuleLight::saveEvent()");
2450   QStringList::Iterator it = theListOfFiles.begin();
2451   // Python interpreter should be initialized and Python module should be
2452   // import first
2453   if ( !myInterp || !myModule || (it == theListOfFiles.end()))
2454     return;
2455
2456   if ( PyObject_HasAttrString(myModule, (char*)"saveFiles") ) {
2457     // temporary set myInitModule because saveEvent() method
2458     // might be called by the framework when this module is inactive,
2459     // but still it should be possible to access this module's data
2460     // from Python
2461     myInitModule = this;
2462
2463     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"saveFiles",
2464                                            (char*)"s", (*it).toLatin1().constData()));
2465
2466     myInitModule = 0;
2467
2468     if( !res ) {
2469       PyErr_Print();
2470     }
2471     else{
2472       // parse the return value
2473       // result can be one string...
2474       if ( PyString_Check( res ) ) {
2475         QString astr = PyString_AsString( res );
2476         //SCRUTE(astr);
2477         theListOfFiles.append(astr);
2478       }
2479       //also result can be a list...
2480       else if ( PyList_Check( res ) ) {
2481         int size = PyList_Size( res );
2482         for ( int i = 0; i < size; i++ ) {
2483           PyObject* value = PyList_GetItem( res, i );
2484           if( value && PyString_Check( value ) ) {
2485             theListOfFiles.append( PyString_AsString( value ) );
2486           }
2487         }
2488       }
2489     }
2490   }
2491 }
2492
2493 /*
2494  * Python dump request.
2495  * Called when user activates dump study operation.
2496  */
2497 void SALOME_PYQT_ModuleLight::dumpPython(QStringList& theListOfFiles)
2498 {
2499   MESSAGE("SALOME_PYQT_ModuleLight::dumpPython()")
2500   // perform synchronous request to Python event dispatcher
2501   class DumpEvent: public PyInterp_LockRequest
2502   {
2503   public:     
2504     DumpEvent(PyInterp_Interp*          _py_interp,
2505               SALOME_PYQT_ModuleLight*  _obj,
2506               QStringList&              _files_list)
2507       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2508         myObj( _obj ) ,
2509         myFilesList(_files_list) {}
2510   protected:
2511     virtual void execute()
2512     {
2513       myObj->dumpEvent(myFilesList);
2514     }
2515   private:
2516     SALOME_PYQT_ModuleLight* myObj;
2517     QStringList&             myFilesList;
2518   };
2519   
2520   // Posting the request only if dispatcher is not busy!
2521   // Executing the request synchronously
2522   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
2523     PyInterp_Dispatcher::Get()->Exec( new DumpEvent( myInterp, this, theListOfFiles ) );
2524 }
2525
2526 void SALOME_PYQT_ModuleLight::dumpEvent(QStringList& theListOfFiles)
2527 {
2528   MESSAGE("SALOME_PYQT_ModuleLight::dumpEvent()");
2529   QStringList::Iterator it = theListOfFiles.begin();
2530   // Python interpreter should be initialized and Python module should be
2531   // import first
2532   if ( !myInterp || !myModule || (it == theListOfFiles.end()))
2533     return;
2534
2535   if ( PyObject_HasAttrString(myModule, (char*)"dumpStudy") ) {
2536     // temporary set myInitModule because dumpEvent() method
2537     // might be called by the framework when this module is inactive,
2538     // but still it should be possible to access this module's data
2539     // from Python
2540     myInitModule = this;
2541
2542     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"dumpStudy",
2543                                            (char*)"s", (*it).toLatin1().constData()));
2544
2545     myInitModule = 0;
2546
2547     if( !res ) {
2548       PyErr_Print();
2549     }
2550     else{
2551       // parse the return value
2552       // result can be one string...
2553       if ( PyString_Check( res ) ) {
2554         QString astr = PyString_AsString( res );
2555         //SCRUTE(astr);
2556         theListOfFiles.append(astr);
2557       }
2558       //also result can be a list...
2559       else if ( PyList_Check( res ) ) {
2560         int size = PyList_Size( res );
2561         for ( int i = 0; i < size; i++ ) {
2562           PyObject* value = PyList_GetItem( res, i );
2563           if( value && PyString_Check( value ) ) {
2564             theListOfFiles.append( PyString_AsString( value ) );
2565           }
2566         }
2567       }
2568     }
2569   }
2570 }
2571
2572 /*
2573  * Open study request.
2574  * Called when user open study.
2575  */
2576 bool SALOME_PYQT_ModuleLight::open(QStringList theListOfFiles)
2577 {
2578   MESSAGE("SALOME_PYQT_ModuleLight::open()");
2579   // perform synchronous request to Python event dispatcher
2580   bool opened = false;
2581   class OpenEvent: public PyInterp_LockRequest
2582   {
2583   public:
2584     OpenEvent(PyInterp_Interp*              _py_interp,
2585               SALOME_PYQT_ModuleLight*      _obj,
2586               QStringList                   _files_list,
2587               bool&                         _opened)
2588       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2589         myObj( _obj ) ,
2590         myFilesList(_files_list),
2591         myOpened(_opened) {}
2592   protected:
2593     virtual void execute()
2594     {
2595       myObj->openEvent(myFilesList,myOpened);
2596     }
2597
2598   private:
2599     SALOME_PYQT_ModuleLight* myObj;
2600     QStringList        myFilesList;
2601     bool& myOpened;
2602   };
2603   
2604   // Posting the request only if dispatcher is not busy!
2605   // Executing the request synchronously
2606   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
2607     PyInterp_Dispatcher::Get()->Exec( new OpenEvent( myInterp, this, theListOfFiles, opened) );
2608   return opened;
2609 }
2610
2611
2612 void SALOME_PYQT_ModuleLight::openEvent(QStringList theListOfFiles, bool &opened)
2613 {
2614   MESSAGE("SALOME_PYQT_ModuleLight::openEvent()");
2615   // Python interpreter should be initialized and Python module should be
2616   // import first
2617   if ( !myInterp || !myModule || theListOfFiles.isEmpty())
2618     return;
2619   QStringList* theList = new QStringList(theListOfFiles);
2620
2621 #if SIP_VERSION < 0x040800
2622   PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
2623 #else
2624   PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2625 #endif
2626   if ( PyObject_HasAttrString(myModule , (char*)"openFiles") ) {
2627     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"openFiles",
2628                                            (char*)"O", sipList.get()));
2629     if( !res || !PyBool_Check( res )) {
2630       PyErr_Print();
2631       opened = false;
2632     }
2633     else{
2634       opened = PyObject_IsTrue( res );
2635       
2636     }
2637   }
2638 }
2639
2640 /*
2641  * Create new empty Data Object and return its entry
2642  */
2643 QString SALOME_PYQT_ModuleLight::createObject(const QString& parent)
2644 {
2645   SALOME_PYQT_DataObjectLight* obj=0;
2646   if(!parent.isEmpty())
2647   {
2648     SALOME_PYQT_DataObjectLight* parentObj = findObject(parent);
2649     if(parentObj)
2650     {
2651       obj = new SALOME_PYQT_DataObjectLight(parentObj);
2652     }
2653   }
2654   else
2655   {
2656       SALOME_PYQT_DataModelLight* dm =
2657         dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel());
2658       if(dm)
2659       {
2660         obj = new SALOME_PYQT_DataObjectLight(dm->getRoot());
2661       }
2662   }  
2663   if (obj)
2664     return obj->entry();
2665   else
2666     return QString::null;
2667 }
2668
2669 /*
2670  * Create new Data Object with name, icon and tooltip
2671  * and return its entry
2672  */
2673 QString SALOME_PYQT_ModuleLight::createObject(const QString& aname,
2674                                               const QString& iconname,
2675                                               const QString& tooltip,
2676                                               const QString& parent)
2677 {
2678   QString entry = createObject(parent);
2679   SALOME_PYQT_DataObjectLight* obj = findObject(entry);
2680
2681   if(obj)
2682   {
2683     obj->setName(aname);
2684     obj->setToolTip(tooltip);
2685     obj->setIcon(iconname);
2686     return obj->entry();
2687   }
2688   else
2689     return QString::null;
2690 }
2691
2692 /*
2693  * Find object by entry
2694  */
2695 SALOME_PYQT_DataObjectLight* SALOME_PYQT_ModuleLight::findObject(const QString& entry)
2696 {
2697   SALOME_PYQT_DataObjectLight* obj = 0;
2698   SALOME_PYQT_DataModelLight* dm =
2699     dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel());
2700   if(!entry.isEmpty() && dm ){
2701     for ( SUIT_DataObjectIterator it( dm->getRoot(), SUIT_DataObjectIterator::DepthLeft ); it.current(); ++it ) { 
2702       SALOME_PYQT_DataObjectLight* curentobj =
2703         dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.current() );
2704       
2705       if(curentobj && curentobj->entry() == entry) {
2706         obj = curentobj;
2707         return obj;
2708       }
2709     }
2710   }
2711   return obj;
2712 }
2713
2714 /*
2715  * Set Name for object
2716  */
2717 void SALOME_PYQT_ModuleLight::setName(const QString& obj, const QString& name)
2718 {
2719   SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2720   if(dataObj) {
2721     dataObj->setName(name);
2722   }
2723 }
2724
2725 /*
2726  * Set Icon for object
2727  */
2728 void SALOME_PYQT_ModuleLight::setIcon(const QString& obj, const QString& iconname)
2729 {
2730   SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2731   if(dataObj) {
2732     dataObj->setIcon(iconname);
2733   }
2734 }
2735
2736 /*
2737  * Return Name of object
2738  */
2739 QString SALOME_PYQT_ModuleLight::getName(const QString& obj)
2740 {
2741   SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2742   if(dataObj) {
2743     return dataObj->name();
2744   }
2745   return QString::null;
2746 }
2747
2748 /*
2749  * Return Tool Tip of object
2750  */
2751 QString SALOME_PYQT_ModuleLight::getToolTip(const QString& obj)
2752 {
2753   SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2754   if(dataObj) {
2755     return dataObj->toolTip();
2756   }
2757   return QString::null;
2758 }
2759
2760
2761 /*
2762  * Set Tool Tip for object
2763  */
2764 void SALOME_PYQT_ModuleLight::setToolTip(const QString& obj, const QString& tooltip)
2765 {
2766   SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2767   if(dataObj) {
2768     dataObj->setToolTip(tooltip);
2769   }
2770 }
2771
2772 /*
2773  * Return color of object
2774  */
2775 QColor SALOME_PYQT_ModuleLight::getColor(const QString& obj)
2776 {
2777   SALOME_PYQT_DataObjectLight* dataObj = findObject( obj );
2778   if( dataObj ) {
2779     return dataObj->color( SUIT_DataObject::Foreground );
2780   }
2781   return QColor();
2782 }
2783
2784 /*
2785  * Set color for object
2786  */
2787 void SALOME_PYQT_ModuleLight::setColor(const QString& obj, const QColor& color)
2788 {
2789   SALOME_PYQT_DataObjectLight* dataObj = findObject( obj );
2790   if( dataObj ) {
2791     dataObj->setColor( color );
2792   }
2793 }
2794
2795 /*
2796  * Return entry of the referenced object (if any)
2797  */
2798 QString SALOME_PYQT_ModuleLight::getReference(const QString& obj)
2799 {
2800   SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2801   if(dataObj) {
2802     return dataObj->refEntry();
2803   }
2804   return QString::null;
2805 }
2806
2807
2808 /*
2809  * Set entry of the referenced object
2810  */
2811 void SALOME_PYQT_ModuleLight::setReference(const QString& obj, const QString& refEntry)
2812 {
2813   SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2814   if(dataObj) {
2815     dataObj->setRefEntry(refEntry);
2816   }
2817 }
2818
2819 /*
2820  * Remove object by entry
2821  */
2822 void SALOME_PYQT_ModuleLight::removeObject(const QString& obj)
2823 {
2824   SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2825   if(dataObj) {
2826     dataObj->parent()->removeChild(dataObj);
2827   }
2828 }
2829
2830
2831 /*
2832  * Remove chields from object 
2833  */
2834 void SALOME_PYQT_ModuleLight::removeChild(const QString& obj)
2835 {
2836   MESSAGE("SALOME_PYQT_ModuleLight::removeChild()");
2837   DataObjectList lst;
2838   if(!obj.isEmpty())
2839     {
2840     SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2841     if(dataObj)
2842     {
2843       dataObj->children(lst);
2844       QListIterator<SUIT_DataObject*> it( lst );
2845       while( it.hasNext() )
2846       {
2847         SALOME_PYQT_DataObjectLight* sobj = dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.next() );
2848         dataObj->removeChild(sobj);
2849       }
2850     }
2851   }
2852   else
2853   {
2854     SALOME_PYQT_DataModelLight* dm =
2855       dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel());
2856     if(dm)
2857     {
2858       dm->getRoot()->children(lst);
2859       QListIterator<SUIT_DataObject*> it( lst );
2860       while(it.hasNext() )
2861       {
2862         SALOME_PYQT_DataObjectLight* sobj = dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.next() );
2863         dm->getRoot()->removeChild(sobj);
2864       }
2865     }
2866   }
2867 }
2868
2869 QStringList SALOME_PYQT_ModuleLight::getChildren(const QString& obj, const bool rec)
2870 {
2871   DataObjectList lst;
2872   QStringList entryList;
2873   if(!obj.isEmpty())
2874   {
2875     SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2876     if(dataObj)
2877     {
2878       dataObj->children(lst,rec);
2879       QListIterator<SUIT_DataObject*> it( lst );
2880       while(it.hasNext())
2881       {
2882         SALOME_PYQT_DataObjectLight* sobj = dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.next() );
2883         entryList.append(sobj->entry());
2884       }
2885     }
2886   }
2887   else
2888   {
2889     SALOME_PYQT_DataModelLight* dm =
2890       dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel());
2891     if(dm)
2892     {
2893       dm->getRoot()->children(lst);
2894       QListIterator<SUIT_DataObject*> it( lst );
2895       while( it.hasNext() )
2896       {
2897         SALOME_PYQT_DataObjectLight* sobj = dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.next() );
2898         entryList.append(sobj->entry());
2899       }
2900     }
2901   }
2902   return entryList;
2903 }
2904
2905 /*!
2906  * Create new instance of data model and return it.
2907  */
2908 CAM_DataModel* SALOME_PYQT_ModuleLight::createDataModel()
2909 {
2910   MESSAGE( "SALOME_PYQT_ModuleLight::createDataModel()" );
2911   return new SALOME_PYQT_DataModelLight(this);
2912 }
2913
2914 /*!
2915  * Returns the Python module object currently loaded.
2916  */
2917 PyObject* SALOME_PYQT_ModuleLight::getPythonModule()
2918 {
2919   return myModule;
2920 }
2921
2922 bool SALOME_PYQT_ModuleLight::isDraggable( const SUIT_DataObject* what ) const
2923 {
2924   MESSAGE("SALOME_PYQT_ModuleLight::isDraggable()");
2925   // perform synchronous request to Python event dispatcher
2926   bool draggable = false;
2927   class IsDraggableEvent: public PyInterp_LockRequest
2928   {
2929   public:
2930     IsDraggableEvent(PyInterp_Interp*          _py_interp,
2931                      SALOME_PYQT_ModuleLight*  _obj,
2932                      LightApp_DataObject*      _data_object,
2933                      bool&                     _is_draggable )
2934       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2935         myObj( _obj ) ,
2936         myDataObject( _data_object ),
2937         myIsDraggable( _is_draggable ) {}
2938   protected:
2939     virtual void execute()
2940     {
2941       myIsDraggable = myObj->isDraggableEvent( myDataObject );
2942     }
2943
2944   private:
2945     SALOME_PYQT_ModuleLight* myObj;
2946     LightApp_DataObject*     myDataObject;
2947     bool&                    myIsDraggable;
2948   };
2949
2950   const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( what );
2951                                                               
2952   // Posting the request only if dispatcher is not busy!
2953   // Executing the request synchronously
2954   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
2955     PyInterp_Dispatcher::Get()->Exec( new IsDraggableEvent( myInterp,
2956                                                             const_cast<SALOME_PYQT_ModuleLight*>( this ),
2957                                                             const_cast<LightApp_DataObject*>( data_object ),
2958                                                             draggable ) );
2959   return draggable;
2960 }
2961
2962 bool SALOME_PYQT_ModuleLight::isDraggableEvent( LightApp_DataObject* what )
2963 {
2964   MESSAGE("SALOME_PYQT_ModuleLight::isDraggableEvent()");
2965
2966   bool draggable = false;
2967
2968   // Python interpreter should be initialized and Python module should be
2969   // import first
2970   if ( !myInterp || !myModule || !what )
2971     return draggable;
2972
2973   if ( PyObject_HasAttrString(myModule , (char*)"isDraggable") ) {
2974     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"isDraggable",
2975                                            (char*)"s", what->entry().toLatin1().constData() ) );
2976     if( !res || !PyBool_Check( res )) {
2977       PyErr_Print();
2978       draggable = false;
2979     }
2980     else{
2981       draggable = PyObject_IsTrue( res );
2982     }
2983   }
2984
2985   return draggable;
2986 }
2987
2988 bool SALOME_PYQT_ModuleLight::isDropAccepted( const SUIT_DataObject* where ) const
2989 {
2990   MESSAGE("SALOME_PYQT_ModuleLight::isDropAccepted()");
2991   // perform synchronous request to Python event dispatcher
2992   bool dropAccepted = false;
2993   class IsDropAcceptedEvent: public PyInterp_LockRequest
2994   {
2995   public:
2996     IsDropAcceptedEvent(PyInterp_Interp*          _py_interp,
2997                         SALOME_PYQT_ModuleLight*  _obj,
2998                         LightApp_DataObject*      _data_object,
2999                         bool&                     _is_drop_accepted )
3000       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
3001         myObj( _obj ) ,
3002         myDataObject( _data_object ),
3003         myIsDropAccepted( _is_drop_accepted ) {}
3004   protected:
3005     virtual void execute()
3006     {
3007       myIsDropAccepted = myObj->isDropAcceptedEvent( myDataObject );
3008     }
3009
3010   private:
3011     SALOME_PYQT_ModuleLight* myObj;
3012     LightApp_DataObject*     myDataObject;
3013     bool&                    myIsDropAccepted;
3014   };
3015   
3016   const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( where );
3017                                                               
3018   // Posting the request only if dispatcher is not busy!
3019   // Executing the request synchronously
3020   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
3021     PyInterp_Dispatcher::Get()->Exec( new IsDropAcceptedEvent( myInterp,
3022                                                                const_cast<SALOME_PYQT_ModuleLight*>( this ),
3023                                                                const_cast<LightApp_DataObject*>( data_object ),
3024                                                                dropAccepted ) );
3025   return dropAccepted;
3026 }
3027
3028 bool SALOME_PYQT_ModuleLight::isDropAcceptedEvent( LightApp_DataObject* where )
3029 {
3030   MESSAGE("SALOME_PYQT_ModuleLight::isDropAcceptedEvent()");
3031
3032   bool dropAccepted = false;
3033
3034   // Python interpreter should be initialized and Python module should be
3035   // import first
3036   if ( !myInterp || !myModule || !where )
3037     return dropAccepted;
3038
3039   if ( PyObject_HasAttrString(myModule , (char*)"isDropAccepted") ) {
3040     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"isDropAccepted",
3041                                            (char*)"s", where->entry().toLatin1().constData() ) );
3042     if( !res || !PyBool_Check( res )) {
3043       PyErr_Print();
3044       dropAccepted = false;
3045     }
3046     else{
3047       dropAccepted = PyObject_IsTrue( res );
3048     }
3049   }
3050
3051   return dropAccepted;
3052 }
3053
3054 void SALOME_PYQT_ModuleLight::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
3055                                            const int row, Qt::DropAction action )
3056 {
3057   MESSAGE("SALOME_PYQT_ModuleLight::dropObjects()");
3058   // perform synchronous request to Python event dispatcher
3059   class DropObjectsEvent: public PyInterp_LockRequest
3060   {
3061   public:
3062     DropObjectsEvent(PyInterp_Interp*          _py_interp,
3063                      SALOME_PYQT_ModuleLight*  _obj,
3064                      const DataObjectList&     _what,
3065                      SUIT_DataObject*          _where,
3066                      const int                 _row,
3067                      Qt::DropAction            _action )
3068       : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
3069         myObj( _obj ) ,
3070         myWhat( _what ),
3071         myWhere( _where ),
3072         myRow ( _row ),
3073         myAction ( _action ){}
3074   protected:
3075     virtual void execute()
3076     {
3077       myObj->dropObjectsEvent( myWhat, myWhere, myRow, myAction );
3078     }
3079
3080   private:
3081     SALOME_PYQT_ModuleLight* myObj;
3082     DataObjectList           myWhat;
3083     SUIT_DataObject*         myWhere;
3084     int                      myRow;
3085     Qt::DropAction           myAction;
3086   };
3087   
3088   // Posting the request only if dispatcher is not busy!
3089   // Executing the request synchronously
3090   if ( !PyInterp_Dispatcher::Get()->IsBusy() )
3091     PyInterp_Dispatcher::Get()->Exec( new DropObjectsEvent( myInterp, this, what, where, row, action ) );
3092 }
3093
3094 void SALOME_PYQT_ModuleLight::dropObjectsEvent( const DataObjectList& what, SUIT_DataObject* where,
3095                                                 const int row, Qt::DropAction action )
3096 {
3097   MESSAGE("SALOME_PYQT_ModuleLight::dropObjectsEvent()");
3098   // Python interpreter should be initialized and Python module should be
3099   // import first
3100   if ( !myInterp || !myModule || what.isEmpty() || !where )
3101     return;
3102
3103   QStringList* theList = new QStringList();
3104
3105   LightApp_DataObject* whereObject = dynamic_cast<LightApp_DataObject*>( where );
3106   if ( !whereObject ) return;
3107   
3108   for ( int i = 0; i < what.count(); i++ ) {
3109     LightApp_DataObject* dataObject = dynamic_cast<LightApp_DataObject*>( what[i] );
3110     if ( dataObject ) theList->append( dataObject->entry() );
3111   }
3112
3113 #if SIP_VERSION < 0x040800
3114   PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
3115 #else
3116   PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
3117 #endif
3118   if ( PyObject_HasAttrString(myModule, (char*)"dropObjects") ) {
3119     PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"dropObjects", (char*)"Osii",
3120                                            sipList.get(),
3121                                            whereObject->entry().toLatin1().constData(),
3122                                            row, action ) );
3123     
3124     if( !res ) {
3125       PyErr_Print();
3126     }
3127   }
3128 }