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