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