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

Last change on this file since 64 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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