/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the QtGui module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "qabstractbutton.h" | |
#include "qabstractitemview.h" | |
#include "qbuttongroup.h" | |
#include "qabstractbutton_p.h" | |
#include "qevent.h" | |
#include "qpainter.h" | |
#include "qapplication.h" | |
#include "qstyle.h" | |
#include "qaction.h" | |
#ifndef QT_NO_ACCESSIBILITY | |
#include "qaccessible.h" | |
#endif | |
QT_BEGIN_NAMESPACE | |
#define AUTO_REPEAT_DELAY 300 | |
#define AUTO_REPEAT_INTERVAL 100 | |
Q_GUI_EXPORT extern bool qt_tab_all_widgets; | |
/*! | |
\class QAbstractButton | |
\brief The QAbstractButton class is the abstract base class of | |
button widgets, providing functionality common to buttons. | |
\ingroup abstractwidgets | |
This class implements an \e abstract button. | |
Subclasses of this class handle user actions, and specify how the button | |
is drawn. | |
QAbstractButton provides support for both push buttons and checkable | |
(toggle) buttons. Checkable buttons are implemented in the QRadioButton | |
and QCheckBox classes. Push buttons are implemented in the | |
QPushButton and QToolButton classes; these also provide toggle | |
behavior if required. | |
Any button can display a label containing text and an icon. setText() | |
sets the text; setIcon() sets the icon. If a button is disabled, its label | |
is changed to give the button a "disabled" appearance. | |
If the button is a text button with a string containing an | |
ampersand ('&'), QAbstractButton automatically creates a shortcut | |
key. For example: | |
\snippet doc/src/snippets/code/src_gui_widgets_qabstractbutton.cpp 0 | |
The \key Alt+C shortcut is assigned to the button, i.e., when the | |
user presses \key Alt+C the button will call animateClick(). See | |
the \l {QShortcut#mnemonic}{QShortcut} documentation for details | |
(to display an actual ampersand, use '&&'). | |
You can also set a custom shortcut key using the setShortcut() | |
function. This is useful mostly for buttons that do not have any | |
text, because they have no automatic shortcut. | |
\snippet doc/src/snippets/code/src_gui_widgets_qabstractbutton.cpp 1 | |
All of the buttons provided by Qt (QPushButton, QToolButton, | |
QCheckBox, and QRadioButton) can display both \l text and \l{icon}{icons}. | |
A button can be made the default button in a dialog are provided by | |
QPushButton::setDefault() and QPushButton::setAutoDefault(). | |
QAbstractButton provides most of the states used for buttons: | |
\list | |
\o isDown() indicates whether the button is \e pressed down. | |
\o isChecked() indicates whether the button is \e checked. Only | |
checkable buttons can be checked and unchecked (see below). | |
\o isEnabled() indicates whether the button can be pressed by the | |
user. | |
\o setAutoRepeat() sets whether the button will auto-repeat if the | |
user holds it down. \l autoRepeatDelay and \l autoRepeatInterval | |
define how auto-repetition is done. | |
\o setCheckable() sets whether the button is a toggle button or not. | |
\endlist | |
The difference between isDown() and isChecked() is as follows. | |
When the user clicks a toggle button to check it, the button is first | |
\e pressed then released into the \e checked state. When the user | |
clicks it again (to uncheck it), the button moves first to the | |
\e pressed state, then to the \e unchecked state (isChecked() and | |
isDown() are both false). | |
QAbstractButton provides four signals: | |
\list 1 | |
\o pressed() is emitted when the left mouse button is pressed while | |
the mouse cursor is inside the button. | |
\o released() is emitted when the left mouse button is released. | |
\o clicked() is emitted when the button is first pressed and then | |
released, when the shortcut key is typed, or when click() or | |
animateClick() is called. | |
\o toggled() is emitted when the state of a toggle button changes. | |
\endlist | |
To subclass QAbstractButton, you must reimplement at least | |
paintEvent() to draw the button's outline and its text or pixmap. It | |
is generally advisable to reimplement sizeHint() as well, and | |
sometimes hitButton() (to determine whether a button press is within | |
the button). For buttons with more than two states (like tri-state | |
buttons), you will also have to reimplement checkStateSet() and | |
nextCheckState(). | |
\sa QButtonGroup | |
*/ | |
QAbstractButtonPrivate::QAbstractButtonPrivate(QSizePolicy::ControlType type) | |
: | |
#ifndef QT_NO_SHORTCUT | |
shortcutId(0), | |
#endif | |
checkable(false), checked(false), autoRepeat(false), autoExclusive(false), | |
down(false), blockRefresh(false), pressed(false), | |
#ifndef QT_NO_BUTTONGROUP | |
group(0), | |
#endif | |
autoRepeatDelay(AUTO_REPEAT_DELAY), | |
autoRepeatInterval(AUTO_REPEAT_INTERVAL), | |
controlType(type) | |
{} | |
#ifndef QT_NO_BUTTONGROUP | |
class QButtonGroupPrivate: public QObjectPrivate | |
{ | |
Q_DECLARE_PUBLIC(QButtonGroup) | |
public: | |
QButtonGroupPrivate():exclusive(true){} | |
QList<QAbstractButton *> buttonList; | |
QPointer<QAbstractButton> checkedButton; | |
void detectCheckedButton(); | |
void notifyChecked(QAbstractButton *button); | |
bool exclusive; | |
QMap<QAbstractButton*, int> mapping; | |
}; | |
QButtonGroup::QButtonGroup(QObject *parent) | |
: QObject(*new QButtonGroupPrivate, parent) | |
{ | |
} | |
QButtonGroup::~QButtonGroup() | |
{ | |
Q_D(QButtonGroup); | |
for (int i = 0; i < d->buttonList.count(); ++i) | |
d->buttonList.at(i)->d_func()->group = 0; | |
} | |
bool QButtonGroup::exclusive() const | |
{ | |
Q_D(const QButtonGroup); | |
return d->exclusive; | |
} | |
void QButtonGroup::setExclusive(bool exclusive) | |
{ | |
Q_D(QButtonGroup); | |
d->exclusive = exclusive; | |
} | |
// TODO: Qt 5: Merge with addButton(QAbstractButton *button, int id) | |
void QButtonGroup::addButton(QAbstractButton *button) | |
{ | |
addButton(button, -1); | |
} | |
void QButtonGroup::addButton(QAbstractButton *button, int id) | |
{ | |
Q_D(QButtonGroup); | |
if (QButtonGroup *previous = button->d_func()->group) | |
previous->removeButton(button); | |
button->d_func()->group = this; | |
d->buttonList.append(button); | |
if (id == -1) { | |
QList<int> ids = d->mapping.values(); | |
if (ids.isEmpty()) | |
d->mapping[button] = -2; | |
else { | |
qSort(ids); | |
d->mapping[button] = ids.first()-1; | |
} | |
} else { | |
d->mapping[button] = id; | |
} | |
if (d->exclusive && button->isChecked()) | |
button->d_func()->notifyChecked(); | |
} | |
void QButtonGroup::removeButton(QAbstractButton *button) | |
{ | |
Q_D(QButtonGroup); | |
if (d->checkedButton == button) { | |
d->detectCheckedButton(); | |
} | |
if (button->d_func()->group == this) { | |
button->d_func()->group = 0; | |
d->buttonList.removeAll(button); | |
d->mapping.remove(button); | |
} | |
} | |
QList<QAbstractButton*> QButtonGroup::buttons() const | |
{ | |
Q_D(const QButtonGroup); | |
return d->buttonList; | |
} | |
QAbstractButton *QButtonGroup::checkedButton() const | |
{ | |
Q_D(const QButtonGroup); | |
return d->checkedButton; | |
} | |
QAbstractButton *QButtonGroup::button(int id) const | |
{ | |
Q_D(const QButtonGroup); | |
return d->mapping.key(id); | |
} | |
void QButtonGroup::setId(QAbstractButton *button, int id) | |
{ | |
Q_D(QButtonGroup); | |
if (button && id != -1) | |
d->mapping[button] = id; | |
} | |
int QButtonGroup::id(QAbstractButton *button) const | |
{ | |
Q_D(const QButtonGroup); | |
return d->mapping.value(button, -1); | |
} | |
int QButtonGroup::checkedId() const | |
{ | |
Q_D(const QButtonGroup); | |
return d->mapping.value(d->checkedButton, -1); | |
} | |
// detect a checked button other than the current one | |
void QButtonGroupPrivate::detectCheckedButton() | |
{ | |
QAbstractButton *previous = checkedButton; | |
checkedButton = 0; | |
if (exclusive) | |
return; | |
for (int i = 0; i < buttonList.count(); i++) { | |
if (buttonList.at(i) != previous && buttonList.at(i)->isChecked()) { | |
checkedButton = buttonList.at(i); | |
return; | |
} | |
} | |
} | |
#endif // QT_NO_BUTTONGROUP | |
QList<QAbstractButton *>QAbstractButtonPrivate::queryButtonList() const | |
{ | |
#ifndef QT_NO_BUTTONGROUP | |
if (group) | |
return group->d_func()->buttonList; | |
#endif | |
QList<QAbstractButton*>candidates = qFindChildren<QAbstractButton *>(parent); | |
if (autoExclusive) { | |
for (int i = candidates.count() - 1; i >= 0; --i) { | |
QAbstractButton *candidate = candidates.at(i); | |
if (!candidate->autoExclusive() | |
#ifndef QT_NO_BUTTONGROUP | |
|| candidate->group() | |
#endif | |
) | |
candidates.removeAt(i); | |
} | |
} | |
return candidates; | |
} | |
QAbstractButton *QAbstractButtonPrivate::queryCheckedButton() const | |
{ | |
#ifndef QT_NO_BUTTONGROUP | |
if (group) | |
return group->d_func()->checkedButton; | |
#endif | |
Q_Q(const QAbstractButton); | |
QList<QAbstractButton *> buttonList = queryButtonList(); | |
if (!autoExclusive || buttonList.count() == 1) // no group | |
return 0; | |
for (int i = 0; i < buttonList.count(); ++i) { | |
QAbstractButton *b = buttonList.at(i); | |
if (b->d_func()->checked && b != q) | |
return b; | |
} | |
return checked ? const_cast<QAbstractButton *>(q) : 0; | |
} | |
void QAbstractButtonPrivate::notifyChecked() | |
{ | |
#ifndef QT_NO_BUTTONGROUP | |
Q_Q(QAbstractButton); | |
if (group) { | |
QAbstractButton *previous = group->d_func()->checkedButton; | |
group->d_func()->checkedButton = q; | |
if (group->d_func()->exclusive && previous && previous != q) | |
previous->nextCheckState(); | |
} else | |
#endif | |
if (autoExclusive) { | |
if (QAbstractButton *b = queryCheckedButton()) | |
b->setChecked(false); | |
} | |
} | |
void QAbstractButtonPrivate::moveFocus(int key) | |
{ | |
QList<QAbstractButton *> buttonList = queryButtonList();; | |
#ifndef QT_NO_BUTTONGROUP | |
bool exclusive = group ? group->d_func()->exclusive : autoExclusive; | |
#else | |
bool exclusive = autoExclusive; | |
#endif | |
QWidget *f = QApplication::focusWidget(); | |
QAbstractButton *fb = qobject_cast<QAbstractButton *>(f); | |
if (!fb || !buttonList.contains(fb)) | |
return; | |
QAbstractButton *candidate = 0; | |
int bestScore = -1; | |
QRect target = f->rect().translated(f->mapToGlobal(QPoint(0,0))); | |
QPoint goal = target.center(); | |
uint focus_flag = qt_tab_all_widgets ? Qt::TabFocus : Qt::StrongFocus; | |
for (int i = 0; i < buttonList.count(); ++i) { | |
QAbstractButton *button = buttonList.at(i); | |
if (button != f && button->window() == f->window() && button->isEnabled() && !button->isHidden() && | |
(autoExclusive || (button->focusPolicy() & focus_flag) == focus_flag)) { | |
QRect buttonRect = button->rect().translated(button->mapToGlobal(QPoint(0,0))); | |
QPoint p = buttonRect.center(); | |
//Priority to widgets that overlap on the same coordinate. | |
//In that case, the distance in the direction will be used as significant score, | |
//take also in account orthogonal distance in case two widget are in the same distance. | |
int score; | |
if ((buttonRect.x() < target.right() && target.x() < buttonRect.right()) | |
&& (key == Qt::Key_Up || key == Qt::Key_Down)) { | |
//one item's is at the vertical of the other | |
score = (qAbs(p.y() - goal.y()) << 16) + qAbs(p.x() - goal.x()); | |
} else if ((buttonRect.y() < target.bottom() && target.y() < buttonRect.bottom()) | |
&& (key == Qt::Key_Left || key == Qt::Key_Right) ) { | |
//one item's is at the horizontal of the other | |
score = (qAbs(p.x() - goal.x()) << 16) + qAbs(p.y() - goal.y()); | |
} else { | |
score = (1 << 30) + (p.y() - goal.y()) * (p.y() - goal.y()) + (p.x() - goal.x()) * (p.x() - goal.x()); | |
} | |
if (score > bestScore && candidate) | |
continue; | |
switch(key) { | |
case Qt::Key_Up: | |
if (p.y() < goal.y()) { | |
candidate = button; | |
bestScore = score; | |
} | |
break; | |
case Qt::Key_Down: | |
if (p.y() > goal.y()) { | |
candidate = button; | |
bestScore = score; | |
} | |
break; | |
case Qt::Key_Left: | |
if (p.x() < goal.x()) { | |
candidate = button; | |
bestScore = score; | |
} | |
break; | |
case Qt::Key_Right: | |
if (p.x() > goal.x()) { | |
candidate = button; | |
bestScore = score; | |
} | |
break; | |
} | |
} | |
} | |
if (exclusive | |
#ifdef QT_KEYPAD_NAVIGATION | |
&& !QApplication::keypadNavigationEnabled() | |
#endif | |
&& candidate | |
&& fb->d_func()->checked | |
&& candidate->d_func()->checkable) | |
candidate->click(); | |
if (candidate) { | |
if (key == Qt::Key_Up || key == Qt::Key_Left) | |
candidate->setFocus(Qt::BacktabFocusReason); | |
else | |
candidate->setFocus(Qt::TabFocusReason); | |
} | |
} | |
void QAbstractButtonPrivate::fixFocusPolicy() | |
{ | |
Q_Q(QAbstractButton); | |
#ifndef QT_NO_BUTTONGROUP | |
if (!group && !autoExclusive) | |
#else | |
if (!autoExclusive) | |
#endif | |
return; | |
QList<QAbstractButton *> buttonList = queryButtonList(); | |
for (int i = 0; i < buttonList.count(); ++i) { | |
QAbstractButton *b = buttonList.at(i); | |
if (!b->isCheckable()) | |
continue; | |
b->setFocusPolicy((Qt::FocusPolicy) ((b == q || !q->isCheckable()) | |
? (b->focusPolicy() | Qt::TabFocus) | |
: (b->focusPolicy() & ~Qt::TabFocus))); | |
} | |
} | |
void QAbstractButtonPrivate::init() | |
{ | |
Q_Q(QAbstractButton); | |
q->setFocusPolicy(Qt::FocusPolicy(q->style()->styleHint(QStyle::SH_Button_FocusPolicy))); | |
q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, controlType)); | |
q->setAttribute(Qt::WA_WState_OwnSizePolicy, false); | |
q->setForegroundRole(QPalette::ButtonText); | |
q->setBackgroundRole(QPalette::Button); | |
} | |
void QAbstractButtonPrivate::refresh() | |
{ | |
Q_Q(QAbstractButton); | |
if (blockRefresh) | |
return; | |
q->update(); | |
#ifndef QT_NO_ACCESSIBILITY | |
QAccessible::updateAccessibility(q, 0, QAccessible::StateChanged); | |
#endif | |
} | |
void QAbstractButtonPrivate::click() | |
{ | |
Q_Q(QAbstractButton); | |
down = false; | |
blockRefresh = true; | |
bool changeState = true; | |
if (checked && queryCheckedButton() == q) { | |
// the checked button of an exclusive or autoexclusive group cannot be unchecked | |
#ifndef QT_NO_BUTTONGROUP | |
if (group ? group->d_func()->exclusive : autoExclusive) | |
#else | |
if (autoExclusive) | |
#endif | |
changeState = false; | |
} | |
QPointer<QAbstractButton> guard(q); | |
if (changeState) { | |
q->nextCheckState(); | |
if (!guard) | |
return; | |
} | |
blockRefresh = false; | |
refresh(); | |
q->repaint(); //flush paint event before invoking potentially expensive operation | |
QApplication::flush(); | |
if (guard) | |
emitReleased(); | |
if (guard) | |
emitClicked(); | |
} | |
void QAbstractButtonPrivate::emitClicked() | |
{ | |
Q_Q(QAbstractButton); | |
QPointer<QAbstractButton> guard(q); | |
emit q->clicked(checked); | |
#ifndef QT_NO_BUTTONGROUP | |
if (guard && group) { | |
emit group->buttonClicked(group->id(q)); | |
if (guard && group) | |
emit group->buttonClicked(q); | |
} | |
#endif | |
} | |
void QAbstractButtonPrivate::emitPressed() | |
{ | |
Q_Q(QAbstractButton); | |
QPointer<QAbstractButton> guard(q); | |
emit q->pressed(); | |
#ifndef QT_NO_BUTTONGROUP | |
if (guard && group) { | |
emit group->buttonPressed(group->id(q)); | |
if (guard && group) | |
emit group->buttonPressed(q); | |
} | |
#endif | |
} | |
void QAbstractButtonPrivate::emitReleased() | |
{ | |
Q_Q(QAbstractButton); | |
QPointer<QAbstractButton> guard(q); | |
emit q->released(); | |
#ifndef QT_NO_BUTTONGROUP | |
if (guard && group) { | |
emit group->buttonReleased(group->id(q)); | |
if (guard && group) | |
emit group->buttonReleased(q); | |
} | |
#endif | |
} | |
/*! | |
Constructs an abstract button with a \a parent. | |
*/ | |
QAbstractButton::QAbstractButton(QWidget *parent) | |
: QWidget(*new QAbstractButtonPrivate, parent, 0) | |
{ | |
Q_D(QAbstractButton); | |
d->init(); | |
} | |
/*! | |
Destroys the button. | |
*/ | |
QAbstractButton::~QAbstractButton() | |
{ | |
#ifndef QT_NO_BUTTONGROUP | |
Q_D(QAbstractButton); | |
if (d->group) | |
d->group->removeButton(this); | |
#endif | |
} | |
/*! \internal | |
*/ | |
QAbstractButton::QAbstractButton(QAbstractButtonPrivate &dd, QWidget *parent) | |
: QWidget(dd, parent, 0) | |
{ | |
Q_D(QAbstractButton); | |
d->init(); | |
} | |
/*! | |
\property QAbstractButton::text | |
\brief the text shown on the button | |
If the button has no text, the text() function will return a an empty | |
string. | |
If the text contains an ampersand character ('&'), a shortcut is | |
automatically created for it. The character that follows the '&' will | |
be used as the shortcut key. Any previous shortcut will be | |
overwritten, or cleared if no shortcut is defined by the text. See the | |
\l {QShortcut#mnemonic}{QShortcut} documentation for details (to | |
display an actual ampersand, use '&&'). | |
There is no default text. | |
*/ | |
void QAbstractButton::setText(const QString &text) | |
{ | |
Q_D(QAbstractButton); | |
if (d->text == text) | |
return; | |
d->text = text; | |
#ifndef QT_NO_SHORTCUT | |
QKeySequence newMnemonic = QKeySequence::mnemonic(text); | |
setShortcut(newMnemonic); | |
#endif | |
d->sizeHint = QSize(); | |
update(); | |
updateGeometry(); | |
#ifndef QT_NO_ACCESSIBILITY | |
QAccessible::updateAccessibility(this, 0, QAccessible::NameChanged); | |
#endif | |
} | |
QString QAbstractButton::text() const | |
{ | |
Q_D(const QAbstractButton); | |
return d->text; | |
} | |
/*! | |
\property QAbstractButton::icon | |
\brief the icon shown on the button | |
The icon's default size is defined by the GUI style, but can be | |
adjusted by setting the \l iconSize property. | |
*/ | |
void QAbstractButton::setIcon(const QIcon &icon) | |
{ | |
Q_D(QAbstractButton); | |
d->icon = icon; | |
d->sizeHint = QSize(); | |
update(); | |
updateGeometry(); | |
} | |
QIcon QAbstractButton::icon() const | |
{ | |
Q_D(const QAbstractButton); | |
return d->icon; | |
} | |
#ifndef QT_NO_SHORTCUT | |
/*! | |
\property QAbstractButton::shortcut | |
\brief the mnemonic associated with the button | |
*/ | |
void QAbstractButton::setShortcut(const QKeySequence &key) | |
{ | |
Q_D(QAbstractButton); | |
if (d->shortcutId != 0) | |
releaseShortcut(d->shortcutId); | |
d->shortcut = key; | |
d->shortcutId = grabShortcut(key); | |
} | |
QKeySequence QAbstractButton::shortcut() const | |
{ | |
Q_D(const QAbstractButton); | |
return d->shortcut; | |
} | |
#endif // QT_NO_SHORTCUT | |
/*! | |
\property QAbstractButton::checkable | |
\brief whether the button is checkable | |
By default, the button is not checkable. | |
\sa checked | |
*/ | |
void QAbstractButton::setCheckable(bool checkable) | |
{ | |
Q_D(QAbstractButton); | |
if (d->checkable == checkable) | |
return; | |
d->checkable = checkable; | |
d->checked = false; | |
} | |
bool QAbstractButton::isCheckable() const | |
{ | |
Q_D(const QAbstractButton); | |
return d->checkable; | |
} | |
/*! | |
\property QAbstractButton::checked | |
\brief whether the button is checked | |
Only checkable buttons can be checked. By default, the button is unchecked. | |
\sa checkable | |
*/ | |
void QAbstractButton::setChecked(bool checked) | |
{ | |
Q_D(QAbstractButton); | |
if (!d->checkable || d->checked == checked) { | |
if (!d->blockRefresh) | |
checkStateSet(); | |
return; | |
} | |
if (!checked && d->queryCheckedButton() == this) { | |
// the checked button of an exclusive or autoexclusive group cannot be unchecked | |
#ifndef QT_NO_BUTTONGROUP | |
if (d->group ? d->group->d_func()->exclusive : d->autoExclusive) | |
return; | |
if (d->group) | |
d->group->d_func()->detectCheckedButton(); | |
#else | |
if (d->autoExclusive) | |
return; | |
#endif | |
} | |
QPointer<QAbstractButton> guard(this); | |
d->checked = checked; | |
if (!d->blockRefresh) | |
checkStateSet(); | |
d->refresh(); | |
if (guard && checked) | |
d->notifyChecked(); | |
if (guard) | |
emit toggled(checked); | |
} | |
bool QAbstractButton::isChecked() const | |
{ | |
Q_D(const QAbstractButton); | |
return d->checked; | |
} | |
/*! | |
\property QAbstractButton::down | |
\brief whether the button is pressed down | |
If this property is true, the button is pressed down. The signals | |
pressed() and clicked() are not emitted if you set this property | |
to true. The default is false. | |
*/ | |
void QAbstractButton::setDown(bool down) | |
{ | |
Q_D(QAbstractButton); | |
if (d->down == down) | |
return; | |
d->down = down; | |
d->refresh(); | |
if (d->autoRepeat && d->down) | |
d->repeatTimer.start(d->autoRepeatDelay, this); | |
else | |
d->repeatTimer.stop(); | |
} | |
bool QAbstractButton::isDown() const | |
{ | |
Q_D(const QAbstractButton); | |
return d->down; | |
} | |
/*! | |
\property QAbstractButton::autoRepeat | |
\brief whether autoRepeat is enabled | |
If autoRepeat is enabled, then the pressed(), released(), and clicked() signals are emitted at | |
regular intervals when the button is down. autoRepeat is off by default. | |
The initial delay and the repetition interval are defined in milliseconds by \l | |
autoRepeatDelay and \l autoRepeatInterval. | |
Note: If a button is pressed down by a shortcut key, then auto-repeat is enabled and timed by the | |
system and not by this class. The pressed(), released(), and clicked() signals will be emitted | |
like in the normal case. | |
*/ | |
void QAbstractButton::setAutoRepeat(bool autoRepeat) | |
{ | |
Q_D(QAbstractButton); | |
if (d->autoRepeat == autoRepeat) | |
return; | |
d->autoRepeat = autoRepeat; | |
if (d->autoRepeat && d->down) | |
d->repeatTimer.start(d->autoRepeatDelay, this); | |
else | |
d->repeatTimer.stop(); | |
} | |
bool QAbstractButton::autoRepeat() const | |
{ | |
Q_D(const QAbstractButton); | |
return d->autoRepeat; | |
} | |
/*! | |
\property QAbstractButton::autoRepeatDelay | |
\brief the initial delay of auto-repetition | |
\since 4.2 | |
If \l autoRepeat is enabled, then autoRepeatDelay defines the initial | |
delay in milliseconds before auto-repetition kicks in. | |
\sa autoRepeat, autoRepeatInterval | |
*/ | |
void QAbstractButton::setAutoRepeatDelay(int autoRepeatDelay) | |
{ | |
Q_D(QAbstractButton); | |
d->autoRepeatDelay = autoRepeatDelay; | |
} | |
int QAbstractButton::autoRepeatDelay() const | |
{ | |
Q_D(const QAbstractButton); | |
return d->autoRepeatDelay; | |
} | |
/*! | |
\property QAbstractButton::autoRepeatInterval | |
\brief the interval of auto-repetition | |
\since 4.2 | |
If \l autoRepeat is enabled, then autoRepeatInterval defines the | |
length of the auto-repetition interval in millisecons. | |
\sa autoRepeat, autoRepeatDelay | |
*/ | |
void QAbstractButton::setAutoRepeatInterval(int autoRepeatInterval) | |
{ | |
Q_D(QAbstractButton); | |
d->autoRepeatInterval = autoRepeatInterval; | |
} | |
int QAbstractButton::autoRepeatInterval() const | |
{ | |
Q_D(const QAbstractButton); | |
return d->autoRepeatInterval; | |
} | |
/*! | |
\property QAbstractButton::autoExclusive | |
\brief whether auto-exclusivity is enabled | |
If auto-exclusivity is enabled, checkable buttons that belong to the | |
same parent widget behave as if they were part of the same | |
exclusive button group. In an exclusive button group, only one button | |
can be checked at any time; checking another button automatically | |
unchecks the previously checked one. | |
The property has no effect on buttons that belong to a button | |
group. | |
autoExclusive is off by default, except for radio buttons. | |
\sa QRadioButton | |
*/ | |
void QAbstractButton::setAutoExclusive(bool autoExclusive) | |
{ | |
Q_D(QAbstractButton); | |
d->autoExclusive = autoExclusive; | |
} | |
bool QAbstractButton::autoExclusive() const | |
{ | |
Q_D(const QAbstractButton); | |
return d->autoExclusive; | |
} | |
#ifndef QT_NO_BUTTONGROUP | |
/*! | |
Returns the group that this button belongs to. | |
If the button is not a member of any QButtonGroup, this function | |
returns 0. | |
\sa QButtonGroup | |
*/ | |
QButtonGroup *QAbstractButton::group() const | |
{ | |
Q_D(const QAbstractButton); | |
return d->group; | |
} | |
#endif // QT_NO_BUTTONGROUP | |
/*! | |
Performs an animated click: the button is pressed immediately, and | |
released \a msec milliseconds later (the default is 100 ms). | |
Calling this function again before the button was released will reset | |
the release timer. | |
All signals associated with a click are emitted as appropriate. | |
This function does nothing if the button is \link setEnabled() | |
disabled. \endlink | |
\sa click() | |
*/ | |
void QAbstractButton::animateClick(int msec) | |
{ | |
if (!isEnabled()) | |
return; | |
Q_D(QAbstractButton); | |
if (d->checkable && focusPolicy() & Qt::ClickFocus) | |
setFocus(); | |
setDown(true); | |
repaint(); //flush paint event before invoking potentially expensive operation | |
QApplication::flush(); | |
if (!d->animateTimer.isActive()) | |
d->emitPressed(); | |
d->animateTimer.start(msec, this); | |
} | |
/*! | |
Performs a click. | |
All the usual signals associated with a click are emitted as | |
appropriate. If the button is checkable, the state of the button is | |
toggled. | |
This function does nothing if the button is \link setEnabled() | |
disabled. \endlink | |
\sa animateClick() | |
*/ | |
void QAbstractButton::click() | |
{ | |
if (!isEnabled()) | |
return; | |
Q_D(QAbstractButton); | |
QPointer<QAbstractButton> guard(this); | |
d->down = true; | |
d->emitPressed(); | |
if (guard) { | |
d->down = false; | |
nextCheckState(); | |
if (guard) | |
d->emitReleased(); | |
if (guard) | |
d->emitClicked(); | |
} | |
} | |
/*! \fn void QAbstractButton::toggle() | |
Toggles the state of a checkable button. | |
\sa checked | |
*/ | |
void QAbstractButton::toggle() | |
{ | |
Q_D(QAbstractButton); | |
setChecked(!d->checked); | |
} | |
/*! This virtual handler is called when setChecked() was called, | |
unless it was called from within nextCheckState(). It allows | |
subclasses to reset their intermediate button states. | |
\sa nextCheckState() | |
*/ | |
void QAbstractButton::checkStateSet() | |
{ | |
} | |
/*! This virtual handler is called when a button is clicked. The | |
default implementation calls setChecked(!isChecked()) if the button | |
isCheckable(). It allows subclasses to implement intermediate button | |
states. | |
\sa checkStateSet() | |
*/ | |
void QAbstractButton::nextCheckState() | |
{ | |
if (isCheckable()) | |
setChecked(!isChecked()); | |
} | |
/*! | |
Returns true if \a pos is inside the clickable button rectangle; | |
otherwise returns false. | |
By default, the clickable area is the entire widget. Subclasses | |
may reimplement this function to provide support for clickable | |
areas of different shapes and sizes. | |
*/ | |
bool QAbstractButton::hitButton(const QPoint &pos) const | |
{ | |
return rect().contains(pos); | |
} | |
/*! \reimp */ | |
bool QAbstractButton::event(QEvent *e) | |
{ | |
// as opposed to other widgets, disabled buttons accept mouse | |
// events. This avoids surprising click-through scenarios | |
if (!isEnabled()) { | |
switch(e->type()) { | |
case QEvent::TabletPress: | |
case QEvent::TabletRelease: | |
case QEvent::TabletMove: | |
case QEvent::MouseButtonPress: | |
case QEvent::MouseButtonRelease: | |
case QEvent::MouseButtonDblClick: | |
case QEvent::MouseMove: | |
case QEvent::HoverMove: | |
case QEvent::HoverEnter: | |
case QEvent::HoverLeave: | |
case QEvent::ContextMenu: | |
#ifndef QT_NO_WHEELEVENT | |
case QEvent::Wheel: | |
#endif | |
return true; | |
default: | |
break; | |
} | |
} | |
#ifndef QT_NO_SHORTCUT | |
if (e->type() == QEvent::Shortcut) { | |
Q_D(QAbstractButton); | |
QShortcutEvent *se = static_cast<QShortcutEvent *>(e); | |
if (d->shortcutId != se->shortcutId()) | |
return false; | |
if (!se->isAmbiguous()) { | |
if (!d->animateTimer.isActive()) | |
animateClick(); | |
} else { | |
if (focusPolicy() != Qt::NoFocus) | |
setFocus(Qt::ShortcutFocusReason); | |
window()->setAttribute(Qt::WA_KeyboardFocusChange); | |
} | |
return true; | |
} | |
#endif | |
return QWidget::event(e); | |
} | |
/*! \reimp */ | |
void QAbstractButton::mousePressEvent(QMouseEvent *e) | |
{ | |
Q_D(QAbstractButton); | |
if (e->button() != Qt::LeftButton) { | |
e->ignore(); | |
return; | |
} | |
if (hitButton(e->pos())) { | |
setDown(true); | |
d->pressed = true; | |
repaint(); //flush paint event before invoking potentially expensive operation | |
QApplication::flush(); | |
d->emitPressed(); | |
e->accept(); | |
} else { | |
e->ignore(); | |
} | |
} | |
/*! \reimp */ | |
void QAbstractButton::mouseReleaseEvent(QMouseEvent *e) | |
{ | |
Q_D(QAbstractButton); | |
d->pressed = false; | |
if (e->button() != Qt::LeftButton) { | |
e->ignore(); | |
return; | |
} | |
if (!d->down) { | |
e->ignore(); | |
return; | |
} | |
if (hitButton(e->pos())) { | |
d->repeatTimer.stop(); | |
d->click(); | |
e->accept(); | |
} else { | |
setDown(false); | |
e->ignore(); | |
} | |
} | |
/*! \reimp */ | |
void QAbstractButton::mouseMoveEvent(QMouseEvent *e) | |
{ | |
Q_D(QAbstractButton); | |
if (!(e->buttons() & Qt::LeftButton) || !d->pressed) { | |
e->ignore(); | |
return; | |
} | |
if (hitButton(e->pos()) != d->down) { | |
setDown(!d->down); | |
repaint(); //flush paint event before invoking potentially expensive operation | |
QApplication::flush(); | |
if (d->down) | |
d->emitPressed(); | |
else | |
d->emitReleased(); | |
e->accept(); | |
} else if (!hitButton(e->pos())) { | |
e->ignore(); | |
} | |
} | |
/*! \reimp */ | |
void QAbstractButton::keyPressEvent(QKeyEvent *e) | |
{ | |
Q_D(QAbstractButton); | |
bool next = true; | |
switch (e->key()) { | |
case Qt::Key_Enter: | |
case Qt::Key_Return: | |
e->ignore(); | |
break; | |
case Qt::Key_Select: | |
case Qt::Key_Space: | |
if (!e->isAutoRepeat()) { | |
setDown(true); | |
repaint(); //flush paint event before invoking potentially expensive operation | |
QApplication::flush(); | |
d->emitPressed(); | |
} | |
break; | |
case Qt::Key_Up: | |
case Qt::Key_Left: | |
next = false; | |
// fall through | |
case Qt::Key_Right: | |
case Qt::Key_Down: | |
#ifdef QT_KEYPAD_NAVIGATION | |
if ((QApplication::keypadNavigationEnabled() | |
&& (e->key() == Qt::Key_Left || e->key() == Qt::Key_Right)) | |
|| (!QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional | |
|| (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down))) { | |
e->ignore(); | |
return; | |
} | |
#endif | |
QWidget *pw; | |
if (d->autoExclusive | |
#ifndef QT_NO_BUTTONGROUP | |
|| d->group | |
#endif | |
#ifndef QT_NO_ITEMVIEWS | |
|| ((pw = parentWidget()) && qobject_cast<QAbstractItemView *>(pw->parentWidget())) | |
#endif | |
) { | |
// ### Using qobject_cast to check if the parent is a viewport of | |
// QAbstractItemView is a crude hack, and should be revisited and | |
// cleaned up when fixing task 194373. It's here to ensure that we | |
// keep compatibility outside QAbstractItemView. | |
d->moveFocus(e->key()); | |
if (hasFocus()) // nothing happend, propagate | |
e->ignore(); | |
} else { | |
focusNextPrevChild(next); | |
} | |
break; | |
case Qt::Key_Escape: | |
if (d->down) { | |
setDown(false); | |
repaint(); //flush paint event before invoking potentially expensive operation | |
QApplication::flush(); | |
d->emitReleased(); | |
break; | |
} | |
// fall through | |
default: | |
e->ignore(); | |
} | |
} | |
/*! \reimp */ | |
void QAbstractButton::keyReleaseEvent(QKeyEvent *e) | |
{ | |
Q_D(QAbstractButton); | |
if (!e->isAutoRepeat()) | |
d->repeatTimer.stop(); | |
switch (e->key()) { | |
case Qt::Key_Select: | |
case Qt::Key_Space: | |
if (!e->isAutoRepeat() && d->down) | |
d->click(); | |
break; | |
default: | |
e->ignore(); | |
} | |
} | |
/*!\reimp | |
*/ | |
void QAbstractButton::timerEvent(QTimerEvent *e) | |
{ | |
Q_D(QAbstractButton); | |
if (e->timerId() == d->repeatTimer.timerId()) { | |
d->repeatTimer.start(d->autoRepeatInterval, this); | |
if (d->down) { | |
QPointer<QAbstractButton> guard(this); | |
nextCheckState(); | |
if (guard) | |
d->emitReleased(); | |
if (guard) | |
d->emitClicked(); | |
if (guard) | |
d->emitPressed(); | |
} | |
} else if (e->timerId() == d->animateTimer.timerId()) { | |
d->animateTimer.stop(); | |
d->click(); | |
} | |
} | |
/*! \reimp */ | |
void QAbstractButton::focusInEvent(QFocusEvent *e) | |
{ | |
Q_D(QAbstractButton); | |
#ifdef QT_KEYPAD_NAVIGATION | |
if (!QApplication::keypadNavigationEnabled()) | |
#endif | |
d->fixFocusPolicy(); | |
QWidget::focusInEvent(e); | |
} | |
/*! \reimp */ | |
void QAbstractButton::focusOutEvent(QFocusEvent *e) | |
{ | |
Q_D(QAbstractButton); | |
if (e->reason() != Qt::PopupFocusReason) | |
d->down = false; | |
QWidget::focusOutEvent(e); | |
} | |
/*! \reimp */ | |
void QAbstractButton::changeEvent(QEvent *e) | |
{ | |
Q_D(QAbstractButton); | |
switch (e->type()) { | |
case QEvent::EnabledChange: | |
if (!isEnabled()) | |
setDown(false); | |
break; | |
default: | |
d->sizeHint = QSize(); | |
break; | |
} | |
QWidget::changeEvent(e); | |
} | |
/*! | |
\fn void QAbstractButton::paintEvent(QPaintEvent *e) | |
\reimp | |
*/ | |
/*! | |
\fn void QAbstractButton::pressed() | |
This signal is emitted when the button is pressed down. | |
\sa released(), clicked() | |
*/ | |
/*! | |
\fn void QAbstractButton::released() | |
This signal is emitted when the button is released. | |
\sa pressed(), clicked(), toggled() | |
*/ | |
/*! | |
\fn void QAbstractButton::clicked(bool checked) | |
This signal is emitted when the button is activated (i.e. pressed down | |
then released while the mouse cursor is inside the button), when the | |
shortcut key is typed, or when click() or animateClick() is called. | |
Notably, this signal is \e not emitted if you call setDown(), | |
setChecked() or toggle(). | |
If the button is checkable, \a checked is true if the button is | |
checked, or false if the button is unchecked. | |
\sa pressed(), released(), toggled() | |
*/ | |
/*! | |
\fn void QAbstractButton::toggled(bool checked) | |
This signal is emitted whenever a checkable button changes its state. | |
\a checked is true if the button is checked, or false if the button is | |
unchecked. | |
This may be the result of a user action, click() slot activation, | |
or because setChecked() was called. | |
The states of buttons in exclusive button groups are updated before this | |
signal is emitted. This means that slots can act on either the "off" | |
signal or the "on" signal emitted by the buttons in the group whose | |
states have changed. | |
For example, a slot that reacts to signals emitted by newly checked | |
buttons but which ignores signals from buttons that have been unchecked | |
can be implemented using the following pattern: | |
\snippet doc/src/snippets/code/src_gui_widgets_qabstractbutton.cpp 2 | |
Button groups can be created using the QButtonGroup class, and | |
updates to the button states monitored with the | |
\l{QButtonGroup::buttonClicked()} signal. | |
\sa checked, clicked() | |
*/ | |
/*! | |
\property QAbstractButton::iconSize | |
\brief the icon size used for this button. | |
The default size is defined by the GUI style. This is a maximum | |
size for the icons. Smaller icons will not be scaled up. | |
*/ | |
QSize QAbstractButton::iconSize() const | |
{ | |
Q_D(const QAbstractButton); | |
if (d->iconSize.isValid()) | |
return d->iconSize; | |
int e = style()->pixelMetric(QStyle::PM_ButtonIconSize, 0, this); | |
return QSize(e, e); | |
} | |
void QAbstractButton::setIconSize(const QSize &size) | |
{ | |
Q_D(QAbstractButton); | |
if (d->iconSize == size) | |
return; | |
d->iconSize = size; | |
d->sizeHint = QSize(); | |
updateGeometry(); | |
if (isVisible()) { | |
update(); | |
} | |
} | |
#ifdef QT3_SUPPORT | |
/*! | |
Use icon() instead. | |
*/ | |
QIcon *QAbstractButton::iconSet() const | |
{ | |
Q_D(const QAbstractButton); | |
if (!d->icon.isNull()) | |
return const_cast<QIcon *>(&d->icon); | |
return 0; | |
} | |
/*! | |
Use QAbstractButton(QWidget *) instead. | |
Call setObjectName() if you want to specify an object name, and | |
setParent() if you want to set the window flags. | |
*/ | |
QAbstractButton::QAbstractButton(QWidget *parent, const char *name, Qt::WindowFlags f) | |
: QWidget(*new QAbstractButtonPrivate, parent, f) | |
{ | |
Q_D(QAbstractButton); | |
setObjectName(QString::fromAscii(name)); | |
d->init(); | |
} | |
/*! \fn bool QAbstractButton::isOn() const | |
Use isChecked() instead. | |
*/ | |
/*! | |
\fn QPixmap *QAbstractButton::pixmap() const | |
This compatibility function always returns 0. | |
Use icon() instead. | |
*/ | |
/*! \fn void QAbstractButton::setPixmap(const QPixmap &p) | |
Use setIcon() instead. | |
*/ | |
/*! \fn void QAbstractButton::setIconSet(const QIcon &icon) | |
Use setIcon() instead. | |
*/ | |
/*! \fn void QAbstractButton::setOn(bool b) | |
Use setChecked() instead. | |
*/ | |
/*! \fn bool QAbstractButton::isToggleButton() const | |
Use isCheckable() instead. | |
*/ | |
/*! | |
\fn void QAbstractButton::setToggleButton(bool b) | |
Use setCheckable() instead. | |
*/ | |
/*! \fn void QAbstractButton::setAccel(const QKeySequence &key) | |
Use setShortcut() instead. | |
*/ | |
/*! \fn QKeySequence QAbstractButton::accel() const | |
Use shortcut() instead. | |
*/ | |
#endif | |
QT_END_NAMESPACE |