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