src/gui/widgets/qpushbutton.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/widgets/qpushbutton.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,734 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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$
+** 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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@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
+
+
+    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() && d->lastAutoDefault == autoDefault())
+        return d->sizeHint;
+    d->lastAutoDefault = autoDefault();
+    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) {
+        connect(this, SIGNAL(pressed()), this, SLOT(_q_popupPressed()), Qt::UniqueConnection);
+    }
+    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*>(parent);
+    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() <= QApplication::desktop()->availableGeometry(q).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() <= QApplication::desktop()->availableGeometry(q).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();
+    } else if (e->type() == QEvent::PolishRequest) {
+        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"