Salome HOME
Update copyrights
[modules/gui.git] / src / Qtx / QtxToolButton.cxx
1 // Copyright (C) 2007-2019  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 #include "QtxToolButton.h"
21 #include "QtxMenu.h"
22
23 #include <QKeyEvent>
24 #include <QWheelEvent>
25
26 namespace
27 {
28   bool isSeparator( QAction* a )
29   {
30     return a->property( "separator" ).toBool();
31   }
32   void setSeparator( QAction* a )
33   {
34     a->setProperty( "separator", true );
35   }
36 }
37
38 /*!
39   \class QtxToolButton
40   \brief Drop-down tool button that behaves like a drop-down combo-box.
41
42   In contrast to the standard combo box, QtxToolButton can show drop-down
43   menu containing groups of items where each group has a separate title.
44 */
45
46 /*!
47   \brief Constructor.
48   \param parent Parent widget.
49 */
50 QtxToolButton::QtxToolButton( QWidget* parent )
51   : QToolButton( parent )
52 {
53   setMenu( new QtxMenu( this ) );
54   setPopupMode( InstantPopup );
55   setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
56   connect( this, SIGNAL( triggered( QAction* ) ), this, SLOT( actionTriggered( QAction* ) ) );
57 }
58
59 /*!
60   \brief Destructor.
61 */
62 QtxToolButton::~QtxToolButton()
63 {
64 }
65
66 /*!
67   \brief Add an item with the given \a text, and containing the specified \a userData.
68   The item is appended to the list of existing items.
69   \param text Item's text.
70   \param userData Item's data.
71   \return Index of just added item.
72 */
73 int QtxToolButton::addItem( const QString& text, const QVariant& userData )
74 {
75   QAction* action = menu()->addAction( text );
76   action->setData( userData );
77   bool currentChanged = false;
78   if ( !menu()->defaultAction() ) {
79     menu()->setDefaultAction( action );
80     currentChanged = true;
81   }
82   internalUpdate();
83   if ( currentChanged ) emitCurrentChanged( false, true );
84   return count()-1;
85 }
86
87 /*!
88   \brief Add an item with the given \a icon and \a text, and containing the specified
89   \a userData. The item is appended to the list of existing items.
90   \param text Item's text.
91   \param icon Item's icon.
92   \param userData Item's data.
93   \return Index of just added item.
94 */
95 int QtxToolButton::addItem( const QIcon& icon, const QString& text, const QVariant& userData )
96 {
97   QAction* action = menu()->addAction( icon, text );
98   action->setData( userData );
99   bool currentChanged = false;
100   if ( !menu()->defaultAction() ) {
101     menu()->setDefaultAction( action );
102     currentChanged = true;
103   }
104   internalUpdate();
105   if ( currentChanged ) emitCurrentChanged( false, true );
106   return count()-1;
107 }
108
109 /*!
110   \brief Add items with given \a texts. Each item is appended to the list of existing
111   items in turn.
112   \param texts Items being added.
113 */
114 void QtxToolButton::addItems( const QStringList& texts )
115 {
116   Q_FOREACH( QString text, texts )
117     menu()->addAction( text );
118   bool currentChanged = false;
119   if ( !menu()->defaultAction() && menu()->actions().count() > 0 ) {
120     menu()->setDefaultAction( menu()->actions()[0] );
121     currentChanged = true;
122   }
123   if ( currentChanged ) emitCurrentChanged( false, true );
124   internalUpdate();
125 }
126
127 /*!
128   \brief Add separator to the end of the items list.
129   \param text Separator's text.
130   \return Index of just added item.
131 */
132 int QtxToolButton::addSeparator( const QString& text )
133 {
134   (qobject_cast<QtxMenu*>(menu()))->addGroup( text );
135   setSeparator( actionAt( count()-1 ) );
136   return count()-1;
137 }
138
139 /*!
140   \brief Add separator to the end of the items list.
141   \param icon Separator's icon.
142   \param text Separator's text.
143   \return Index of just added item.
144 */
145 int QtxToolButton::addSeparator( const QIcon& icon, const QString& text )
146 {
147   (qobject_cast<QtxMenu*>(menu()))->addGroup( icon, text );
148   setSeparator( actionAt( count()-1 ) );
149   return count()-1;
150 }
151
152 /*!
153   \brief Remove item with given \a index.
154   \param index Index of item to be removed.
155 */
156 void QtxToolButton::removeItem( int index )
157 {
158   QAction* action = actionAt( index );
159   if ( !action ) return;
160   QAction* current = menu()->defaultAction();
161   menu()->removeAction( action );
162   bool currentChanged = false;
163   if ( action == current ) {
164     for ( int i = index; i < count(); i++ ) {
165       QAction* a = actionAt( i );
166       if ( a && !isSeparator( a ) ) {
167         menu()->setDefaultAction( a );
168         currentChanged = true;
169         break;
170       }
171     }
172     for ( int i = index-1; i >= 0; i-- ) {
173       QAction* a = actionAt( i );
174       if ( a && !isSeparator( a ) ) {
175         menu()->setDefaultAction( a );
176         currentChanged = true;
177         break;
178       }
179     }
180   }
181   internalUpdate();
182   if ( currentChanged ) emitCurrentChanged( false, true );
183 }
184
185 /*!
186   \brief Get the number of items.
187   \return Number of items.
188 */
189 int QtxToolButton::count() const
190 {
191   return menu()->actions().count();
192 }
193
194 /*!
195   \brief Get data of the current item.
196   \return Current item's data (invalid QVariant if list of items is empty
197   or current item doesn't have data).
198 */
199 QVariant QtxToolButton::currentData() const
200 {
201   QAction* action = menu()->defaultAction();
202   return action == 0 ? QVariant() : action->data();
203 }
204
205 /*!
206   \brief Get index of current item.
207   \return Current item's index; -1 if list of items is empty or if there's no
208   current item.
209 */
210 int QtxToolButton::currentIndex() const
211 {
212   QAction* action = menu()->defaultAction();
213   return action == 0 ? -1 : menu()->actions().indexOf( action );
214 }
215
216 /*!
217   \brief Get text of current item.
218   \return Current item's text; null sting if list of items is empty or
219   if there's no current item.
220 */
221 QString QtxToolButton::currentText() const
222 {
223   QAction* action = menu()->defaultAction();
224   return action == 0 ? QString() : action->text();
225 }
226
227 /*!
228   \brief Get custom data of the item at given \a index.
229   \param index Item's index.
230   \return Item's data (invalid QVariant if index is out of range).
231 */
232 QVariant QtxToolButton::itemData( int index ) const
233 {
234   QAction* action = actionAt( index );
235   return action == 0 ? QVariant() : action->data();
236 }
237
238 /*!
239   \brief Get icon of the item at given \a index.
240   \param index Item's index.
241   \return Item's icon.
242 */
243 QIcon QtxToolButton::itemIcon( int index ) const
244 {
245   QAction* action = actionAt( index );
246   return action == 0 ? QIcon() : action->icon();
247 }
248
249 /*!
250   \brief Get text of the item at given \a index.
251   \param index Item's index.
252   \return Item's text.
253 */
254 QString QtxToolButton::itemText( int index ) const
255 {
256   QAction* action = actionAt( index );
257   return action == 0 ? QString() : action->text();
258 }
259
260 /*!
261   \brief Set custom data of the item at given \a index.
262   \param index Item's index.
263   \param value Item's data.
264 */
265 void QtxToolButton::setItemData( int index, const QVariant& value )
266 {
267   QAction* action = actionAt( index );
268   if ( action ) action->setData( value );
269 }
270
271 /*!
272   \brief Set icon of the item at given \a index.
273   \param index Item's index.
274   \param icon Item's icon.
275 */
276 void QtxToolButton::setItemIcon( int index, const QIcon& icon )
277 {
278   QAction* action = actionAt( index );
279   if ( action ) action->setIcon( icon );
280   internalUpdate();
281 }
282
283 /*!
284   \brief Set text of the item at given \a index.
285   \param index Item's index.
286   \param text Item's text.
287 */
288 void QtxToolButton::setItemText( int index, const QString& text )
289 {
290   QAction* action = actionAt( index );
291   bool currentChanged = false;
292   if ( action ) {
293     currentChanged = menu()->defaultAction() == action && action->text() != text;
294     action->setText( text );
295   }
296   internalUpdate();
297   if ( currentChanged )
298     emit currentTextChanged( text );
299 }
300
301 /*!
302   \brief Search item with given \a text.
303   \param Item's text.
304   \return Item's index; -1 if item is not found.
305 */
306 int QtxToolButton::findText( const QString& text )
307 {
308   int index = -1;
309   for ( int i = 0; i < count() && index == -1; i++ ) {
310     QAction* action = actionAt( i );
311     if ( isSeparator( action ) ) continue;
312     if ( action->text() == text ) index = i;
313   }
314   return index;
315 }
316
317 /*!
318   \brief Clear widget.
319 */
320 void QtxToolButton::clear()
321 {
322   QAction* action = menu()->defaultAction();
323   menu()->clear();
324   internalUpdate();
325   if ( action ) emitCurrentChanged( false, true );
326 }
327
328 /*!
329   \brief Set current item by given \a index.
330   \param index Item's index.
331 */
332 void QtxToolButton::setCurrentIndex( int index )
333 {
334   bool currentChanged = false;
335   if ( index == -1 ) {
336     currentChanged = currentIndex() != -1;
337     menu()->setDefaultAction( 0 );
338   }
339   else if ( index >= count() )
340     return;
341   else {
342     QAction* action = actionAt( index );
343     if ( !isSeparator( action ) ) {
344       currentChanged = currentIndex() != index;
345       menu()->setDefaultAction( action );
346     }
347   }
348   internalUpdate();
349   if ( currentChanged ) emitCurrentChanged( false, true );
350 }
351
352 /*!
353   \brief Set current item by given \a text.
354   \param index Item's index.
355 */
356 void QtxToolButton::setCurrentText( const QString& text )
357 {
358   int index = findText( text );
359   if ( index != -1 )
360     setCurrentIndex( index );
361 }
362
363 /*!
364   \brief Reimplemented from QToolButton::keyPressEvent().
365   Process key press event.
366   \param e Key press event.
367 */
368 void QtxToolButton::keyPressEvent( QKeyEvent* e )
369 {
370   Move move = NoMove;
371   switch ( e->key() ) {
372   case Qt::Key_Up:
373   case Qt::Key_PageUp:
374     move = ( e->modifiers() & Qt::ControlModifier ) ? MoveFirst : MoveUp;
375     break;
376   case Qt::Key_Down:
377     if ( e->modifiers() & Qt::AltModifier ) {
378       showMenu();
379       return;
380     }
381   case Qt::Key_PageDown:
382     move = ( e->modifiers() & Qt::ControlModifier ) ? MoveLast : MoveDown;
383     break;
384   case Qt::Key_Home:
385     move = MoveFirst;
386     break;
387   case Qt::Key_End:
388     move = MoveLast;
389     break;
390   case Qt::Key_F4:
391     if ( !e->modifiers() ) {
392       showMenu();
393       return;
394     }
395     break;
396   case Qt::Key_Space:
397     showMenu();
398     return;
399   case Qt::Key_Enter:
400   case Qt::Key_Return:
401   case Qt::Key_Escape:
402     e->ignore();
403     break;
404   case Qt::Key_Select:
405     showMenu();
406     return;
407   case Qt::Key_Left:
408     move = ( e->modifiers() & Qt::ControlModifier ) ? MoveFirst : MoveUp;
409     break;
410   case Qt::Key_Right:
411     move = ( e->modifiers() & Qt::ControlModifier ) ? MoveLast : MoveDown;
412     break;
413   default:
414     e->ignore();
415     break;
416   }
417   moveIndex( move );
418 }
419
420 /*!
421   \brief Reimplemented from QToolButton::wheelEvent().
422   Process mouse wheel event.
423   \param e Mouse wheel event.
424 */
425 void QtxToolButton::wheelEvent( QWheelEvent* e )
426 {
427   Move move = NoMove;
428   if ( e->delta() > 0 )
429     move = ( e->modifiers() & Qt::ControlModifier ) ? MoveFirst : MoveUp;
430   else if ( e->delta() < 0 )
431     move = ( e->modifiers() & Qt::ControlModifier ) ? MoveLast : MoveDown;
432   moveIndex( move );
433 }
434
435 /*!
436   \brief Called when menu action is triggered.
437   \internal
438 */
439 void QtxToolButton::actionTriggered( QAction* action )
440 {
441   if ( action && !isSeparator( action ) ) {
442     int index = currentIndex();
443     menu()->setDefaultAction( action );
444     internalUpdate();
445     int newIndex = currentIndex();
446     emitCurrentChanged( true, index != newIndex );
447   }
448 }
449
450 /*!
451   \brief Update content of the widget.
452   \internal
453 */
454 void QtxToolButton::internalUpdate()
455 {
456   QAction* action = menu()->defaultAction();
457   setText( action == 0 ? "" : action->text() );
458   setIcon( action == 0 ? QIcon() : action->icon() );
459 }
460
461 /*!
462   \brief Get menu action at given index.
463   \internal
464 */
465 QAction* QtxToolButton::actionAt( int index ) const
466 {
467   return ( index >=0 && index < count() ) ? menu()->actions()[index] : 0;
468 }
469
470 /*!
471   \brief Move current index.
472   \internal
473 */
474 void QtxToolButton::moveIndex( Move move )
475 {
476   int index = currentIndex();
477   int newIndex = index;
478   switch ( move ) {
479   case MoveUp:
480   case MoveFirst:
481     for ( int i = index-1; i >= 0; i-- ) {
482       QAction* a = actionAt( i );
483       if ( a && !isSeparator( a ) ) {
484         newIndex = i;
485         if ( move == MoveUp )
486           break;
487       }
488     }
489     break;
490   case MoveDown:
491   case MoveLast:
492     for ( int i = index+1; i < count(); i++ ) {
493       QAction* a = actionAt( i );
494       if ( a && !isSeparator( a ) ) {
495         newIndex = i;
496         if ( move == MoveDown )
497           break;
498       }
499     }
500     break;
501   default:
502     break;
503   }
504   if ( newIndex != index ) {
505     menu()->setDefaultAction( actionAt( newIndex ) );
506     internalUpdate();
507     emitCurrentChanged( true, true );
508   }
509 }
510
511 /*!
512   \brief Emit `currentChanged()` signal.
513   \internal
514 */
515 void QtxToolButton::emitCurrentChanged( bool activate, bool changed )
516 {
517   QAction* action = menu()->defaultAction();
518   int index = action == 0 ? -1 : menu()->actions().indexOf( action );
519   QString text = action == 0 ? QString() : action->text();
520   if ( activate ) {
521     emit activated( index );
522     emit activated( text );
523   }
524   if ( changed ) {
525     emit currentIndexChanged( index );
526     emit currentIndexChanged( text );
527     emit currentTextChanged( text );
528   }
529 }