1 // Copyright (C) 2007-2022 CEA/DEN, EDF R&D, OPEN CASCADE
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.
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.
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
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 // File : LightApp_ModuleAction.cxx
21 // Author : Sergey TELKOV, Vadim SANDLER
23 #include "LightApp_ModuleAction.h"
25 #include <QtxActionSet.h>
26 #include <QtxComboBox.h>
27 #include <QtxResourceMgr.h>
28 #include <QVBoxLayout>
29 #include <QApplication>
32 #include <QSignalMapper>
34 #include <utilities.h>
36 // Prevent slot compilation error
37 #pragma push_macro("slots")
39 #include "PyInterp_Utils.h"
40 #pragma pop_macro("slots")
43 \class LightApp_ModuleAction::ActionSet
44 \brief Internal class to represent list of modules buttons.
48 class LightApp_ModuleAction::ActionSet : public QtxActionSet
51 ActionSet( QObject* );
52 QAction* moduleAction( const QString& ) const;
53 int moduleId( const QString& ) const;
54 int moduleId( QAction* ) const;
55 void setVisible( bool );
61 \param parent parent object
63 LightApp_ModuleAction::ActionSet::ActionSet( QObject* parent )
64 : QtxActionSet( parent )
69 \brief Get action corresponding to the specified module.
71 \param name module name
72 \return module action or 0 if \a name is invalid
74 QAction* LightApp_ModuleAction::ActionSet::moduleAction( const QString& name ) const
78 QList<QAction*> alist = actions();
79 for ( QList<QAction*>::const_iterator it = alist.begin(); it != alist.end() && !a; ++it )
81 if ( (*it)->text() == name )
89 \brief Get module action identifier.
91 \param name module name
92 \return module action ID or -1 if \a name is invalid
94 int LightApp_ModuleAction::ActionSet::moduleId( const QString& name ) const
98 QList<QAction*> alist = actions();
99 for ( QList<QAction*>::const_iterator it = alist.begin(); it != alist.end() && id == -1; ++it )
101 if ( (*it)->text() == name )
102 id = actionId( *it );
109 \brief Get module action identifier.
111 \param a module action
112 \return module action ID or -1 if \a a is null or invalid
114 int LightApp_ModuleAction::ActionSet::moduleId( QAction* a ) const
116 return actionId( a );
120 \brief Show/hide modules actions.
122 \param on new visibility state
124 void LightApp_ModuleAction::ActionSet::setVisible( bool on )
126 QList<QAction*> alist = actions();
127 for ( QList<QAction*>::const_iterator it = alist.begin(); it != alist.end(); ++it )
128 (*it)->setVisible( on );
130 QtxActionSet::setVisible( on );
134 \class LightApp_ModuleAction::ComboAction
135 \brief Internal class to represent combo box with the list of modules in the toolbar.
142 \param parent parent object
144 LightApp_ModuleAction::ComboAction::ComboAction( QObject* parent )
145 : QtxAction( parent )
150 \brief Get list of associated widgets.
152 \return list of created widgets (QtxComboBox)
154 QList<QtxComboBox*> LightApp_ModuleAction::ComboAction::widgets() const
156 QList<QtxComboBox*> lst;
158 QList<QWidget*> wlist = createdWidgets();
159 for ( QList<QWidget*>::const_iterator wit = wlist.begin(); wit != wlist.end(); ++wit )
160 lst += qobject_cast<QtxComboBox*>(*wit);
166 \brief Create combo box widget by request from the toolbar.
168 \param parent parent widget (should be QToolBar or its successor)
169 \return new custom widget, containing combo box
171 QWidget* LightApp_ModuleAction::ComboAction::createWidget( QWidget* parent )
173 if ( !parent->inherits( "QToolBar" ) )
176 QtxComboBox* cb = new QtxComboBox( parent );
177 cb->setSizeAdjustPolicy( QComboBox::AdjustToContents );
178 cb->setFocusPolicy( Qt::NoFocus );
179 connect( cb, SIGNAL( activatedId( int ) ), this, SIGNAL( activatedId( int ) ) );
184 \fn void LightApp_ModuleAction::ComboAction::activatedId( int id );
186 \brief Emitted when the combo box item is activated
187 \param item identifier
191 \class LightApp_ModuleAction::ActivateEvent
192 \brief Internal class to represent custom event for transfer the activation item id.
195 class LightApp_ModuleAction::ActivateEvent : public QEvent
198 ActivateEvent( QEvent::Type type, int id ) : QEvent( type ), myId( id ) {};
201 int id() const { return myId; }
208 \class LightApp_ModuleAction
209 \brief An action, representing the list of modules to be inserted to the
212 In the toolbar this action is represented as the combo box with the list of
213 available modules, and a set of buttons for each module. Additionally, combo box
214 contains an item representing "neutral point" of the application (i.e. no active module).
216 In menu, the action is represented as a plain list of items, one per module.
218 Only one module can be active at the moment. It can be set programmatically
219 with setActiveModule() function. Use this method with empty string to turn
220 to the "neutral point". To get active module, use activeModule() function.
222 When user activates/deactivates a module, the moduleActivated() signal
225 The action also provides an additional separate item "Add modules"; when
226 this button is pressed, a adding() signal is emitted. This signal
227 can be connected to a dedicated slot aimed to dynamically add a new module
228 into the application. In addition, a button "Remove module" shows a dropdown menu
229 with the list of user modules; when any item is selected, the removing() signal
230 is emitted. This signal may be connected to a slot aimed to dynamically remove
231 selected user module from application.
233 It is possible to customize which elements to show via the setMode() of setModeEnabled()
234 functions. By default, all elements are shown. The following choices are possible:
236 - LightApp_ModuleAction::Buttons: show separate items for all modules
237 - LightApp_ModuleAction::List: show combo box with list of modules (in toolbar only)
238 - LightApp_ModuleAction::AddRemove: show "Add modules" and "Remove modules" items
239 - LightApp_ModuleAction::All: show all items
244 \param resMgr resource manager
245 \param parent parent object
247 LightApp_ModuleAction::LightApp_ModuleAction( QtxResourceMgr* resMgr, QObject* parent )
248 : QtxAction( parent )
250 setText( tr( "APP_NAME" ) );
251 setIcon( resMgr->loadPixmap( "LightApp", tr( "APP_DEFAULT_ICO" ), false ) );
255 myCombo = new ComboAction( this );
256 myAdd = new QtxAction( tr( "ADD_MODULE"),
257 resMgr->loadPixmap( "LightApp", tr( "ICON_ADD_MODULE" ), false ),
260 myRemove = new QtxAction( tr( "REMOVE_MODULE"),
261 resMgr->loadPixmap( "LightApp", tr( "ICON_REMOVE_MODULE" ), false ),
262 tr( "REMOVE_MODULE"),
264 myRemove->setEnabled( false );
265 myRemove->setMenu( new QMenu() );
266 myInfo = new QtxAction( tr( "INFO_MODULE"),
267 resMgr->loadPixmap( "LightApp", tr( "ICON_INFO_MODULE" ), false ),
270 mySeparator = new QAction( this );
271 mySeparator->setSeparator( true );
272 mySet = new ActionSet( this );
274 myMapper = new QSignalMapper( this );
276 connect( this, SIGNAL( changed() ), this, SLOT( onChanged() ) );
277 connect( myAdd, SIGNAL( triggered( bool ) ), this, SIGNAL( adding() ) );
278 connect( myInfo, SIGNAL( triggered( bool ) ), this, SIGNAL( showExtInfo() ) );
279 connect( mySet, SIGNAL( triggered( int ) ), this, SLOT( onTriggered( int ) ) );
280 connect( myCombo, SIGNAL( activatedId( int ) ), this, SLOT( onComboActivated( int ) ) );
281 connect( myMapper, SIGNAL( mapped( QString ) ), this, SIGNAL( removing( QString ) ) );
287 LightApp_ModuleAction::~LightApp_ModuleAction()
292 \brief Get number of registered modules.
293 \return modules count
295 int LightApp_ModuleAction::count() const
297 return modules().count();
301 \brief Get list of modules.
302 \return modules names list
304 QStringList LightApp_ModuleAction::modules() const
308 QList<QAction*> alist = mySet->actions();
309 for ( QList<QAction*>::const_iterator it = alist.begin(); it != alist.end(); ++it )
310 lst.append( (*it)->text() );
316 \brief Get module icon.
317 \param name module name
321 QIcon LightApp_ModuleAction::moduleIcon( const QString& name ) const
323 QAction* a = mySet->moduleAction( name );
324 return a ? a->icon() : QIcon();
328 \brief Set module icon.
329 \param name module name
330 \param ico new module icon
333 void LightApp_ModuleAction::setModuleIcon( const QString& name, const QIcon& ico )
335 QAction* a = mySet->moduleAction( name );
344 \brief Get module action.
345 \param name module name
347 QAction* LightApp_ModuleAction::moduleAction( const QString& name ) const
349 return mySet->moduleAction( name );
353 \brief Add module into the list.
354 \param name module name
355 \param ico module icon
356 \param idx position in the module list (if -1, the module is added to the end of list)
359 void LightApp_ModuleAction::insertModule( const QString& name, const QIcon& ico,
362 insertModule( name, ico, false, idx );
366 \brief Add module into the list.
367 \param name module name
368 \param ico module icon
369 \param isCustom \c false to insert regular module, \c true to insert user module
370 \param idx position in the module list (if -1, the module is added to the end of list)
373 void LightApp_ModuleAction::insertModule( const QString& name, const QIcon& ico,
374 bool isCustom, const int idx)
377 QtxAction* a = new QtxAction( name, ico, name, 0, this, true );
378 a->setStatusTip( tr( "ACTIVATE_MODULE_TOP" ).arg( name ) );
380 // Commented because the next call mySet->insertAction() overrides it with
381 // action id int != 0 value, so test a->data().toBool() is always true after that.
382 // Leave it here to mention that we need other approach to mark module as custom.
383 // a->setData( isCustom );
385 mySet->insertAction( a, -1, idx );
390 \brief Remove module from the list.
391 \param name module name
394 void LightApp_ModuleAction::removeModule( const QString& name )
396 MESSAGE("Start to remove module...");
398 int id = mySet->moduleId( name );
402 MESSAGE("Can't get a module's id! Return");
406 MESSAGE("Remove action by id...");
407 mySet->removeAction(id);
411 MESSAGE("Module was removed");
415 \brief Add an installed extension. Now only to the Remove button's menu.
416 \param name an extension's name
417 \sa removeExtension()
419 void LightApp_ModuleAction::insertExtension(const QString& name)
421 MESSAGE("Insert an extension's action...");
422 SCRUTE(name.toStdString());
424 myRemove->setEnabled(true);
426 // Find a place to insert in the alphabetical order
427 QAction* insertBefore = nullptr;
428 foreach(QAction* curAction, myRemove->menu()->actions())
430 int compareRes = QString::compare(curAction->text(), name, Qt::CaseInsensitive);
433 return; // already added
435 else if (compareRes > 0)
437 insertBefore = curAction;
439 SCRUTE(insertBefore->text().toStdString());
444 QAction* inserted = new QAction(name);
446 myRemove->menu()->insertAction(insertBefore, inserted);
447 connect(inserted, SIGNAL(triggered()), myMapper, SLOT(map()));
448 myMapper->setMapping(inserted, name);
450 MESSAGE("An extension's action was inserted");
454 \brief Remove an installed extension.
455 \param name an extension's name
456 \sa insertExtension()
458 void LightApp_ModuleAction::removeExtension(const QString& name)
460 MESSAGE("Remove an extension's action...");
461 SCRUTE(name.toStdString());
463 foreach(QAction* ma, myRemove->menu()->actions())
465 if (ma->text() == name)
467 myRemove->menu()->removeAction(ma);
469 MESSAGE("Extension's action was removed");
474 myRemove->setEnabled(!myRemove->menu()->actions().isEmpty());
481 \brief Get active module.
483 If there is no active module ("neutral point"), then the null string
486 \return active module name
487 \sa setActiveModule()
489 QString LightApp_ModuleAction::activeModule() const
491 QAction* a = active();
492 return a ? a->text() : QString();
496 \brief Set active module.
498 To turn to the "neutral point" (no active module), pass empty string.
500 \param name new active module name
503 void LightApp_ModuleAction::setActiveModule( const QString& name )
505 if ( name == activeModule() )
508 int id = mySet->moduleId( name );
509 if ( name.isEmpty() || id != -1 )
510 activate( id, false );
514 \brief Set action display mode.
515 \param mode action display options (combination of flags)
518 void LightApp_ModuleAction::setMode( const LightApp_ModuleAction::Mode& mode )
525 \brief Enable / disable action display mode.
526 \param mode action display options (combination of flags)
527 \param enabled \c true to enable mode, \c false to disable mode
530 void LightApp_ModuleAction::setModeEnabled( const LightApp_ModuleAction::Mode& mode, bool enabled )
540 \brief Get action display mode.
541 \param mode action display mode
544 bool LightApp_ModuleAction::isModeEnabled( const LightApp_ModuleAction::Mode& mode ) const
546 return (bool)( myMode & mode );
550 \brief Called when the action is added to the widget.
551 \param w widget (not used)
553 void LightApp_ModuleAction::addedTo( QWidget* w )
555 if ( w->inherits( "QToolBar" ) )
556 w->insertAction( this, myCombo );
557 w->insertAction( this, myAdd );
558 w->insertAction( this, myRemove );
559 w->insertAction( this, myInfo );
560 w->insertAction( this, mySeparator );
561 w->insertAction( this, mySet );
566 \brief Remove action from widget.
567 \param w widget (menu or toolbar)
568 \return \c true if the action is removed successfully and \c false otherwise.
571 void LightApp_ModuleAction::removedFrom( QWidget* w )
573 if ( w->inherits( "QToolBar" ) )
574 w->removeAction( myCombo );
575 w->removeAction( myAdd );
576 w->removeAction( myRemove );
577 w->removeAction( myInfo );
578 w->removeAction( mySeparator );
579 w->removeAction( mySet );
583 \brief Perform delayed activation with specified id.
584 \param e custom event
585 \return \c true if the event was processed successfully and \c false otherwise.
587 bool LightApp_ModuleAction::event( QEvent* e )
589 if ( e->type() == QEvent::MaxUser ) {
590 activate( ((ActivateEvent*)e)->id(), false );
593 return QtxAction::event( e );
597 \fn void LightApp_ModuleAction::moduleActivated( const QString& name );
598 \brief Emitted when the module is activated
599 \param name module name (empty string for neutral point)
603 \brief Update an action.
606 void LightApp_ModuleAction::update()
608 QList<QtxComboBox*> lst = myCombo->widgets();
609 for ( QList<QtxComboBox*>::const_iterator it = lst.begin(); it != lst.end(); ++it )
613 myCombo->setVisible( myMode & List );
614 if ( QString::compare(getenv("SALOME_ON_DEMAND"),"HIDE", Qt::CaseInsensitive) != 0)
616 myAdd->setVisible( myMode & AddRemove );
617 myRemove->setVisible( myMode & AddRemove );
618 myInfo->setVisible( myMode & All );
622 myAdd->setVisible(false);
623 myRemove->setVisible( false );
624 myInfo->setVisible( false );
626 mySet->setVisible( myMode & Buttons );
630 \brief Update combo box.
634 void LightApp_ModuleAction::update( QtxComboBox* cb )
639 bool blocked = cb->blockSignals( true );
640 int curId = mySet->moduleId( active() );
641 QList<QAction*> alist = mySet->actions();
644 cb->addItem( icon(), text() );
647 for ( QList<QAction*>::const_iterator it = alist.begin(); it != alist.end(); ++it )
650 int id = mySet->moduleId( a );
651 cb->addItem( a->icon(), a->text() );
652 cb->setId( cb->count() - 1, id );
655 cb->setCurrentId( curId );
656 cb->blockSignals( blocked );
660 \brief Update extension actions based on dependencies.
664 void LightApp_ModuleAction::updateExtActions()
666 MESSAGE("Check dependencies to update extensions actions...");
668 // It should be set on the app start
669 auto extRootDir = getenv("SALOME_APPLICATION_DIR");
672 MESSAGE("Cannot get SALOME_APPLICATION_DIR env variable! Cancel adding selected extensions.");
677 // Import Python module that manages SALOME extensions.
678 PyLockWrapper lck; // acquire GIL
679 PyObjWrapper extensionQuery = PyImport_ImportModule((char*)"SalomeOnDemandTK.extension_query");
680 PyObjWrapper extCanRemoveDict = PyObject_CallMethod(extensionQuery, (char*)"ext_canremove_flags", (char*)"s", extRootDir);
681 if (!extCanRemoveDict || extCanRemoveDict == Py_None)
683 MESSAGE("Couldn't get <ext>:<can_remove> dictionary from SalomeOnDemandTK.extension_query! Return.");
687 // Iterate extensions' actions to disable ones we can't remove because of dependencies.
688 foreach(QAction* curAction, myRemove->menu()->actions())
690 const std::string action_name = curAction->text().toStdString();
693 PyObject* canRemoveObject = PyDict_GetItemString(extCanRemoveDict, action_name.c_str());
694 if (!canRemoveObject)
696 MESSAGE("Couldn't get can remove flag from dictionary! Skip.");
700 const int isTrueRes = PyObject_IsTrue(canRemoveObject);
703 MESSAGE("PyObject_IsTrue() failed. Using false value instead.");
705 const bool canRemove = isTrueRes == 1;
708 curAction->setEnabled(canRemove);
713 \brief Get an action corresponding to the active module.
715 \return active module action or 0 if there is no active module
717 QAction* LightApp_ModuleAction::active() const
721 QList<QAction*> alist = mySet->actions();
722 for ( QList<QAction*>::const_iterator it = alist.begin(); it != alist.end() && !a; ++it )
724 if ( (*it)->isChecked() )
732 \brief Activate a module item.
734 \param id module identifier
735 \param fromAction \c true if function is called from the module button
737 void LightApp_ModuleAction::activate( int id, bool fromAction )
739 bool checked = false;
741 QList<QAction*> alist = mySet->actions();
742 for ( QList<QAction*>::const_iterator it = alist.begin(); it != alist.end(); ++it )
744 if ( mySet->moduleId( *it ) != id ) {
745 (*it)->setChecked( false );
749 (*it)->setChecked( true );
750 checked = (*it)->isChecked();
754 QList<QtxComboBox*> widgets = myCombo->widgets();
755 for ( QList<QtxComboBox*>::const_iterator wit = widgets.begin(); wit != widgets.end(); ++wit )
757 QtxComboBox* cb = *wit;
758 bool blocked = cb->signalsBlocked();
759 cb->blockSignals( true );
760 cb->setCurrentId( checked ? id : -1 );
761 cb->blockSignals( blocked );
764 emit moduleActivated( activeModule() );
768 \brief Called when module button is triggered.
770 \param id module identifier
772 void LightApp_ModuleAction::onTriggered( int id )
778 \brief Called when action state is changed.
781 This slot is used to prevent making the parent action visible.
783 void LightApp_ModuleAction::onChanged()
788 bool block = signalsBlocked();
789 blockSignals( true );
791 blockSignals( block );
795 \brief Called when combo box item is activated.
796 \param id module identifier
798 void LightApp_ModuleAction::onComboActivated( int id )
800 QApplication::postEvent( this, new ActivateEvent( QEvent::MaxUser, id ) );