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