diff options
Diffstat (limited to 'src/gui/widgets/qpushbutton.cpp')
-rw-r--r-- | src/gui/widgets/qpushbutton.cpp | 732 |
1 files changed, 732 insertions, 0 deletions
diff --git a/src/gui/widgets/qpushbutton.cpp b/src/gui/widgets/qpushbutton.cpp new file mode 100644 index 0000000..03ca751 --- /dev/null +++ b/src/gui/widgets/qpushbutton.cpp @@ -0,0 +1,732 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qapplication.h" +#include "qbitmap.h" +#include "qdesktopwidget.h" +#include "qdialog.h" +#include <private/qdialog_p.h> +#include "qdrawutil.h" +#include "qevent.h" +#include "qfontmetrics.h" +#include "qmenu.h" +#include "qstylepainter.h" +#include "qpixmap.h" +#include "qpointer.h" +#include "qpushbutton.h" +#include "qstyle.h" +#include "qstyleoption.h" +#include "qtoolbar.h" +#include "qdebug.h" +#include "qlayoutitem.h" +#include "qdialogbuttonbox.h" + +#ifndef QT_NO_ACCESSIBILITY +#include "qaccessible.h" +#endif + +#include "private/qpushbutton_p.h" + +QT_BEGIN_NAMESPACE + + +/*! + \class QPushButton + \brief The QPushButton widget provides a command button. + + \ingroup basicwidgets + \mainclass + + The push button, or command button, is perhaps the most commonly + used widget in any graphical user interface. Push (click) a button + to command the computer to perform some action, or to answer a + question. Typical buttons are OK, Apply, Cancel, Close, Yes, No + and Help. + + A command button is rectangular and typically displays a text + label describing its action. A shortcut key can be specified by + preceding the preferred character with an ampersand in the + text. For example: + + \snippet doc/src/snippets/code/src_gui_widgets_qpushbutton.cpp 0 + + In this example the shortcut is \e{Alt+D}. See the \l + {QShortcut#mnemonic}{QShortcut} documentation for details (to + display an actual ampersand, use '&&'). + + Push buttons display a textual label, and optionally a small + icon. These can be set using the constructors and changed later + using setText() and setIcon(). If the button is disabled the + appearance of the text and icon will be manipulated with respect + to the GUI style to make the button look "disabled". + + A push button emits the signal clicked() when it is activated by + the mouse, the Spacebar or by a keyboard shortcut. Connect to + this signal to perform the button's action. Push buttons also + provide less commonly used signals, for example, pressed() and + released(). + + Command buttons in dialogs are by default auto-default buttons, + i.e. they become the default push button automatically when they + receive the keyboard input focus. A default button is a push + button that is activated when the user presses the Enter or Return + key in a dialog. You can change this with setAutoDefault(). Note + that auto-default buttons reserve a little extra space which is + necessary to draw a default-button indicator. If you do not want + this space around your buttons, call setAutoDefault(false). + + Being so central, the button widget has grown to accommodate a + great many variations in the past decade. The Microsoft style + guide now shows about ten different states of Windows push buttons + and the text implies that there are dozens more when all the + combinations of features are taken into consideration. + + The most important modes or states are: + \list + \i Available or not (grayed out, disabled). + \i Standard push button, toggling push button or menu button. + \i On or off (only for toggling push buttons). + \i Default or normal. The default button in a dialog can generally + be "clicked" using the Enter or Return key. + \i Auto-repeat or not. + \i Pressed down or not. + \endlist + + As a general rule, use a push button when the application or + dialog window performs an action when the user clicks on it (such + as Apply, Cancel, Close and Help) \e and when the widget is + supposed to have a wide, rectangular shape with a text label. + Small, typically square buttons that change the state of the + window rather than performing an action (such as the buttons in + the top-right corner of the QFileDialog) are not command buttons, + but tool buttons. Qt provides a special class (QToolButton) for + these buttons. + + If you need toggle behavior (see setCheckable()) or a button + that auto-repeats the activation signal when being pushed down + like the arrows in a scroll bar (see setAutoRepeat()), a command + button is probably not what you want. When in doubt, use a tool + button. + + A variation of a command button is a menu button. These provide + not just one command, but several, since when they are clicked + they pop up a menu of options. Use the method setMenu() to + associate a popup menu with a push button. + + Other classes of buttons are option buttons (see QRadioButton) and + check boxes (see QCheckBox). + + \table 100% + \row \o \inlineimage macintosh-pushbutton.png Screenshot of a Macintosh style push button + \o A push button shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}. + + Note that when a button's width becomes smaller than 50 or + its height becomes smaller than 30, the button's corners are + changed from round to square. Use the setMinimumSize() + function to prevent this behavior. + + \row \o \inlineimage windowsxp-pushbutton.png Screenshot of a Windows XP style push button + \o A push button shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}. + \row \o \inlineimage plastique-pushbutton.png Screenshot of a Plastique style push button + \o A push button shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}. + \endtable + + In Qt, the QAbstractButton base class provides most of the modes + and other API, and QPushButton provides GUI logic. + See QAbstractButton for more information about the API. + + \sa QToolButton, QRadioButton, QCheckBox, {fowler}{GUI Design Handbook: Push Button} +*/ + +/*! + \property QPushButton::autoDefault + \brief whether the push button is an auto default button + + If this property is set to true then the push button is an auto + default button. + + In some GUI styles a default button is drawn with an extra frame + around it, up to 3 pixels or more. Qt automatically keeps this + space free around auto-default buttons, i.e. auto-default buttons + may have a slightly larger size hint. + + This property's default is true for buttons that have a QDialog + parent; otherwise it defaults to false. + + See the \l default property for details of how \l default and + auto-default interact. +*/ + +/*! + \property QPushButton::default + \brief whether the push button is the default button + + Default and autodefault buttons decide what happens when the user + presses enter in a dialog. + + A button with this property set to true (i.e., the dialog's + \e default button,) will automatically be pressed when the user presses enter, + with one exception: if an \a autoDefault button currently has focus, the autoDefault + button is pressed. When the dialog has \l autoDefault buttons but no default button, + pressing enter will press either the \l autoDefault button that currently has focus, or if no + button has focus, the next \l autoDefault button in the focus chain. + + In a dialog, only one push button at a time can be the default + button. This button is then displayed with an additional frame + (depending on the GUI style). + + The default button behavior is provided only in dialogs. Buttons + can always be clicked from the keyboard by pressing Spacebar when + the button has focus. + + If the default property is set to false on the current default button + while the dialog is visible, a new default will automatically be + assigned the next time a pushbutton in the dialog receives focus. + + This property's default is false. +*/ + +/*! + \property QPushButton::flat + \brief whether the button border is raised + + This property's default is false. If this property is set, most + styles will not paint the button background unless the button is + being pressed. setAutoFillBackground() can be used to ensure that + the background is filled using the QPalette::Button brush. +*/ + +/*! + Constructs a push button with no text and a \a parent. +*/ + +QPushButton::QPushButton(QWidget *parent) + : QAbstractButton(*new QPushButtonPrivate, parent) +{ + Q_D(QPushButton); + d->init(); +} + +/*! + Constructs a push button with the parent \a parent and the text \a + text. +*/ + +QPushButton::QPushButton(const QString &text, QWidget *parent) + : QAbstractButton(*new QPushButtonPrivate, parent) +{ + Q_D(QPushButton); + setText(text); + d->init(); +} + + +/*! + Constructs a push button with an \a icon and a \a text, and a \a parent. + + Note that you can also pass a QPixmap object as an icon (thanks to + the implicit type conversion provided by C++). + +*/ +QPushButton::QPushButton(const QIcon& icon, const QString &text, QWidget *parent) + : QAbstractButton(*new QPushButtonPrivate, parent) +{ + Q_D(QPushButton); + setText(text); + setIcon(icon); + d->init(); +} + +/*! \internal + */ +QPushButton::QPushButton(QPushButtonPrivate &dd, QWidget *parent) + : QAbstractButton(dd, parent) +{ + Q_D(QPushButton); + d->init(); +} + +/*! + Destroys the push button. +*/ +QPushButton::~QPushButton() +{ +} + +QDialog *QPushButtonPrivate::dialogParent() const +{ + Q_Q(const QPushButton); + const QWidget *p = q; + while (p && !p->isWindow()) { + p = p->parentWidget(); + if (const QDialog *dialog = qobject_cast<const QDialog *>(p)) + return const_cast<QDialog *>(dialog); + } + return 0; +} + +/*! + Initialize \a option with the values from this QPushButton. This method is useful + for subclasses when they need a QStyleOptionButton, but don't want to fill + in all the information themselves. + + \sa QStyleOption::initFrom() +*/ +void QPushButton::initStyleOption(QStyleOptionButton *option) const +{ + if (!option) + return; + + Q_D(const QPushButton); + option->initFrom(this); + option->features = QStyleOptionButton::None; + if (d->flat) + option->features |= QStyleOptionButton::Flat; +#ifndef QT_NO_MENU + if (d->menu) + option->features |= QStyleOptionButton::HasMenu; +#endif + if (autoDefault() || d->defaultButton) + option->features |= QStyleOptionButton::AutoDefaultButton; + if (d->defaultButton) + option->features |= QStyleOptionButton::DefaultButton; + if (d->down || d->menuOpen) + option->state |= QStyle::State_Sunken; + if (d->checked) + option->state |= QStyle::State_On; + if (!d->flat && !d->down) + option->state |= QStyle::State_Raised; + option->text = d->text; + option->icon = d->icon; + option->iconSize = iconSize(); +} + +void QPushButton::setAutoDefault(bool enable) +{ + Q_D(QPushButton); + uint state = enable ? QPushButtonPrivate::On : QPushButtonPrivate::Off; + if (d->autoDefault != QPushButtonPrivate::Auto && d->autoDefault == state) + return; + d->autoDefault = state; + d->sizeHint = QSize(); + update(); + updateGeometry(); +} + +bool QPushButton::autoDefault() const +{ + Q_D(const QPushButton); + if(d->autoDefault == QPushButtonPrivate::Auto) + return ( d->dialogParent() != 0 ); + return d->autoDefault; +} + +void QPushButton::setDefault(bool enable) +{ + Q_D(QPushButton); + if (d->defaultButton == enable) + return; + d->defaultButton = enable; + if (d->defaultButton) { + if (QDialog *dlg = d->dialogParent()) + dlg->d_func()->setMainDefault(this); + } + update(); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(this, 0, QAccessible::StateChanged); +#endif +} + +bool QPushButton::isDefault() const +{ + Q_D(const QPushButton); + return d->defaultButton; +} + +/*! + \reimp +*/ +QSize QPushButton::sizeHint() const +{ + Q_D(const QPushButton); + if (d->sizeHint.isValid()) + return d->sizeHint; + ensurePolished(); + + int w = 0, h = 0; + + QStyleOptionButton opt; + initStyleOption(&opt); + + // calculate contents size... +#ifndef QT_NO_ICON + + bool showButtonBoxIcons = qobject_cast<QDialogButtonBox*>(parentWidget()) + && style()->styleHint(QStyle::SH_DialogButtonBox_ButtonsHaveIcons); + + if (!icon().isNull() || showButtonBoxIcons) { + int ih = opt.iconSize.height(); + int iw = opt.iconSize.width() + 4; + w += iw; + h = qMax(h, ih); + } +#endif + QString s(text()); + bool empty = s.isEmpty(); + if (empty) + s = QString::fromLatin1("XXXX"); + QFontMetrics fm = fontMetrics(); + QSize sz = fm.size(Qt::TextShowMnemonic, s); + if(!empty || !w) + w += sz.width(); + if(!empty || !h) + h = qMax(h, sz.height()); + opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height +#ifndef QT_NO_MENU + if (menu()) + w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this); +#endif + d->sizeHint = (style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(w, h), this). + expandedTo(QApplication::globalStrut())); + return d->sizeHint; +} + +/*! + \reimp + */ +QSize QPushButton::minimumSizeHint() const +{ + return sizeHint(); +} + + +/*!\reimp +*/ +void QPushButton::paintEvent(QPaintEvent *) +{ + QStylePainter p(this); + QStyleOptionButton option; + initStyleOption(&option); + p.drawControl(QStyle::CE_PushButton, option); +} + + +/*! \reimp */ +void QPushButton::keyPressEvent(QKeyEvent *e) +{ + Q_D(QPushButton); + switch (e->key()) { + case Qt::Key_Enter: + case Qt::Key_Return: + if (autoDefault() || d->defaultButton) { + click(); + break; + } + // fall through + default: + QAbstractButton::keyPressEvent(e); + } +} + +/*! + \reimp +*/ +void QPushButton::focusInEvent(QFocusEvent *e) +{ + Q_D(QPushButton); + if (e->reason() != Qt::PopupFocusReason && autoDefault() && !d->defaultButton) { + d->defaultButton = true; + QDialog *dlg = qobject_cast<QDialog*>(window()); + if (dlg) + dlg->d_func()->setDefault(this); + } + QAbstractButton::focusInEvent(e); +} + +/*! + \reimp +*/ +void QPushButton::focusOutEvent(QFocusEvent *e) +{ + Q_D(QPushButton); + if (e->reason() != Qt::PopupFocusReason && autoDefault() && d->defaultButton) { + QDialog *dlg = qobject_cast<QDialog*>(window()); + if (dlg) + dlg->d_func()->setDefault(0); + else + d->defaultButton = false; + } + + QAbstractButton::focusOutEvent(e); +#ifndef QT_NO_MENU + if (d->menu && d->menu->isVisible()) // restore pressed status + setDown(true); +#endif +} + +#ifndef QT_NO_MENU +/*! + Associates the popup menu \a menu with this push button. This + turns the button into a menu button, which in some styles will + produce a small triangle to the right of the button's text. + + Ownership of the menu is \e not transferred to the push button. + + \table 100% + \row + \o \inlineimage plastique-pushbutton-menu.png Screenshot of a Plastique style push button with popup menu. + \o \inlineimage cleanlooks-pushbutton-menu.png Screenshot of a Cleanlooks style push button with popup menu. + \o Push buttons with popup menus shown in the \l{Plastique Style Widget Gallery}{Plastique widget style} + (left) and \l{Cleanlooks Style Widget Gallery}{Cleanlooks widget style} (right). + \endtable + + \sa menu() +*/ +void QPushButton::setMenu(QMenu* menu) +{ + Q_D(QPushButton); + if (menu == d->menu) + return; + + if (menu && !d->menu) { + disconnect(this, SIGNAL(pressed()), this, SLOT(_q_popupPressed())); + connect(this, SIGNAL(pressed()), this, SLOT(_q_popupPressed())); + } + if (d->menu) + removeAction(d->menu->menuAction()); + d->menu = menu; + if (d->menu) + addAction(d->menu->menuAction()); + + d->resetLayoutItemMargins(); + d->sizeHint = QSize(); + update(); + updateGeometry(); +} + +/*! + Returns the button's associated popup menu or 0 if no popup menu + has been set. + + \sa setMenu() +*/ +QMenu* QPushButton::menu() const +{ + Q_D(const QPushButton); + return d->menu; +} + +/*! + Shows (pops up) the associated popup menu. If there is no such + menu, this function does nothing. This function does not return + until the popup menu has been closed by the user. +*/ +void QPushButton::showMenu() +{ + Q_D(QPushButton); + if (!d || !d->menu) + return; + setDown(true); + d->_q_popupPressed(); +} + +void QPushButtonPrivate::_q_popupPressed() +{ + Q_Q(QPushButton); + if (!down || !menu) + return; + + menu->setNoReplayFor(q); + bool horizontal = true; +#if !defined(QT_NO_TOOLBAR) + QToolBar *tb = qobject_cast<QToolBar*>(q->parentWidget()); + if (tb && tb->orientation() == Qt::Vertical) + horizontal = false; +#endif + QWidgetItem item(q); + QRect rect = item.geometry(); + rect.setRect(rect.x() - q->x(), rect.y() - q->y(), rect.width(), rect.height()); + + QSize menuSize = menu->sizeHint(); + QPoint globalPos = q->mapToGlobal(rect.topLeft()); + int x = globalPos.x(); + int y = globalPos.y(); + if (horizontal) { + if (globalPos.y() + rect.height() + menuSize.height() <= qApp->desktop()->height()) { + y += rect.height(); + } else { + y -= menuSize.height(); + } + if (q->layoutDirection() == Qt::RightToLeft) + x += rect.width() - menuSize.width(); + } else { + if (globalPos.x() + rect.width() + menu->sizeHint().width() <= qApp->desktop()->width()) + x += rect.width(); + else + x -= menuSize.width(); + } + QPointer<QPushButton> guard(q); + + //Because of a delay in menu effects, we must keep track of the + //menu visibility to avoid flicker on button release + menuOpen = true; + menu->exec(QPoint(x, y)); + if (guard) { + menuOpen = false; + q->setDown(false); + } +} +#endif // QT_NO_MENU + +void QPushButtonPrivate::resetLayoutItemMargins() +{ + Q_Q(QPushButton); + QStyleOptionButton opt; + q->initStyleOption(&opt); + setLayoutItemMargins(QStyle::SE_PushButtonLayoutItem, &opt); +} + +void QPushButton::setFlat(bool flat) +{ + Q_D(QPushButton); + if (d->flat == flat) + return; + d->flat = flat; + d->resetLayoutItemMargins(); + d->sizeHint = QSize(); + update(); + updateGeometry(); +} + +bool QPushButton::isFlat() const +{ + Q_D(const QPushButton); + return d->flat; +} + +/*! \reimp */ +bool QPushButton::event(QEvent *e) +{ + Q_D(QPushButton); + if (e->type() == QEvent::ParentChange) { + if (QDialog *dialog = d->dialogParent()) { + if (d->defaultButton) + dialog->d_func()->setMainDefault(this); + } + } else if (e->type() == QEvent::StyleChange +#ifdef Q_WS_MAC + || e->type() == QEvent::MacSizeChange +#endif + ) { + d->resetLayoutItemMargins(); + updateGeometry(); + } + return QAbstractButton::event(e); +} + +#ifdef QT3_SUPPORT +/*! + Use one of the constructors that doesn't take the \a name + argument and then use setObjectName() instead. +*/ +QPushButton::QPushButton(QWidget *parent, const char *name) + : QAbstractButton(*new QPushButtonPrivate, parent) +{ + Q_D(QPushButton); + setObjectName(QString::fromAscii(name)); + d->init(); +} + +/*! + Use one of the constructors that doesn't take the \a name + argument and then use setObjectName() instead. +*/ +QPushButton::QPushButton(const QString &text, QWidget *parent, const char *name) + : QAbstractButton(*new QPushButtonPrivate, parent) +{ + Q_D(QPushButton); + setObjectName(QString::fromAscii(name)); + setText(text); + d->init(); +} + +/*! + Use one of the constructors that doesn't take the \a name + argument and then use setObjectName() instead. +*/ +QPushButton::QPushButton(const QIcon& icon, const QString &text, QWidget *parent, const char *name) + : QAbstractButton(*new QPushButtonPrivate, parent) +{ + Q_D(QPushButton); + setObjectName(QString::fromAscii(name)); + setText(text); + setIcon(icon); + d->init(); +} +#endif + +/*! + \fn void QPushButton::openPopup() + + Use showMenu() instead. +*/ + +/*! + \fn bool QPushButton::isMenuButton() const + + Use menu() != 0 instead. +*/ + +/*! + \fn void QPushButton::setPopup(QMenu* popup) + + Use setMenu() instead. +*/ + +/*! + \fn QMenu* QPushButton::popup() const + + Use menu() instead. +*/ + +QT_END_NAMESPACE + +#include "moc_qpushbutton.cpp" |