1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qtoolbutton.h"
43 #ifndef QT_NO_TOOLBUTTON
44 
45 #include <qapplication.h>
46 #include <qdesktopwidget.h>
47 #include <qdrawutil.h>
48 #include <qevent.h>
49 #include <qicon.h>
50 #include <qmenu.h>
51 #include <qpainter.h>
52 #include <qpointer.h>
53 #include <qstyle.h>
54 #include <qstyleoption.h>
55 #include <qtooltip.h>
56 #include <qmainwindow.h>
57 #include <qtoolbar.h>
58 #include <qvariant.h>
59 #include <qstylepainter.h>
60 #include <private/qabstractbutton_p.h>
61 #include <private/qaction_p.h>
62 #include <private/qmenu_p.h>
63 
64 QT_BEGIN_NAMESPACE
65 
66 class QToolButtonPrivate : public QAbstractButtonPrivate
67 {
68     Q_DECLARE_PUBLIC(QToolButton)
69 public:
70     void init();
71 #ifndef QT_NO_MENU
72     void _q_buttonPressed();
73     void popupTimerDone();
74     void _q_updateButtonDown();
75     void _q_menuTriggered(QAction *);
76 #endif
77     bool updateHoverControl(const QPoint &pos);
78     void _q_actionTriggered();
79     QStyle::SubControl newHoverControl(const QPoint &pos);
80     QStyle::SubControl hoverControl;
81     QRect hoverRect;
82     QPointer<QAction> menuAction; //the menu set by the user (setMenu)
83     QBasicTimer popupTimer;
84     int delay;
85     Qt::ArrowType arrowType;
86     Qt::ToolButtonStyle toolButtonStyle;
87     QToolButton::ToolButtonPopupMode popupMode;
88     enum { NoButtonPressed=0, MenuButtonPressed=1, ToolButtonPressed=2 };
89     uint buttonPressed : 2;
90     uint menuButtonDown          : 1;
91     uint autoRaise             : 1;
92     uint repeat                : 1;
93     QAction *defaultAction;
94 #ifndef QT_NO_MENU
95     bool hasMenu() const;
96     //workaround for task 177850
97     QList<QAction *> actionsCopy;
98 #endif
99 #ifdef QT3_SUPPORT
100     bool userDefinedPopupDelay;
101 #endif
102 };
103 
104 #ifndef QT_NO_MENU
hasMenu() const105 bool QToolButtonPrivate::hasMenu() const
106 {
107     return ((defaultAction && defaultAction->menu())
108             || (menuAction && menuAction->menu())
109             || actions.size() > (defaultAction ? 1 : 0));
110 }
111 #endif
112 
113 /*!
114     \class QToolButton
115     \brief The QToolButton class provides a quick-access button to
116     commands or options, usually used inside a QToolBar.
117 
118     \ingroup basicwidgets
119 
120 
121     A tool button is a special button that provides quick-access to
122     specific commands or options. As opposed to a normal command
123     button, a tool button usually doesn't show a text label, but shows
124     an icon instead.
125 
126     Tool buttons are normally created when new QAction instances are
127     created with QToolBar::addAction() or existing actions are added
128     to a toolbar with QToolBar::addAction(). It is also possible to
129     construct tool buttons in the same way as any other widget, and
130     arrange them alongside other widgets in layouts.
131 
132     One classic use of a tool button is to select tools; for example,
133     the "pen" tool in a drawing program. This would be implemented
134     by using a QToolButton as a toggle button (see setToggleButton()).
135 
136     QToolButton supports auto-raising. In auto-raise mode, the button
137     draws a 3D frame only when the mouse points at it. The feature is
138     automatically turned on when a button is used inside a QToolBar.
139     Change it with setAutoRaise().
140 
141     A tool button's icon is set as QIcon. This makes it possible to
142     specify different pixmaps for the disabled and active state. The
143     disabled pixmap is used when the button's functionality is not
144     available. The active pixmap is displayed when the button is
145     auto-raised because the mouse pointer is hovering over it.
146 
147     The button's look and dimension is adjustable with
148     setToolButtonStyle() and setIconSize(). When used inside a
149     QToolBar in a QMainWindow, the button automatically adjusts to
150     QMainWindow's settings (see QMainWindow::setToolButtonStyle() and
151     QMainWindow::setIconSize()). Instead of an icon, a tool button can
152     also display an arrow symbol, specified with
153     \l{QToolButton::arrowType} {arrowType}.
154 
155     A tool button can offer additional choices in a popup menu. The
156     popup menu can be set using setMenu(). Use setPopupMode() to
157     configure the different modes available for tool buttons with a
158     menu set. The default mode is DelayedPopupMode which is sometimes
159     used with the "Back" button in a web browser.  After pressing and
160     holding the button down for a while, a menu pops up showing a list
161     of possible pages to jump to. The default delay is 600 ms; you can
162     adjust it with setPopupDelay().
163 
164     \table 100%
165     \row \o \inlineimage assistant-toolbar.png Qt Assistant's toolbar with tool buttons
166     \row \o Qt Assistant's toolbar contains tool buttons that are associated
167          with actions used in other parts of the main window.
168     \endtable
169 
170     \sa QPushButton, QToolBar, QMainWindow, QAction,
171         {fowler}{GUI Design Handbook: Push Button}
172 */
173 
174 /*!
175     \fn void QToolButton::triggered(QAction *action)
176 
177     This signal is emitted when the given \a action is triggered.
178 
179     The action may also be associated with other parts of the user interface,
180     such as menu items and keyboard shortcuts. Sharing actions in this
181     way helps make the user interface more consistent and is often less work
182     to implement.
183 */
184 
185 /*!
186     Constructs an empty tool button with parent \a
187     parent.
188 */
QToolButton(QWidget * parent)189 QToolButton::QToolButton(QWidget * parent)
190     : QAbstractButton(*new QToolButtonPrivate, parent)
191 {
192     Q_D(QToolButton);
193     d->init();
194 }
195 
196 #ifdef QT3_SUPPORT
197 /*!
198     Constructs an empty tool button called \a name, with parent \a
199     parent.
200 */
201 
QToolButton(QWidget * parent,const char * name)202 QToolButton::QToolButton(QWidget * parent, const char *name)
203     : QAbstractButton(*new QToolButtonPrivate, parent)
204 {
205     Q_D(QToolButton);
206     setObjectName(QString::fromAscii(name));
207     d->init();
208 }
209 
210 /*!
211     Constructs a tool button called \a name, that is a child of \a
212     parent.
213 
214     The tool button will display the given \a icon, with its text
215     label and tool tip set to \a textLabel and its status bar message
216     set to \a statusTip. It will be connected to the \a slot in
217     object \a receiver.
218 */
219 
QToolButton(const QIcon & icon,const QString & textLabel,const QString & statusTip,QObject * receiver,const char * slot,QWidget * parent,const char * name)220 QToolButton::QToolButton(const QIcon& icon, const QString &textLabel,
221                          const QString& statusTip,
222                          QObject * receiver, const char *slot,
223                          QWidget * parent, const char *name)
224     : QAbstractButton(*new QToolButtonPrivate, parent)
225 {
226     Q_D(QToolButton);
227     setObjectName(QString::fromAscii(name));
228     d->init();
229     setIcon(icon);
230     setText(textLabel);
231     if (receiver && slot)
232         connect(this, SIGNAL(clicked()), receiver, slot);
233 #ifndef QT_NO_TOOLTIP
234     if (!textLabel.isEmpty())
235         setToolTip(textLabel);
236 #endif
237 #ifndef QT_NO_STATUSTIP
238     if (!statusTip.isEmpty())
239         setStatusTip(statusTip);
240 #else
241     Q_UNUSED(statusTip);
242 #endif
243 }
244 
245 
246 /*!
247     Constructs a tool button as an arrow button. The Qt::ArrowType \a
248     type defines the arrow direction. Possible values are
249     Qt::LeftArrow, Qt::RightArrow, Qt::UpArrow, and Qt::DownArrow.
250 
251     An arrow button has auto-repeat turned on by default.
252 
253     The \a parent and \a name arguments are sent to the QWidget
254     constructor.
255 */
QToolButton(Qt::ArrowType type,QWidget * parent,const char * name)256 QToolButton::QToolButton(Qt::ArrowType type, QWidget *parent, const char *name)
257     : QAbstractButton(*new QToolButtonPrivate, parent)
258 {
259     Q_D(QToolButton);
260     setObjectName(QString::fromAscii(name));
261     d->init();
262     setAutoRepeat(true);
263     d->arrowType = type;
264 }
265 
266 #endif
267 
268 
269 /*  Set-up code common to all the constructors */
270 
init()271 void QToolButtonPrivate::init()
272 {
273     Q_Q(QToolButton);
274     delay = q->style()->styleHint(QStyle::SH_ToolButton_PopupDelay, 0, q);
275 #ifdef QT3_SUPPORT
276     userDefinedPopupDelay = false;
277 #endif
278     defaultAction = 0;
279 #ifndef QT_NO_TOOLBAR
280     if (qobject_cast<QToolBar*>(parent))
281         autoRaise = true;
282     else
283 #endif
284         autoRaise = false;
285     arrowType = Qt::NoArrow;
286     menuButtonDown = false;
287     popupMode = QToolButton::DelayedPopup;
288     buttonPressed = QToolButtonPrivate::NoButtonPressed;
289 
290     toolButtonStyle = Qt::ToolButtonIconOnly;
291     hoverControl = QStyle::SC_None;
292 
293     q->setFocusPolicy(Qt::TabFocus);
294     q->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed,
295                                  QSizePolicy::ToolButton));
296 
297 #ifndef QT_NO_MENU
298     QObject::connect(q, SIGNAL(pressed()), q, SLOT(_q_buttonPressed()));
299 #endif
300 
301     setLayoutItemMargins(QStyle::SE_ToolButtonLayoutItem);
302 
303 }
304 
305 /*!
306     Initialize \a option with the values from this QToolButton. This method
307     is useful for subclasses when they need a QStyleOptionToolButton, but don't want
308     to fill in all the information themselves.
309 
310     \sa QStyleOption::initFrom()
311 */
initStyleOption(QStyleOptionToolButton * option) const312 void QToolButton::initStyleOption(QStyleOptionToolButton *option) const
313 {
314     if (!option)
315         return;
316 
317     Q_D(const QToolButton);
318     option->initFrom(this);
319     bool forceNoText = false;
320     option->iconSize = iconSize(); //default value
321 
322 #ifndef QT_NO_TOOLBAR
323     if (parentWidget()) {
324         if (QToolBar *toolBar = qobject_cast<QToolBar *>(parentWidget())) {
325             option->iconSize = toolBar->iconSize();
326         }
327 #ifdef QT3_SUPPORT
328         else if (parentWidget()->inherits("Q3ToolBar")) {
329             if (!option->iconSize.isValid()) {
330                 int iconSize = style()->pixelMetric(QStyle::PM_ToolBarIconSize, option, this);
331                 option->iconSize = d->icon.actualSize(QSize(iconSize, iconSize));
332             }
333             forceNoText = d->toolButtonStyle == Qt::ToolButtonIconOnly;
334         }
335 #endif
336     }
337 #endif // QT_NO_TOOLBAR
338 
339     if (!forceNoText)
340         option->text = d->text;
341     option->icon = d->icon;
342     option->arrowType = d->arrowType;
343     if (d->down)
344         option->state |= QStyle::State_Sunken;
345     if (d->checked)
346         option->state |= QStyle::State_On;
347     if (d->autoRaise)
348         option->state |= QStyle::State_AutoRaise;
349     if (!d->checked && !d->down)
350         option->state |= QStyle::State_Raised;
351 
352     option->subControls = QStyle::SC_ToolButton;
353     option->activeSubControls = QStyle::SC_None;
354 
355     option->features = QStyleOptionToolButton::None;
356     if (d->popupMode == QToolButton::MenuButtonPopup) {
357         option->subControls |= QStyle::SC_ToolButtonMenu;
358         option->features |= QStyleOptionToolButton::MenuButtonPopup;
359     }
360     if (option->state & QStyle::State_MouseOver) {
361         option->activeSubControls = d->hoverControl;
362     }
363     if (d->menuButtonDown) {
364         option->state |= QStyle::State_Sunken;
365         option->activeSubControls |= QStyle::SC_ToolButtonMenu;
366     }
367     if (d->down) {
368         option->state |= QStyle::State_Sunken;
369         option->activeSubControls |= QStyle::SC_ToolButton;
370     }
371 
372 
373     if (d->arrowType != Qt::NoArrow)
374         option->features |= QStyleOptionToolButton::Arrow;
375     if (d->popupMode == QToolButton::DelayedPopup)
376         option->features |= QStyleOptionToolButton::PopupDelay;
377 #ifndef QT_NO_MENU
378     if (d->hasMenu())
379         option->features |= QStyleOptionToolButton::HasMenu;
380 #endif
381     if (d->toolButtonStyle == Qt::ToolButtonFollowStyle) {
382         option->toolButtonStyle = Qt::ToolButtonStyle(style()->styleHint(QStyle::SH_ToolButtonStyle, option, this));
383     } else
384         option->toolButtonStyle = d->toolButtonStyle;
385 
386     if (option->toolButtonStyle == Qt::ToolButtonTextBesideIcon) {
387         // If the action is not prioritized, remove the text label to save space
388         if (d->defaultAction && d->defaultAction->priority() < QAction::NormalPriority)
389             option->toolButtonStyle = Qt::ToolButtonIconOnly;
390     }
391 
392     if (d->icon.isNull() && d->arrowType == Qt::NoArrow && !forceNoText) {
393         if (!d->text.isEmpty())
394             option->toolButtonStyle = Qt::ToolButtonTextOnly;
395         else if (option->toolButtonStyle != Qt::ToolButtonTextOnly)
396             option->toolButtonStyle = Qt::ToolButtonIconOnly;
397     }
398 
399     option->pos = pos();
400     option->font = font();
401 }
402 
403 /*!
404     Destroys the object and frees any allocated resources.
405 */
406 
~QToolButton()407 QToolButton::~QToolButton()
408 {
409 }
410 
411 /*!
412     \reimp
413 */
sizeHint() const414 QSize QToolButton::sizeHint() const
415 {
416     Q_D(const QToolButton);
417     if (d->sizeHint.isValid())
418         return d->sizeHint;
419     ensurePolished();
420 
421     int w = 0, h = 0;
422     QStyleOptionToolButton opt;
423     initStyleOption(&opt);
424 
425     QFontMetrics fm = fontMetrics();
426     if (opt.toolButtonStyle != Qt::ToolButtonTextOnly) {
427         QSize icon = opt.iconSize;
428         w = icon.width();
429         h = icon.height();
430 #ifdef Q_WS_MAC
431         extern CGFloat qt_mac_get_scalefactor();
432         w /= qt_mac_get_scalefactor();
433         h /= qt_mac_get_scalefactor();
434 #endif
435     }
436 
437     if (opt.toolButtonStyle != Qt::ToolButtonIconOnly) {
438         QSize textSize = fm.size(Qt::TextShowMnemonic, text());
439         textSize.setWidth(textSize.width() + fm.width(QLatin1Char(' '))*2);
440         if (opt.toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
441             h += 4 + textSize.height();
442             if (textSize.width() > w)
443                 w = textSize.width();
444         } else if (opt.toolButtonStyle == Qt::ToolButtonTextBesideIcon) {
445             w += 4 + textSize.width();
446             if (textSize.height() > h)
447                 h = textSize.height();
448         } else { // TextOnly
449             w = textSize.width();
450             h = textSize.height();
451         }
452     }
453 
454     opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height
455     if (d->popupMode == MenuButtonPopup)
456         w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this);
457 
458     d->sizeHint = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), this).
459                   expandedTo(QApplication::globalStrut());
460     return d->sizeHint;
461 }
462 
463 /*!
464     \reimp
465  */
minimumSizeHint() const466 QSize QToolButton::minimumSizeHint() const
467 {
468     return sizeHint();
469 }
470 
471 /*!
472     \enum QToolButton::TextPosition
473     \compat
474 
475     This enum describes the position of the tool button's text label in
476     relation to the tool button's icon.
477 
478     \value BesideIcon The text appears beside the icon.
479     \value BelowIcon The text appears below the icon.
480     \omitvalue Right
481     \omitvalue Under
482 */
483 
484 /*!
485     \property QToolButton::toolButtonStyle
486     \brief whether the tool button displays an icon only, text only,
487     or text beside/below the icon.
488 
489     The default is Qt::ToolButtonIconOnly.
490 
491     To have the style of toolbuttons follow the system settings (as available
492     in GNOME and KDE desktop environments), set this property to Qt::ToolButtonFollowStyle.
493 
494     QToolButton automatically connects this slot to the relevant
495     signal in the QMainWindow in which is resides.
496 */
497 
498 /*!
499     \property QToolButton::arrowType
500     \brief whether the button displays an arrow instead of a normal icon
501 
502     This displays an arrow as the icon for the QToolButton.
503 
504     By default, this property is set to Qt::NoArrow.
505 */
506 
toolButtonStyle() const507 Qt::ToolButtonStyle QToolButton::toolButtonStyle() const
508 {
509     Q_D(const QToolButton);
510     return d->toolButtonStyle;
511 }
512 
arrowType() const513 Qt::ArrowType QToolButton::arrowType() const
514 {
515     Q_D(const QToolButton);
516     return d->arrowType;
517 }
518 
519 
setToolButtonStyle(Qt::ToolButtonStyle style)520 void QToolButton::setToolButtonStyle(Qt::ToolButtonStyle style)
521 {
522     Q_D(QToolButton);
523     if (d->toolButtonStyle == style)
524         return;
525 
526     d->toolButtonStyle = style;
527     d->sizeHint = QSize();
528     updateGeometry();
529     if (isVisible()) {
530         update();
531     }
532 }
533 
setArrowType(Qt::ArrowType type)534 void QToolButton::setArrowType(Qt::ArrowType type)
535 {
536     Q_D(QToolButton);
537     if (d->arrowType == type)
538         return;
539 
540     d->arrowType = type;
541     d->sizeHint = QSize();
542     updateGeometry();
543     if (isVisible()) {
544         update();
545     }
546 }
547 
548 /*!
549     \fn void QToolButton::paintEvent(QPaintEvent *event)
550 
551     Paints the button in response to the paint \a event.
552 */
paintEvent(QPaintEvent *)553 void QToolButton::paintEvent(QPaintEvent *)
554 {
555     QStylePainter p(this);
556     QStyleOptionToolButton opt;
557     initStyleOption(&opt);
558     p.drawComplexControl(QStyle::CC_ToolButton, opt);
559 }
560 
561 /*!
562     \reimp
563  */
actionEvent(QActionEvent * event)564 void QToolButton::actionEvent(QActionEvent *event)
565 {
566     Q_D(QToolButton);
567     QAction *action = event->action();
568     switch (event->type()) {
569     case QEvent::ActionChanged:
570         if (action == d->defaultAction)
571             setDefaultAction(action); // update button state
572         break;
573     case QEvent::ActionAdded:
574         connect(action, SIGNAL(triggered()), this, SLOT(_q_actionTriggered()));
575         break;
576     case QEvent::ActionRemoved:
577         if (d->defaultAction == action)
578             d->defaultAction = 0;
579 #ifndef QT_NO_MENU
580         if (action == d->menuAction)
581             d->menuAction = 0;
582 #endif
583         action->disconnect(this);
584         break;
585     default:
586         ;
587     }
588     QAbstractButton::actionEvent(event);
589 }
590 
newHoverControl(const QPoint & pos)591 QStyle::SubControl QToolButtonPrivate::newHoverControl(const QPoint &pos)
592 {
593     Q_Q(QToolButton);
594     QStyleOptionToolButton opt;
595     q->initStyleOption(&opt);
596     opt.subControls = QStyle::SC_All;
597     hoverControl = q->style()->hitTestComplexControl(QStyle::CC_ToolButton, &opt, pos, q);
598     if (hoverControl == QStyle::SC_None)
599         hoverRect = QRect();
600     else
601         hoverRect = q->style()->subControlRect(QStyle::CC_ToolButton, &opt, hoverControl, q);
602     return hoverControl;
603 }
604 
updateHoverControl(const QPoint & pos)605 bool QToolButtonPrivate::updateHoverControl(const QPoint &pos)
606 {
607     Q_Q(QToolButton);
608     QRect lastHoverRect = hoverRect;
609     QStyle::SubControl lastHoverControl = hoverControl;
610     bool doesHover = q->testAttribute(Qt::WA_Hover);
611     if (lastHoverControl != newHoverControl(pos) && doesHover) {
612         q->update(lastHoverRect);
613         q->update(hoverRect);
614         return true;
615     }
616     return !doesHover;
617 }
618 
_q_actionTriggered()619 void QToolButtonPrivate::_q_actionTriggered()
620 {
621     Q_Q(QToolButton);
622     if (QAction *action = qobject_cast<QAction *>(q->sender()))
623         emit q->triggered(action);
624 }
625 
626 /*!
627     \reimp
628  */
enterEvent(QEvent * e)629 void QToolButton::enterEvent(QEvent * e)
630 {
631     Q_D(QToolButton);
632     if (d->autoRaise)
633         update();
634     if (d->defaultAction)
635         d->defaultAction->hover();
636     QAbstractButton::enterEvent(e);
637 }
638 
639 
640 /*!
641     \reimp
642  */
leaveEvent(QEvent * e)643 void QToolButton::leaveEvent(QEvent * e)
644 {
645     Q_D(QToolButton);
646     if (d->autoRaise)
647         update();
648 
649     QAbstractButton::leaveEvent(e);
650 }
651 
652 
653 /*!
654     \reimp
655  */
timerEvent(QTimerEvent * e)656 void QToolButton::timerEvent(QTimerEvent *e)
657 {
658 #ifndef QT_NO_MENU
659     Q_D(QToolButton);
660     if (e->timerId() == d->popupTimer.timerId()) {
661         d->popupTimerDone();
662         return;
663     }
664 #endif
665     QAbstractButton::timerEvent(e);
666 }
667 
668 
669 /*!
670     \reimp
671 */
changeEvent(QEvent * e)672 void QToolButton::changeEvent(QEvent *e)
673 {
674 #ifndef QT_NO_TOOLBAR
675     Q_D(QToolButton);
676     if (e->type() == QEvent::ParentChange) {
677         if (qobject_cast<QToolBar*>(parentWidget()))
678             d->autoRaise = true;
679     } else if (e->type() == QEvent::StyleChange
680 #ifdef Q_WS_MAC
681                || e->type() == QEvent::MacSizeChange
682 #endif
683                ) {
684 #ifdef QT3_SUPPORT
685         if (!d->userDefinedPopupDelay)
686 #endif
687         d->delay = style()->styleHint(QStyle::SH_ToolButton_PopupDelay, 0, this);
688         d->setLayoutItemMargins(QStyle::SE_ToolButtonLayoutItem);
689     }
690 #endif
691     QAbstractButton::changeEvent(e);
692 }
693 
694 /*!
695     \reimp
696 */
mousePressEvent(QMouseEvent * e)697 void QToolButton::mousePressEvent(QMouseEvent *e)
698 {
699     Q_D(QToolButton);
700 #ifndef QT_NO_MENU
701     QStyleOptionToolButton opt;
702     initStyleOption(&opt);
703     if (e->button() == Qt::LeftButton && (d->popupMode == MenuButtonPopup)) {
704         QRect popupr = style()->subControlRect(QStyle::CC_ToolButton, &opt,
705                                                QStyle::SC_ToolButtonMenu, this);
706         if (popupr.isValid() && popupr.contains(e->pos())) {
707             d->buttonPressed = QToolButtonPrivate::MenuButtonPressed;
708             showMenu();
709             return;
710         }
711     }
712 #endif
713     d->buttonPressed = QToolButtonPrivate::ToolButtonPressed;
714     QAbstractButton::mousePressEvent(e);
715 }
716 
717 /*!
718     \reimp
719 */
mouseReleaseEvent(QMouseEvent * e)720 void QToolButton::mouseReleaseEvent(QMouseEvent *e)
721 {
722     Q_D(QToolButton);
723     QAbstractButton::mouseReleaseEvent(e);
724     d->buttonPressed = QToolButtonPrivate::NoButtonPressed;
725 }
726 
727 /*!
728     \reimp
729 */
hitButton(const QPoint & pos) const730 bool QToolButton::hitButton(const QPoint &pos) const
731 {
732     Q_D(const QToolButton);
733     if(QAbstractButton::hitButton(pos))
734         return (d->buttonPressed != QToolButtonPrivate::MenuButtonPressed);
735     return false;
736 }
737 
738 #ifdef QT3_SUPPORT
739 
740 /*!
741     Use icon() instead.
742 */
onIconSet() const743 QIcon QToolButton::onIconSet() const
744 {
745     return icon();
746 }
747 
748 /*!
749     Use icon() instead.
750 */
offIconSet() const751 QIcon QToolButton::offIconSet() const
752 {
753     return icon();
754 }
755 
756 
757 /*!
758   \obsolete
759 
760   Use setIcon() instead.
761 */
setOnIconSet(const QIcon & set)762 void QToolButton::setOnIconSet(const QIcon& set)
763 {
764     setIcon(set);
765 }
766 
767 /*!
768   \obsolete
769 
770   Use setIcon() instead.
771 */
setOffIconSet(const QIcon & set)772 void QToolButton::setOffIconSet(const QIcon& set)
773 {
774     setIcon(set);
775 }
776 
777 
778 /*! \overload
779     \obsolete
780 
781   Since Qt 3.0, QIcon contains both the On and Off icons.
782 
783   For ease of porting, this function ignores the \a on parameter and
784   sets the \l{QAbstractButton::icon} {icon} property. If you relied on
785   the \a on parameter, you probably want to update your code to use
786   the QIcon On/Off mechanism.
787 
788   \sa icon QIcon::State
789 */
790 
setIconSet(const QIcon & set,bool)791 void QToolButton::setIconSet(const QIcon & set, bool /* on */)
792 {
793     QAbstractButton::setIcon(set);
794 }
795 
796 /*! \overload
797     \obsolete
798 
799   Since Qt 3.0, QIcon contains both the On and Off icons.
800 
801   For ease of porting, this function ignores the \a on parameter and
802   returns the \l{QAbstractButton::icon} {icon} property. If you relied
803   on the \a on parameter, you probably want to update your code to use
804   the QIcon On/Off mechanism.
805 */
iconSet(bool) const806 QIcon QToolButton::iconSet(bool /* on */) const
807 {
808     return QAbstractButton::icon();
809 }
810 
811 #endif
812 
813 #ifndef QT_NO_MENU
814 /*!
815     Associates the given \a menu with this tool button.
816 
817     The menu will be shown according to the button's \l popupMode.
818 
819     Ownership of the menu is not transferred to the tool button.
820 
821     \sa menu()
822 */
setMenu(QMenu * menu)823 void QToolButton::setMenu(QMenu* menu)
824 {
825     Q_D(QToolButton);
826 
827     if (d->menuAction)
828         removeAction(d->menuAction);
829 
830     if (menu) {
831         d->menuAction = menu->menuAction();
832         addAction(d->menuAction);
833     } else {
834         d->menuAction = 0;
835     }
836     update();
837 }
838 
839 /*!
840     Returns the associated menu, or 0 if no menu has been defined.
841 
842     \sa setMenu()
843 */
menu() const844 QMenu* QToolButton::menu() const
845 {
846     Q_D(const QToolButton);
847     if (d->menuAction)
848         return d->menuAction->menu();
849     return 0;
850 }
851 
852 /*!
853     Shows (pops up) the associated popup menu. If there is no such
854     menu, this function does nothing. This function does not return
855     until the popup menu has been closed by the user.
856 */
showMenu()857 void QToolButton::showMenu()
858 {
859     Q_D(QToolButton);
860     if (!d->hasMenu()) {
861         d->menuButtonDown = false;
862         return; // no menu to show
863     }
864     // prevent recursions spinning another event loop
865     if (d->menuButtonDown)
866         return;
867 
868     d->menuButtonDown = true;
869     repaint();
870     d->popupTimer.stop();
871     d->popupTimerDone();
872 }
873 
_q_buttonPressed()874 void QToolButtonPrivate::_q_buttonPressed()
875 {
876     Q_Q(QToolButton);
877     if (!hasMenu())
878         return; // no menu to show
879     if (popupMode == QToolButton::MenuButtonPopup)
880         return;
881     else if (delay > 0 && !popupTimer.isActive() && popupMode == QToolButton::DelayedPopup)
882         popupTimer.start(delay, q);
883     else if (delay == 0 || popupMode == QToolButton::InstantPopup)
884         q->showMenu();
885 }
886 
popupTimerDone()887 void QToolButtonPrivate::popupTimerDone()
888 {
889     Q_Q(QToolButton);
890     popupTimer.stop();
891     if (!menuButtonDown && !down)
892         return;
893 
894     menuButtonDown = true;
895     QPointer<QMenu> actualMenu;
896     bool mustDeleteActualMenu = false;
897     if(menuAction) {
898         actualMenu = menuAction->menu();
899     } else if (defaultAction && defaultAction->menu()) {
900         actualMenu = defaultAction->menu();
901     } else {
902         actualMenu = new QMenu(q);
903         mustDeleteActualMenu = true;
904         for(int i = 0; i < actions.size(); i++)
905             actualMenu->addAction(actions.at(i));
906     }
907     repeat = q->autoRepeat();
908     q->setAutoRepeat(false);
909     bool horizontal = true;
910 #if !defined(QT_NO_TOOLBAR)
911     QToolBar *tb = qobject_cast<QToolBar*>(parent);
912     if (tb && tb->orientation() == Qt::Vertical)
913         horizontal = false;
914 #endif
915     QPoint p;
916     QRect screen = QApplication::desktop()->availableGeometry(q);
917     QSize sh = ((QToolButton*)(QMenu*)actualMenu)->receivers(SIGNAL(aboutToShow()))? QSize() : actualMenu->sizeHint();
918     QRect rect = q->rect();
919     if (horizontal) {
920         if (q->isRightToLeft()) {
921             if (q->mapToGlobal(QPoint(0, rect.bottom())).y() + sh.height() <= screen.height()) {
922                 p = q->mapToGlobal(rect.bottomRight());
923             } else {
924                 p = q->mapToGlobal(rect.topRight() - QPoint(0, sh.height()));
925             }
926             p.rx() -= sh.width();
927         } else {
928             if (q->mapToGlobal(QPoint(0, rect.bottom())).y() + sh.height() <= screen.height()) {
929                 p = q->mapToGlobal(rect.bottomLeft());
930             } else {
931                 p = q->mapToGlobal(rect.topLeft() - QPoint(0, sh.height()));
932             }
933         }
934     } else {
935         if (q->isRightToLeft()) {
936             if (q->mapToGlobal(QPoint(rect.left(), 0)).x() - sh.width() <= screen.x()) {
937                 p = q->mapToGlobal(rect.topRight());
938             } else {
939                 p = q->mapToGlobal(rect.topLeft());
940                 p.rx() -= sh.width();
941             }
942         } else {
943             if (q->mapToGlobal(QPoint(rect.right(), 0)).x() + sh.width() <= screen.right()) {
944                 p = q->mapToGlobal(rect.topRight());
945             } else {
946                 p = q->mapToGlobal(rect.topLeft() - QPoint(sh.width(), 0));
947             }
948         }
949     }
950     p.rx() = qMax(screen.left(), qMin(p.x(), screen.right() - sh.width()));
951     p.ry() += 1;
952     QPointer<QToolButton> that = q;
953     actualMenu->setNoReplayFor(q);
954     if (!mustDeleteActualMenu) //only if action are not in this widget
955         QObject::connect(actualMenu, SIGNAL(triggered(QAction*)), q, SLOT(_q_menuTriggered(QAction*)));
956     QObject::connect(actualMenu, SIGNAL(aboutToHide()), q, SLOT(_q_updateButtonDown()));
957     actualMenu->d_func()->causedPopup.widget = q;
958     actualMenu->d_func()->causedPopup.action = defaultAction;
959     actionsCopy = q->actions(); //(the list of action may be modified in slots)
960     actualMenu->exec(p);
961     QObject::disconnect(actualMenu, SIGNAL(aboutToHide()), q, SLOT(_q_updateButtonDown()));
962     if (mustDeleteActualMenu)
963         delete actualMenu;
964     else
965         QObject::disconnect(actualMenu, SIGNAL(triggered(QAction*)), q, SLOT(_q_menuTriggered(QAction*)));
966 
967     if (!that)
968         return;
969 
970     actionsCopy.clear();
971 
972     if (repeat)
973         q->setAutoRepeat(true);
974 }
975 
_q_updateButtonDown()976 void QToolButtonPrivate::_q_updateButtonDown()
977 {
978     Q_Q(QToolButton);
979     menuButtonDown = false;
980     if (q->isDown())
981         q->setDown(false);
982     else
983         q->repaint();
984 }
985 
_q_menuTriggered(QAction * action)986 void QToolButtonPrivate::_q_menuTriggered(QAction *action)
987 {
988     Q_Q(QToolButton);
989     if (action && !actionsCopy.contains(action))
990         emit q->triggered(action);
991 }
992 #endif // QT_NO_MENU
993 
994 #ifdef QT3_SUPPORT
995 /*!
996     \fn void QToolButton::setPopupDelay(int delay)
997 
998     Use the style hint QStyle::SH_ToolButton_PopupDelay instead.
999 */
setPopupDelay(int delay)1000 void QToolButton::setPopupDelay(int delay)
1001 {
1002     Q_D(QToolButton);
1003     d->userDefinedPopupDelay = true;
1004     d->delay = delay;
1005 
1006     update();
1007 }
1008 
1009 /*!
1010     Use the style hint QStyle::SH_ToolButton_PopupDelay instead.
1011 */
popupDelay() const1012 int QToolButton::popupDelay() const
1013 {
1014     Q_D(const QToolButton);
1015     return d->delay;
1016 }
1017 #endif
1018 
1019 #ifndef QT_NO_MENU
1020 /*! \enum QToolButton::ToolButtonPopupMode
1021 
1022     Describes how a menu should be popped up for tool buttons that has
1023     a menu set or contains a list of actions.
1024 
1025     \value DelayedPopup After pressing and holding the tool button
1026     down for a certain amount of time (the timeout is style dependant,
1027     see QStyle::SH_ToolButton_PopupDelay), the menu is displayed.  A
1028     typical application example is the "back" button in some web
1029     browsers's tool bars. If the user clicks it, the browser simply
1030     browses back to the previous page.  If the user presses and holds
1031     the button down for a while, the tool button shows a menu
1032     containing the current history list
1033 
1034     \value MenuButtonPopup In this mode the tool button displays a
1035     special arrow to indicate that a menu is present. The menu is
1036     displayed when the arrow part of the button is pressed.
1037 
1038     \value InstantPopup The menu is displayed, without delay, when
1039     the tool button is pressed. In this mode, the button's own action
1040     is not triggered.
1041 */
1042 
1043 /*!
1044     \property QToolButton::popupMode
1045     \brief describes the way that popup menus are used with tool buttons
1046 
1047     By default, this property is set to \l DelayedPopup.
1048 */
1049 
setPopupMode(ToolButtonPopupMode mode)1050 void QToolButton::setPopupMode(ToolButtonPopupMode mode)
1051 {
1052     Q_D(QToolButton);
1053     d->popupMode = mode;
1054 }
1055 
popupMode() const1056 QToolButton::ToolButtonPopupMode QToolButton::popupMode() const
1057 {
1058     Q_D(const QToolButton);
1059     return d->popupMode;
1060 }
1061 #endif
1062 
1063 /*!
1064     \property QToolButton::autoRaise
1065     \brief whether auto-raising is enabled or not.
1066 
1067     The default is disabled (i.e. false).
1068 
1069     This property is currently ignored on Mac OS X when using QMacStyle.
1070 */
setAutoRaise(bool enable)1071 void QToolButton::setAutoRaise(bool enable)
1072 {
1073     Q_D(QToolButton);
1074     d->autoRaise = enable;
1075 
1076     update();
1077 }
1078 
autoRaise() const1079 bool QToolButton::autoRaise() const
1080 {
1081     Q_D(const QToolButton);
1082     return d->autoRaise;
1083 }
1084 
1085 /*!
1086   Sets the default action to \a action.
1087 
1088   If a tool button has a default action, the action defines the
1089   button's properties like text, icon, tool tip, etc.
1090  */
setDefaultAction(QAction * action)1091 void QToolButton::setDefaultAction(QAction *action)
1092 {
1093     Q_D(QToolButton);
1094 #ifndef QT_NO_MENU
1095     bool hadMenu = false;
1096     hadMenu = d->hasMenu();
1097 #endif
1098     d->defaultAction = action;
1099     if (!action)
1100         return;
1101     if (!actions().contains(action))
1102         addAction(action);
1103     setText(action->iconText());
1104     setIcon(action->icon());
1105 #ifndef QT_NO_TOOLTIP
1106     setToolTip(action->toolTip());
1107 #endif
1108 #ifndef QT_NO_STATUSTIP
1109     setStatusTip(action->statusTip());
1110 #endif
1111 #ifndef QT_NO_WHATSTHIS
1112     setWhatsThis(action->whatsThis());
1113 #endif
1114 #ifndef QT_NO_MENU
1115     if (action->menu() && !hadMenu) {
1116         // new 'default' popup mode defined introduced by tool bar. We
1117         // should have changed QToolButton's default instead. Do that
1118         // in 4.2.
1119         setPopupMode(QToolButton::MenuButtonPopup);
1120     }
1121 #endif
1122     setCheckable(action->isCheckable());
1123     setChecked(action->isChecked());
1124     setEnabled(action->isEnabled());
1125     if (action->d_func()->fontSet)
1126         setFont(action->font());
1127 }
1128 
1129 
1130 /*!
1131   Returns the default action.
1132 
1133   \sa setDefaultAction()
1134  */
defaultAction() const1135 QAction *QToolButton::defaultAction() const
1136 {
1137     Q_D(const QToolButton);
1138     return d->defaultAction;
1139 }
1140 
1141 
1142 
1143 /*!
1144   \reimp
1145  */
nextCheckState()1146 void QToolButton::nextCheckState()
1147 {
1148     Q_D(QToolButton);
1149     if (!d->defaultAction)
1150         QAbstractButton::nextCheckState();
1151     else
1152         d->defaultAction->trigger();
1153 }
1154 
1155 /*! \reimp */
event(QEvent * event)1156 bool QToolButton::event(QEvent *event)
1157 {
1158     switch(event->type()) {
1159     case QEvent::HoverEnter:
1160     case QEvent::HoverLeave:
1161     case QEvent::HoverMove:
1162     if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
1163         d_func()->updateHoverControl(he->pos());
1164         break;
1165     default:
1166         break;
1167     }
1168     return QAbstractButton::event(event);
1169 }
1170 
1171 /*! \internal
1172  */
QToolButton(QToolButtonPrivate & dd,QWidget * parent)1173 QToolButton::QToolButton(QToolButtonPrivate &dd, QWidget *parent)
1174     :QAbstractButton(dd, parent)
1175 {
1176     Q_D(QToolButton);
1177     d->init();
1178 }
1179 
1180 /*!
1181     \fn void QToolButton::setPixmap(const QPixmap &pixmap)
1182 
1183     Use setIcon(QIcon(pixmap)) instead.
1184 */
1185 
1186 /*!
1187     \fn void QToolButton::setIconSet(const QIcon &icon)
1188 
1189     Use setIcon() instead.
1190 */
1191 
1192 /*!
1193     \fn void QToolButton::setTextLabel(const QString &text, bool tooltip)
1194 
1195     Use setText() and setToolTip() instead.
1196 */
1197 
1198 /*!
1199     \fn QString QToolButton::textLabel() const
1200 
1201     Use text() instead.
1202 */
1203 
1204 /*!
1205     \fn QIcon QToolButton::iconSet() const
1206 
1207     Use icon() instead.
1208 */
1209 
1210 /*!
1211     \fn void QToolButton::openPopup()
1212 
1213     Use showMenu() instead.
1214 */
1215 
1216 /*!
1217     \fn void QToolButton::setPopup(QMenu* popup)
1218 
1219     Use setMenu() instead.
1220 */
1221 
1222 /*!
1223     \fn QMenu* QToolButton::popup() const
1224 
1225     Use menu() instead.
1226 */
1227 
1228 /*!
1229     \fn TextPosition QToolButton::textPosition() const
1230 
1231     Use toolButtonStyle() instead.
1232 */
1233 
1234 /*!
1235     \fn void QToolButton::setTextPosition(QToolButton::TextPosition pos)
1236 
1237     Use setToolButtonStyle() instead.
1238 */
1239 
1240 /*!
1241     \fn bool QToolButton::usesBigPixmap() const
1242 
1243     Use iconSize() instead.
1244 */
1245 
1246 /*!
1247     \fn void QToolButton::setUsesBigPixmap(bool enable)
1248 
1249     Use setIconSize() instead.
1250 */
1251 
1252 /*!
1253     \fn bool QToolButton::usesTextLabel() const
1254 
1255     Use toolButtonStyle() instead.
1256 */
1257 
1258 /*!
1259     \fn void QToolButton::setUsesTextLabel(bool enable)
1260 
1261     Use setToolButtonStyle() instead.
1262 */
1263 
1264 QT_END_NAMESPACE
1265 
1266 #include "moc_qtoolbutton.cpp"
1267 
1268 #endif
1269