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