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