Merge remote-tracking branch 'origin/stable' into dev
[qt:qtbase.git] / src / widgets / widgets / qdialogbuttonbox.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtCore/qhash.h>
43 #include <QtWidgets/qpushbutton.h>
44 #include <QtWidgets/qstyle.h>
45 #include <QtWidgets/qlayout.h>
46 #include <QtWidgets/qdialog.h>
47 #include <QtWidgets/qapplication.h>
48 #include <private/qwidget_p.h>
49 #include <private/qguiapplication_p.h>
50 #include <QtGui/qpa/qplatformdialoghelper.h>
51 #include <QtGui/qpa/qplatformtheme.h>
52 #include <QtWidgets/qaction.h>
53
54 #include "qdialogbuttonbox.h"
55
56 QT_BEGIN_NAMESPACE
57
58 /*!
59     \class QDialogButtonBox
60     \since 4.2
61     \brief The QDialogButtonBox class is a widget that presents buttons in a
62     layout that is appropriate to the current widget style.
63
64     \ingroup dialog-classes
65     \inmodule QtWidgets
66
67     Dialogs and message boxes typically present buttons in a layout that
68     conforms to the interface guidelines for that platform. Invariably,
69     different platforms have different layouts for their dialogs.
70     QDialogButtonBox allows a developer to add buttons to it and will
71     automatically use the appropriate layout for the user's desktop
72     environment.
73
74     Most buttons for a dialog follow certain roles. Such roles include:
75
76     \list
77     \li Accepting or rejecting the dialog.
78     \li Asking for help.
79     \li Performing actions on the dialog itself (such as resetting fields or
80        applying changes).
81     \endlist
82
83     There can also be alternate ways of dismissing the dialog which may cause
84     destructive results.
85
86     Most dialogs have buttons that can almost be considered standard (e.g.
87     \uicontrol OK and \uicontrol Cancel buttons). It is sometimes convenient to create these
88     buttons in a standard way.
89
90     There are a couple ways of using QDialogButtonBox. One ways is to create
91     the buttons (or button texts) yourself and add them to the button box,
92     specifying their role.
93
94     \snippet dialogs/extension/finddialog.cpp 1
95
96     Alternatively, QDialogButtonBox provides several standard buttons (e.g. OK, Cancel, Save)
97     that you can use. They exist as flags so you can OR them together in the constructor.
98
99     \snippet dialogs/tabdialog/tabdialog.cpp 2
100
101     You can mix and match normal buttons and standard buttons.
102
103     Currently the buttons are laid out in the following way if the button box is horizontal:
104     \table
105     \row \li \inlineimage buttonbox-gnomelayout-horizontal.png GnomeLayout Horizontal
106          \li Button box laid out in horizontal GnomeLayout
107     \row \li \inlineimage buttonbox-kdelayout-horizontal.png KdeLayout Horizontal
108          \li Button box laid out in horizontal KdeLayout
109     \row \li \inlineimage buttonbox-maclayout-horizontal.png MacLayout Horizontal
110          \li Button box laid out in horizontal MacLayout
111     \row \li \inlineimage buttonbox-winlayout-horizontal.png  WinLayout Horizontal
112          \li Button box laid out in horizontal WinLayout
113     \endtable
114
115     The buttons are laid out the following way if the button box is vertical:
116
117     \table
118     \row \li GnomeLayout
119          \li KdeLayout
120          \li MacLayout
121          \li WinLayout
122     \row \li \inlineimage buttonbox-gnomelayout-vertical.png GnomeLayout Vertical
123          \li \inlineimage buttonbox-kdelayout-vertical.png KdeLayout Vertical
124          \li \inlineimage buttonbox-maclayout-vertical.png MacLayout Vertical
125          \li \inlineimage buttonbox-winlayout-vertical.png WinLayout Vertical
126     \endtable
127
128     Additionally, button boxes that contain only buttons with ActionRole or
129     HelpRole can be considered modeless and have an alternate look on Mac OS X:
130
131     \table
132     \row \li modeless horizontal MacLayout
133          \li \inlineimage buttonbox-mac-modeless-horizontal.png Screenshot of modeless horizontal MacLayout
134     \row \li modeless vertical MacLayout
135          \li \inlineimage buttonbox-mac-modeless-vertical.png Screenshot of modeless vertical MacLayout
136     \endtable
137
138     When a button is clicked in the button box, the clicked() signal is emitted
139     for the actual button is that is pressed. For convenience, if the button
140     has an AcceptRole, RejectRole, or HelpRole, the accepted(), rejected(), or
141     helpRequested() signals are emitted respectively.
142
143     If you want a specific button to be default you need to call
144     QPushButton::setDefault() on it yourself. However, if there is no default
145     button set and to preserve which button is the default button across
146     platforms when using the QPushButton::autoDefault property, the first push
147     button with the accept role is made the default button when the
148     QDialogButtonBox is shown,
149
150     \sa QMessageBox, QPushButton, QDialog
151 */
152
153 enum {
154     AcceptRole      = QDialogButtonBox::AcceptRole,
155     RejectRole      = QDialogButtonBox::RejectRole,
156     DestructiveRole = QDialogButtonBox::DestructiveRole,
157     ActionRole      = QDialogButtonBox::ActionRole,
158     HelpRole        = QDialogButtonBox::HelpRole,
159     YesRole         = QDialogButtonBox::YesRole,
160     NoRole          = QDialogButtonBox::NoRole,
161     ApplyRole       = QDialogButtonBox::ApplyRole,
162     ResetRole       = QDialogButtonBox::ResetRole,
163
164     AlternateRole   = 0x10000000,
165     Stretch         = 0x20000000,
166     EOL             = 0x40000000,
167     Reverse         = 0x80000000
168 };
169
170 static QDialogButtonBox::ButtonRole roleFor(QDialogButtonBox::StandardButton button)
171 {
172     return static_cast<QDialogButtonBox::ButtonRole>(QMessageDialogOptions::buttonRole(
173         static_cast<QMessageDialogOptions::StandardButton>(button)));
174 }
175
176 static const uint layouts[2][5][14] =
177 {
178     // Qt::Horizontal
179     {
180         // WinLayout
181         { ResetRole, Stretch, YesRole, AcceptRole, AlternateRole, DestructiveRole, NoRole, ActionRole, RejectRole, ApplyRole,
182            HelpRole, EOL, EOL, EOL },
183
184         // MacLayout
185         { HelpRole, ResetRole, ApplyRole, ActionRole, Stretch, DestructiveRole | Reverse,
186           AlternateRole | Reverse, RejectRole | Reverse, AcceptRole | Reverse, NoRole | Reverse, YesRole | Reverse, EOL, EOL },
187
188         // KdeLayout
189         { HelpRole, ResetRole, Stretch, YesRole, NoRole, ActionRole, AcceptRole, AlternateRole,
190           ApplyRole, DestructiveRole, RejectRole, EOL },
191
192         // GnomeLayout
193         { HelpRole, ResetRole, Stretch, ActionRole, ApplyRole | Reverse, DestructiveRole | Reverse,
194           AlternateRole | Reverse, RejectRole | Reverse, AcceptRole | Reverse, NoRole | Reverse, YesRole | Reverse, EOL },
195
196         // Mac modeless
197         { ResetRole, ApplyRole, ActionRole, Stretch, HelpRole, EOL, EOL, EOL, EOL, EOL, EOL, EOL, EOL, EOL }
198     },
199
200     // Qt::Vertical
201     {
202         // WinLayout
203         { ActionRole, YesRole, AcceptRole, AlternateRole, DestructiveRole, NoRole, RejectRole, ApplyRole, ResetRole,
204           HelpRole, Stretch, EOL, EOL, EOL },
205
206         // MacLayout
207         { YesRole, NoRole, AcceptRole, RejectRole, AlternateRole, DestructiveRole, Stretch, ActionRole, ApplyRole,
208           ResetRole, HelpRole, EOL, EOL },
209
210         // KdeLayout
211         { AcceptRole, AlternateRole, ApplyRole, ActionRole, YesRole, NoRole, Stretch, ResetRole,
212           DestructiveRole, RejectRole, HelpRole, EOL },
213
214         // GnomeLayout
215         { YesRole, NoRole, AcceptRole, RejectRole, AlternateRole, DestructiveRole, ApplyRole, ActionRole, Stretch,
216           ResetRole, HelpRole, EOL, EOL, EOL },
217
218         // Mac modeless
219         { ActionRole, ApplyRole, ResetRole, Stretch, HelpRole, EOL, EOL, EOL, EOL, EOL, EOL, EOL, EOL, EOL }
220     }
221 };
222
223
224 class QDialogButtonBoxPrivate : public QWidgetPrivate
225 {
226     Q_DECLARE_PUBLIC(QDialogButtonBox)
227
228 public:
229     QDialogButtonBoxPrivate(Qt::Orientation orient);
230
231     QList<QAbstractButton *> buttonLists[QDialogButtonBox::NRoles];
232     QHash<QPushButton *, QDialogButtonBox::StandardButton> standardButtonHash;
233
234     Qt::Orientation orientation;
235     QDialogButtonBox::ButtonLayout layoutPolicy;
236     QBoxLayout *buttonLayout;
237     bool internalRemove;
238     bool center;
239
240     void createStandardButtons(QDialogButtonBox::StandardButtons buttons);
241
242     void layoutButtons();
243     void initLayout();
244     void resetLayout();
245     QPushButton *createButton(QDialogButtonBox::StandardButton button, bool doLayout = true);
246     void addButton(QAbstractButton *button, QDialogButtonBox::ButtonRole role, bool doLayout = true);
247     void _q_handleButtonDestroyed();
248     void _q_handleButtonClicked();
249     void addButtonsToLayout(const QList<QAbstractButton *> &buttonList, bool reverse);
250     void retranslateStrings();
251 };
252
253 QDialogButtonBoxPrivate::QDialogButtonBoxPrivate(Qt::Orientation orient)
254     : orientation(orient), buttonLayout(0), internalRemove(false), center(false)
255 {
256 }
257
258 void QDialogButtonBoxPrivate::initLayout()
259 {
260     Q_Q(QDialogButtonBox);
261     layoutPolicy = QDialogButtonBox::ButtonLayout(q->style()->styleHint(QStyle::SH_DialogButtonLayout, 0, q));
262     bool createNewLayout = buttonLayout == 0
263         || (orientation == Qt::Horizontal && qobject_cast<QVBoxLayout *>(buttonLayout) != 0)
264         || (orientation == Qt::Vertical && qobject_cast<QHBoxLayout *>(buttonLayout) != 0);
265     if (createNewLayout) {
266         delete buttonLayout;
267         if (orientation == Qt::Horizontal)
268             buttonLayout = new QHBoxLayout(q);
269         else
270             buttonLayout = new QVBoxLayout(q);
271     }
272
273     int left, top, right, bottom;
274     setLayoutItemMargins(QStyle::SE_PushButtonLayoutItem);
275     getLayoutItemMargins(&left, &top, &right, &bottom);
276     buttonLayout->setContentsMargins(-left, -top, -right, -bottom);
277
278     if (!q->testAttribute(Qt::WA_WState_OwnSizePolicy)) {
279         QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Fixed, QSizePolicy::ButtonBox);
280         if (orientation == Qt::Vertical)
281             sp.transpose();
282         q->setSizePolicy(sp);
283         q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
284     }
285
286     // ### move to a real init() function
287     q->setFocusPolicy(Qt::TabFocus);
288 }
289
290 void QDialogButtonBoxPrivate::resetLayout()
291 {
292     //delete buttonLayout;
293     initLayout();
294     layoutButtons();
295 }
296
297 void QDialogButtonBoxPrivate::addButtonsToLayout(const QList<QAbstractButton *> &buttonList,
298                                                  bool reverse)
299 {
300     int start = reverse ? buttonList.count() - 1 : 0;
301     int end = reverse ? -1 : buttonList.count();
302     int step = reverse ? -1 : 1;
303
304     for (int i = start; i != end; i += step) {
305         QAbstractButton *button = buttonList.at(i);
306         buttonLayout->addWidget(button);
307         button->show();
308     }
309 }
310
311 void QDialogButtonBoxPrivate::layoutButtons()
312 {
313     Q_Q(QDialogButtonBox);
314     const int MacGap = 36 - 8;    // 8 is the default gap between a widget and a spacer item
315
316     for (int i = buttonLayout->count() - 1; i >= 0; --i) {
317         QLayoutItem *item = buttonLayout->takeAt(i);
318         if (QWidget *widget = item->widget())
319             widget->hide();
320         delete item;
321     }
322
323     int tmpPolicy = layoutPolicy;
324
325     static const int M = 5;
326     static const int ModalRoles[M] = { AcceptRole, RejectRole, DestructiveRole, YesRole, NoRole };
327     if (tmpPolicy == QDialogButtonBox::MacLayout) {
328         bool hasModalButton = false;
329         for (int i = 0; i < M; ++i) {
330             if (!buttonLists[ModalRoles[i]].isEmpty()) {
331                 hasModalButton = true;
332                 break;
333             }
334         }
335         if (!hasModalButton)
336             tmpPolicy = 4;  // Mac modeless
337     }
338
339     const uint *currentLayout = layouts[orientation == Qt::Vertical][tmpPolicy];
340
341     if (center)
342         buttonLayout->addStretch();
343
344     QList<QAbstractButton *> acceptRoleList = buttonLists[AcceptRole];
345
346     while (*currentLayout != EOL) {
347         int role = (*currentLayout & ~Reverse);
348         bool reverse = (*currentLayout & Reverse);
349
350         switch (role) {
351         case Stretch:
352             if (!center)
353                 buttonLayout->addStretch();
354             break;
355         case AcceptRole: {
356             if (acceptRoleList.isEmpty())
357                 break;
358             // Only the first one
359             QAbstractButton *button = acceptRoleList.first();
360             buttonLayout->addWidget(button);
361             button->show();
362         }
363             break;
364         case AlternateRole:
365             {
366                 if (acceptRoleList.size() < 2)
367                     break;
368                 QList<QAbstractButton *> list = acceptRoleList;
369                 list.removeFirst();
370                 addButtonsToLayout(list, reverse);
371             }
372             break;
373         case DestructiveRole:
374             {
375                 const QList<QAbstractButton *> &list = buttonLists[role];
376
377                 /*
378                     Mac: Insert a gap on the left of the destructive
379                     buttons to ensure that they don't get too close to
380                     the help and action buttons (but only if there are
381                     some buttons to the left of the destructive buttons
382                     (and the stretch, whence buttonLayout->count() > 1
383                     and not 0)).
384                 */
385                 if (tmpPolicy == QDialogButtonBox::MacLayout
386                         && !list.isEmpty() && buttonLayout->count() > 1)
387                     buttonLayout->addSpacing(MacGap);
388
389                 addButtonsToLayout(list, reverse);
390
391                 /*
392                     Insert a gap between the destructive buttons and the
393                     accept and reject buttons.
394                 */
395                 if (tmpPolicy == QDialogButtonBox::MacLayout && !list.isEmpty())
396                     buttonLayout->addSpacing(MacGap);
397             }
398             break;
399         case RejectRole:
400         case ActionRole:
401         case HelpRole:
402         case YesRole:
403         case NoRole:
404         case ApplyRole:
405         case ResetRole:
406             addButtonsToLayout(buttonLists[role], reverse);
407         }
408         ++currentLayout;
409     }
410
411     QWidget *lastWidget = 0;
412     q->setFocusProxy(0);
413     for (int i = 0; i < buttonLayout->count(); ++i) {
414         QLayoutItem *item = buttonLayout->itemAt(i);
415         if (QWidget *widget = item->widget()) {
416             if (lastWidget)
417                 QWidget::setTabOrder(lastWidget, widget);
418             else
419                 q->setFocusProxy(widget);
420             lastWidget = widget;
421         }
422     }
423
424     if (center)
425         buttonLayout->addStretch();
426 }
427
428 QPushButton *QDialogButtonBoxPrivate::createButton(QDialogButtonBox::StandardButton sbutton,
429                                                    bool doLayout)
430 {
431     Q_Q(QDialogButtonBox);
432     int icon = 0;
433
434     switch (sbutton) {
435     case QDialogButtonBox::Ok:
436         icon = QStyle::SP_DialogOkButton;
437         break;
438     case QDialogButtonBox::Save:
439         icon = QStyle::SP_DialogSaveButton;
440         break;
441     case QDialogButtonBox::Open:
442         icon = QStyle::SP_DialogOpenButton;
443         break;
444     case QDialogButtonBox::Cancel:
445         icon = QStyle::SP_DialogCancelButton;
446         break;
447     case QDialogButtonBox::Close:
448         icon = QStyle::SP_DialogCloseButton;
449         break;
450     case QDialogButtonBox::Apply:
451         icon = QStyle::SP_DialogApplyButton;
452         break;
453     case QDialogButtonBox::Reset:
454         icon = QStyle::SP_DialogResetButton;
455         break;
456     case QDialogButtonBox::Help:
457         icon = QStyle::SP_DialogHelpButton;
458         break;
459     case QDialogButtonBox::Discard:
460         icon = QStyle::SP_DialogDiscardButton;
461         break;
462     case QDialogButtonBox::Yes:
463         icon = QStyle::SP_DialogYesButton;
464         break;
465     case QDialogButtonBox::No:
466         icon = QStyle::SP_DialogNoButton;
467         break;
468     case QDialogButtonBox::YesToAll:
469     case QDialogButtonBox::NoToAll:
470     case QDialogButtonBox::SaveAll:
471     case QDialogButtonBox::Abort:
472     case QDialogButtonBox::Retry:
473     case QDialogButtonBox::Ignore:
474     case QDialogButtonBox::RestoreDefaults:
475         break;
476     case QDialogButtonBox::NoButton:
477         return 0;
478         ;
479     }
480     QPushButton *button = new QPushButton(QGuiApplicationPrivate::platformTheme()->standardButtonText(sbutton), q);
481     QStyle *style = q->style();
482     if (style->styleHint(QStyle::SH_DialogButtonBox_ButtonsHaveIcons, 0, q) && icon != 0)
483         button->setIcon(style->standardIcon(QStyle::StandardPixmap(icon), 0, q));
484     if (style != QApplication::style()) // Propagate style
485         button->setStyle(style);
486     standardButtonHash.insert(button, sbutton);
487     if (roleFor(sbutton) != QDialogButtonBox::InvalidRole) {
488         addButton(button, roleFor(sbutton), doLayout);
489     } else {
490         qWarning("QDialogButtonBox::createButton: Invalid ButtonRole, button not added");
491     }
492
493 #ifdef Q_WS_MAC
494     // Since mnemonics is off by default on Mac, we add a Cmd-D
495     // shortcut here to e.g. make the "Don't Save" button work nativly:
496     if (sbutton == QDialogButtonBox::Discard)
497         button->setShortcut(QKeySequence(QLatin1String("Ctrl+D")));
498 #endif
499
500     return button;
501 }
502
503 void QDialogButtonBoxPrivate::addButton(QAbstractButton *button, QDialogButtonBox::ButtonRole role,
504                                         bool doLayout)
505 {
506     Q_Q(QDialogButtonBox);
507     QObject::connect(button, SIGNAL(clicked()), q, SLOT(_q_handleButtonClicked()));
508     QObject::connect(button, SIGNAL(destroyed()), q, SLOT(_q_handleButtonDestroyed()));
509     buttonLists[role].append(button);
510     if (doLayout)
511         layoutButtons();
512 }
513
514 void QDialogButtonBoxPrivate::createStandardButtons(QDialogButtonBox::StandardButtons buttons)
515 {
516     uint i = QDialogButtonBox::FirstButton;
517     while (i <= QDialogButtonBox::LastButton) {
518         if (i & buttons) {
519             createButton(QDialogButtonBox::StandardButton(i), false);
520         }
521         i = i << 1;
522     }
523     layoutButtons();
524 }
525
526 void QDialogButtonBoxPrivate::retranslateStrings()
527 {
528     typedef QHash<QPushButton *, QDialogButtonBox::StandardButton>::iterator Iterator;
529
530     const Iterator end = standardButtonHash.end();
531     for (Iterator it = standardButtonHash.begin(); it != end; ++it) {
532         const QString text = QGuiApplicationPrivate::platformTheme()->standardButtonText(it.value());
533         if (!text.isEmpty())
534             it.key()->setText(text);
535     }
536 }
537
538 /*!
539     Constructs an empty, horizontal button box with the given \a parent.
540
541     \sa orientation, addButton()
542 */
543 QDialogButtonBox::QDialogButtonBox(QWidget *parent)
544     : QWidget(*new QDialogButtonBoxPrivate(Qt::Horizontal), parent, 0)
545 {
546     d_func()->initLayout();
547 }
548
549 /*!
550     Constructs an empty button box with the given \a orientation and \a parent.
551
552     \sa orientation, addButton()
553 */
554 QDialogButtonBox::QDialogButtonBox(Qt::Orientation orientation, QWidget *parent)
555     : QWidget(*new QDialogButtonBoxPrivate(orientation), parent, 0)
556 {
557     d_func()->initLayout();
558 }
559
560 /*!
561     \since 5.2
562
563     Constructs a horizontal button box with the given \a parent, containing
564     the standard buttons specified by \a buttons.
565
566     \sa orientation, addButton()
567 */
568 QDialogButtonBox::QDialogButtonBox(StandardButtons buttons, QWidget *parent)
569     : QWidget(*new QDialogButtonBoxPrivate(Qt::Horizontal), parent, 0)
570 {
571     d_func()->initLayout();
572     d_func()->createStandardButtons(buttons);
573 }
574
575 /*!
576     Constructs a button box with the given \a orientation and \a parent, containing
577     the standard buttons specified by \a buttons.
578
579     \sa orientation, addButton()
580 */
581 QDialogButtonBox::QDialogButtonBox(StandardButtons buttons, Qt::Orientation orientation,
582                                    QWidget *parent)
583     : QWidget(*new QDialogButtonBoxPrivate(orientation), parent, 0)
584 {
585     d_func()->initLayout();
586     d_func()->createStandardButtons(buttons);
587 }
588
589 /*!
590     Destroys the button box.
591 */
592 QDialogButtonBox::~QDialogButtonBox()
593 {
594 }
595
596 /*!
597     \enum QDialogButtonBox::ButtonRole
598     \enum QMessageBox::ButtonRole
599
600     This enum describes the roles that can be used to describe buttons in
601     the button box. Combinations of these roles are as flags used to
602     describe different aspects of their behavior.
603
604     \value InvalidRole The button is invalid.
605     \value AcceptRole Clicking the button causes the dialog to be accepted
606            (e.g. OK).
607     \value RejectRole Clicking the button causes the dialog to be rejected
608            (e.g. Cancel).
609     \value DestructiveRole Clicking the button causes a destructive change
610            (e.g. for Discarding Changes) and closes the dialog.
611     \value ActionRole Clicking the button causes changes to the elements within
612            the dialog.
613     \value HelpRole The button can be clicked to request help.
614     \value YesRole The button is a "Yes"-like button.
615     \value NoRole The button is a "No"-like button.
616     \value ApplyRole The button applies current changes.
617     \value ResetRole The button resets the dialog's fields to default values.
618
619     \omitvalue NRoles
620
621     \sa StandardButton
622 */
623
624 /*!
625     \enum QDialogButtonBox::StandardButton
626
627     These enums describe flags for standard buttons. Each button has a
628     defined \l ButtonRole.
629
630     \value Ok An "OK" button defined with the \l AcceptRole.
631     \value Open An "Open" button defined with the \l AcceptRole.
632     \value Save A "Save" button defined with the \l AcceptRole.
633     \value Cancel A "Cancel" button defined with the \l RejectRole.
634     \value Close A "Close" button defined with the \l RejectRole.
635     \value Discard A "Discard" or "Don't Save" button, depending on the platform,
636                     defined with the \l DestructiveRole.
637     \value Apply An "Apply" button defined with the \l ApplyRole.
638     \value Reset A "Reset" button defined with the \l ResetRole.
639     \value RestoreDefaults A "Restore Defaults" button defined with the \l ResetRole.
640     \value Help A "Help" button defined with the \l HelpRole.
641     \value SaveAll A "Save All" button defined with the \l AcceptRole.
642     \value Yes A "Yes" button defined with the \l YesRole.
643     \value YesToAll A "Yes to All" button defined with the \l YesRole.
644     \value No A "No" button defined with the \l NoRole.
645     \value NoToAll A "No to All" button defined with the \l NoRole.
646     \value Abort An "Abort" button defined with the \l RejectRole.
647     \value Retry A "Retry" button defined with the \l AcceptRole.
648     \value Ignore An "Ignore" button defined with the \l AcceptRole.
649
650     \value NoButton An invalid button.
651
652     \omitvalue FirstButton
653     \omitvalue LastButton
654
655     \sa ButtonRole, standardButtons
656 */
657
658 /*!
659     \enum QDialogButtonBox::ButtonLayout
660
661     This enum describes the layout policy to be used when arranging the buttons
662     contained in the button box.
663
664     \value WinLayout Use a policy appropriate for applications on Windows.
665     \value MacLayout Use a policy appropriate for applications on Mac OS X.
666     \value KdeLayout Use a policy appropriate for applications on KDE.
667     \value GnomeLayout Use a policy appropriate for applications on GNOME.
668
669     The button layout is specified by the \l{style()}{current style}. However,
670     on the X11 platform, it may be influenced by the desktop environment.
671 */
672
673 /*!
674     \fn void QDialogButtonBox::clicked(QAbstractButton *button)
675
676     This signal is emitted when a button inside the button box is clicked. The
677     specific button that was pressed is specified by \a button.
678
679     \sa accepted(), rejected(), helpRequested()
680 */
681
682 /*!
683     \fn void QDialogButtonBox::accepted()
684
685     This signal is emitted when a button inside the button box is clicked, as long
686     as it was defined with the \l AcceptRole or \l YesRole.
687
688     \sa rejected(), clicked(), helpRequested()
689 */
690
691 /*!
692     \fn void QDialogButtonBox::rejected()
693
694     This signal is emitted when a button inside the button box is clicked, as long
695     as it was defined with the \l RejectRole or \l NoRole.
696
697     \sa accepted(), helpRequested(), clicked()
698 */
699
700 /*!
701     \fn void QDialogButtonBox::helpRequested()
702
703     This signal is emitted when a button inside the button box is clicked, as long
704     as it was defined with the \l HelpRole.
705
706     \sa accepted(), rejected(), clicked()
707 */
708
709 /*!
710     \property QDialogButtonBox::orientation
711     \brief the orientation of the button box
712
713     By default, the orientation is horizontal (i.e. the buttons are laid out
714     side by side). The possible orientations are Qt::Horizontal and
715     Qt::Vertical.
716 */
717 Qt::Orientation QDialogButtonBox::orientation() const
718 {
719     return d_func()->orientation;
720 }
721
722 void QDialogButtonBox::setOrientation(Qt::Orientation orientation)
723 {
724     Q_D(QDialogButtonBox);
725     if (orientation == d->orientation)
726         return;
727
728     d->orientation = orientation;
729     d->resetLayout();
730 }
731
732 /*!
733     Clears the button box, deleting all buttons within it.
734
735     \sa removeButton(), addButton()
736 */
737 void QDialogButtonBox::clear()
738 {
739     Q_D(QDialogButtonBox);
740     // Remove the created standard buttons, they should be in the other lists, which will
741     // do the deletion
742     d->standardButtonHash.clear();
743     for (int i = 0; i < NRoles; ++i) {
744         QList<QAbstractButton *> &list = d->buttonLists[i];
745         while (list.count()) {
746             QAbstractButton *button = list.takeAt(0);
747             QObject::disconnect(button, SIGNAL(destroyed()), this, SLOT(_q_handleButtonDestroyed()));
748             delete button;
749         }
750     }
751 }
752
753 /*!
754     Returns a list of all the buttons that have been added to the button box.
755
756     \sa buttonRole(), addButton(), removeButton()
757 */
758 QList<QAbstractButton *> QDialogButtonBox::buttons() const
759 {
760     Q_D(const QDialogButtonBox);
761     QList<QAbstractButton *> finalList;
762     for (int i = 0; i < NRoles; ++i) {
763         const QList<QAbstractButton *> &list = d->buttonLists[i];
764         for (int j = 0; j < list.count(); ++j)
765             finalList.append(list.at(j));
766     }
767     return finalList;
768 }
769
770 /*!
771     Returns the button role for the specified \a button. This function returns
772     \l InvalidRole if \a button is 0 or has not been added to the button box.
773
774     \sa buttons(), addButton()
775 */
776 QDialogButtonBox::ButtonRole QDialogButtonBox::buttonRole(QAbstractButton *button) const
777 {
778     Q_D(const QDialogButtonBox);
779     for (int i = 0; i < NRoles; ++i) {
780         const QList<QAbstractButton *> &list = d->buttonLists[i];
781         for (int j = 0; j < list.count(); ++j) {
782             if (list.at(j) == button)
783                 return ButtonRole(i);
784         }
785     }
786     return InvalidRole;
787 }
788
789 /*!
790     Removes \a button from the button box without deleting it and sets its parent to zero.
791
792     \sa clear(), buttons(), addButton()
793 */
794 void QDialogButtonBox::removeButton(QAbstractButton *button)
795 {
796     Q_D(QDialogButtonBox);
797
798     if (!button)
799         return;
800
801     // Remove it from the standard button hash first and then from the roles
802     if (QPushButton *pushButton = qobject_cast<QPushButton *>(button))
803         d->standardButtonHash.remove(pushButton);
804     for (int i = 0; i < NRoles; ++i) {
805         QList<QAbstractButton *> &list = d->buttonLists[i];
806         for (int j = 0; j < list.count(); ++j) {
807             if (list.at(j) == button) {
808                 list.takeAt(j);
809                 if (!d->internalRemove) {
810                     disconnect(button, SIGNAL(clicked()), this, SLOT(_q_handleButtonClicked()));
811                     disconnect(button, SIGNAL(destroyed()), this, SLOT(_q_handleButtonDestroyed()));
812                 }
813                 break;
814             }
815         }
816     }
817     if (!d->internalRemove)
818         button->setParent(0);
819 }
820
821 /*!
822     Adds the given \a button to the button box with the specified \a role.
823     If the role is invalid, the button is not added.
824
825     If the button has already been added, it is removed and added again with the
826     new role.
827
828     \note The button box takes ownership of the button.
829
830     \sa removeButton(), clear()
831 */
832 void QDialogButtonBox::addButton(QAbstractButton *button, ButtonRole role)
833 {
834     Q_D(QDialogButtonBox);
835     if (role <= InvalidRole || role >= NRoles) {
836         qWarning("QDialogButtonBox::addButton: Invalid ButtonRole, button not added");
837         return;
838     }
839     removeButton(button);
840     button->setParent(this);
841     d->addButton(button, role);
842 }
843
844 /*!
845     Creates a push button with the given \a text, adds it to the button box for the
846     specified \a role, and returns the corresponding push button. If \a role is
847     invalid, no button is created, and zero is returned.
848
849     \sa removeButton(), clear()
850 */
851 QPushButton *QDialogButtonBox::addButton(const QString &text, ButtonRole role)
852 {
853     Q_D(QDialogButtonBox);
854     if (role <= InvalidRole || role >= NRoles) {
855         qWarning("QDialogButtonBox::addButton: Invalid ButtonRole, button not added");
856         return 0;
857     }
858     QPushButton *button = new QPushButton(text, this);
859     d->addButton(button, role);
860     return button;
861 }
862
863 /*!
864     Adds a standard \a button to the button box if it is valid to do so, and returns
865     a push button. If \a button is invalid, it is not added to the button box, and
866     zero is returned.
867
868     \sa removeButton(), clear()
869 */
870 QPushButton *QDialogButtonBox::addButton(StandardButton button)
871 {
872     Q_D(QDialogButtonBox);
873     return d->createButton(button);
874 }
875
876 /*!
877     \property QDialogButtonBox::standardButtons
878     \brief collection of standard buttons in the button box
879
880     This property controls which standard buttons are used by the button box.
881
882     \sa addButton()
883 */
884 void QDialogButtonBox::setStandardButtons(StandardButtons buttons)
885 {
886     Q_D(QDialogButtonBox);
887     // Clear out all the old standard buttons, then recreate them.
888     qDeleteAll(d->standardButtonHash.keys());
889     d->standardButtonHash.clear();
890
891     d->createStandardButtons(buttons);
892 }
893
894 QDialogButtonBox::StandardButtons QDialogButtonBox::standardButtons() const
895 {
896     Q_D(const QDialogButtonBox);
897     StandardButtons standardButtons = NoButton;
898     QHash<QPushButton *, StandardButton>::const_iterator it = d->standardButtonHash.constBegin();
899     while (it != d->standardButtonHash.constEnd()) {
900         standardButtons |= it.value();
901         ++it;
902     }
903     return standardButtons;
904 }
905
906 /*!
907     Returns the QPushButton corresponding to the standard button \a which,
908     or 0 if the standard button doesn't exist in this button box.
909
910     \sa standardButton(), standardButtons(), buttons()
911 */
912 QPushButton *QDialogButtonBox::button(StandardButton which) const
913 {
914     Q_D(const QDialogButtonBox);
915     return d->standardButtonHash.key(which);
916 }
917
918 /*!
919     Returns the standard button enum value corresponding to the given \a button,
920     or NoButton if the given \a button isn't a standard button.
921
922     \sa button(), buttons(), standardButtons()
923 */
924 QDialogButtonBox::StandardButton QDialogButtonBox::standardButton(QAbstractButton *button) const
925 {
926     Q_D(const QDialogButtonBox);
927     return d->standardButtonHash.value(static_cast<QPushButton *>(button));
928 }
929
930 void QDialogButtonBoxPrivate::_q_handleButtonClicked()
931 {
932     Q_Q(QDialogButtonBox);
933     if (QAbstractButton *button = qobject_cast<QAbstractButton *>(q->sender())) {
934         emit q->clicked(button);
935
936         switch (q->buttonRole(button)) {
937         case AcceptRole:
938         case YesRole:
939             emit q->accepted();
940             break;
941         case RejectRole:
942         case NoRole:
943             emit q->rejected();
944             break;
945         case HelpRole:
946             emit q->helpRequested();
947             break;
948         default:
949             break;
950         }
951     }
952 }
953
954 void QDialogButtonBoxPrivate::_q_handleButtonDestroyed()
955 {
956     Q_Q(QDialogButtonBox);
957     if (QObject *object = q->sender()) {
958         QBoolBlocker skippy(internalRemove);
959         q->removeButton(static_cast<QAbstractButton *>(object));
960     }
961 }
962
963 /*!
964     \property QDialogButtonBox::centerButtons
965     \brief whether the buttons in the button box are centered
966
967     By default, this property is \c false. This behavior is appopriate
968     for most types of dialogs. A notable exception is message boxes
969     on most platforms (e.g. Windows), where the button box is
970     centered horizontally.
971
972     \sa QMessageBox
973 */
974 void QDialogButtonBox::setCenterButtons(bool center)
975 {
976     Q_D(QDialogButtonBox);
977     if (d->center != center) {
978         d->center = center;
979         d->resetLayout();
980     }
981 }
982
983 bool QDialogButtonBox::centerButtons() const
984 {
985     Q_D(const QDialogButtonBox);
986     return d->center;
987 }
988
989 /*!
990     \reimp
991 */
992 void QDialogButtonBox::changeEvent(QEvent *event)
993 {
994     typedef QHash<QPushButton *, QDialogButtonBox::StandardButton> StandardButtonHash;
995
996     Q_D(QDialogButtonBox);
997     switch (event->type()) {
998     case QEvent::StyleChange:  // Propagate style
999         if (!d->standardButtonHash.empty()) {
1000             QStyle *newStyle = style();
1001             const StandardButtonHash::iterator end = d->standardButtonHash.end();
1002             for (StandardButtonHash::iterator it = d->standardButtonHash.begin(); it != end; ++it)
1003                 it.key()->setStyle(newStyle);
1004         }
1005         // fallthrough intended
1006 #ifdef Q_OS_MAC
1007     case QEvent::MacSizeChange:
1008 #endif
1009         d->resetLayout();
1010         QWidget::changeEvent(event);
1011         break;
1012     default:
1013         QWidget::changeEvent(event);
1014         break;
1015     }
1016 }
1017
1018 /*!
1019     \reimp
1020 */
1021 bool QDialogButtonBox::event(QEvent *event)
1022 {
1023     Q_D(QDialogButtonBox);
1024     if (event->type() == QEvent::Show) {
1025         QList<QAbstractButton *> acceptRoleList = d->buttonLists[AcceptRole];
1026         QPushButton *firstAcceptButton = acceptRoleList.isEmpty() ? 0 : qobject_cast<QPushButton *>(acceptRoleList.at(0));
1027         bool hasDefault = false;
1028         QWidget *dialog = 0;
1029         QWidget *p = this;
1030         while (p && !p->isWindow()) {
1031             p = p->parentWidget();
1032             if ((dialog = qobject_cast<QDialog *>(p)))
1033                 break;
1034         }
1035
1036         foreach (QPushButton *pb, (dialog ? dialog : this)->findChildren<QPushButton *>()) {
1037             if (pb->isDefault() && pb != firstAcceptButton) {
1038                 hasDefault = true;
1039                 break;
1040             }
1041         }
1042         if (!hasDefault && firstAcceptButton)
1043             firstAcceptButton->setDefault(true);
1044     }else if (event->type() == QEvent::LanguageChange) {
1045         d->retranslateStrings();
1046     }
1047     return QWidget::event(event);
1048 }
1049
1050 QT_END_NAMESPACE
1051
1052 #include "moc_qdialogbuttonbox.cpp"