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 <QtWidgets/qtwidgetsglobal.h>
41#if QT_CONFIG(colordialog)
42#include "qcolordialog.h"
43#endif
44#if QT_CONFIG(fontdialog)
45#include "qfontdialog.h"
46#endif
47#if QT_CONFIG(filedialog)
48#include "qfiledialog.h"
49#endif
50
51#include "qevent.h"
52#include "qdesktopwidget.h"
53#include <private/qdesktopwidget_p.h>
54#include "qapplication.h"
55#include "qlayout.h"
56#if QT_CONFIG(sizegrip)
57#include "qsizegrip.h"
58#endif
59#if QT_CONFIG(whatsthis)
60#include "qwhatsthis.h"
61#endif
62#if QT_CONFIG(menu)
63#include "qmenu.h"
64#endif
65#include "qcursor.h"
66#if QT_CONFIG(messagebox)
67#include "qmessagebox.h"
68#endif
69#if QT_CONFIG(errormessage)
70#include "qerrormessage.h"
71#endif
72#include <qpa/qplatformtheme.h>
73#include "private/qdialog_p.h"
74#include "private/qguiapplication_p.h"
75#ifndef QT_NO_ACCESSIBILITY
76#include "qaccessible.h"
77#endif
78
79QT_BEGIN_NAMESPACE
80
81static inline int themeDialogType(const QDialog *dialog)
82{
83#if QT_CONFIG(filedialog)
84 if (qobject_cast<const QFileDialog *>(object: dialog))
85 return QPlatformTheme::FileDialog;
86#endif
87#if QT_CONFIG(colordialog)
88 if (qobject_cast<const QColorDialog *>(object: dialog))
89 return QPlatformTheme::ColorDialog;
90#endif
91#if QT_CONFIG(fontdialog)
92 if (qobject_cast<const QFontDialog *>(object: dialog))
93 return QPlatformTheme::FontDialog;
94#endif
95#if QT_CONFIG(messagebox)
96 if (qobject_cast<const QMessageBox *>(object: dialog))
97 return QPlatformTheme::MessageDialog;
98#endif
99#if QT_CONFIG(errormessage)
100 if (qobject_cast<const QErrorMessage *>(object: dialog))
101 return QPlatformTheme::MessageDialog;
102#endif
103#if !QT_CONFIG(filedialog) && !QT_CONFIG(colordialog) && !QT_CONFIG(fontdialog) && \
104 !QT_CONFIG(messagebox) && !QT_CONFIG(errormessage)
105 Q_UNUSED(dialog);
106#endif
107 return -1;
108}
109
110QDialogPrivate::~QDialogPrivate()
111{
112 delete m_platformHelper;
113}
114
115QPlatformDialogHelper *QDialogPrivate::platformHelper() const
116{
117 // Delayed creation of the platform, ensuring that
118 // that qobject_cast<> on the dialog works in the plugin.
119 if (!m_platformHelperCreated && canBeNativeDialog()) {
120 m_platformHelperCreated = true;
121 QDialogPrivate *ncThis = const_cast<QDialogPrivate *>(this);
122 QDialog *dialog = ncThis->q_func();
123 const int type = themeDialogType(dialog);
124 if (type >= 0) {
125 m_platformHelper = QGuiApplicationPrivate::platformTheme()
126 ->createPlatformDialogHelper(type: static_cast<QPlatformTheme::DialogType>(type));
127 if (m_platformHelper) {
128 QObject::connect(sender: m_platformHelper, SIGNAL(accept()), receiver: dialog, SLOT(accept()));
129 QObject::connect(sender: m_platformHelper, SIGNAL(reject()), receiver: dialog, SLOT(reject()));
130 ncThis->initHelper(m_platformHelper);
131 }
132 }
133 }
134 return m_platformHelper;
135}
136
137bool QDialogPrivate::canBeNativeDialog() const
138{
139 QDialogPrivate *ncThis = const_cast<QDialogPrivate *>(this);
140 QDialog *dialog = ncThis->q_func();
141 const int type = themeDialogType(dialog);
142 if (type >= 0)
143 return QGuiApplicationPrivate::platformTheme()
144 ->usePlatformNativeDialog(type: static_cast<QPlatformTheme::DialogType>(type));
145 return false;
146}
147
148/*!
149 \internal
150
151 Properly hides dialog and sets the \a resultCode.
152 */
153void QDialogPrivate::hide(int resultCode)
154{
155 Q_Q(QDialog);
156
157 q->setResult(resultCode);
158 q->hide();
159
160 close_helper(mode: QWidgetPrivate::CloseNoEvent);
161 resetModalitySetByOpen();
162}
163
164/*!
165 \internal
166
167 Emits finished() signal with \a resultCode. If the \a dialogCode
168 is equal to 0 emits rejected(), if the \a dialogCode is equal to
169 1 emits accepted().
170 */
171void QDialogPrivate::finalize(int resultCode, int dialogCode)
172{
173 Q_Q(QDialog);
174
175 if (dialogCode == QDialog::Accepted)
176 emit q->accepted();
177 else if (dialogCode == QDialog::Rejected)
178 emit q->rejected();
179
180 emit q->finished(result: resultCode);
181}
182
183QWindow *QDialogPrivate::transientParentWindow() const
184{
185 Q_Q(const QDialog);
186 if (const QWidget *parent = q->nativeParentWidget())
187 return parent->windowHandle();
188 else if (q->windowHandle())
189 return q->windowHandle()->transientParent();
190 return nullptr;
191}
192
193bool QDialogPrivate::setNativeDialogVisible(bool visible)
194{
195 if (QPlatformDialogHelper *helper = platformHelper()) {
196 if (visible) {
197 Q_Q(QDialog);
198 helperPrepareShow(helper);
199 nativeDialogInUse = helper->show(windowFlags: q->windowFlags(), windowModality: q->windowModality(), parent: transientParentWindow());
200 } else if (nativeDialogInUse) {
201 helper->hide();
202 }
203 }
204 return nativeDialogInUse;
205}
206
207QVariant QDialogPrivate::styleHint(QPlatformDialogHelper::StyleHint hint) const
208{
209 if (const QPlatformDialogHelper *helper = platformHelper())
210 return helper->styleHint(hint);
211 return QPlatformDialogHelper::defaultStyleHint(hint);
212}
213
214void QDialogPrivate::deletePlatformHelper()
215{
216 delete m_platformHelper;
217 m_platformHelper = nullptr;
218 m_platformHelperCreated = false;
219 nativeDialogInUse = false;
220}
221
222/*!
223 \class QDialog
224 \brief The QDialog class is the base class of dialog windows.
225
226 \ingroup dialog-classes
227 \ingroup abstractwidgets
228 \inmodule QtWidgets
229
230 A dialog window is a top-level window mostly used for short-term
231 tasks and brief communications with the user. QDialogs may be
232 modal or modeless. QDialogs can
233 provide a \l{#return}{return value}, and they can have \l{#default}{default buttons}. QDialogs can also have a QSizeGrip in their
234 lower-right corner, using setSizeGripEnabled().
235
236 Note that QDialog (and any other widget that has type \c Qt::Dialog) uses
237 the parent widget slightly differently from other classes in Qt. A dialog is
238 always a top-level widget, but if it has a parent, its default location is
239 centered on top of the parent's top-level widget (if it is not top-level
240 itself). It will also share the parent's taskbar entry.
241
242 Use the overload of the QWidget::setParent() function to change
243 the ownership of a QDialog widget. This function allows you to
244 explicitly set the window flags of the reparented widget; using
245 the overloaded function will clear the window flags specifying the
246 window-system properties for the widget (in particular it will
247 reset the Qt::Dialog flag).
248
249 \note The parent relationship of the dialog does \e{not} imply
250 that the dialog will always be stacked on top of the parent
251 window. To ensure that the dialog is always on top, make the
252 dialog modal. This also applies for child windows of the dialog
253 itself. To ensure that child windows of the dialog stay on top
254 of the dialog, make the child windows modal as well.
255
256 \section1 Modal Dialogs
257
258 A \b{modal} dialog is a dialog that blocks input to other
259 visible windows in the same application. Dialogs that are used to
260 request a file name from the user or that are used to set
261 application preferences are usually modal. Dialogs can be
262 \l{Qt::ApplicationModal}{application modal} (the default) or
263 \l{Qt::WindowModal}{window modal}.
264
265 When an application modal dialog is opened, the user must finish
266 interacting with the dialog and close it before they can access
267 any other window in the application. Window modal dialogs only
268 block access to the window associated with the dialog, allowing
269 the user to continue to use other windows in an application.
270
271 The most common way to display a modal dialog is to call its
272 exec() function. When the user closes the dialog, exec() will
273 provide a useful \l{#return}{return value}. To close the dialog
274 and return the appropriate value, you must connect a default button,
275 e.g. an \uicontrol OK button to the accept() slot and a
276 \uicontrol Cancel button to the reject() slot. Alternatively, you
277 can call the done() slot with \c Accepted or \c Rejected.
278
279 An alternative is to call setModal(true) or setWindowModality(),
280 then show(). Unlike exec(), show() returns control to the caller
281 immediately. Calling setModal(true) is especially useful for
282 progress dialogs, where the user must have the ability to interact
283 with the dialog, e.g. to cancel a long running operation. If you
284 use show() and setModal(true) together to perform a long operation,
285 you must call QCoreApplication::processEvents() periodically during
286 processing to enable the user to interact with the dialog. (See
287 QProgressDialog.)
288
289 \section1 Modeless Dialogs
290
291 A \b{modeless} dialog is a dialog that operates
292 independently of other windows in the same application. Find and
293 replace dialogs in word-processors are often modeless to allow the
294 user to interact with both the application's main window and with
295 the dialog.
296
297 Modeless dialogs are displayed using show(), which returns control
298 to the caller immediately.
299
300 If you invoke the \l{QWidget::show()}{show()} function after hiding
301 a dialog, the dialog will be displayed in its original position. This is
302 because the window manager decides the position for windows that
303 have not been explicitly placed by the programmer. To preserve the
304 position of a dialog that has been moved by the user, save its position
305 in your \l{QWidget::closeEvent()}{closeEvent()} handler and then
306 move the dialog to that position, before showing it again.
307
308 \target default
309 \section1 Default Button
310
311 A dialog's \e default button is the button that's pressed when the
312 user presses Enter (Return). This button is used to signify that
313 the user accepts the dialog's settings and wants to close the
314 dialog. Use QPushButton::setDefault(), QPushButton::isDefault()
315 and QPushButton::autoDefault() to set and control the dialog's
316 default button.
317
318 \target escapekey
319 \section1 Escape Key
320
321 If the user presses the Esc key in a dialog, QDialog::reject()
322 will be called. This will cause the window to close: The \l{QCloseEvent}{close event} cannot be \l{QEvent::ignore()}{ignored}.
323
324 \section1 Extensibility
325
326 Extensibility is the ability to show the dialog in two ways: a
327 partial dialog that shows the most commonly used options, and a
328 full dialog that shows all the options. Typically an extensible
329 dialog will initially appear as a partial dialog, but with a
330 \uicontrol More toggle button. If the user presses the \uicontrol More button down,
331 the dialog is expanded. The \l{Extension Example} shows how to achieve
332 extensible dialogs using Qt.
333
334 \target return
335 \section1 Return Value (Modal Dialogs)
336
337 Modal dialogs are often used in situations where a return value is
338 required, e.g. to indicate whether the user pressed \uicontrol OK or
339 \uicontrol Cancel. A dialog can be closed by calling the accept() or the
340 reject() slots, and exec() will return \c Accepted or \c Rejected
341 as appropriate. The exec() call returns the result of the dialog.
342 The result is also available from result() if the dialog has not
343 been destroyed.
344
345 In order to modify your dialog's close behavior, you can reimplement
346 the functions accept(), reject() or done(). The
347 \l{QWidget::closeEvent()}{closeEvent()} function should only be
348 reimplemented to preserve the dialog's position or to override the
349 standard close or reject behavior.
350
351 \target examples
352 \section1 Code Examples
353
354 A modal dialog:
355
356 \snippet dialogs/dialogs.cpp 1
357
358 A modeless dialog:
359
360 \snippet dialogs/dialogs.cpp 0
361
362 \sa QDialogButtonBox, QTabWidget, QWidget, QProgressDialog,
363 {fowler}{GUI Design Handbook: Dialogs, Standard}, {Extension Example},
364 {Standard Dialogs Example}
365*/
366
367/*! \enum QDialog::DialogCode
368
369 The value returned by a modal dialog.
370
371 \value Accepted
372 \value Rejected
373*/
374
375/*!
376 \property QDialog::sizeGripEnabled
377 \brief whether the size grip is enabled
378
379 A QSizeGrip is placed in the bottom-right corner of the dialog when this
380 property is enabled. By default, the size grip is disabled.
381*/
382
383
384/*!
385 Constructs a dialog with parent \a parent.
386
387 A dialog is always a top-level widget, but if it has a parent, its
388 default location is centered on top of the parent. It will also
389 share the parent's taskbar entry.
390
391 The widget flags \a f are passed on to the QWidget constructor.
392 If, for example, you don't want a What's This button in the title bar
393 of the dialog, pass Qt::WindowTitleHint | Qt::WindowSystemMenuHint in \a f.
394
395 \sa QWidget::setWindowFlags()
396*/
397
398QDialog::QDialog(QWidget *parent, Qt::WindowFlags f)
399 : QWidget(*new QDialogPrivate, parent,
400 f | ((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : Qt::WindowType(0)))
401{
402}
403
404/*!
405 \overload
406 \internal
407*/
408QDialog::QDialog(QDialogPrivate &dd, QWidget *parent, Qt::WindowFlags f)
409 : QWidget(dd, parent, f | ((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : Qt::WindowType(0)))
410{
411}
412
413/*!
414 Destroys the QDialog, deleting all its children.
415*/
416
417QDialog::~QDialog()
418{
419 QT_TRY {
420 // Need to hide() here, as our (to-be) overridden hide()
421 // will not be called in ~QWidget.
422 hide();
423 } QT_CATCH(...) {
424 // we're in the destructor - just swallow the exception
425 }
426}
427
428/*!
429 \internal
430 This function is called by the push button \a pushButton when it
431 becomes the default button. If \a pushButton is \nullptr, the dialogs
432 default default button becomes the default button. This is what a
433 push button calls when it loses focus.
434*/
435#if QT_CONFIG(pushbutton)
436void QDialogPrivate::setDefault(QPushButton *pushButton)
437{
438 Q_Q(QDialog);
439 bool hasMain = false;
440 QList<QPushButton*> list = q->findChildren<QPushButton*>();
441 for (int i=0; i<list.size(); ++i) {
442 QPushButton *pb = list.at(i);
443 if (pb->window() == q) {
444 if (pb == mainDef)
445 hasMain = true;
446 if (pb != pushButton)
447 pb->setDefault(false);
448 }
449 }
450 if (!pushButton && hasMain)
451 mainDef->setDefault(true);
452 if (!hasMain)
453 mainDef = pushButton;
454}
455
456/*!
457 \internal
458 This function sets the default default push button to \a pushButton.
459 This function is called by QPushButton::setDefault().
460*/
461void QDialogPrivate::setMainDefault(QPushButton *pushButton)
462{
463 mainDef = nullptr;
464 setDefault(pushButton);
465}
466
467/*!
468 \internal
469 Hides the default button indicator. Called when non auto-default
470 push button get focus.
471 */
472void QDialogPrivate::hideDefault()
473{
474 Q_Q(QDialog);
475 QList<QPushButton*> list = q->findChildren<QPushButton*>();
476 for (int i=0; i<list.size(); ++i) {
477 list.at(i)->setDefault(false);
478 }
479}
480#endif
481
482void QDialogPrivate::resetModalitySetByOpen()
483{
484 Q_Q(QDialog);
485 if (resetModalityTo != -1 && !q->testAttribute(attribute: Qt::WA_SetWindowModality)) {
486 // open() changed the window modality and the user didn't touch it afterwards; restore it
487 q->setWindowModality(Qt::WindowModality(resetModalityTo));
488 q->setAttribute(Qt::WA_SetWindowModality, on: wasModalitySet);
489#ifdef Q_OS_MAC
490 Q_ASSERT(resetModalityTo != Qt::WindowModal);
491 q->setParent(q->parentWidget(), Qt::Dialog);
492#endif
493 }
494 resetModalityTo = -1;
495}
496
497/*!
498 In general returns the modal dialog's result code, \c Accepted or
499 \c Rejected.
500
501 \note When called on a QMessageBox instance, the returned value is a
502 value of the \l QMessageBox::StandardButton enum.
503
504 Do not call this function if the dialog was constructed with the
505 Qt::WA_DeleteOnClose attribute.
506*/
507int QDialog::result() const
508{
509 Q_D(const QDialog);
510 return d->rescode;
511}
512
513/*!
514 \fn void QDialog::setResult(int i)
515
516 Sets the modal dialog's result code to \a i.
517
518 \note We recommend that you use one of the values defined by
519 QDialog::DialogCode.
520*/
521void QDialog::setResult(int r)
522{
523 Q_D(QDialog);
524 d->rescode = r;
525}
526
527/*!
528 \since 4.5
529
530 Shows the dialog as a \l{QDialog#Modal Dialogs}{window modal dialog},
531 returning immediately.
532
533 \sa exec(), show(), result(), setWindowModality()
534*/
535void QDialog::open()
536{
537 Q_D(QDialog);
538
539 Qt::WindowModality modality = windowModality();
540 if (modality != Qt::WindowModal) {
541 d->resetModalityTo = modality;
542 d->wasModalitySet = testAttribute(attribute: Qt::WA_SetWindowModality);
543 setWindowModality(Qt::WindowModal);
544 setAttribute(Qt::WA_SetWindowModality, on: false);
545#ifdef Q_OS_MAC
546 setParent(parentWidget(), Qt::Sheet);
547#endif
548 }
549
550 setResult(0);
551 show();
552}
553
554/*!
555 Shows the dialog as a \l{QDialog#Modal Dialogs}{modal dialog},
556 blocking until the user closes it. The function returns a \l
557 DialogCode result.
558
559 If the dialog is \l{Qt::ApplicationModal}{application modal}, users cannot
560 interact with any other window in the same application until they close
561 the dialog. If the dialog is \l{Qt::ApplicationModal}{window modal}, only
562 interaction with the parent window is blocked while the dialog is open.
563 By default, the dialog is application modal.
564
565 \note Avoid using this function; instead, use \c{open()}. Unlike exec(),
566 open() is asynchronous, and does not spin an additional event loop. This
567 prevents a series of dangerous bugs from happening (e.g. deleting the
568 dialog's parent while the dialog is open via exec()). When using open() you
569 can connect to the finished() signal of QDialog to be notified when the
570 dialog is closed.
571
572 \sa open(), show(), result(), setWindowModality()
573*/
574
575int QDialog::exec()
576{
577 Q_D(QDialog);
578
579 if (Q_UNLIKELY(d->eventLoop)) {
580 qWarning(msg: "QDialog::exec: Recursive call detected");
581 return -1;
582 }
583
584 bool deleteOnClose = testAttribute(attribute: Qt::WA_DeleteOnClose);
585 setAttribute(Qt::WA_DeleteOnClose, on: false);
586
587 d->resetModalitySetByOpen();
588
589 bool wasShowModal = testAttribute(attribute: Qt::WA_ShowModal);
590 setAttribute(Qt::WA_ShowModal, on: true);
591 setResult(0);
592
593 show();
594
595 QPointer<QDialog> guard = this;
596 if (d->nativeDialogInUse) {
597 d->platformHelper()->exec();
598 } else {
599 QEventLoop eventLoop;
600 d->eventLoop = &eventLoop;
601 (void) eventLoop.exec(flags: QEventLoop::DialogExec);
602 }
603 if (guard.isNull())
604 return QDialog::Rejected;
605 d->eventLoop = nullptr;
606
607 setAttribute(Qt::WA_ShowModal, on: wasShowModal);
608
609 int res = result();
610 if (d->nativeDialogInUse)
611 d->helperDone(static_cast<QDialog::DialogCode>(res), d->platformHelper());
612 if (deleteOnClose)
613 delete this;
614 return res;
615}
616
617/*!
618 Closes the dialog and sets its result code to \a r. The finished() signal
619 will emit \a r; if \a r is QDialog::Accepted or QDialog::Rejected, the
620 accepted() or the rejected() signals will also be emitted, respectively.
621
622 If this dialog is shown with exec(), done() also causes the local event loop
623 to finish, and exec() to return \a r.
624
625 As with QWidget::close(), done() deletes the dialog if the
626 Qt::WA_DeleteOnClose flag is set. If the dialog is the application's
627 main widget, the application terminates. If the dialog is the
628 last window closed, the QApplication::lastWindowClosed() signal is
629 emitted.
630
631 \sa accept(), reject(), QApplication::activeWindow(), QCoreApplication::quit()
632*/
633
634void QDialog::done(int r)
635{
636 Q_D(QDialog);
637 d->hide(resultCode: r);
638 d->finalize(resultCode: r, dialogCode: r);
639}
640
641/*!
642 Hides the modal dialog and sets the result code to \c Accepted.
643
644 \sa reject(), done()
645*/
646
647void QDialog::accept()
648{
649 done(r: Accepted);
650}
651
652/*!
653 Hides the modal dialog and sets the result code to \c Rejected.
654
655 \sa accept(), done()
656*/
657
658void QDialog::reject()
659{
660 done(r: Rejected);
661}
662
663/*! \reimp */
664bool QDialog::eventFilter(QObject *o, QEvent *e)
665{
666 return QWidget::eventFilter(watched: o, event: e);
667}
668
669/*****************************************************************************
670 Event handlers
671 *****************************************************************************/
672
673#ifndef QT_NO_CONTEXTMENU
674/*! \reimp */
675void QDialog::contextMenuEvent(QContextMenuEvent *e)
676{
677#if !QT_CONFIG(whatsthis) || !QT_CONFIG(menu)
678 Q_UNUSED(e);
679#else
680 QWidget *w = childAt(p: e->pos());
681 if (!w) {
682 w = rect().contains(p: e->pos()) ? this : nullptr;
683 if (!w)
684 return;
685 }
686 while (w && w->whatsThis().size() == 0 && !w->testAttribute(attribute: Qt::WA_CustomWhatsThis))
687 w = w->isWindow() ? nullptr : w->parentWidget();
688 if (w) {
689 QPointer<QMenu> p = new QMenu(this);
690 QAction *wt = p.data()->addAction(text: tr(s: "What's This?"));
691 if (p.data()->exec(pos: e->globalPos()) == wt) {
692 QHelpEvent e(QEvent::WhatsThis, w->rect().center(),
693 w->mapToGlobal(w->rect().center()));
694 QCoreApplication::sendEvent(receiver: w, event: &e);
695 }
696 delete p.data();
697 }
698#endif
699}
700#endif // QT_NO_CONTEXTMENU
701
702/*! \reimp */
703void QDialog::keyPressEvent(QKeyEvent *e)
704{
705#ifndef QT_NO_SHORTCUT
706 // Calls reject() if Escape is pressed. Simulates a button
707 // click for the default button if Enter is pressed. Move focus
708 // for the arrow keys. Ignore the rest.
709 if (e->matches(key: QKeySequence::Cancel)) {
710 reject();
711 } else
712#endif
713 if (!e->modifiers() || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) {
714 switch (e->key()) {
715#if QT_CONFIG(pushbutton)
716 case Qt::Key_Enter:
717 case Qt::Key_Return: {
718 QList<QPushButton*> list = findChildren<QPushButton*>();
719 for (int i=0; i<list.size(); ++i) {
720 QPushButton *pb = list.at(i);
721 if (pb->isDefault() && pb->isVisible()) {
722 if (pb->isEnabled())
723 pb->click();
724 return;
725 }
726 }
727 }
728 break;
729#endif
730 default:
731 e->ignore();
732 return;
733 }
734 } else {
735 e->ignore();
736 }
737}
738
739/*! \reimp */
740void QDialog::closeEvent(QCloseEvent *e)
741{
742#if QT_CONFIG(whatsthis)
743 if (isModal() && QWhatsThis::inWhatsThisMode())
744 QWhatsThis::leaveWhatsThisMode();
745#endif
746 if (isVisible()) {
747 QPointer<QObject> that = this;
748 reject();
749 if (that && isVisible())
750 e->ignore();
751 } else {
752 e->accept();
753 }
754}
755
756/*****************************************************************************
757 Geometry management.
758 *****************************************************************************/
759
760/*! \reimp
761*/
762
763void QDialog::setVisible(bool visible)
764{
765 Q_D(QDialog);
766 if (!testAttribute(attribute: Qt::WA_DontShowOnScreen) && d->canBeNativeDialog() && d->setNativeDialogVisible(visible))
767 return;
768
769 // We should not block windows by the invisible modal dialog
770 // if a platform-specific dialog is implemented as an in-process
771 // Qt window, because in this case it will also be blocked.
772 const bool dontBlockWindows = testAttribute(attribute: Qt::WA_DontShowOnScreen)
773 && d->styleHint(hint: QPlatformDialogHelper::DialogIsQtWindow).toBool();
774 Qt::WindowModality oldModality;
775 bool wasModalitySet;
776
777 if (dontBlockWindows) {
778 oldModality = windowModality();
779 wasModalitySet = testAttribute(attribute: Qt::WA_SetWindowModality);
780 setWindowModality(Qt::NonModal);
781 }
782
783 if (visible) {
784 if (testAttribute(attribute: Qt::WA_WState_ExplicitShowHide) && !testAttribute(attribute: Qt::WA_WState_Hidden))
785 return;
786
787 QWidget::setVisible(visible);
788#if QT_DEPRECATED_SINCE(5, 13)
789QT_WARNING_PUSH
790QT_WARNING_DISABLE_DEPRECATED
791 showExtension(d->doShowExtension);
792QT_WARNING_POP
793#endif
794 QWidget *fw = window()->focusWidget();
795 if (!fw)
796 fw = this;
797
798 /*
799 The following block is to handle a special case, and does not
800 really follow propper logic in concern of autoDefault and TAB
801 order. However, it's here to ease usage for the users. If a
802 dialog has a default QPushButton, and first widget in the TAB
803 order also is a QPushButton, then we give focus to the main
804 default QPushButton. This simplifies code for the developers,
805 and actually catches most cases... If not, then they simply
806 have to use [widget*]->setFocus() themselves...
807 */
808#if QT_CONFIG(pushbutton)
809 if (d->mainDef && fw->focusPolicy() == Qt::NoFocus) {
810 QWidget *first = fw;
811 while ((first = first->nextInFocusChain()) != fw && first->focusPolicy() == Qt::NoFocus)
812 ;
813 if (first != d->mainDef && qobject_cast<QPushButton*>(object: first))
814 d->mainDef->setFocus();
815 }
816 if (!d->mainDef && isWindow()) {
817 QWidget *w = fw;
818 while ((w = w->nextInFocusChain()) != fw) {
819 QPushButton *pb = qobject_cast<QPushButton *>(object: w);
820 if (pb && pb->autoDefault() && pb->focusPolicy() != Qt::NoFocus) {
821 pb->setDefault(true);
822 break;
823 }
824 }
825 }
826#endif
827 if (fw && !fw->hasFocus()) {
828 QFocusEvent e(QEvent::FocusIn, Qt::TabFocusReason);
829 QCoreApplication::sendEvent(receiver: fw, event: &e);
830 }
831
832#ifndef QT_NO_ACCESSIBILITY
833 QAccessibleEvent event(this, QAccessible::DialogStart);
834 QAccessible::updateAccessibility(event: &event);
835#endif
836
837 } else {
838 if (testAttribute(attribute: Qt::WA_WState_ExplicitShowHide) && testAttribute(attribute: Qt::WA_WState_Hidden))
839 return;
840
841#ifndef QT_NO_ACCESSIBILITY
842 if (isVisible()) {
843 QAccessibleEvent event(this, QAccessible::DialogEnd);
844 QAccessible::updateAccessibility(event: &event);
845 }
846#endif
847
848 // Reimplemented to exit a modal event loop when the dialog is hidden.
849 QWidget::setVisible(visible);
850 if (d->eventLoop)
851 d->eventLoop->exit();
852 }
853
854 if (dontBlockWindows) {
855 setWindowModality(oldModality);
856 setAttribute(Qt::WA_SetWindowModality, on: wasModalitySet);
857 }
858
859#if QT_CONFIG(pushbutton)
860 const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
861 if (d->mainDef && isActiveWindow()
862 && theme->themeHint(hint: QPlatformTheme::DialogSnapToDefaultButton).toBool())
863 QCursor::setPos(d->mainDef->mapToGlobal(d->mainDef->rect().center()));
864#endif
865}
866
867/*!\reimp */
868void QDialog::showEvent(QShowEvent *event)
869{
870 if (!event->spontaneous() && !testAttribute(attribute: Qt::WA_Moved)) {
871 Qt::WindowStates state = windowState();
872 adjustPosition(parentWidget());
873 setAttribute(Qt::WA_Moved, on: false); // not really an explicit position
874 if (state != windowState())
875 setWindowState(state);
876 }
877}
878
879/*! \internal */
880void QDialog::adjustPosition(QWidget* w)
881{
882
883 if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme())
884 if (theme->themeHint(hint: QPlatformTheme::WindowAutoPlacement).toBool())
885 return;
886 QPoint p(0, 0);
887 int extraw = 0, extrah = 0, scrn = 0;
888 if (w)
889 w = w->window();
890 QRect desk;
891 if (w) {
892 scrn = QDesktopWidgetPrivate::screenNumber(widget: w);
893 } else if (QDesktopWidgetPrivate::isVirtualDesktop()) {
894 scrn = QDesktopWidgetPrivate::screenNumber(QCursor::pos());
895 } else {
896 scrn = QDesktopWidgetPrivate::screenNumber(widget: this);
897 }
898 desk = QDesktopWidgetPrivate::availableGeometry(screen: scrn);
899
900 QWidgetList list = QApplication::topLevelWidgets();
901 for (int i = 0; (extraw == 0 || extrah == 0) && i < list.size(); ++i) {
902 QWidget * current = list.at(i);
903 if (current->isVisible()) {
904 int framew = current->geometry().x() - current->x();
905 int frameh = current->geometry().y() - current->y();
906
907 extraw = qMax(a: extraw, b: framew);
908 extrah = qMax(a: extrah, b: frameh);
909 }
910 }
911
912 // sanity check for decoration frames. With embedding, we
913 // might get extraordinary values
914 if (extraw == 0 || extrah == 0 || extraw >= 10 || extrah >= 40) {
915 extrah = 40;
916 extraw = 10;
917 }
918
919
920 if (w) {
921 // Use pos() if the widget is embedded into a native window
922 QPoint pp;
923 if (w->windowHandle() && qvariant_cast<WId>(v: w->windowHandle()->property(name: "_q_embedded_native_parent_handle")))
924 pp = w->pos();
925 else
926 pp = w->mapToGlobal(QPoint(0,0));
927 p = QPoint(pp.x() + w->width()/2,
928 pp.y() + w->height()/ 2);
929 } else {
930 // p = middle of the desktop
931 p = QPoint(desk.x() + desk.width()/2, desk.y() + desk.height()/2);
932 }
933
934 // p = origin of this
935 p = QPoint(p.x()-width()/2 - extraw,
936 p.y()-height()/2 - extrah);
937
938
939 if (p.x() + extraw + width() > desk.x() + desk.width())
940 p.setX(desk.x() + desk.width() - width() - extraw);
941 if (p.x() < desk.x())
942 p.setX(desk.x());
943
944 if (p.y() + extrah + height() > desk.y() + desk.height())
945 p.setY(desk.y() + desk.height() - height() - extrah);
946 if (p.y() < desk.y())
947 p.setY(desk.y());
948
949 // QTBUG-52735: Manually set the correct target screen since scaling in a
950 // subsequent call to QWindow::resize() may otherwise use the wrong factor
951 // if the screen changed notification is still in an event queue.
952 if (scrn >= 0) {
953 if (QWindow *window = windowHandle())
954 window->setScreen(QGuiApplication::screens().at(i: scrn));
955 }
956
957 move(p);
958}
959
960#if QT_DEPRECATED_SINCE(5, 13)
961/*!
962 \obsolete
963
964 If \a orientation is Qt::Horizontal, the extension will be displayed
965 to the right of the dialog's main area. If \a orientation is
966 Qt::Vertical, the extension will be displayed below the dialog's main
967 area.
968
969 Instead of using this functionality, we recommend that you simply call
970 show() or hide() on the part of the dialog that you want to use as an
971 extension. See the \l{Extension Example} for details.
972
973 \sa setExtension()
974*/
975void QDialog::setOrientation(Qt::Orientation orientation)
976{
977 Q_D(QDialog);
978 d->orientation = orientation;
979}
980
981/*!
982 \obsolete
983
984 Returns the dialog's extension orientation.
985
986 Instead of using this functionality, we recommend that you simply call
987 show() or hide() on the part of the dialog that you want to use as an
988 extension. See the \l{Extension Example} for details.
989
990 \sa extension()
991*/
992Qt::Orientation QDialog::orientation() const
993{
994 Q_D(const QDialog);
995 return d->orientation;
996}
997
998/*!
999 \obsolete
1000
1001 Sets the widget, \a extension, to be the dialog's extension,
1002 deleting any previous extension. The dialog takes ownership of the
1003 extension. Note that if \nullptr is passed, any existing extension will be
1004 deleted. This function must only be called while the dialog is hidden.
1005
1006 Instead of using this functionality, we recommend that you simply call
1007 show() or hide() on the part of the dialog that you want to use as an
1008 extension. See the \l{Extension Example} for details.
1009
1010 \sa showExtension(), setOrientation()
1011*/
1012void QDialog::setExtension(QWidget* extension)
1013{
1014 Q_D(QDialog);
1015 delete d->extension;
1016 d->extension = extension;
1017
1018 if (!extension)
1019 return;
1020
1021 if (extension->parentWidget() != this)
1022 extension->setParent(this);
1023 extension->hide();
1024}
1025
1026/*!
1027 \obsolete
1028
1029 Returns the dialog's extension or \nullptr if no extension has been
1030 defined.
1031
1032 Instead of using this functionality, we recommend that you simply call
1033 show() or hide() on the part of the dialog that you want to use as an
1034 extension. See the \l{Extension Example} for details.
1035
1036 \sa showExtension(), setOrientation()
1037*/
1038QWidget* QDialog::extension() const
1039{
1040 Q_D(const QDialog);
1041 return d->extension;
1042}
1043
1044
1045/*!
1046 \obsolete
1047
1048 If \a showIt is true, the dialog's extension is shown; otherwise the
1049 extension is hidden.
1050
1051 Instead of using this functionality, we recommend that you simply call
1052 show() or hide() on the part of the dialog that you want to use as an
1053 extension. See the \l{Extension Example} for details.
1054
1055 \sa show(), setExtension(), setOrientation()
1056*/
1057void QDialog::showExtension(bool showIt)
1058{
1059 Q_D(QDialog);
1060 d->doShowExtension = showIt;
1061 if (!d->extension)
1062 return;
1063 if (!testAttribute(attribute: Qt::WA_WState_Visible))
1064 return;
1065 if (d->extension->isVisible() == showIt)
1066 return;
1067
1068 if (showIt) {
1069 d->size = size();
1070 d->min = minimumSize();
1071 d->max = maximumSize();
1072 if (layout())
1073 layout()->setEnabled(false);
1074 QSize s(d->extension->sizeHint()
1075 .expandedTo(otherSize: d->extension->minimumSize())
1076 .boundedTo(otherSize: d->extension->maximumSize()));
1077 if (d->orientation == Qt::Horizontal) {
1078 int h = qMax(a: height(), b: s.height());
1079 d->extension->setGeometry(ax: width(), ay: 0, aw: s.width(), ah: h);
1080 setFixedSize(w: width() + s.width(), h);
1081 } else {
1082 int w = qMax(a: width(), b: s.width());
1083 d->extension->setGeometry(ax: 0, ay: height(), aw: w, ah: s.height());
1084 setFixedSize(w, h: height() + s.height());
1085 }
1086 d->extension->show();
1087#if QT_CONFIG(sizegrip)
1088 const bool sizeGripEnabled = isSizeGripEnabled();
1089 setSizeGripEnabled(false);
1090 d->sizeGripEnabled = sizeGripEnabled;
1091#endif
1092 } else {
1093 d->extension->hide();
1094 // workaround for CDE window manager that won't shrink with (-1,-1)
1095 setMinimumSize(d->min.expandedTo(otherSize: QSize(1, 1)));
1096 setMaximumSize(d->max);
1097 resize(d->size);
1098 if (layout())
1099 layout()->setEnabled(true);
1100#if QT_CONFIG(sizegrip)
1101 setSizeGripEnabled(d->sizeGripEnabled);
1102#endif
1103 }
1104}
1105#endif
1106
1107/*! \reimp */
1108QSize QDialog::sizeHint() const
1109{
1110 Q_D(const QDialog);
1111 if (d->extension) {
1112 if (d->orientation == Qt::Horizontal)
1113 return QSize(QWidget::sizeHint().width(),
1114 qMax(a: QWidget::sizeHint().height(),b: d->extension->sizeHint().height()));
1115 else
1116 return QSize(qMax(a: QWidget::sizeHint().width(), b: d->extension->sizeHint().width()),
1117 QWidget::sizeHint().height());
1118 }
1119 return QWidget::sizeHint();
1120}
1121
1122
1123/*! \reimp */
1124QSize QDialog::minimumSizeHint() const
1125{
1126 Q_D(const QDialog);
1127 if (d->extension) {
1128 if (d->orientation == Qt::Horizontal)
1129 return QSize(QWidget::minimumSizeHint().width(),
1130 qMax(a: QWidget::minimumSizeHint().height(), b: d->extension->minimumSizeHint().height()));
1131 else
1132 return QSize(qMax(a: QWidget::minimumSizeHint().width(), b: d->extension->minimumSizeHint().width()),
1133 QWidget::minimumSizeHint().height());
1134 }
1135
1136 return QWidget::minimumSizeHint();
1137}
1138
1139/*!
1140 \property QDialog::modal
1141 \brief whether show() should pop up the dialog as modal or modeless
1142
1143 By default, this property is \c false and show() pops up the dialog
1144 as modeless. Setting this property to true is equivalent to setting
1145 QWidget::windowModality to Qt::ApplicationModal.
1146
1147 exec() ignores the value of this property and always pops up the
1148 dialog as modal.
1149
1150 \sa QWidget::windowModality, show(), exec()
1151*/
1152
1153void QDialog::setModal(bool modal)
1154{
1155 setAttribute(Qt::WA_ShowModal, on: modal);
1156}
1157
1158
1159bool QDialog::isSizeGripEnabled() const
1160{
1161#if QT_CONFIG(sizegrip)
1162 Q_D(const QDialog);
1163 return !!d->resizer;
1164#else
1165 return false;
1166#endif
1167}
1168
1169
1170void QDialog::setSizeGripEnabled(bool enabled)
1171{
1172#if !QT_CONFIG(sizegrip)
1173 Q_UNUSED(enabled);
1174#else
1175 Q_D(QDialog);
1176#if QT_CONFIG(sizegrip)
1177 d->sizeGripEnabled = enabled;
1178 if (enabled && d->doShowExtension)
1179 return;
1180#endif
1181 if (!enabled != !d->resizer) {
1182 if (enabled) {
1183 d->resizer = new QSizeGrip(this);
1184 // adjustSize() processes all events, which is suboptimal
1185 d->resizer->resize(d->resizer->sizeHint());
1186 if (isRightToLeft())
1187 d->resizer->move(rect().bottomLeft() -d->resizer->rect().bottomLeft());
1188 else
1189 d->resizer->move(rect().bottomRight() -d->resizer->rect().bottomRight());
1190 d->resizer->raise();
1191 d->resizer->show();
1192 } else {
1193 delete d->resizer;
1194 d->resizer = nullptr;
1195 }
1196 }
1197#endif // QT_CONFIG(sizegrip)
1198}
1199
1200
1201
1202/*! \reimp */
1203void QDialog::resizeEvent(QResizeEvent *)
1204{
1205#if QT_CONFIG(sizegrip)
1206 Q_D(QDialog);
1207 if (d->resizer) {
1208 if (isRightToLeft())
1209 d->resizer->move(rect().bottomLeft() -d->resizer->rect().bottomLeft());
1210 else
1211 d->resizer->move(rect().bottomRight() -d->resizer->rect().bottomRight());
1212 d->resizer->raise();
1213 }
1214#endif
1215}
1216
1217/*! \fn void QDialog::finished(int result)
1218 \since 4.1
1219
1220 This signal is emitted when the dialog's \a result code has been
1221 set, either by the user or by calling done(), accept(), or
1222 reject().
1223
1224 Note that this signal is \e not emitted when hiding the dialog
1225 with hide() or setVisible(false). This includes deleting the
1226 dialog while it is visible.
1227
1228 \sa accepted(), rejected()
1229*/
1230
1231/*! \fn void QDialog::accepted()
1232 \since 4.1
1233
1234 This signal is emitted when the dialog has been accepted either by
1235 the user or by calling accept() or done() with the
1236 QDialog::Accepted argument.
1237
1238 Note that this signal is \e not emitted when hiding the dialog
1239 with hide() or setVisible(false). This includes deleting the
1240 dialog while it is visible.
1241
1242 \sa finished(), rejected()
1243*/
1244
1245/*! \fn void QDialog::rejected()
1246 \since 4.1
1247
1248 This signal is emitted when the dialog has been rejected either by
1249 the user or by calling reject() or done() with the
1250 QDialog::Rejected argument.
1251
1252 Note that this signal is \e not emitted when hiding the dialog
1253 with hide() or setVisible(false). This includes deleting the
1254 dialog while it is visible.
1255
1256 \sa finished(), accepted()
1257*/
1258
1259QT_END_NAMESPACE
1260#include "moc_qdialog.cpp"
1261

source code of qtbase/src/widgets/dialogs/qdialog.cpp