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

source code of qtbase/src/widgets/widgets/qdialogbuttonbox.cpp