Salome HOME
Update copyrights 2014.
[modules/gui.git] / src / SALOME_PYQT / SALOME_PYQT_GUILight / SALOME_PYQT_ModuleLight.cxx
1 // Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 // File   : SALOME_PYQT_ModuleLight.cxx
21 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
22
23 #include "SALOME_PYQT_DataModelLight.h"
24 #include "SALOME_PYQT_ModuleLight.h"
25 #include "SALOME_PYQT_PyModule.h"
26
27 #include "CAM_Application.h"
28 #include "SUITApp_init_python.hxx"
29 #include "SUIT_DataObjectIterator.h"
30 #include "LightApp_Application.h"
31 #include "SUIT_DataBrowser.h"
32 #include "sipAPISalomePyQtGUILight.h"
33
34 #ifndef GUI_DISABLE_CORBA
35 #include <Container_init_python.hxx>
36 #endif
37
38 // Py_ssize_t for old Pythons
39 // This code is as recommended by"
40 // http://www.python.org/dev/peps/pep-0353/#conversion-guidelines
41 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
42 typedef int Py_ssize_t;
43 # define PY_SSIZE_T_MAX INT_MAX
44 # define PY_SSIZE_T_MIN INT_MIN
45 #endif
46
47 //
48 // NB: Python requests.
49 // General rule for Python requests created by Python-based GUI modules
50 // (SALOME_PYQT_ModuleLight and other ones):
51 // all requests should be executed SYNCHRONOUSLY within the main GUI thread.
52 // However, it is obligatory that ANY Python call is wrapped with a request object,
53 // so that ALL Python API calls are serialized with PyInterp_Dispatcher.
54 //
55 // NB: Library initialization
56 // Since the SalomePyQtGUILight library is not imported in Python it's initialization function
57 // should be called manually (and only once) in order to initialize global sip data
58 // and to get C API from sip : sipBuildResult for example
59 //
60
61 #define INIT_FUNCTION initSalomePyQtGUILight
62 #if defined(SIP_STATIC_MODULE)
63 extern "C" void INIT_FUNCTION();
64 #else
65 PyMODINIT_FUNC INIT_FUNCTION();
66 #endif
67
68 /*!
69   \fn CAM_Module* createModule()
70   \brief Module factory function.
71   \internal
72   
73   Creates an instance of SALOME_PYQT_Module object by request
74   of an application when the module is loaded and initialized.
75
76   \return new module object
77 */
78
79 extern "C" 
80 {
81   SALOME_PYQT_LIGHT_EXPORT CAM_Module* createModule() 
82   {
83     static bool alreadyInitialized = false;
84
85     // make initialization only once (see comment above) !
86     if ( !alreadyInitialized ) {
87       static PyThreadState* gtstate = 0;
88 #ifndef GUI_DISABLE_CORBA
89       if ( SUIT_PYTHON::initialized )
90         gtstate = SUIT_PYTHON::_gtstate;
91       else
92         gtstate = KERNEL_PYTHON::_gtstate;
93 #else
94       gtstate = SUIT_PYTHON::_gtstate;
95 #endif
96       PyEval_RestoreThread( gtstate );
97
98       INIT_FUNCTION();
99
100       PyEval_ReleaseThread( gtstate );
101
102       alreadyInitialized = !alreadyInitialized;
103     }
104
105     return new SALOME_PYQT_ModuleLight();
106   }
107 }
108
109 /*!
110   \class SALOME_PYQT_ModuleLight
111   \brief This class implements GUI module for "light-weight" (no-CORBA-engine)
112   Python-based SALOME modules.
113 */
114
115 /*!
116   \brief Constructor
117 */
118 SALOME_PYQT_ModuleLight::SALOME_PYQT_ModuleLight()
119   : LightApp_Module( "noname" ) // name is set explicitly at the module initialization
120 {
121   // initialize helper
122   myHelper = new PyModuleHelper( this );
123 }
124
125 /*!
126   \brief Destructor
127 */
128 SALOME_PYQT_ModuleLight::~SALOME_PYQT_ModuleLight()
129 {
130   // as myHelper is a QObject, it should be deleted automatically
131 }
132
133 /*!
134   \brief Initialization of the module.
135   \param app parent application object
136   \sa PyModuleHelper::initialize()
137 */
138 void SALOME_PYQT_ModuleLight::initialize( CAM_Application* app )
139 {
140   // call base implementation
141   LightApp_Module::initialize( app );
142
143   // ... then call helper
144   myHelper->initialize( app );
145   SUIT_DataBrowser* ob = getApp()->objectBrowser();
146   if (ob && ob->model()) {
147     connect( ob->model(), SIGNAL( clicked( SUIT_DataObject*, int ) ),
148              myHelper, SLOT( onObjectBrowserClicked( SUIT_DataObject*, int ) ), Qt::UniqueConnection );
149   }
150 }
151
152 /*!
153   \brief Activation of the module.
154   \param study parent study
155   \return \c true if activation is successful and \c false otherwise
156   \sa PyModuleHelper::activate()
157 */
158 bool SALOME_PYQT_ModuleLight::activateModule( SUIT_Study* study )
159 {
160   // call base implementation and then helper
161   return LightApp_Module::activateModule( study ) && myHelper->activate( study );
162 }
163
164 /*!
165   \brief Deactivation of the module.
166   \param study parent study
167   \return \c true if deactivation is successful and \c false otherwise
168   \sa PyModuleHelper::deactivate()
169 */
170 bool SALOME_PYQT_ModuleLight::deactivateModule( SUIT_Study* study )
171 {  
172   // call helper
173   bool res = myHelper->deactivate( study );
174     
175   // ... then call base implementation
176   return LightApp_Module::deactivateModule( study ) && res;
177 }
178
179 /*!
180   \brief Close of the module.
181
182   This function is usually used in order to close the module's 
183   specific menus and toolbars and perform other such actions
184   required when the module is closed.
185 */
186 void SALOME_PYQT_ModuleLight::onModelClosed()
187 {
188   // call helper
189   myHelper->modelClosed(application()->activeStudy());
190   LightApp_Module::onModelClosed();
191 }
192
193
194 /*!
195   \brief Get the dockable windows associated with the module.
196   \param winMap output map of dockable windows in form { <window_type> : <dock_area> }
197   \sa PyModuleHelper::windows()
198 */
199 void SALOME_PYQT_ModuleLight::windows( QMap<int, int>& winMap ) const
200 {
201   // get list of dockable windows from helper
202   winMap = myHelper->windows();
203 }
204
205 /*!
206   \brief Define the compatible view windows associated with the module.
207   \param viewList output list of view windows types
208   \sa PyModuleHelper::viewManagers()
209 */
210 void SALOME_PYQT_ModuleLight::viewManagers( QStringList& viewList ) const
211 {
212   // get list of view types from helper
213   viewList = myHelper->viewManagers();
214 }
215
216 /*!
217   \brief Process study activation.
218   \sa PyModuleHelper::studyActivated()
219 */
220 void SALOME_PYQT_ModuleLight::studyActivated()
221 {
222   // call helper
223   myHelper->studyActivated( application()->activeStudy() );
224 }
225
226 /*!
227   \brief Process context popup menu request.
228   \param context popup menu context (e.g. "ObjectBrowser")
229   \param menu popup menu
230   \param title popup menu title (not used)
231   \sa PyModuleHelper::contextMenu()
232 */
233 void SALOME_PYQT_ModuleLight::contextMenuPopup( const QString& context, 
234                                                 QMenu*         menu, 
235                                                 QString&       /*title*/ )
236 {
237   // call helper
238   myHelper->contextMenu( context, menu );
239 }
240
241 /*!
242   \brief Export preferences for the Python module.
243   \sa PyModuleHelper::createPreferences()
244 */
245 void SALOME_PYQT_ModuleLight::createPreferences()
246 {
247   // call helper
248   myHelper->createPreferences();
249 }
250
251 /*!
252   \brief Process module's preferences changing.
253   \param section preference resources section
254   \param parameter preference resources parameter name
255   \sa PyModuleHelper::preferencesChanged()
256 */
257 void SALOME_PYQT_ModuleLight::preferencesChanged( const QString& section, const QString& parameter )
258 {
259   // call helper
260   myHelper->preferencesChanged( section, parameter );
261 }
262
263 /*!
264   \brief Save module data. Called when user saves study.
265   \param files output list of files where module stores data
266   \sa PyModuleHelper::save()
267 */
268 void SALOME_PYQT_ModuleLight::save( QStringList& files )
269 {
270   // call helper
271   myHelper->save( files );
272 }
273
274 /*
275  \brief Load module data. Called when user opens study 
276  and activates module.
277  \param files list of files where module data is stored
278  \sa PyModuleHelper::load()
279 */
280 bool SALOME_PYQT_ModuleLight::load( const QStringList& files )
281 {
282   // call helper
283   return myHelper->load( files );
284 }
285
286 /*!
287   \brief Dump module data to the Python script. 
288   Called when user activates dump study operation.
289   \param files output list of files where module stores python script
290   \sa PyModuleHelper::dumpPython()
291 */
292 void SALOME_PYQT_ModuleLight::dumpPython( QStringList& files )
293 {
294   // call helper
295   myHelper->dumpPython( files );
296 }
297
298 /*!
299   \brief Test if object \a what can be dragged by the user.
300   \param what data object being tested
301   \return \c true if object can be dragged or \c false otherwise
302   \sa PyModuleHelper::isDraggable()
303 */
304 bool SALOME_PYQT_ModuleLight::isDraggable( const SUIT_DataObject* what ) const
305 {
306   // call helper
307   return myHelper->isDraggable( what );
308 }
309
310 /*!
311   \brief Test if drop operation can be done on the \a where object.
312   \param where data object being tested
313   \return \c true if if drop operation is supported by object or \c false otherwise
314   \sa PyModuleHelper::isDropAccepted()
315 */
316 bool SALOME_PYQT_ModuleLight::isDropAccepted( const SUIT_DataObject* where ) const
317 {
318   // call helper
319   return myHelper->isDropAccepted( where );
320 }
321
322 /*!
323   \brief Perform drop operation
324   \param what list of data objects being dropped
325   \param where target data object for drop operation
326   \param row line (child item index) where drop operation is performed to
327   \param action current drop action (copy or move)
328   \sa PyModuleHelper::dropObjects()
329 */
330 void SALOME_PYQT_ModuleLight::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
331                                            const int row, Qt::DropAction action )
332 {
333   // call helper
334   myHelper->dropObjects( what, where, row, action );
335 }
336
337 /*!
338   \brief Create new empty data object
339   \param parent entry of parent data object
340   \return entry of created data object
341 */
342 QString SALOME_PYQT_ModuleLight::createObject( const QString& parent )
343 {
344   QString entry;
345   SALOME_PYQT_DataObjectLight* obj = 0;
346
347   if ( !parent.isEmpty() ) {
348     SALOME_PYQT_DataObjectLight* parentObj = findObject( parent );
349     if ( parentObj )
350       obj = new SALOME_PYQT_DataObjectLight( parentObj );
351   }
352   else {
353     SALOME_PYQT_DataModelLight* dm =
354       dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel() );
355     if ( dm )
356       obj = new SALOME_PYQT_DataObjectLight( dm->getRoot() );
357   }  
358   if ( obj )
359     entry = obj->entry();
360   return entry;
361 }
362
363 /*!
364  \brief Create new data object with specified name, icon and tooltip
365  \param name data object name
366  \param icon data object icon
367  \param toolTip data object tooltip
368  \param parent entry of parent data object
369  \return entry of created data object
370 */
371 QString SALOME_PYQT_ModuleLight::createObject( const QString& name,
372                                                const QString& icon,
373                                                const QString& toolTip,
374                                                const QString& parent )
375 {
376   QString entry = createObject( parent );
377   SALOME_PYQT_DataObjectLight* obj = findObject( entry );
378   if ( obj ) {
379     obj->setName( name);
380     obj->setToolTip( toolTip );
381     obj->setIcon( icon );
382   }
383   return entry;
384 }
385
386 /*!
387   \brief Set data object name
388   \param entry data object entry
389   \param name data object name
390 */
391 void SALOME_PYQT_ModuleLight::setName( const QString& entry, const QString& name )
392 {
393   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
394   if ( dataObj )
395     dataObj->setName( name );
396 }
397
398 /*!
399   \brief Get data object name
400   \param entry data object entry
401   \return data object name
402 */
403 QString SALOME_PYQT_ModuleLight::getName( const QString& entry ) const
404 {
405   QString name;
406   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
407   if ( dataObj )
408     name = dataObj->name();
409   return name;
410 }
411
412 /*!
413   \brief Set data object icon
414   \param entry data object entry
415   \param icon data object icon file name (icon is loaded from module resources)
416 */
417 void SALOME_PYQT_ModuleLight::setIcon( const QString& entry, const QString& icon )
418 {
419   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
420   if ( dataObj )
421     dataObj->setIcon( icon );
422 }
423
424 /*!
425   \brief Set data object tooltip
426   \param entry data object entry
427   \param toolTip data object tooltip
428 */
429 void SALOME_PYQT_ModuleLight::setToolTip( const QString& entry, const QString& toolTip )
430 {
431   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
432   if ( dataObj )
433     dataObj->setToolTip( toolTip );
434 }
435
436 /*!
437   \brief Get data object tooltip
438   \param entry data object entry
439   \return data object tooltip
440 */
441 QString SALOME_PYQT_ModuleLight::getToolTip( const QString& entry ) const
442 {
443   QString toolTip;
444   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
445   if ( dataObj )
446     toolTip = dataObj->toolTip();
447   return toolTip;
448 }
449
450 /*!
451   \brief Set data object color
452   \param entry data object entry
453   \param color data object color
454  */
455 void SALOME_PYQT_ModuleLight::setColor( const QString& entry, const QColor& color )
456 {
457   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
458   if ( dataObj )
459     dataObj->setColor( color );
460 }
461
462 /*!
463   \brief Get data object color
464   \param entry data object entry
465   \return data object color
466 */
467 QColor SALOME_PYQT_ModuleLight::getColor( const QString& entry ) const
468 {
469   QColor color;
470   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
471   if ( dataObj )
472     color = dataObj->color( SUIT_DataObject::Foreground );
473   return color;
474 }
475
476 void SALOME_PYQT_ModuleLight::setObjectPosition( const QString& theEntry, int thePos )
477 {
478   SALOME_PYQT_DataObjectLight* dataObj = findObject( theEntry );
479   if ( dataObj )
480     dataObj->setPosition(thePos);
481 }
482
483 int SALOME_PYQT_ModuleLight::getObjectPosition( const QString& theEntry )
484 {
485   SALOME_PYQT_DataObjectLight* dataObj = findObject( theEntry );
486   if ( dataObj )
487     return dataObj->position();
488   return -1;
489 }
490
491
492 /*!
493   \brief Set reference to another data object
494   \param entry data object entry
495   \param refEntry referenced data object entry
496 */
497 void SALOME_PYQT_ModuleLight::setReference( const QString& entry, const QString& refEntry )
498 {
499   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
500   if ( dataObj )
501     dataObj->setRefEntry( refEntry );
502 }
503
504 /*
505   \brief Get entry of the referenced object (if there's any)
506   \param entry data object entry
507   \return referenced data object entry
508 */
509 QString SALOME_PYQT_ModuleLight::getReference( const QString& entry ) const
510 {
511   QString refEntry;
512   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
513   if ( dataObj )
514     refEntry = dataObj->refEntry();
515   return refEntry;
516 }
517
518 /*!
519   \brief Remove object by entry
520   \param entry data object entry
521 */
522 void SALOME_PYQT_ModuleLight::removeObject( const QString& entry )
523 {
524   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
525   if ( dataObj && dataObj->parent() )
526     dataObj->parent()->removeChild( dataObj );
527 }
528
529 /*!
530   \brief Remove all child data objects from specified data object
531   \param entry data object entry
532 */
533 void SALOME_PYQT_ModuleLight::removeChildren( const QString& entry )
534 {
535   SUIT_DataObject* dataObj = 0;
536   if ( !entry.isEmpty() ) {
537     dataObj = findObject( entry );
538   }
539   else {
540     SALOME_PYQT_DataModelLight* dm =
541       dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel() );
542     if ( dm ) dataObj = dm->getRoot();
543   }
544   if ( dataObj ) {
545     DataObjectList children;
546     dataObj->children( children );
547     QListIterator<SUIT_DataObject*> it( children );
548     while ( it.hasNext() ) {
549       dataObj->removeChild( it.next() );
550     }
551   }
552 }
553
554 /*!
555   \brief Get entries of all child data objects of specified data object
556   \param entry data object entry
557   \param recursive \c true for recursive processing
558 */
559 QStringList SALOME_PYQT_ModuleLight::getChildren( const QString& entry, const bool recursive ) const
560 {
561   QStringList entryList;
562   SUIT_DataObject* dataObj = 0;
563   if ( !entry.isEmpty() ) {
564     dataObj = findObject( entry );
565   }
566   else {
567     SALOME_PYQT_DataModelLight* dm =
568       dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel() );
569     if ( dm ) dataObj = dm->getRoot();
570   }
571   if ( dataObj ) {
572     DataObjectList lst;
573     dataObj->children( lst, recursive );
574     QListIterator<SUIT_DataObject*> it( lst );
575     while ( it.hasNext() ) {
576       SALOME_PYQT_DataObjectLight* sobj = dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.next() );
577       entryList.append( sobj->entry() );
578     }
579   }
580   return entryList;
581 }
582
583 /*!
584   \brief Create new instance of data model and return it.
585 */
586 CAM_DataModel* SALOME_PYQT_ModuleLight::createDataModel()
587 {
588   return new SALOME_PYQT_DataModelLight( this );
589 }
590
591 /*!
592   \brief Find data object by its entry
593   \param entry data object entry
594   \return data object with given entry or 0 if object isn't found
595 */
596 SALOME_PYQT_DataObjectLight* SALOME_PYQT_ModuleLight::findObject( const QString& entry ) const
597 {
598   SALOME_PYQT_DataObjectLight* obj = 0;
599   SALOME_PYQT_DataModelLight* dm =
600     dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel() );
601   if ( !entry.isEmpty() && dm ) {
602     for ( SUIT_DataObjectIterator it( dm->getRoot(), SUIT_DataObjectIterator::DepthLeft ); it.current() && !obj; ++it ) { 
603       SALOME_PYQT_DataObjectLight* curentobj =
604         dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.current() );
605       if ( curentobj && curentobj->entry() == entry )
606         obj = curentobj;
607     }
608   }
609   return obj;
610 }