source: trunk/src/gui/widgets/qpushbutton.cpp@ 754

Last change on this file since 754 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 22.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qapplication.h"
43#include "qbitmap.h"
44#include "qdesktopwidget.h"
45#include "qdialog.h"
46#include <private/qdialog_p.h>
47#include "qdrawutil.h"
48#include "qevent.h"
49#include "qfontmetrics.h"
50#include "qmenu.h"
51#include "qstylepainter.h"
52#include "qpixmap.h"
53#include "qpointer.h"
54#include "qpushbutton.h"
55#include "qstyle.h"
56#include "qstyleoption.h"
57#include "qtoolbar.h"
58#include "qdebug.h"
59#include "qlayoutitem.h"
60#include "qdialogbuttonbox.h"
61
62#ifndef QT_NO_ACCESSIBILITY
63#include "qaccessible.h"
64#endif
65
66#include "private/qmenu_p.h"
67#include "private/qpushbutton_p.h"
68
69QT_BEGIN_NAMESPACE
70
71
72/*!
73 \class QPushButton
74 \brief The QPushButton widget provides a command button.
75
76 \ingroup basicwidgets
77
78
79 The push button, or command button, is perhaps the most commonly
80 used widget in any graphical user interface. Push (click) a button
81 to command the computer to perform some action, or to answer a
82 question. Typical buttons are OK, Apply, Cancel, Close, Yes, No
83 and Help.
84
85 A command button is rectangular and typically displays a text
86 label describing its action. A shortcut key can be specified by
87 preceding the preferred character with an ampersand in the
88 text. For example:
89
90 \snippet doc/src/snippets/code/src_gui_widgets_qpushbutton.cpp 0
91
92 In this example the shortcut is \e{Alt+D}. See the \l
93 {QShortcut#mnemonic}{QShortcut} documentation for details (to
94 display an actual ampersand, use '&&').
95
96 Push buttons display a textual label, and optionally a small
97 icon. These can be set using the constructors and changed later
98 using setText() and setIcon(). If the button is disabled the
99 appearance of the text and icon will be manipulated with respect
100 to the GUI style to make the button look "disabled".
101
102 A push button emits the signal clicked() when it is activated by
103 the mouse, the Spacebar or by a keyboard shortcut. Connect to
104 this signal to perform the button's action. Push buttons also
105 provide less commonly used signals, for example, pressed() and
106 released().
107
108 Command buttons in dialogs are by default auto-default buttons,
109 i.e. they become the default push button automatically when they
110 receive the keyboard input focus. A default button is a push
111 button that is activated when the user presses the Enter or Return
112 key in a dialog. You can change this with setAutoDefault(). Note
113 that auto-default buttons reserve a little extra space which is
114 necessary to draw a default-button indicator. If you do not want
115 this space around your buttons, call setAutoDefault(false).
116
117 Being so central, the button widget has grown to accommodate a
118 great many variations in the past decade. The Microsoft style
119 guide now shows about ten different states of Windows push buttons
120 and the text implies that there are dozens more when all the
121 combinations of features are taken into consideration.
122
123 The most important modes or states are:
124 \list
125 \i Available or not (grayed out, disabled).
126 \i Standard push button, toggling push button or menu button.
127 \i On or off (only for toggling push buttons).
128 \i Default or normal. The default button in a dialog can generally
129 be "clicked" using the Enter or Return key.
130 \i Auto-repeat or not.
131 \i Pressed down or not.
132 \endlist
133
134 As a general rule, use a push button when the application or
135 dialog window performs an action when the user clicks on it (such
136 as Apply, Cancel, Close and Help) \e and when the widget is
137 supposed to have a wide, rectangular shape with a text label.
138 Small, typically square buttons that change the state of the
139 window rather than performing an action (such as the buttons in
140 the top-right corner of the QFileDialog) are not command buttons,
141 but tool buttons. Qt provides a special class (QToolButton) for
142 these buttons.
143
144 If you need toggle behavior (see setCheckable()) or a button
145 that auto-repeats the activation signal when being pushed down
146 like the arrows in a scroll bar (see setAutoRepeat()), a command
147 button is probably not what you want. When in doubt, use a tool
148 button.
149
150 A variation of a command button is a menu button. These provide
151 not just one command, but several, since when they are clicked
152 they pop up a menu of options. Use the method setMenu() to
153 associate a popup menu with a push button.
154
155 Other classes of buttons are option buttons (see QRadioButton) and
156 check boxes (see QCheckBox).
157
158 \table 100%
159 \row \o \inlineimage macintosh-pushbutton.png Screenshot of a Macintosh style push button
160 \o A push button shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
161
162 Note that when a button's width becomes smaller than 50 or
163 its height becomes smaller than 30, the button's corners are
164 changed from round to square. Use the setMinimumSize()
165 function to prevent this behavior.
166
167 \row \o \inlineimage windowsxp-pushbutton.png Screenshot of a Windows XP style push button
168 \o A push button shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}.
169 \row \o \inlineimage plastique-pushbutton.png Screenshot of a Plastique style push button
170 \o A push button shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}.
171 \endtable
172
173 In Qt, the QAbstractButton base class provides most of the modes
174 and other API, and QPushButton provides GUI logic.
175 See QAbstractButton for more information about the API.
176
177 \sa QToolButton, QRadioButton, QCheckBox, {fowler}{GUI Design Handbook: Push Button}
178*/
179
180/*!
181 \property QPushButton::autoDefault
182 \brief whether the push button is an auto default button
183
184 If this property is set to true then the push button is an auto
185 default button.
186
187 In some GUI styles a default button is drawn with an extra frame
188 around it, up to 3 pixels or more. Qt automatically keeps this
189 space free around auto-default buttons, i.e. auto-default buttons
190 may have a slightly larger size hint.
191
192 This property's default is true for buttons that have a QDialog
193 parent; otherwise it defaults to false.
194
195 See the \l default property for details of how \l default and
196 auto-default interact.
197*/
198
199/*!
200 \property QPushButton::default
201 \brief whether the push button is the default button
202
203 Default and autodefault buttons decide what happens when the user
204 presses enter in a dialog.
205
206 A button with this property set to true (i.e., the dialog's
207 \e default button,) will automatically be pressed when the user presses enter,
208 with one exception: if an \a autoDefault button currently has focus, the autoDefault
209 button is pressed. When the dialog has \l autoDefault buttons but no default button,
210 pressing enter will press either the \l autoDefault button that currently has focus, or if no
211 button has focus, the next \l autoDefault button in the focus chain.
212
213 In a dialog, only one push button at a time can be the default
214 button. This button is then displayed with an additional frame
215 (depending on the GUI style).
216
217 The default button behavior is provided only in dialogs. Buttons
218 can always be clicked from the keyboard by pressing Spacebar when
219 the button has focus.
220
221 If the default property is set to false on the current default button
222 while the dialog is visible, a new default will automatically be
223 assigned the next time a pushbutton in the dialog receives focus.
224
225 This property's default is false.
226*/
227
228/*!
229 \property QPushButton::flat
230 \brief whether the button border is raised
231
232 This property's default is false. If this property is set, most
233 styles will not paint the button background unless the button is
234 being pressed. setAutoFillBackground() can be used to ensure that
235 the background is filled using the QPalette::Button brush.
236*/
237
238/*!
239 Constructs a push button with no text and a \a parent.
240*/
241
242QPushButton::QPushButton(QWidget *parent)
243 : QAbstractButton(*new QPushButtonPrivate, parent)
244{
245 Q_D(QPushButton);
246 d->init();
247}
248
249/*!
250 Constructs a push button with the parent \a parent and the text \a
251 text.
252*/
253
254QPushButton::QPushButton(const QString &text, QWidget *parent)
255 : QAbstractButton(*new QPushButtonPrivate, parent)
256{
257 Q_D(QPushButton);
258 setText(text);
259 d->init();
260}
261
262
263/*!
264 Constructs a push button with an \a icon and a \a text, and a \a parent.
265
266 Note that you can also pass a QPixmap object as an icon (thanks to
267 the implicit type conversion provided by C++).
268
269*/
270QPushButton::QPushButton(const QIcon& icon, const QString &text, QWidget *parent)
271 : QAbstractButton(*new QPushButtonPrivate, parent)
272{
273 Q_D(QPushButton);
274 setText(text);
275 setIcon(icon);
276 d->init();
277}
278
279/*! \internal
280 */
281QPushButton::QPushButton(QPushButtonPrivate &dd, QWidget *parent)
282 : QAbstractButton(dd, parent)
283{
284 Q_D(QPushButton);
285 d->init();
286}
287
288/*!
289 Destroys the push button.
290*/
291QPushButton::~QPushButton()
292{
293}
294
295QDialog *QPushButtonPrivate::dialogParent() const
296{
297 Q_Q(const QPushButton);
298 const QWidget *p = q;
299 while (p && !p->isWindow()) {
300 p = p->parentWidget();
301 if (const QDialog *dialog = qobject_cast<const QDialog *>(p))
302 return const_cast<QDialog *>(dialog);
303 }
304 return 0;
305}
306
307/*!
308 Initialize \a option with the values from this QPushButton. This method is useful
309 for subclasses when they need a QStyleOptionButton, but don't want to fill
310 in all the information themselves.
311
312 \sa QStyleOption::initFrom()
313*/
314void QPushButton::initStyleOption(QStyleOptionButton *option) const
315{
316 if (!option)
317 return;
318
319 Q_D(const QPushButton);
320 option->initFrom(this);
321 option->features = QStyleOptionButton::None;
322 if (d->flat)
323 option->features |= QStyleOptionButton::Flat;
324#ifndef QT_NO_MENU
325 if (d->menu)
326 option->features |= QStyleOptionButton::HasMenu;
327#endif
328 if (autoDefault() || d->defaultButton)
329 option->features |= QStyleOptionButton::AutoDefaultButton;
330 if (d->defaultButton)
331 option->features |= QStyleOptionButton::DefaultButton;
332 if (d->down || d->menuOpen)
333 option->state |= QStyle::State_Sunken;
334 if (d->checked)
335 option->state |= QStyle::State_On;
336 if (!d->flat && !d->down)
337 option->state |= QStyle::State_Raised;
338 option->text = d->text;
339 option->icon = d->icon;
340 option->iconSize = iconSize();
341}
342
343void QPushButton::setAutoDefault(bool enable)
344{
345 Q_D(QPushButton);
346 uint state = enable ? QPushButtonPrivate::On : QPushButtonPrivate::Off;
347 if (d->autoDefault != QPushButtonPrivate::Auto && d->autoDefault == state)
348 return;
349 d->autoDefault = state;
350 d->sizeHint = QSize();
351 update();
352 updateGeometry();
353}
354
355bool QPushButton::autoDefault() const
356{
357 Q_D(const QPushButton);
358 if(d->autoDefault == QPushButtonPrivate::Auto)
359 return ( d->dialogParent() != 0 );
360 return d->autoDefault;
361}
362
363void QPushButton::setDefault(bool enable)
364{
365 Q_D(QPushButton);
366 if (d->defaultButton == enable)
367 return;
368 d->defaultButton = enable;
369 if (d->defaultButton) {
370 if (QDialog *dlg = d->dialogParent())
371 dlg->d_func()->setMainDefault(this);
372 }
373 update();
374#ifndef QT_NO_ACCESSIBILITY
375 QAccessible::updateAccessibility(this, 0, QAccessible::StateChanged);
376#endif
377}
378
379bool QPushButton::isDefault() const
380{
381 Q_D(const QPushButton);
382 return d->defaultButton;
383}
384
385/*!
386 \reimp
387*/
388QSize QPushButton::sizeHint() const
389{
390 Q_D(const QPushButton);
391 if (d->sizeHint.isValid() && d->lastAutoDefault == autoDefault())
392 return d->sizeHint;
393 d->lastAutoDefault = autoDefault();
394 ensurePolished();
395
396 int w = 0, h = 0;
397
398 QStyleOptionButton opt;
399 initStyleOption(&opt);
400
401 // calculate contents size...
402#ifndef QT_NO_ICON
403
404 bool showButtonBoxIcons = qobject_cast<QDialogButtonBox*>(parentWidget())
405 && style()->styleHint(QStyle::SH_DialogButtonBox_ButtonsHaveIcons);
406
407 if (!icon().isNull() || showButtonBoxIcons) {
408 int ih = opt.iconSize.height();
409 int iw = opt.iconSize.width() + 4;
410 w += iw;
411 h = qMax(h, ih);
412 }
413#endif
414 QString s(text());
415 bool empty = s.isEmpty();
416 if (empty)
417 s = QString::fromLatin1("XXXX");
418 QFontMetrics fm = fontMetrics();
419 QSize sz = fm.size(Qt::TextShowMnemonic, s);
420 if(!empty || !w)
421 w += sz.width();
422 if(!empty || !h)
423 h = qMax(h, sz.height());
424 opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height
425#ifndef QT_NO_MENU
426 if (menu())
427 w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this);
428#endif
429 d->sizeHint = (style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(w, h), this).
430 expandedTo(QApplication::globalStrut()));
431 return d->sizeHint;
432}
433
434/*!
435 \reimp
436 */
437QSize QPushButton::minimumSizeHint() const
438{
439 return sizeHint();
440}
441
442
443/*!\reimp
444*/
445void QPushButton::paintEvent(QPaintEvent *)
446{
447 QStylePainter p(this);
448 QStyleOptionButton option;
449 initStyleOption(&option);
450 p.drawControl(QStyle::CE_PushButton, option);
451}
452
453
454/*! \reimp */
455void QPushButton::keyPressEvent(QKeyEvent *e)
456{
457 Q_D(QPushButton);
458 switch (e->key()) {
459 case Qt::Key_Enter:
460 case Qt::Key_Return:
461 if (autoDefault() || d->defaultButton) {
462 click();
463 break;
464 }
465 // fall through
466 default:
467 QAbstractButton::keyPressEvent(e);
468 }
469}
470
471/*!
472 \reimp
473*/
474void QPushButton::focusInEvent(QFocusEvent *e)
475{
476 Q_D(QPushButton);
477 if (e->reason() != Qt::PopupFocusReason && autoDefault() && !d->defaultButton) {
478 d->defaultButton = true;
479 QDialog *dlg = qobject_cast<QDialog*>(window());
480 if (dlg)
481 dlg->d_func()->setDefault(this);
482 }
483 QAbstractButton::focusInEvent(e);
484}
485
486/*!
487 \reimp
488*/
489void QPushButton::focusOutEvent(QFocusEvent *e)
490{
491 Q_D(QPushButton);
492 if (e->reason() != Qt::PopupFocusReason && autoDefault() && d->defaultButton) {
493 QDialog *dlg = qobject_cast<QDialog*>(window());
494 if (dlg)
495 dlg->d_func()->setDefault(0);
496 else
497 d->defaultButton = false;
498 }
499
500 QAbstractButton::focusOutEvent(e);
501#ifndef QT_NO_MENU
502 if (d->menu && d->menu->isVisible()) // restore pressed status
503 setDown(true);
504#endif
505}
506
507#ifndef QT_NO_MENU
508/*!
509 Associates the popup menu \a menu with this push button. This
510 turns the button into a menu button, which in some styles will
511 produce a small triangle to the right of the button's text.
512
513 Ownership of the menu is \e not transferred to the push button.
514
515 \table 100%
516 \row
517 \o \inlineimage plastique-pushbutton-menu.png Screenshot of a Plastique style push button with popup menu.
518 \o \inlineimage cleanlooks-pushbutton-menu.png Screenshot of a Cleanlooks style push button with popup menu.
519 \o Push buttons with popup menus shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}
520 (left) and \l{Cleanlooks Style Widget Gallery}{Cleanlooks widget style} (right).
521 \endtable
522
523 \sa menu()
524*/
525void QPushButton::setMenu(QMenu* menu)
526{
527 Q_D(QPushButton);
528 if (menu == d->menu)
529 return;
530
531 if (menu && !d->menu) {
532 connect(this, SIGNAL(pressed()), this, SLOT(_q_popupPressed()), Qt::UniqueConnection);
533 }
534 if (d->menu)
535 removeAction(d->menu->menuAction());
536 d->menu = menu;
537 if (d->menu)
538 addAction(d->menu->menuAction());
539
540 d->resetLayoutItemMargins();
541 d->sizeHint = QSize();
542 update();
543 updateGeometry();
544}
545
546/*!
547 Returns the button's associated popup menu or 0 if no popup menu
548 has been set.
549
550 \sa setMenu()
551*/
552QMenu* QPushButton::menu() const
553{
554 Q_D(const QPushButton);
555 return d->menu;
556}
557
558/*!
559 Shows (pops up) the associated popup menu. If there is no such
560 menu, this function does nothing. This function does not return
561 until the popup menu has been closed by the user.
562*/
563void QPushButton::showMenu()
564{
565 Q_D(QPushButton);
566 if (!d || !d->menu)
567 return;
568 setDown(true);
569 d->_q_popupPressed();
570}
571
572void QPushButtonPrivate::_q_popupPressed()
573{
574 Q_Q(QPushButton);
575 if (!down || !menu)
576 return;
577
578 menu->setNoReplayFor(q);
579
580 QPoint menuPos = adjustedMenuPosition();
581
582 QPointer<QPushButton> guard(q);
583 QMenuPrivate::get(menu)->causedPopup.widget = guard;
584
585 //Because of a delay in menu effects, we must keep track of the
586 //menu visibility to avoid flicker on button release
587 menuOpen = true;
588 menu->exec(menuPos);
589 if (guard) {
590 menuOpen = false;
591 q->setDown(false);
592 }
593}
594
595QPoint QPushButtonPrivate::adjustedMenuPosition()
596{
597 Q_Q(QPushButton);
598
599 bool horizontal = true;
600#if !defined(QT_NO_TOOLBAR)
601 QToolBar *tb = qobject_cast<QToolBar*>(parent);
602 if (tb && tb->orientation() == Qt::Vertical)
603 horizontal = false;
604#endif
605
606 QWidgetItem item(q);
607 QRect rect = item.geometry();
608 rect.setRect(rect.x() - q->x(), rect.y() - q->y(), rect.width(), rect.height());
609
610 QSize menuSize = menu->sizeHint();
611 QPoint globalPos = q->mapToGlobal(rect.topLeft());
612 int x = globalPos.x();
613 int y = globalPos.y();
614 if (horizontal) {
615 if (globalPos.y() + rect.height() + menuSize.height() <= QApplication::desktop()->availableGeometry(q).height()) {
616 y += rect.height();
617 } else {
618 y -= menuSize.height();
619 }
620 if (q->layoutDirection() == Qt::RightToLeft)
621 x += rect.width() - menuSize.width();
622 } else {
623 if (globalPos.x() + rect.width() + menu->sizeHint().width() <= QApplication::desktop()->availableGeometry(q).width())
624 x += rect.width();
625 else
626 x -= menuSize.width();
627 }
628
629 return QPoint(x,y);
630}
631
632#endif // QT_NO_MENU
633
634void QPushButtonPrivate::resetLayoutItemMargins()
635{
636 Q_Q(QPushButton);
637 QStyleOptionButton opt;
638 q->initStyleOption(&opt);
639 setLayoutItemMargins(QStyle::SE_PushButtonLayoutItem, &opt);
640}
641
642void QPushButton::setFlat(bool flat)
643{
644 Q_D(QPushButton);
645 if (d->flat == flat)
646 return;
647 d->flat = flat;
648 d->resetLayoutItemMargins();
649 d->sizeHint = QSize();
650 update();
651 updateGeometry();
652}
653
654bool QPushButton::isFlat() const
655{
656 Q_D(const QPushButton);
657 return d->flat;
658}
659
660/*! \reimp */
661bool QPushButton::event(QEvent *e)
662{
663 Q_D(QPushButton);
664 if (e->type() == QEvent::ParentChange) {
665 if (QDialog *dialog = d->dialogParent()) {
666 if (d->defaultButton)
667 dialog->d_func()->setMainDefault(this);
668 }
669 } else if (e->type() == QEvent::StyleChange
670#ifdef Q_WS_MAC
671 || e->type() == QEvent::MacSizeChange
672#endif
673 ) {
674 d->resetLayoutItemMargins();
675 updateGeometry();
676 } else if (e->type() == QEvent::PolishRequest) {
677 updateGeometry();
678 }
679 return QAbstractButton::event(e);
680}
681
682#ifdef QT3_SUPPORT
683/*!
684 Use one of the constructors that doesn't take the \a name
685 argument and then use setObjectName() instead.
686*/
687QPushButton::QPushButton(QWidget *parent, const char *name)
688 : QAbstractButton(*new QPushButtonPrivate, parent)
689{
690 Q_D(QPushButton);
691 setObjectName(QString::fromAscii(name));
692 d->init();
693}
694
695/*!
696 Use one of the constructors that doesn't take the \a name
697 argument and then use setObjectName() instead.
698*/
699QPushButton::QPushButton(const QString &text, QWidget *parent, const char *name)
700 : QAbstractButton(*new QPushButtonPrivate, parent)
701{
702 Q_D(QPushButton);
703 setObjectName(QString::fromAscii(name));
704 setText(text);
705 d->init();
706}
707
708/*!
709 Use one of the constructors that doesn't take the \a name
710 argument and then use setObjectName() instead.
711*/
712QPushButton::QPushButton(const QIcon& icon, const QString &text, QWidget *parent, const char *name)
713 : QAbstractButton(*new QPushButtonPrivate, parent)
714{
715 Q_D(QPushButton);
716 setObjectName(QString::fromAscii(name));
717 setText(text);
718 setIcon(icon);
719 d->init();
720}
721#endif
722
723/*!
724 \fn void QPushButton::openPopup()
725
726 Use showMenu() instead.
727*/
728
729/*!
730 \fn bool QPushButton::isMenuButton() const
731
732 Use menu() != 0 instead.
733*/
734
735/*!
736 \fn void QPushButton::setPopup(QMenu* popup)
737
738 Use setMenu() instead.
739*/
740
741/*!
742 \fn QMenu* QPushButton::popup() const
743
744 Use menu() instead.
745*/
746
747QT_END_NAMESPACE
748
749#include "moc_qpushbutton.cpp"
Note: See TracBrowser for help on using the repository browser.