]> SALOME platform Git repositories - modules/gui.git/blob - src/Qtx/QtxMultiAction.cxx
Salome HOME
eb8bf5190d8c912b07f18aa28d10616b7f2bf851
[modules/gui.git] / src / Qtx / QtxMultiAction.cxx
1 //  Copyright (C) 2007-2008  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 //  Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 //  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 //  This library is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU Lesser General Public
8 //  License as published by the Free Software Foundation; either
9 //  version 2.1 of the License.
10 //
11 //  This library is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 //  Lesser General Public License for more details.
15 //
16 //  You should have received a copy of the GNU Lesser General Public
17 //  License along with this library; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 //  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22 // File:      QtxMultiAction.cxx
23 // Author:    Sergey TELKOV
24 //
25 #include "QtxMultiAction.h"
26
27 #include <QMenu>
28 #include <QLayout>
29 #include <QToolBar>
30 #include <QPainter>
31 #include <QHelpEvent>
32 #include <QToolButton>
33 #include <QApplication>
34 #include <QStyleOptionButton>
35
36 /*!
37   \class QtxMultiAction::Filter
38   \brief Waches for the buttons in the popup menu 
39   to update the tool buttons state.
40   \internal
41 */
42
43 class QtxMultiAction::Filter : public QObject
44 {
45 public:
46   //! \brief Constructor
47   Filter( QObject* parent ) : QObject( parent ) {}
48   //! \brief Destructor
49   ~Filter() {}
50   //! \brief Process events from the child tool buttons
51   bool eventFilter( QObject* o, QEvent* e )
52   {
53     if ( e->type() == QEvent::Leave ) {
54       QToolButton* tb = qobject_cast<QToolButton*>( o );
55       if ( tb )
56         tb->setDown( false );
57     }
58     return QObject::eventFilter( o, e );
59   }
60 };
61
62 /*!
63   \class QtxMultiAction::Menu
64   \brief Custom menu to be used with the toolbuttons as drop down list.
65   \internal
66 */
67
68 class QtxMultiAction::Menu : public QMenu
69 {
70 public:
71   //! \brief Constructor
72   Menu( QWidget* parent = 0 ) : QMenu( parent ) {}
73   //! \brief Destructor
74   ~Menu() {};
75
76 protected:
77   //! \brief Paint the button
78   virtual bool event( QEvent* e )
79   {
80     bool res = false;
81     switch ( e->type() )
82     {
83     case QEvent::ToolTip:
84     case QEvent::WhatsThis:
85     case QEvent::QueryWhatsThis:
86       {
87         QHelpEvent* help = static_cast<QHelpEvent*>( e );
88         QWidget* w = QApplication::widgetAt( help->globalPos() );
89         if ( w && Qtx::isParent( w, this ) )
90         {
91           QHelpEvent he( help->type(), w->mapFromGlobal( help->globalPos() ), help->globalPos() );
92           QApplication::sendEvent( w, &he );
93           res = true;
94         }
95       }
96       break;
97     case QEvent::StatusTip:
98     case QEvent::WhatsThisClicked:
99       if ( parentWidget() )
100       {
101         QApplication::sendEvent( parentWidget(), e );
102         res = true;
103       }
104       break;
105     default:
106       res = QMenu::event( e );
107       break;
108     }
109     return res;
110   }
111 };
112
113 /*!
114   \class QtxMultiAction::Button
115   \brief Custom button to be used in the toolbar.
116   \internal
117 */
118
119 class QtxMultiAction::Button : public QToolButton
120 {
121 public:
122   //! \brief Constructor
123   Button( QWidget* parent = 0 ) : QToolButton( parent ) {}
124   //! \brief Destructor
125   ~Button() {};
126
127 protected:
128   //! \brief Paint the button
129   virtual void paintEvent( QPaintEvent* e )
130   {
131     QToolButton::paintEvent( e );
132
133     int s = 10;
134     int m = -2;
135     int w = width();
136     int h = height();
137
138     QStyleOptionButton opt;
139     opt.initFrom( this );
140     QRect rect = opt.rect;
141     int x = rect.x(), y = rect.y();
142     if ( isDown() )
143       opt.rect = QRect( x + w - s - m, y + h - s - m, s, s );
144     else
145       opt.rect = QRect( x + w - s - m - 1, y + h - s - m - 1, s, s );
146
147     QPainter p( this );
148     style()->drawPrimitive( QStyle::PE_IndicatorSpinDown, &opt, &p );
149   }
150 };
151
152 /*!
153   \class QtxMultiAction
154   \brief The class QtxMultiAction implements modifiable action.
155
156   The QtxMultiAction class provides a possibility to assign a set of actions 
157   (insertAction() function). The action can be used in the toolbar (and even
158   in the menu) to show drop-down menu with the list of the assigned actions.
159
160   Initially the first action from the list becomes current and it is activated
161   when the tool button is clicked by the user. If user presses and holds the mouse
162   button at the tool button, it shows the popup menu with all the assigned actions.
163   When the user selects any action from the popup menu, it becames current.
164 */
165
166 /*!
167   \brief Constructor.
168   \param parent parent object
169 */
170 QtxMultiAction::QtxMultiAction( QObject* parent )
171 : QtxActionSet( parent ),
172   myCurrent( 0 )
173 {
174   setVisible( true );
175   setMenu( new QMenu( 0 ) );
176
177   connect( this, SIGNAL( triggered( QAction* ) ), this, SLOT( onTriggered( QAction* ) ) );
178 }
179
180 /*!
181   \brief Constructor.
182   \param txt action menu text
183   \param parent parent object
184 */
185 QtxMultiAction::QtxMultiAction( const QString& txt, QObject* parent )
186 : QtxActionSet( parent ),
187   myCurrent( 0 )
188 {
189   setText( txt );
190   setVisible( true );
191   setMenu( new QMenu( 0 ) );
192
193   connect( this, SIGNAL( triggered( QAction* ) ), this, SLOT( onTriggered( QAction* ) ) );
194 }
195
196 /*!
197   \brief Constructor.
198   \param ico action menu icon
199   \param txt action menu text
200   \param parent parent object
201 */
202 QtxMultiAction::QtxMultiAction( const QIcon& ico, const QString& txt, QObject* parent )
203 : QtxActionSet( parent ),
204   myCurrent( 0 )
205 {
206   setIcon( ico );
207   setText( txt );
208   setVisible( true );
209   setMenu( new QMenu( 0 ) );
210
211   connect( this, SIGNAL( triggered( QAction* ) ), this, SLOT( onTriggered( QAction* ) ) );
212 }
213
214 /*!
215   \brief Destructor
216 */
217 QtxMultiAction::~QtxMultiAction()
218 {
219 }
220
221 /*!
222   \brief Set current action.
223   \param a action to be set current
224 */
225 void QtxMultiAction::setActiveAction( QAction* a )
226 {
227   if ( a && actions().contains( a ) && a != myCurrent && a->isEnabled() )
228   {
229     myCurrent = a;
230     updateAction();
231   }
232 }
233
234 /*!
235   \brief Get current action.
236   \return current action (0 if there is no active action)
237 */
238 QAction* QtxMultiAction::activeAction() const
239 {
240   return myCurrent;
241 }
242
243 /*!
244   \brief Called when the user activates the current action 
245   (for example by clicking the tool button).
246   \param on (not used)
247 */
248 void QtxMultiAction::onClicked( bool /*on*/ )
249 {
250   if ( myCurrent )
251     myCurrent->activate( QAction::Trigger );
252 }
253
254 /*!
255   \brief Called when user activates any action from the
256   dropdown menu.
257   \param a action being activated
258 */
259 void QtxMultiAction::onTriggered( QAction* a )
260 {
261   if ( !a )
262     return;
263
264   QList<QWidget*> lst = createdWidgets();
265   for ( QList<QWidget*>::iterator it = lst.begin(); it != lst.end(); ++it )
266   {
267     QToolButton* tb = ::qobject_cast<QToolButton*>( *it );
268     if ( tb && tb->menu() )
269       tb->menu()->hide();
270   }
271
272   if ( myCurrent != a )
273   {
274     myCurrent = a;
275     updateAction();
276   }
277 }
278
279 /*!
280   \brief Update action.
281 */
282 void QtxMultiAction::updateAction()
283 {
284   QtxActionSet::updateAction();
285
286   QList<QWidget*> lst = createdWidgets();
287   for ( QList<QWidget*>::iterator it = lst.begin(); it != lst.end(); ++it )
288     updateButton( ::qobject_cast<QToolButton*>( *it ) );
289 }
290
291 /*!
292   \brief Update child (popup menu) action.
293   \param w widget menu widget
294 */
295 void QtxMultiAction::updateAction( QWidget* w )
296 {
297   if ( !w )
298     return;
299
300   if ( w->inherits( "QMenu" ) )
301   {
302     QtxActionSet::updateAction( menu() );
303
304     QApplication::instance()->removeEventFilter( this );
305
306     menu()->removeAction( this );
307
308     QApplication::instance()->installEventFilter( this );
309   }
310 }
311
312 /*!
313   \brief Check if the action itself should be invisible
314   (only child action are shown)
315   \return \c true if the action itself should be visible
316 */
317 bool QtxMultiAction::isEmptyAction() const
318 {
319   return false;
320 }
321
322 /*!
323   \brief Create widget to be displayed in the toolbar.
324   \param parent parent widget (should be toolbar)
325   \return toolbar button
326 */
327 QWidget* QtxMultiAction::createWidget( QWidget* parent )
328 {
329   QToolBar* tb = ::qobject_cast<QToolBar*>( parent );
330   if ( !tb )
331     return 0;
332
333   QToolButton* w = new QToolButton( tb );
334   w->setMenu( new Menu( w ) );
335   w->setMouseTracking( true );
336   w->setFocusPolicy( Qt::NoFocus );
337   w->setIconSize( tb->iconSize() );
338   w->setToolButtonStyle( tb->toolButtonStyle() );
339
340   connect( w, SIGNAL( clicked( bool ) ), this, SLOT( onClicked( bool ) ) );
341   connect( tb, SIGNAL( iconSizeChanged( const QSize& ) ), w, SLOT( setIconSize( QSize ) ) );
342   connect( tb, SIGNAL( toolButtonStyleChanged( Qt::ToolButtonStyle ) ),
343            w, SLOT( setToolButtonStyle( Qt::ToolButtonStyle ) ) );
344
345   updateButton( w );
346   return w;
347 }
348
349 /*!
350   \brief Called when the child action is added to this action.
351   \param a child action being added
352 */
353 void QtxMultiAction::actionAdded( QAction* a )
354 {
355   connect( a, SIGNAL( changed() ), this, SLOT( onActionChanged() ) );
356   onActionChanged();
357 }
358
359 /*!
360   \brief Called when the child action is removed from this action.
361   \param a child action being removed
362 */
363 void QtxMultiAction::actionRemoved( QAction* a )
364 {
365   disconnect( a, SIGNAL( changed() ), this, SLOT( onActionChanged() ) );
366
367   if ( myCurrent != a )
368     return;
369
370   myCurrent = 0;
371
372   onActionChanged();
373
374   updateAction();
375 }
376
377 /*!
378   \brief Update toolbar button.
379   \param btn toolbar button
380 */
381 void QtxMultiAction::updateButton( QToolButton* btn )
382 {
383   if ( !btn )
384     return;
385
386   btn->setIcon( myCurrent ? myCurrent->icon() : QIcon() );
387   btn->setText( myCurrent ? myCurrent->text() : QString() );
388   btn->setToolTip( myCurrent ? myCurrent->toolTip() : QString() );
389   btn->setStatusTip( myCurrent ? myCurrent->statusTip() : QString() );
390
391   QMenu* pm = btn->menu();
392   if ( !pm )
393     return;
394
395   pm->clear();
396   for ( int i = 0; pm->layout() && i < pm->layout()->count(); i++ )
397     delete pm->layout()->widget();
398
399   delete pm->layout();
400
401   QVBoxLayout* vbox = new QVBoxLayout( pm );
402   vbox->setMargin( 1 );
403   vbox->setSpacing( 0 );
404   Filter* filter = new Filter( vbox );
405   QList<QAction*> actList = actions();
406   for ( QList<QAction*>::iterator itr = actList.begin(); itr != actList.end(); ++itr )
407   {
408     QToolButton* b = new QToolButton( pm );
409     b->setDefaultAction( *itr );
410     b->setToolTip( (*itr)->toolTip() );
411     b->setStatusTip( (*itr)->statusTip() );
412     b->setAutoRaise( true );
413     b->setIconSize( btn->iconSize() );
414     b->setToolButtonStyle( btn->toolButtonStyle() );
415     b->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
416     b->installEventFilter( filter );
417     vbox->addWidget( b );
418   }
419 }
420
421 /*!
422   \brief Called when any child action is enabled/disabled.
423   
424   If the current action is disabled, the multi-action switches
425   to first found enabled. If all child actions are disabled, the
426   action itself is also disabled.
427 */
428 void QtxMultiAction::onActionChanged()
429 {
430   if ( myCurrent && myCurrent->isEnabled() )
431     return;
432
433   QList<QAction*> alist = actions();
434   QAction* a = 0;
435   for ( QList<QAction*>::ConstIterator it = alist.begin(); it != alist.end() && !a; ++it ) {
436     if ( (*it)->isEnabled() )
437       a = *it;
438   }
439
440   if ( a )
441     myCurrent = a;
442   else
443     myCurrent = alist.isEmpty() ? 0 : alist.first();
444
445   setEnabled( myCurrent && myCurrent->isEnabled() );
446   updateAction();
447 }