source: trunk/src/widgets/qpushbutton.cpp@ 90

Last change on this file since 90 was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

  • Property svn:keywords set to Id
File size: 20.5 KB
Line 
1/****************************************************************************
2** $Id: qpushbutton.cpp 2 2005-11-16 15:49:26Z dmik $
3**
4** Implementation of QPushButton class
5**
6** Created : 940221
7**
8** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
9**
10** This file is part of the widgets module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "qpushbutton.h"
39#ifndef QT_NO_PUSHBUTTON
40#include "qdialog.h"
41#include "qfontmetrics.h"
42#include "qpainter.h"
43#include "qdrawutil.h"
44#include "qpixmap.h"
45#include "qbitmap.h"
46#include "qpopupmenu.h"
47#include "qguardedptr.h"
48#include "qapplication.h"
49#include "qtoolbar.h"
50#include "qstyle.h"
51#if defined(QT_ACCESSIBILITY_SUPPORT)
52#include "qaccessible.h"
53#endif
54
55/*!
56 \class QPushButton qpushbutton.h
57 \brief The QPushButton widget provides a command button.
58
59 \ingroup basic
60 \mainclass
61
62 The push button, or command button, is perhaps the most commonly
63 used widget in any graphical user interface. Push (click) a button
64 to command the computer to perform some action, or to answer a
65 question. Typical buttons are OK, Apply, Cancel, Close, Yes, No
66 and Help.
67
68 A command button is rectangular and typically displays a text
69 label describing its action. An underlined character in the label
70 (signified by preceding it with an ampersand in the text)
71 indicates an accelerator key, e.g.
72 \code
73 QPushButton *pb = new QPushButton( "&Download", this );
74 \endcode
75 In this example the accelerator is \e{Alt+D}, and the label text
76 will be displayed as <b><u>D</u>ownload</b>.
77
78 Push buttons can display a textual label or a pixmap, and
79 optionally a small icon. These can be set using the constructors
80 and changed later using setText(), setPixmap() and setIconSet().
81 If the button is disabled the appearance of the text or pixmap and
82 iconset will be manipulated with respect to the GUI style to make
83 the button look "disabled".
84
85 A push button emits the signal clicked() when it is activated by
86 the mouse, the Spacebar or by a keyboard accelerator. Connect to
87 this signal to perform the button's action. Push buttons also
88 provide less commonly used signals, for example, pressed() and
89 released().
90
91 Command buttons in dialogs are by default auto-default buttons,
92 i.e. they become the default push button automatically when they
93 receive the keyboard input focus. A default button is a push
94 button that is activated when the user presses the Enter or Return
95 key in a dialog. You can change this with setAutoDefault(). Note
96 that auto-default buttons reserve a little extra space which is
97 necessary to draw a default-button indicator. If you do not want
98 this space around your buttons, call setAutoDefault(FALSE).
99
100 Being so central, the button widget has grown to accommodate a
101 great many variations in the past decade. The Microsoft style
102 guide now shows about ten different states of Windows push buttons
103 and the text implies that there are dozens more when all the
104 combinations of features are taken into consideration.
105
106 The most important modes or states are:
107 \list
108 \i Available or not (grayed out, disabled).
109 \i Standard push button, toggling push button or menu button.
110 \i On or off (only for toggling push buttons).
111 \i Default or normal. The default button in a dialog can generally
112 be "clicked" using the Enter or Return key.
113 \i Auto-repeat or not.
114 \i Pressed down or not.
115 \endlist
116
117 As a general rule, use a push button when the application or
118 dialog window performs an action when the user clicks on it (such
119 as Apply, Cancel, Close and Help) \e and when the widget is
120 supposed to have a wide, rectangular shape with a text label.
121 Small, typically square buttons that change the state of the
122 window rather than performing an action (such as the buttons in
123 the top-right corner of the QFileDialog) are not command buttons,
124 but tool buttons. Qt provides a special class (QToolButton) for
125 these buttons.
126
127 If you need toggle behavior (see setToggleButton()) or a button
128 that auto-repeats the activation signal when being pushed down
129 like the arrows in a scroll bar (see setAutoRepeat()), a command
130 button is probably not what you want. When in doubt, use a tool
131 button.
132
133 A variation of a command button is a menu button. These provide
134 not just one command, but several, since when they are clicked
135 they pop up a menu of options. Use the method setPopup() to
136 associate a popup menu with a push button.
137
138 Other classes of buttons are option buttons (see QRadioButton) and
139 check boxes (see QCheckBox).
140
141 <img src="qpushbt-m.png"> <img src="qpushbt-w.png">
142
143 In Qt, the QButton abstract base class provides most of the modes
144 and other API, and QPushButton provides GUI logic. See QButton for
145 more information about the API.
146
147 \important text, setText, text, pixmap, setPixmap, accel, setAccel,
148 isToggleButton, setDown, isDown, isOn, state, autoRepeat,
149 isExclusiveToggle, group, setAutoRepeat, toggle, pressed, released,
150 clicked, toggled, state stateChanged
151
152 \sa QToolButton, QRadioButton QCheckBox
153 \link guibooks.html#fowler GUI Design Handbook: Push Button\endlink
154*/
155
156/*!
157 \property QPushButton::autoDefault
158 \brief whether the push button is the auto default button
159
160 If this property is set to TRUE then the push button is the auto
161 default button in a dialog.
162
163 In some GUI styles a default button is drawn with an extra frame
164 around it, up to 3 pixels or more. Qt automatically keeps this
165 space free around auto-default buttons, i.e. auto-default buttons
166 may have a slightly larger size hint.
167
168 This property's default is TRUE for buttons that have a QDialog
169 parent; otherwise it defaults to FALSE.
170
171 See the \l default property for details of how \l default and
172 auto-default interact.
173*/
174
175/*!
176 \property QPushButton::autoMask
177 \brief whether the button is automatically masked
178
179 \sa QWidget::setAutoMask()
180*/
181
182/*!
183 \property QPushButton::default
184 \brief whether the push button is the default button
185
186 If this property is set to TRUE then the push button will be
187 pressed if the user presses the Enter (or Return) key in a dialog.
188
189 Regardless of focus, if the user presses Enter: If there is a
190 default button the default button is pressed; otherwise, if
191 there are one or more \l autoDefault buttons the first \l autoDefault
192 button that is next in the tab order is pressed. If there are no
193 default or \l autoDefault buttons only pressing Space on a button
194 with focus, mouse clicking, or using an accelerator will press a
195 button.
196
197 In a dialog, only one push button at a time can be the default
198 button. This button is then displayed with an additional frame
199 (depending on the GUI style).
200
201 The default button behavior is provided only in dialogs. Buttons
202 can always be clicked from the keyboard by pressing Enter (or
203 Return) or the Spacebar when the button has focus.
204
205 This property's default is FALSE.
206*/
207
208/*!
209 \property QPushButton::flat
210 \brief whether the border is disabled
211
212 This property's default is FALSE.
213*/
214
215/*!
216 \property QPushButton::iconSet
217 \brief the icon set on the push button
218
219 This property will return 0 if the push button has no iconset.
220*/
221
222/*!
223 \property QPushButton::on
224 \brief whether the push button is toggled
225
226 This property should only be set for toggle push buttons. The
227 default value is FALSE.
228
229 \sa isOn(), toggle(), toggled(), isToggleButton()
230*/
231
232/*!
233 \property QPushButton::toggleButton
234 \brief whether the button is a toggle button
235
236 Toggle buttons have an on/off state similar to \link QCheckBox
237 check boxes. \endlink A push button is initially not a toggle
238 button.
239
240 \sa setOn(), toggle(), isToggleButton() toggled()
241*/
242
243/*! \property QPushButton::menuButton
244 \brief whether the push button has a menu button on it
245 \obsolete
246
247 If this property is set to TRUE, then a down arrow is drawn on the push
248 button to indicate that a menu will pop up if the user clicks on the
249 arrow.
250*/
251
252class QPushButtonPrivate
253{
254public:
255 QPushButtonPrivate()
256 :iconset( 0 )
257 {}
258 ~QPushButtonPrivate()
259 {
260#ifndef QT_NO_ICONSET
261 delete iconset;
262#endif
263 }
264#ifndef QT_NO_POPUPMENU
265 QGuardedPtr<QPopupMenu> popup;
266#endif
267 QIconSet* iconset;
268};
269
270
271/*!
272 Constructs a push button with no text.
273
274 The \a parent and \a name arguments are sent on to the QWidget
275 constructor.
276*/
277
278QPushButton::QPushButton( QWidget *parent, const char *name )
279 : QButton( parent, name )
280{
281 init();
282}
283
284/*!
285 Constructs a push button called \a name with the parent \a parent
286 and the text \a text.
287*/
288
289QPushButton::QPushButton( const QString &text, QWidget *parent,
290 const char *name )
291 : QButton( parent, name )
292{
293 init();
294 setText( text );
295}
296
297
298/*!
299 Constructs a push button with an \a icon and a \a text.
300
301 Note that you can also pass a QPixmap object as an icon (thanks to
302 the implicit type conversion provided by C++).
303
304 The \a parent and \a name arguments are sent to the QWidget
305 constructor.
306*/
307#ifndef QT_NO_ICONSET
308QPushButton::QPushButton( const QIconSet& icon, const QString &text,
309 QWidget *parent, const char *name )
310 : QButton( parent, name )
311{
312 init();
313 setText( text );
314 setIconSet( icon );
315}
316#endif
317
318
319/*!
320 Destroys the push button.
321*/
322QPushButton::~QPushButton()
323{
324 delete d;
325}
326
327void QPushButton::init()
328{
329 d = 0;
330 defButton = FALSE;
331 lastEnabled = FALSE;
332 hasMenuArrow = FALSE;
333 flt = FALSE;
334#ifndef QT_NO_DIALOG
335 autoDefButton = ::qt_cast<QDialog*>(topLevelWidget()) != 0;
336#else
337 autoDefButton = FALSE;
338#endif
339 setBackgroundMode( PaletteButton );
340 setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) );
341}
342
343
344/*
345 Makes the push button a toggle button if \a enable is TRUE or a normal
346 push button if \a enable is FALSE.
347
348 Toggle buttons have an on/off state similar to \link QCheckBox check
349 boxes. \endlink A push button is initially not a toggle button.
350
351 \sa setOn(), toggle(), isToggleButton() toggled()
352*/
353
354void QPushButton::setToggleButton( bool enable )
355{
356 QButton::setToggleButton( enable );
357}
358
359
360/*
361 Switches a toggle button on if \a enable is TRUE or off if \a enable is
362 FALSE.
363 \sa isOn(), toggle(), toggled(), isToggleButton()
364*/
365
366void QPushButton::setOn( bool enable )
367{
368 if ( !isToggleButton() )
369 return;
370 QButton::setOn( enable );
371}
372
373void QPushButton::setAutoDefault( bool enable )
374{
375 if ( (bool)autoDefButton == enable )
376 return;
377 autoDefButton = enable;
378 update();
379 updateGeometry();
380}
381
382
383void QPushButton::setDefault( bool enable )
384{
385 if ( (bool)defButton == enable )
386 return; // no change
387 defButton = enable;
388#ifndef QT_NO_DIALOG
389 if ( defButton && ::qt_cast<QDialog*>(topLevelWidget()) )
390 ((QDialog*)topLevelWidget())->setMainDefault( this );
391#endif
392 update();
393#if defined(QT_ACCESSIBILITY_SUPPORT)
394 QAccessible::updateAccessibility( this, 0, QAccessible::StateChanged );
395#endif
396}
397
398
399/*!
400 \reimp
401*/
402QSize QPushButton::sizeHint() const
403{
404 constPolish();
405
406 int w = 0, h = 0;
407
408 // calculate contents size...
409#ifndef QT_NO_ICONSET
410 if ( iconSet() && !iconSet()->isNull() ) {
411 int iw = iconSet()->pixmap( QIconSet::Small, QIconSet::Normal ).width() + 4;
412 int ih = iconSet()->pixmap( QIconSet::Small, QIconSet::Normal ).height();
413 w += iw;
414 h = QMAX( h, ih );
415 }
416#endif
417 if ( isMenuButton() )
418 w += style().pixelMetric(QStyle::PM_MenuButtonIndicator, this);
419
420 if ( pixmap() ) {
421 QPixmap *pm = (QPixmap *)pixmap();
422 w += pm->width();
423 h += pm->height();
424 } else {
425 QString s( text() );
426 bool empty = s.isEmpty();
427 if ( empty )
428 s = QString::fromLatin1("XXXX");
429 QFontMetrics fm = fontMetrics();
430 QSize sz = fm.size( ShowPrefix, s );
431 if(!empty || !w)
432 w += sz.width();
433 if(!empty || !h)
434 h = QMAX(h, sz.height());
435 }
436
437 return (style().sizeFromContents(QStyle::CT_PushButton, this, QSize(w, h)).
438 expandedTo(QApplication::globalStrut()));
439}
440
441
442/*!
443 \reimp
444*/
445void QPushButton::move( int x, int y )
446{
447 QWidget::move( x, y );
448}
449
450/*!
451 \reimp
452*/
453void QPushButton::move( const QPoint &p )
454{
455 move( p.x(), p.y() );
456}
457
458/*!
459 \reimp
460*/
461void QPushButton::resize( int w, int h )
462{
463 QWidget::resize( w, h );
464}
465
466/*!
467 \reimp
468*/
469void QPushButton::resize( const QSize &s )
470{
471 resize( s.width(), s.height() );
472}
473
474/*!
475 \reimp
476*/
477void QPushButton::setGeometry( int x, int y, int w, int h )
478{
479 QWidget::setGeometry( x, y, w, h );
480}
481
482/*!
483 \reimp
484*/
485void QPushButton::setGeometry( const QRect &r )
486{
487 QWidget::setGeometry( r );
488}
489
490/*!
491 \reimp
492 */
493void QPushButton::resizeEvent( QResizeEvent * )
494{
495 if ( autoMask() )
496 updateMask();
497}
498
499/*!
500 \reimp
501*/
502void QPushButton::drawButton( QPainter *paint )
503{
504 int diw = 0;
505 if ( isDefault() || autoDefault() ) {
506 diw = style().pixelMetric(QStyle::PM_ButtonDefaultIndicator, this);
507
508 if ( diw > 0 ) {
509 if (backgroundMode() == X11ParentRelative) {
510 erase( 0, 0, width(), diw );
511 erase( 0, 0, diw, height() );
512 erase( 0, height() - diw, width(), diw );
513 erase( width() - diw, 0, diw, height() );
514 } else if ( parentWidget() && parentWidget()->backgroundPixmap() ){
515 // pseudo tranparency
516 paint->drawTiledPixmap( 0, 0, width(), diw,
517 *parentWidget()->backgroundPixmap(),
518 x(), y() );
519 paint->drawTiledPixmap( 0, 0, diw, height(),
520 *parentWidget()->backgroundPixmap(),
521 x(), y() );
522 paint->drawTiledPixmap( 0, height()-diw, width(), diw,
523 *parentWidget()->backgroundPixmap(),
524 x(), y()+height() );
525 paint->drawTiledPixmap( width()-diw, 0, diw, height(),
526 *parentWidget()->backgroundPixmap(),
527 x()+width(), y() );
528 } else {
529 paint->fillRect( 0, 0, width(), diw,
530 colorGroup().brush(QColorGroup::Background) );
531 paint->fillRect( 0, 0, diw, height(),
532 colorGroup().brush(QColorGroup::Background) );
533 paint->fillRect( 0, height()-diw, width(), diw,
534 colorGroup().brush(QColorGroup::Background) );
535 paint->fillRect( width()-diw, 0, diw, height(),
536 colorGroup().brush(QColorGroup::Background) );
537 }
538
539 }
540 }
541
542 QStyle::SFlags flags = QStyle::Style_Default;
543 if (isEnabled())
544 flags |= QStyle::Style_Enabled;
545 if (hasFocus())
546 flags |= QStyle::Style_HasFocus;
547 if (isDown())
548 flags |= QStyle::Style_Down;
549 if (isOn())
550 flags |= QStyle::Style_On;
551 if (! isFlat() && ! isDown())
552 flags |= QStyle::Style_Raised;
553 if (isDefault())
554 flags |= QStyle::Style_ButtonDefault;
555
556 style().drawControl(QStyle::CE_PushButton, paint, this, rect(), colorGroup(), flags);
557 drawButtonLabel( paint );
558
559 lastEnabled = isEnabled();
560}
561
562
563/*!
564 \reimp
565*/
566void QPushButton::drawButtonLabel( QPainter *paint )
567{
568
569 QStyle::SFlags flags = QStyle::Style_Default;
570 if (isEnabled())
571 flags |= QStyle::Style_Enabled;
572 if (hasFocus())
573 flags |= QStyle::Style_HasFocus;
574 if (isDown())
575 flags |= QStyle::Style_Down;
576 if (isOn())
577 flags |= QStyle::Style_On;
578 if (! isFlat() && ! isDown())
579 flags |= QStyle::Style_Raised;
580 if (isDefault())
581 flags |= QStyle::Style_ButtonDefault;
582
583 style().drawControl(QStyle::CE_PushButtonLabel, paint, this,
584 style().subRect(QStyle::SR_PushButtonContents, this),
585 colorGroup(), flags);
586}
587
588
589/*!
590 \reimp
591 */
592void QPushButton::updateMask()
593{
594 QBitmap bm( size() );
595 bm.fill( color0 );
596
597 {
598 QPainter p( &bm, this );
599 style().drawControlMask(QStyle::CE_PushButton, &p, this, rect());
600 }
601
602 setMask( bm );
603}
604
605/*!
606 \reimp
607*/
608void QPushButton::focusInEvent( QFocusEvent *e )
609{
610 if (autoDefButton && !defButton) {
611 defButton = TRUE;
612#ifndef QT_NO_DIALOG
613 if ( defButton && ::qt_cast<QDialog*>(topLevelWidget()) )
614 ((QDialog*)topLevelWidget())->setDefault( this );
615#endif
616 }
617 QButton::focusInEvent( e );
618}
619
620/*!
621 \reimp
622*/
623void QPushButton::focusOutEvent( QFocusEvent *e )
624{
625#ifndef QT_NO_DIALOG
626 if ( defButton && autoDefButton ) {
627 if ( ::qt_cast<QDialog*>(topLevelWidget()) )
628 ((QDialog*)topLevelWidget())->setDefault( 0 );
629 }
630#endif
631
632 QButton::focusOutEvent( e );
633#ifndef QT_NO_POPUPMENU
634 if ( popup() && popup()->isVisible() ) // restore pressed status
635 setDown( TRUE );
636#endif
637}
638
639
640#ifndef QT_NO_POPUPMENU
641/*!
642 Associates the popup menu \a popup with this push button and thus
643 turns it into a menu button.
644
645 Ownership of the popup menu is \e not transferred to the push
646 button.
647
648 \sa popup()
649*/
650void QPushButton::setPopup( QPopupMenu* popup )
651{
652 if ( !d )
653 d = new QPushButtonPrivate;
654 if ( popup && !d->popup )
655 connect( this, SIGNAL( pressed() ), this, SLOT( popupPressed() ) );
656
657 d->popup = popup;
658 setIsMenuButton( popup != 0 );
659}
660#endif //QT_NO_POPUPMENU
661#ifndef QT_NO_ICONSET
662void QPushButton::setIconSet( const QIconSet& icon )
663{
664 if ( !d )
665 d = new QPushButtonPrivate;
666 if ( !icon.isNull() ) {
667 if ( d->iconset )
668 *d->iconset = icon;
669 else
670 d->iconset = new QIconSet( icon );
671 } else if ( d->iconset) {
672 delete d->iconset;
673 d->iconset = 0;
674 }
675
676 update();
677 updateGeometry();
678}
679
680
681QIconSet* QPushButton::iconSet() const
682{
683 return d ? d->iconset : 0;
684}
685#endif // QT_NO_ICONSET
686#ifndef QT_NO_POPUPMENU
687/*!
688 Returns the button's associated popup menu or 0 if no popup menu
689 has been set.
690
691 \sa setPopup()
692*/
693QPopupMenu* QPushButton::popup() const
694{
695 return d ? (QPopupMenu*)d->popup : 0;
696}
697
698void QPushButton::popupPressed()
699{
700 QPopupMenu* popup = d ? (QPopupMenu*) d->popup : 0;
701 if ( isDown() && popup ) {
702 bool horizontal = TRUE;
703 bool topLeft = TRUE; // ### always TRUE
704#ifndef QT_NO_TOOLBAR
705 QToolBar *tb = ::qt_cast<QToolBar*>(parentWidget());
706 if ( tb && tb->orientation() == Vertical )
707 horizontal = FALSE;
708#endif
709 if ( horizontal ) {
710 if ( topLeft ) {
711 if ( mapToGlobal( QPoint( 0, rect().bottom() ) ).y() + popup->sizeHint().height() <= qApp->desktop()->height() )
712 popup->exec( mapToGlobal( rect().bottomLeft() ) );
713 else
714 popup->exec( mapToGlobal( rect().topLeft() - QPoint( 0, popup->sizeHint().height() ) ) );
715 } else {
716 QSize sz( popup->sizeHint() );
717 QPoint p = mapToGlobal( rect().topLeft() );
718 p.ry() -= sz.height();
719 popup->exec( p );
720 }
721 }
722 else {
723 if ( topLeft ) {
724 if ( mapToGlobal( QPoint( rect().right(), 0 ) ).x() + popup->sizeHint().width() <= qApp->desktop()->width() )
725 popup->exec( mapToGlobal( rect().topRight() ) );
726 else
727 popup->exec( mapToGlobal( rect().topLeft() - QPoint( popup->sizeHint().width(), 0 ) ) );
728 } else {
729 QSize sz( popup->sizeHint() );
730 QPoint p = mapToGlobal( rect().topLeft() );
731 p.rx() -= sz.width();
732 popup->exec( p );
733 }
734 }
735 setDown( FALSE );
736 }
737}
738#endif
739
740void QPushButton::setFlat( bool f )
741{
742 flt = f;
743 update();
744}
745
746bool QPushButton::isFlat() const
747{
748 return flt;
749}
750
751/*!
752 \obsolete
753 \fn virtual void QPushButton::setIsMenuButton( bool enable )
754*/
755
756#endif
Note: See TracBrowser for help on using the repository browser.