1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets 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 https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "private/qabstractbutton_p.h"
41 
42 #if QT_CONFIG(itemviews)
43 #include "qabstractitemview.h"
44 #endif
45 #if QT_CONFIG(buttongroup)
46 #include "qbuttongroup.h"
47 #include "private/qapplication_p.h"
48 #include "private/qbuttongroup_p.h"
49 #endif
50 #include "qabstractbutton_p.h"
51 #include "qevent.h"
52 #include "qpainter.h"
53 #include "qapplication.h"
54 #include "qstyle.h"
55 #include "qaction.h"
56 #ifndef QT_NO_ACCESSIBILITY
57 #include "qaccessible.h"
58 #endif
59 
60 #include <algorithm>
61 
62 QT_BEGIN_NAMESPACE
63 
64 #define AUTO_REPEAT_DELAY  300
65 #define AUTO_REPEAT_INTERVAL 100
66 
67 Q_WIDGETS_EXPORT extern bool qt_tab_all_widgets();
68 
69 /*!
70     \class QAbstractButton
71 
72     \brief The QAbstractButton class is the abstract base class of
73     button widgets, providing functionality common to buttons.
74 
75     \ingroup abstractwidgets
76     \inmodule QtWidgets
77 
78     This class implements an \e abstract button.
79     Subclasses of this class handle user actions, and specify how the button
80     is drawn.
81 
82     QAbstractButton provides support for both push buttons and checkable
83     (toggle) buttons. Checkable buttons are implemented in the QRadioButton
84     and QCheckBox classes. Push buttons are implemented in the
85     QPushButton and QToolButton classes; these also provide toggle
86     behavior if required.
87 
88     Any button can display a label containing text and an icon. setText()
89     sets the text; setIcon() sets the icon. If a button is disabled, its label
90     is changed to give the button a "disabled" appearance.
91 
92     If the button is a text button with a string containing an
93     ampersand ('&'), QAbstractButton automatically creates a shortcut
94     key. For example:
95 
96     \snippet code/src_gui_widgets_qabstractbutton.cpp 0
97 
98     The \uicontrol Alt+C shortcut is assigned to the button, i.e., when the
99     user presses \uicontrol Alt+C the button will call animateClick(). See
100     the \l {QShortcut#mnemonic}{QShortcut} documentation for details. To
101     display an actual ampersand, use '&&'.
102 
103     You can also set a custom shortcut key using the setShortcut()
104     function. This is useful mostly for buttons that do not have any
105     text, and therefore can't have any automatic shortcut.
106 
107     \snippet code/src_gui_widgets_qabstractbutton.cpp 1
108 
109     All the buttons provided by Qt (QPushButton, QToolButton,
110     QCheckBox, and QRadioButton) can display both \l text and \l{icon}{icons}.
111 
112     A button can be made the default button in a dialog by means of
113     QPushButton::setDefault() and QPushButton::setAutoDefault().
114 
115     QAbstractButton provides most of the states used for buttons:
116 
117     \list
118 
119     \li isDown() indicates whether the button is \e pressed down.
120 
121     \li isChecked() indicates whether the button is \e checked.  Only
122     checkable buttons can be checked and unchecked (see below).
123 
124     \li isEnabled() indicates whether the button can be pressed by the
125     user. \note As opposed to other widgets, buttons derived from
126     QAbstractButton accept mouse and context menu events
127     when disabled.
128 
129     \li setAutoRepeat() sets whether the button will auto-repeat if the
130     user holds it down. \l autoRepeatDelay and \l autoRepeatInterval
131     define how auto-repetition is done.
132 
133     \li setCheckable() sets whether the button is a toggle button or not.
134 
135     \endlist
136 
137     The difference between isDown() and isChecked() is as follows.
138     When the user clicks a toggle button to check it, the button is first
139     \e pressed then released into the \e checked state. When the user
140     clicks it again (to uncheck it), the button moves first to the
141     \e pressed state, then to the \e unchecked state (isChecked() and
142     isDown() are both false).
143 
144     QAbstractButton provides four signals:
145 
146     \list 1
147 
148     \li pressed() is emitted when the left mouse button is pressed while
149     the mouse cursor is inside the button.
150 
151     \li released() is emitted when the left mouse button is released.
152 
153     \li clicked() is emitted when the button is first pressed and then
154     released, when the shortcut key is typed, or when click() or
155     animateClick() is called.
156 
157     \li toggled() is emitted when the state of a toggle button changes.
158 
159     \endlist
160 
161     To subclass QAbstractButton, you must reimplement at least
162     paintEvent() to draw the button's outline and its text or pixmap. It
163     is generally advisable to reimplement sizeHint() as well, and
164     sometimes hitButton() (to determine whether a button press is within
165     the button). For buttons with more than two states (like tri-state
166     buttons), you will also have to reimplement checkStateSet() and
167     nextCheckState().
168 
169     \sa QButtonGroup
170 */
171 
QAbstractButtonPrivate(QSizePolicy::ControlType type)172 QAbstractButtonPrivate::QAbstractButtonPrivate(QSizePolicy::ControlType type)
173     :
174 #ifndef QT_NO_SHORTCUT
175     shortcutId(0),
176 #endif
177     checkable(false), checked(false), autoRepeat(false), autoExclusive(false),
178     down(false), blockRefresh(false), pressed(false),
179 #if QT_CONFIG(buttongroup)
180     group(nullptr),
181 #endif
182     autoRepeatDelay(AUTO_REPEAT_DELAY),
183     autoRepeatInterval(AUTO_REPEAT_INTERVAL),
184     controlType(type)
185 {}
186 
queryButtonList() const187 QList<QAbstractButton *>QAbstractButtonPrivate::queryButtonList() const
188 {
189 #if QT_CONFIG(buttongroup)
190     if (group)
191         return group->d_func()->buttonList;
192 #endif
193 
194     QList<QAbstractButton*>candidates = parent->findChildren<QAbstractButton *>();
195     if (autoExclusive) {
196         auto isNoMemberOfMyAutoExclusiveGroup = [](QAbstractButton *candidate) {
197             return !candidate->autoExclusive()
198 #if QT_CONFIG(buttongroup)
199                 || candidate->group()
200 #endif
201                 ;
202         };
203         candidates.erase(std::remove_if(candidates.begin(), candidates.end(),
204                                         isNoMemberOfMyAutoExclusiveGroup),
205                          candidates.end());
206     }
207     return candidates;
208 }
209 
queryCheckedButton() const210 QAbstractButton *QAbstractButtonPrivate::queryCheckedButton() const
211 {
212 #if QT_CONFIG(buttongroup)
213     if (group)
214         return group->d_func()->checkedButton;
215 #endif
216 
217     Q_Q(const QAbstractButton);
218     QList<QAbstractButton *> buttonList = queryButtonList();
219     if (!autoExclusive || buttonList.count() == 1) // no group
220         return nullptr;
221 
222     for (int i = 0; i < buttonList.count(); ++i) {
223         QAbstractButton *b = buttonList.at(i);
224         if (b->d_func()->checked && b != q)
225             return b;
226     }
227     return checked  ? const_cast<QAbstractButton *>(q) : nullptr;
228 }
229 
notifyChecked()230 void QAbstractButtonPrivate::notifyChecked()
231 {
232 #if QT_CONFIG(buttongroup)
233     Q_Q(QAbstractButton);
234     if (group) {
235         QAbstractButton *previous = group->d_func()->checkedButton;
236         group->d_func()->checkedButton = q;
237         if (group->d_func()->exclusive && previous && previous != q)
238             previous->nextCheckState();
239     } else
240 #endif
241     if (autoExclusive) {
242         if (QAbstractButton *b = queryCheckedButton())
243             b->setChecked(false);
244     }
245 }
246 
moveFocus(int key)247 void QAbstractButtonPrivate::moveFocus(int key)
248 {
249     QList<QAbstractButton *> buttonList = queryButtonList();
250 #if QT_CONFIG(buttongroup)
251     bool exclusive = group ? group->d_func()->exclusive : autoExclusive;
252 #else
253     bool exclusive = autoExclusive;
254 #endif
255     QWidget *f = QApplication::focusWidget();
256     QAbstractButton *fb = qobject_cast<QAbstractButton *>(f);
257     if (!fb || !buttonList.contains(fb))
258         return;
259 
260     QAbstractButton *candidate = nullptr;
261     int bestScore = -1;
262     QRect target = f->rect().translated(f->mapToGlobal(QPoint(0,0)));
263     QPoint goal = target.center();
264     uint focus_flag = qt_tab_all_widgets() ? Qt::TabFocus : Qt::StrongFocus;
265 
266     for (int i = 0; i < buttonList.count(); ++i) {
267         QAbstractButton *button = buttonList.at(i);
268         if (button != f && button->window() == f->window() && button->isEnabled() && !button->isHidden() &&
269             (exclusive || (button->focusPolicy() & focus_flag) == focus_flag)) {
270             QRect buttonRect = button->rect().translated(button->mapToGlobal(QPoint(0,0)));
271             QPoint p = buttonRect.center();
272 
273             //Priority to widgets that overlap on the same coordinate.
274             //In that case, the distance in the direction will be used as significant score,
275             //take also in account orthogonal distance in case two widget are in the same distance.
276             int score;
277             if ((buttonRect.x() < target.right() && target.x() < buttonRect.right())
278                   && (key == Qt::Key_Up || key == Qt::Key_Down)) {
279                 //one item's is at the vertical of the other
280                 score = (qAbs(p.y() - goal.y()) << 16) + qAbs(p.x() - goal.x());
281             } else if ((buttonRect.y() < target.bottom() && target.y() < buttonRect.bottom())
282                         && (key == Qt::Key_Left || key == Qt::Key_Right) ) {
283                 //one item's is at the horizontal of the other
284                 score = (qAbs(p.x() - goal.x()) << 16) + qAbs(p.y() - goal.y());
285             } else {
286                 score = (1 << 30) + (p.y() - goal.y()) * (p.y() - goal.y()) + (p.x() - goal.x()) * (p.x() - goal.x());
287             }
288 
289             if (score > bestScore && candidate)
290                 continue;
291 
292             switch(key) {
293             case Qt::Key_Up:
294                 if (p.y() < goal.y()) {
295                     candidate = button;
296                     bestScore = score;
297                 }
298                 break;
299             case Qt::Key_Down:
300                 if (p.y() > goal.y()) {
301                     candidate = button;
302                     bestScore = score;
303                 }
304                 break;
305             case Qt::Key_Left:
306                 if (p.x() < goal.x()) {
307                     candidate = button;
308                     bestScore = score;
309                 }
310                 break;
311             case Qt::Key_Right:
312                 if (p.x() > goal.x()) {
313                     candidate = button;
314                     bestScore = score;
315                 }
316                 break;
317             }
318         }
319     }
320 
321     if (exclusive
322 #ifdef QT_KEYPAD_NAVIGATION
323         && !QApplicationPrivate::keypadNavigationEnabled()
324 #endif
325         && candidate
326         && fb->d_func()->checked
327         && candidate->d_func()->checkable)
328         candidate->click();
329 
330     if (candidate) {
331         if (key == Qt::Key_Up || key == Qt::Key_Left)
332             candidate->setFocus(Qt::BacktabFocusReason);
333         else
334             candidate->setFocus(Qt::TabFocusReason);
335     }
336 }
337 
fixFocusPolicy()338 void QAbstractButtonPrivate::fixFocusPolicy()
339 {
340     Q_Q(QAbstractButton);
341 #if QT_CONFIG(buttongroup)
342     if (!group && !autoExclusive)
343 #else
344     if (!autoExclusive)
345 #endif
346         return;
347 
348     QList<QAbstractButton *> buttonList = queryButtonList();
349     for (int i = 0; i < buttonList.count(); ++i) {
350         QAbstractButton *b = buttonList.at(i);
351         if (!b->isCheckable())
352             continue;
353         b->setFocusPolicy((Qt::FocusPolicy) ((b == q || !q->isCheckable())
354                                          ? (b->focusPolicy() | Qt::TabFocus)
355                                          :  (b->focusPolicy() & ~Qt::TabFocus)));
356     }
357 }
358 
init()359 void QAbstractButtonPrivate::init()
360 {
361     Q_Q(QAbstractButton);
362 
363     q->setFocusPolicy(Qt::FocusPolicy(q->style()->styleHint(QStyle::SH_Button_FocusPolicy)));
364     q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, controlType));
365     q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
366     q->setForegroundRole(QPalette::ButtonText);
367     q->setBackgroundRole(QPalette::Button);
368 }
369 
refresh()370 void QAbstractButtonPrivate::refresh()
371 {
372     Q_Q(QAbstractButton);
373 
374     if (blockRefresh)
375         return;
376     q->update();
377 }
378 
click()379 void QAbstractButtonPrivate::click()
380 {
381     Q_Q(QAbstractButton);
382 
383     down = false;
384     blockRefresh = true;
385     bool changeState = true;
386     if (checked && queryCheckedButton() == q) {
387         // the checked button of an exclusive or autoexclusive group cannot be unchecked
388 #if QT_CONFIG(buttongroup)
389         if (group ? group->d_func()->exclusive : autoExclusive)
390 #else
391         if (autoExclusive)
392 #endif
393             changeState = false;
394     }
395 
396     QPointer<QAbstractButton> guard(q);
397     if (changeState) {
398         q->nextCheckState();
399         if (!guard)
400             return;
401     }
402     blockRefresh = false;
403     refresh();
404     q->repaint();
405     if (guard)
406         emitReleased();
407     if (guard)
408         emitClicked();
409 }
410 
emitClicked()411 void QAbstractButtonPrivate::emitClicked()
412 {
413     Q_Q(QAbstractButton);
414     QPointer<QAbstractButton> guard(q);
415     emit q->clicked(checked);
416 #if QT_CONFIG(buttongroup)
417     if (guard && group) {
418         const int id = group->id(q);
419         emit group->idClicked(id);
420 #if QT_DEPRECATED_SINCE(5, 15)
421 QT_WARNING_PUSH
422 QT_WARNING_DISABLE_DEPRECATED
423         if (guard && group)
424             emit group->buttonClicked(id);
425 QT_WARNING_POP
426 #endif
427         if (guard && group)
428             emit group->buttonClicked(q);
429     }
430 #endif
431 }
432 
emitPressed()433 void QAbstractButtonPrivate::emitPressed()
434 {
435     Q_Q(QAbstractButton);
436     QPointer<QAbstractButton> guard(q);
437     emit q->pressed();
438 #if QT_CONFIG(buttongroup)
439     if (guard && group) {
440         const int id = group->id(q);
441         emit group->idPressed(id);
442 #if QT_DEPRECATED_SINCE(5, 15)
443 QT_WARNING_PUSH
444 QT_WARNING_DISABLE_DEPRECATED
445         if (guard && group)
446             emit group->buttonPressed(id);
447 QT_WARNING_POP
448 #endif
449         if (guard && group)
450             emit group->buttonPressed(q);
451     }
452 #endif
453 }
454 
emitReleased()455 void QAbstractButtonPrivate::emitReleased()
456 {
457     Q_Q(QAbstractButton);
458     QPointer<QAbstractButton> guard(q);
459     emit q->released();
460 #if QT_CONFIG(buttongroup)
461     if (guard && group) {
462         const int id = group->id(q);
463         emit group->idReleased(id);
464 #if QT_DEPRECATED_SINCE(5, 15)
465 QT_WARNING_PUSH
466 QT_WARNING_DISABLE_DEPRECATED
467         if (guard && group)
468             emit group->buttonReleased(id);
469 QT_WARNING_POP
470 #endif
471         if (guard && group)
472             emit group->buttonReleased(q);
473     }
474 #endif
475 }
476 
emitToggled(bool checked)477 void QAbstractButtonPrivate::emitToggled(bool checked)
478 {
479     Q_Q(QAbstractButton);
480     QPointer<QAbstractButton> guard(q);
481     emit q->toggled(checked);
482 #if QT_CONFIG(buttongroup)
483     if (guard && group) {
484         const int id = group->id(q);
485         emit group->idToggled(id, checked);
486 #if QT_DEPRECATED_SINCE(5, 15)
487 QT_WARNING_PUSH
488 QT_WARNING_DISABLE_DEPRECATED
489         if (guard && group)
490             emit group->buttonToggled(id, checked);
491 QT_WARNING_POP
492 #endif
493         if (guard && group)
494             emit group->buttonToggled(q, checked);
495     }
496 #endif
497 }
498 
499 /*!
500     Constructs an abstract button with a \a parent.
501 */
QAbstractButton(QWidget * parent)502 QAbstractButton::QAbstractButton(QWidget *parent)
503     : QWidget(*new QAbstractButtonPrivate, parent, { })
504 {
505     Q_D(QAbstractButton);
506     d->init();
507 }
508 
509 /*!
510     Destroys the button.
511  */
~QAbstractButton()512  QAbstractButton::~QAbstractButton()
513 {
514 #if QT_CONFIG(buttongroup)
515     Q_D(QAbstractButton);
516     if (d->group)
517         d->group->removeButton(this);
518 #endif
519 }
520 
521 
522 /*! \internal
523  */
QAbstractButton(QAbstractButtonPrivate & dd,QWidget * parent)524 QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent)
525     : QWidget(dd, parent, { })
526 {
527     Q_D(QAbstractButton);
528     d->init();
529 }
530 
531 /*!
532 \property QAbstractButton::text
533 \brief the text shown on the button
534 
535 If the button has no text, the text() function will return an empty
536 string.
537 
538 If the text contains an ampersand character ('&'), a shortcut is
539 automatically created for it. The character that follows the '&' will
540 be used as the shortcut key. Any previous shortcut will be
541 overwritten or cleared if no shortcut is defined by the text. See the
542 \l {QShortcut#mnemonic}{QShortcut} documentation for details. To
543 display an actual ampersand, use '&&'.
544 
545 There is no default text.
546 */
547 
setText(const QString & text)548 void QAbstractButton::setText(const QString &text)
549 {
550     Q_D(QAbstractButton);
551     if (d->text == text)
552         return;
553     d->text = text;
554 #ifndef QT_NO_SHORTCUT
555     QKeySequence newMnemonic = QKeySequence::mnemonic(text);
556     setShortcut(newMnemonic);
557 #endif
558     d->sizeHint = QSize();
559     update();
560     updateGeometry();
561 #ifndef QT_NO_ACCESSIBILITY
562     QAccessibleEvent event(this, QAccessible::NameChanged);
563     QAccessible::updateAccessibility(&event);
564 #endif
565 }
566 
text() const567 QString QAbstractButton::text() const
568 {
569     Q_D(const QAbstractButton);
570     return d->text;
571 }
572 
573 
574 /*!
575   \property QAbstractButton::icon
576   \brief the icon shown on the button
577 
578   The icon's default size is defined by the GUI style, but can be
579   adjusted by setting the \l iconSize property.
580 */
setIcon(const QIcon & icon)581 void QAbstractButton::setIcon(const QIcon &icon)
582 {
583     Q_D(QAbstractButton);
584     d->icon = icon;
585     d->sizeHint = QSize();
586     update();
587     updateGeometry();
588 }
589 
icon() const590 QIcon QAbstractButton::icon() const
591 {
592     Q_D(const QAbstractButton);
593     return d->icon;
594 }
595 
596 #ifndef QT_NO_SHORTCUT
597 /*!
598 \property QAbstractButton::shortcut
599 \brief the mnemonic associated with the button
600 */
601 
setShortcut(const QKeySequence & key)602 void QAbstractButton::setShortcut(const QKeySequence &key)
603 {
604     Q_D(QAbstractButton);
605     if (d->shortcutId != 0)
606         releaseShortcut(d->shortcutId);
607     d->shortcut = key;
608     d->shortcutId = grabShortcut(key);
609 }
610 
shortcut() const611 QKeySequence QAbstractButton::shortcut() const
612 {
613     Q_D(const QAbstractButton);
614     return d->shortcut;
615 }
616 #endif // QT_NO_SHORTCUT
617 
618 /*!
619 \property QAbstractButton::checkable
620 \brief whether the button is checkable
621 
622 By default, the button is not checkable.
623 
624 \sa checked
625 */
setCheckable(bool checkable)626 void QAbstractButton::setCheckable(bool checkable)
627 {
628     Q_D(QAbstractButton);
629     if (d->checkable == checkable)
630         return;
631 
632     d->checkable = checkable;
633     d->checked = false;
634 }
635 
isCheckable() const636 bool QAbstractButton::isCheckable() const
637 {
638     Q_D(const QAbstractButton);
639     return d->checkable;
640 }
641 
642 /*!
643 \property QAbstractButton::checked
644 \brief whether the button is checked
645 
646 Only checkable buttons can be checked. By default, the button is unchecked.
647 
648 \sa checkable
649 */
setChecked(bool checked)650 void QAbstractButton::setChecked(bool checked)
651 {
652     Q_D(QAbstractButton);
653     if (!d->checkable || d->checked == checked) {
654         if (!d->blockRefresh)
655             checkStateSet();
656         return;
657     }
658 
659     if (!checked && d->queryCheckedButton() == this) {
660         // the checked button of an exclusive or autoexclusive group cannot be  unchecked
661 #if QT_CONFIG(buttongroup)
662         if (d->group ? d->group->d_func()->exclusive : d->autoExclusive)
663             return;
664         if (d->group)
665             d->group->d_func()->detectCheckedButton();
666 #else
667         if (d->autoExclusive)
668             return;
669 #endif
670     }
671 
672     QPointer<QAbstractButton> guard(this);
673 
674     d->checked = checked;
675     if (!d->blockRefresh)
676         checkStateSet();
677     d->refresh();
678 
679     if (guard && checked)
680         d->notifyChecked();
681     if (guard)
682         d->emitToggled(checked);
683 
684 
685 #ifndef QT_NO_ACCESSIBILITY
686     QAccessible::State s;
687     s.checked = true;
688     QAccessibleStateChangeEvent event(this, s);
689     QAccessible::updateAccessibility(&event);
690 #endif
691 }
692 
isChecked() const693 bool QAbstractButton::isChecked() const
694 {
695     Q_D(const QAbstractButton);
696     return d->checked;
697 }
698 
699 /*!
700   \property QAbstractButton::down
701   \brief whether the button is pressed down
702 
703   If this property is \c true, the button is pressed down. The signals
704   pressed() and clicked() are not emitted if you set this property
705   to true. The default is false.
706 */
707 
setDown(bool down)708 void QAbstractButton::setDown(bool down)
709 {
710     Q_D(QAbstractButton);
711     if (d->down == down)
712         return;
713     d->down = down;
714     d->refresh();
715     if (d->autoRepeat && d->down)
716         d->repeatTimer.start(d->autoRepeatDelay, this);
717     else
718         d->repeatTimer.stop();
719 }
720 
isDown() const721 bool QAbstractButton::isDown() const
722 {
723     Q_D(const QAbstractButton);
724     return d->down;
725 }
726 
727 /*!
728 \property QAbstractButton::autoRepeat
729 \brief whether autoRepeat is enabled
730 
731 If autoRepeat is enabled, then the pressed(), released(), and clicked() signals are emitted at
732 regular intervals when the button is down. autoRepeat is off by default.
733 The initial delay and the repetition interval are defined in milliseconds by \l
734 autoRepeatDelay and \l autoRepeatInterval.
735 
736 Note: If a button is pressed down by a shortcut key, then auto-repeat is enabled and timed by the
737 system and not by this class. The pressed(), released(), and clicked() signals will be emitted
738 like in the normal case.
739 */
740 
setAutoRepeat(bool autoRepeat)741 void QAbstractButton::setAutoRepeat(bool autoRepeat)
742 {
743     Q_D(QAbstractButton);
744     if (d->autoRepeat == autoRepeat)
745         return;
746     d->autoRepeat = autoRepeat;
747     if (d->autoRepeat && d->down)
748         d->repeatTimer.start(d->autoRepeatDelay, this);
749     else
750         d->repeatTimer.stop();
751 }
752 
autoRepeat() const753 bool QAbstractButton::autoRepeat() const
754 {
755     Q_D(const QAbstractButton);
756     return d->autoRepeat;
757 }
758 
759 /*!
760     \property QAbstractButton::autoRepeatDelay
761     \brief the initial delay of auto-repetition
762     \since 4.2
763 
764     If \l autoRepeat is enabled, then autoRepeatDelay defines the initial
765     delay in milliseconds before auto-repetition kicks in.
766 
767     \sa autoRepeat, autoRepeatInterval
768 */
769 
setAutoRepeatDelay(int autoRepeatDelay)770 void QAbstractButton::setAutoRepeatDelay(int autoRepeatDelay)
771 {
772     Q_D(QAbstractButton);
773     d->autoRepeatDelay = autoRepeatDelay;
774 }
775 
autoRepeatDelay() const776 int QAbstractButton::autoRepeatDelay() const
777 {
778     Q_D(const QAbstractButton);
779     return d->autoRepeatDelay;
780 }
781 
782 /*!
783     \property QAbstractButton::autoRepeatInterval
784     \brief the interval of auto-repetition
785     \since 4.2
786 
787     If \l autoRepeat is enabled, then autoRepeatInterval defines the
788     length of the auto-repetition interval in millisecons.
789 
790     \sa autoRepeat, autoRepeatDelay
791 */
792 
setAutoRepeatInterval(int autoRepeatInterval)793 void QAbstractButton::setAutoRepeatInterval(int autoRepeatInterval)
794 {
795     Q_D(QAbstractButton);
796     d->autoRepeatInterval = autoRepeatInterval;
797 }
798 
autoRepeatInterval() const799 int QAbstractButton::autoRepeatInterval() const
800 {
801     Q_D(const QAbstractButton);
802     return d->autoRepeatInterval;
803 }
804 
805 
806 
807 /*!
808 \property QAbstractButton::autoExclusive
809 \brief whether auto-exclusivity is enabled
810 
811 If auto-exclusivity is enabled, checkable buttons that belong to the
812 same parent widget behave as if they were part of the same
813 exclusive button group. In an exclusive button group, only one button
814 can be checked at any time; checking another button automatically
815 unchecks the previously checked one.
816 
817 The property has no effect on buttons that belong to a button
818 group.
819 
820 autoExclusive is off by default, except for radio buttons.
821 
822 \sa QRadioButton
823 */
setAutoExclusive(bool autoExclusive)824 void QAbstractButton::setAutoExclusive(bool autoExclusive)
825 {
826     Q_D(QAbstractButton);
827     d->autoExclusive = autoExclusive;
828 }
829 
autoExclusive() const830 bool QAbstractButton::autoExclusive() const
831 {
832     Q_D(const QAbstractButton);
833     return d->autoExclusive;
834 }
835 
836 #if QT_CONFIG(buttongroup)
837 /*!
838   Returns the group that this button belongs to.
839 
840   If the button is not a member of any QButtonGroup, this function
841   returns \nullptr.
842 
843   \sa QButtonGroup
844 */
group() const845 QButtonGroup *QAbstractButton::group() const
846 {
847     Q_D(const QAbstractButton);
848     return d->group;
849 }
850 #endif // QT_CONFIG(buttongroup)
851 
852 /*!
853 Performs an animated click: the button is pressed immediately, and
854 released \a msec milliseconds later (the default is 100 ms).
855 
856 Calling this function again before the button is released resets
857 the release timer.
858 
859 All signals associated with a click are emitted as appropriate.
860 
861 This function does nothing if the button is \l{setEnabled()}{disabled.}
862 
863 \sa click()
864 */
animateClick(int msec)865 void QAbstractButton::animateClick(int msec)
866 {
867     if (!isEnabled())
868         return;
869     Q_D(QAbstractButton);
870     if (d->checkable && focusPolicy() & Qt::ClickFocus)
871         setFocus();
872     setDown(true);
873     repaint();
874     if (!d->animateTimer.isActive())
875         d->emitPressed();
876     d->animateTimer.start(msec, this);
877 }
878 
879 /*!
880 Performs a click.
881 
882 All the usual signals associated with a click are emitted as
883 appropriate. If the button is checkable, the state of the button is
884 toggled.
885 
886 This function does nothing if the button is \l{setEnabled()}{disabled.}
887 
888 \sa animateClick()
889  */
click()890 void QAbstractButton::click()
891 {
892     if (!isEnabled())
893         return;
894     Q_D(QAbstractButton);
895     QPointer<QAbstractButton> guard(this);
896     d->down = true;
897     d->emitPressed();
898     if (guard) {
899         d->down = false;
900         nextCheckState();
901         if (guard)
902             d->emitReleased();
903         if (guard)
904             d->emitClicked();
905     }
906 }
907 
908 /*! \fn void QAbstractButton::toggle()
909 
910     Toggles the state of a checkable button.
911 
912      \sa checked
913 */
toggle()914 void QAbstractButton::toggle()
915 {
916     Q_D(QAbstractButton);
917     setChecked(!d->checked);
918 }
919 
920 
921 /*! This virtual handler is called when setChecked() is used,
922 unless it is called from within nextCheckState(). It allows
923 subclasses to reset their intermediate button states.
924 
925 \sa nextCheckState()
926  */
checkStateSet()927 void QAbstractButton::checkStateSet()
928 {
929 }
930 
931 /*! This virtual handler is called when a button is clicked. The
932 default implementation calls setChecked(!isChecked()) if the button
933 isCheckable(). It allows subclasses to implement intermediate button
934 states.
935 
936 \sa checkStateSet()
937 */
nextCheckState()938 void QAbstractButton::nextCheckState()
939 {
940     if (isCheckable())
941         setChecked(!isChecked());
942 }
943 
944 /*!
945 Returns \c true if \a pos is inside the clickable button rectangle;
946 otherwise returns \c false.
947 
948 By default, the clickable area is the entire widget. Subclasses
949 may reimplement this function to provide support for clickable
950 areas of different shapes and sizes.
951 */
hitButton(const QPoint & pos) const952 bool QAbstractButton::hitButton(const QPoint &pos) const
953 {
954     return rect().contains(pos);
955 }
956 
957 /*! \reimp */
event(QEvent * e)958 bool QAbstractButton::event(QEvent *e)
959 {
960     // as opposed to other widgets, disabled buttons accept mouse
961     // events. This avoids surprising click-through scenarios
962     if (!isEnabled()) {
963         switch(e->type()) {
964         case QEvent::TabletPress:
965         case QEvent::TabletRelease:
966         case QEvent::TabletMove:
967         case QEvent::MouseButtonPress:
968         case QEvent::MouseButtonRelease:
969         case QEvent::MouseButtonDblClick:
970         case QEvent::MouseMove:
971         case QEvent::HoverMove:
972         case QEvent::HoverEnter:
973         case QEvent::HoverLeave:
974         case QEvent::ContextMenu:
975 #if QT_CONFIG(wheelevent)
976         case QEvent::Wheel:
977 #endif
978             return true;
979         default:
980             break;
981         }
982     }
983 
984 #ifndef QT_NO_SHORTCUT
985     if (e->type() == QEvent::Shortcut) {
986         Q_D(QAbstractButton);
987         QShortcutEvent *se = static_cast<QShortcutEvent *>(e);
988         if (d->shortcutId != se->shortcutId())
989             return false;
990         if (!se->isAmbiguous()) {
991             if (!d->animateTimer.isActive())
992                 animateClick();
993         } else {
994             if (focusPolicy() != Qt::NoFocus)
995                 setFocus(Qt::ShortcutFocusReason);
996             window()->setAttribute(Qt::WA_KeyboardFocusChange);
997         }
998         return true;
999     }
1000 #endif
1001     return QWidget::event(e);
1002 }
1003 
1004 /*! \reimp */
mousePressEvent(QMouseEvent * e)1005 void QAbstractButton::mousePressEvent(QMouseEvent *e)
1006 {
1007     Q_D(QAbstractButton);
1008     if (e->button() != Qt::LeftButton) {
1009         e->ignore();
1010         return;
1011     }
1012     if (hitButton(e->pos())) {
1013         setDown(true);
1014         d->pressed = true;
1015         repaint();
1016         d->emitPressed();
1017         e->accept();
1018     } else {
1019         e->ignore();
1020     }
1021 }
1022 
1023 /*! \reimp */
mouseReleaseEvent(QMouseEvent * e)1024 void QAbstractButton::mouseReleaseEvent(QMouseEvent *e)
1025 {
1026     Q_D(QAbstractButton);
1027 
1028     if (e->button() != Qt::LeftButton) {
1029         e->ignore();
1030         return;
1031     }
1032 
1033     d->pressed = false;
1034 
1035     if (!d->down) {
1036         // refresh is required by QMacStyle to resume the default button animation
1037         d->refresh();
1038         e->ignore();
1039         return;
1040     }
1041 
1042     if (hitButton(e->pos())) {
1043         d->repeatTimer.stop();
1044         d->click();
1045         e->accept();
1046     } else {
1047         setDown(false);
1048         e->ignore();
1049     }
1050 }
1051 
1052 /*! \reimp */
mouseMoveEvent(QMouseEvent * e)1053 void QAbstractButton::mouseMoveEvent(QMouseEvent *e)
1054 {
1055     Q_D(QAbstractButton);
1056     if (!(e->buttons() & Qt::LeftButton) || !d->pressed) {
1057         e->ignore();
1058         return;
1059     }
1060 
1061     if (hitButton(e->pos()) != d->down) {
1062         setDown(!d->down);
1063         repaint();
1064         if (d->down)
1065             d->emitPressed();
1066         else
1067             d->emitReleased();
1068         e->accept();
1069     } else if (!hitButton(e->pos())) {
1070         e->ignore();
1071     }
1072 }
1073 
1074 /*! \reimp */
keyPressEvent(QKeyEvent * e)1075 void QAbstractButton::keyPressEvent(QKeyEvent *e)
1076 {
1077     Q_D(QAbstractButton);
1078     bool next = true;
1079     switch (e->key()) {
1080     case Qt::Key_Enter:
1081     case Qt::Key_Return:
1082         e->ignore();
1083         break;
1084     case Qt::Key_Select:
1085     case Qt::Key_Space:
1086         if (!e->isAutoRepeat()) {
1087             setDown(true);
1088             repaint();
1089             d->emitPressed();
1090         }
1091         break;
1092     case Qt::Key_Up:
1093         next = false;
1094         Q_FALLTHROUGH();
1095     case Qt::Key_Left:
1096     case Qt::Key_Right:
1097     case Qt::Key_Down: {
1098 #ifdef QT_KEYPAD_NAVIGATION
1099         if ((QApplicationPrivate::keypadNavigationEnabled()
1100                 && (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right))
1101                 || (!QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional
1102                 || (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down))) {
1103             e->ignore();
1104             return;
1105         }
1106 #endif
1107         QWidget *pw = parentWidget();
1108         if (d->autoExclusive
1109 #if QT_CONFIG(buttongroup)
1110         || d->group
1111 #endif
1112 #if QT_CONFIG(itemviews)
1113         || (pw && qobject_cast<QAbstractItemView *>(pw->parentWidget()))
1114 #endif
1115         ) {
1116             // ### Using qobject_cast to check if the parent is a viewport of
1117             // QAbstractItemView is a crude hack, and should be revisited and
1118             // cleaned up when fixing task 194373. It's here to ensure that we
1119             // keep compatibility outside QAbstractItemView.
1120             d->moveFocus(e->key());
1121             if (hasFocus()) // nothing happend, propagate
1122                 e->ignore();
1123         } else {
1124             // Prefer parent widget, use this if parent is absent
1125             QWidget *w = pw ? pw : this;
1126             bool reverse = (w->layoutDirection() == Qt::RightToLeft);
1127             if ((e->key() == Qt::Key_Left && !reverse)
1128                 || (e->key() == Qt::Key_Right && reverse)) {
1129                 next = false;
1130             }
1131             focusNextPrevChild(next);
1132         }
1133         break;
1134     }
1135     default:
1136 #ifndef QT_NO_SHORTCUT
1137         if (e->matches(QKeySequence::Cancel) && d->down) {
1138             setDown(false);
1139             repaint();
1140             d->emitReleased();
1141             return;
1142         }
1143 #endif
1144         e->ignore();
1145     }
1146 }
1147 
1148 /*! \reimp */
keyReleaseEvent(QKeyEvent * e)1149 void QAbstractButton::keyReleaseEvent(QKeyEvent *e)
1150 {
1151     Q_D(QAbstractButton);
1152 
1153     if (!e->isAutoRepeat())
1154         d->repeatTimer.stop();
1155 
1156     switch (e->key()) {
1157     case Qt::Key_Select:
1158     case Qt::Key_Space:
1159         if (!e->isAutoRepeat() && d->down)
1160             d->click();
1161         break;
1162     default:
1163         e->ignore();
1164     }
1165 }
1166 
1167 /*!\reimp
1168  */
timerEvent(QTimerEvent * e)1169 void QAbstractButton::timerEvent(QTimerEvent *e)
1170 {
1171     Q_D(QAbstractButton);
1172     if (e->timerId() == d->repeatTimer.timerId()) {
1173         d->repeatTimer.start(d->autoRepeatInterval, this);
1174         if (d->down) {
1175             QPointer<QAbstractButton> guard(this);
1176             nextCheckState();
1177             if (guard)
1178                 d->emitReleased();
1179             if (guard)
1180                 d->emitClicked();
1181             if (guard)
1182                 d->emitPressed();
1183         }
1184     } else if (e->timerId() == d->animateTimer.timerId()) {
1185         d->animateTimer.stop();
1186         d->click();
1187     }
1188 }
1189 
1190 /*! \reimp */
focusInEvent(QFocusEvent * e)1191 void QAbstractButton::focusInEvent(QFocusEvent *e)
1192 {
1193     Q_D(QAbstractButton);
1194 #ifdef QT_KEYPAD_NAVIGATION
1195     if (!QApplicationPrivate::keypadNavigationEnabled())
1196 #endif
1197     d->fixFocusPolicy();
1198     QWidget::focusInEvent(e);
1199 }
1200 
1201 /*! \reimp */
focusOutEvent(QFocusEvent * e)1202 void QAbstractButton::focusOutEvent(QFocusEvent *e)
1203 {
1204     Q_D(QAbstractButton);
1205     if (e->reason() != Qt::PopupFocusReason && d->down) {
1206         d->down = false;
1207         d->emitReleased();
1208     }
1209     QWidget::focusOutEvent(e);
1210 }
1211 
1212 /*! \reimp */
changeEvent(QEvent * e)1213 void QAbstractButton::changeEvent(QEvent *e)
1214 {
1215     Q_D(QAbstractButton);
1216     switch (e->type()) {
1217     case QEvent::EnabledChange:
1218         if (!isEnabled() && d->down) {
1219             d->down = false;
1220             d->emitReleased();
1221         }
1222         break;
1223     default:
1224         d->sizeHint = QSize();
1225         break;
1226     }
1227     QWidget::changeEvent(e);
1228 }
1229 
1230 /*!
1231     \fn void QAbstractButton::paintEvent(QPaintEvent *e)
1232     \reimp
1233 */
1234 
1235 /*!
1236     \fn void QAbstractButton::pressed()
1237 
1238     This signal is emitted when the button is pressed down.
1239 
1240     \sa released(), clicked()
1241 */
1242 
1243 /*!
1244     \fn void QAbstractButton::released()
1245 
1246     This signal is emitted when the button is released.
1247 
1248     \sa pressed(), clicked(), toggled()
1249 */
1250 
1251 /*!
1252 \fn void QAbstractButton::clicked(bool checked)
1253 
1254 This signal is emitted when the button is activated (i.e., pressed down
1255 then released while the mouse cursor is inside the button), when the
1256 shortcut key is typed, or when click() or animateClick() is called.
1257 Notably, this signal is \e not emitted if you call setDown(),
1258 setChecked() or toggle().
1259 
1260 If the button is checkable, \a checked is true if the button is
1261 checked, or false if the button is unchecked.
1262 
1263 \sa pressed(), released(), toggled()
1264 */
1265 
1266 /*!
1267 \fn void QAbstractButton::toggled(bool checked)
1268 
1269 This signal is emitted whenever a checkable button changes its state.
1270 \a checked is true if the button is checked, or false if the button is
1271 unchecked.
1272 
1273 This may be the result of a user action, click() slot activation,
1274 or because setChecked() is called.
1275 
1276 The states of buttons in exclusive button groups are updated before this
1277 signal is emitted. This means that slots can act on either the "off"
1278 signal or the "on" signal emitted by the buttons in the group whose
1279 states have changed.
1280 
1281 For example, a slot that reacts to signals emitted by newly checked
1282 buttons but which ignores signals from buttons that have been unchecked
1283 can be implemented using the following pattern:
1284 
1285 \snippet code/src_gui_widgets_qabstractbutton.cpp 2
1286 
1287 Button groups can be created using the QButtonGroup class, and
1288 updates to the button states monitored with the
1289 \l{QButtonGroup::buttonClicked()} signal.
1290 
1291 \sa checked, clicked()
1292 */
1293 
1294 /*!
1295     \property QAbstractButton::iconSize
1296     \brief the icon size used for this button.
1297 
1298     The default size is defined by the GUI style. This is a maximum
1299     size for the icons. Smaller icons will not be scaled up.
1300 */
1301 
iconSize() const1302 QSize QAbstractButton::iconSize() const
1303 {
1304     Q_D(const QAbstractButton);
1305     if (d->iconSize.isValid())
1306         return d->iconSize;
1307     int e = style()->pixelMetric(QStyle::PM_ButtonIconSize, nullptr, this);
1308     return QSize(e, e);
1309 }
1310 
setIconSize(const QSize & size)1311 void QAbstractButton::setIconSize(const QSize &size)
1312 {
1313     Q_D(QAbstractButton);
1314     if (d->iconSize == size)
1315         return;
1316 
1317     d->iconSize = size;
1318     d->sizeHint = QSize();
1319     updateGeometry();
1320     if (isVisible()) {
1321         update();
1322     }
1323 }
1324 
1325 
1326 
1327 QT_END_NAMESPACE
1328 
1329 #include "moc_qabstractbutton.cpp"
1330