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