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 
79 QT_BEGIN_NAMESPACE
80 
themeDialogType(const QDialog * dialog)81 static inline int themeDialogType(const QDialog *dialog)
82 {
83 #if QT_CONFIG(filedialog)
84     if (qobject_cast<const QFileDialog *>(dialog))
85         return QPlatformTheme::FileDialog;
86 #endif
87 #if QT_CONFIG(colordialog)
88     if (qobject_cast<const QColorDialog *>(dialog))
89         return QPlatformTheme::ColorDialog;
90 #endif
91 #if QT_CONFIG(fontdialog)
92     if (qobject_cast<const QFontDialog *>(dialog))
93         return QPlatformTheme::FontDialog;
94 #endif
95 #if QT_CONFIG(messagebox)
96     if (qobject_cast<const QMessageBox *>(dialog))
97         return QPlatformTheme::MessageDialog;
98 #endif
99 #if QT_CONFIG(errormessage)
100     if (qobject_cast<const QErrorMessage *>(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 
~QDialogPrivate()110 QDialogPrivate::~QDialogPrivate()
111 {
112     delete m_platformHelper;
113 }
114 
platformHelper() const115 QPlatformDialogHelper *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(static_cast<QPlatformTheme::DialogType>(type));
127             if (m_platformHelper) {
128                 QObject::connect(m_platformHelper, SIGNAL(accept()), dialog, SLOT(accept()));
129                 QObject::connect(m_platformHelper, SIGNAL(reject()), dialog, SLOT(reject()));
130                 ncThis->initHelper(m_platformHelper);
131             }
132         }
133     }
134     return m_platformHelper;
135 }
136 
canBeNativeDialog() const137 bool 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(static_cast<QPlatformTheme::DialogType>(type));
145     return false;
146 }
147 
148 /*!
149     \internal
150 
151     Properly hides dialog and sets the \a resultCode.
152  */
hide(int resultCode)153 void QDialogPrivate::hide(int resultCode)
154 {
155     Q_Q(QDialog);
156 
157     q->setResult(resultCode);
158     q->hide();
159 
160     close_helper(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  */
finalize(int resultCode,int dialogCode)171 void 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(resultCode);
181 }
182 
transientParentWindow() const183 QWindow *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 
setNativeDialogVisible(bool visible)193 bool QDialogPrivate::setNativeDialogVisible(bool visible)
194 {
195     if (QPlatformDialogHelper *helper = platformHelper()) {
196         if (visible) {
197             Q_Q(QDialog);
198             helperPrepareShow(helper);
199             nativeDialogInUse = helper->show(q->windowFlags(), q->windowModality(), transientParentWindow());
200         } else if (nativeDialogInUse) {
201             helper->hide();
202         }
203     }
204     return nativeDialogInUse;
205 }
206 
styleHint(QPlatformDialogHelper::StyleHint hint) const207 QVariant 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 
deletePlatformHelper()214 void 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 
QDialog(QWidget * parent,Qt::WindowFlags f)398 QDialog::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 */
QDialog(QDialogPrivate & dd,QWidget * parent,Qt::WindowFlags f)408 QDialog::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 
~QDialog()417 QDialog::~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)
setDefault(QPushButton * pushButton)436 void 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 */
setMainDefault(QPushButton * pushButton)461 void 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  */
hideDefault()472 void 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 
resetModalitySetByOpen()482 void QDialogPrivate::resetModalitySetByOpen()
483 {
484     Q_Q(QDialog);
485     if (resetModalityTo != -1 && !q->testAttribute(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, 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 */
result() const507 int 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 */
setResult(int r)521 void 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 */
open()535 void 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(Qt::WA_SetWindowModality);
543         setWindowModality(Qt::WindowModal);
544         setAttribute(Qt::WA_SetWindowModality, 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 
exec()575 int QDialog::exec()
576 {
577     Q_D(QDialog);
578 
579     if (Q_UNLIKELY(d->eventLoop)) {
580         qWarning("QDialog::exec: Recursive call detected");
581         return -1;
582     }
583 
584     bool deleteOnClose = testAttribute(Qt::WA_DeleteOnClose);
585     setAttribute(Qt::WA_DeleteOnClose, false);
586 
587     d->resetModalitySetByOpen();
588 
589     bool wasShowModal = testAttribute(Qt::WA_ShowModal);
590     setAttribute(Qt::WA_ShowModal, 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(QEventLoop::DialogExec);
602     }
603     if (guard.isNull())
604         return QDialog::Rejected;
605     d->eventLoop = nullptr;
606 
607     setAttribute(Qt::WA_ShowModal, 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 
done(int r)634 void QDialog::done(int r)
635 {
636     Q_D(QDialog);
637     d->hide(r);
638     d->finalize(r, r);
639 }
640 
641 /*!
642   Hides the modal dialog and sets the result code to \c Accepted.
643 
644   \sa reject(), done()
645 */
646 
accept()647 void QDialog::accept()
648 {
649     done(Accepted);
650 }
651 
652 /*!
653   Hides the modal dialog and sets the result code to \c Rejected.
654 
655   \sa accept(), done()
656 */
657 
reject()658 void QDialog::reject()
659 {
660     done(Rejected);
661 }
662 
663 /*! \reimp */
eventFilter(QObject * o,QEvent * e)664 bool QDialog::eventFilter(QObject *o, QEvent *e)
665 {
666     return QWidget::eventFilter(o, e);
667 }
668 
669 /*****************************************************************************
670   Event handlers
671  *****************************************************************************/
672 
673 #ifndef QT_NO_CONTEXTMENU
674 /*! \reimp */
contextMenuEvent(QContextMenuEvent * e)675 void QDialog::contextMenuEvent(QContextMenuEvent *e)
676 {
677 #if !QT_CONFIG(whatsthis) || !QT_CONFIG(menu)
678     Q_UNUSED(e);
679 #else
680     QWidget *w = childAt(e->pos());
681     if (!w) {
682         w = rect().contains(e->pos()) ? this : nullptr;
683         if (!w)
684             return;
685     }
686     while (w && w->whatsThis().size() == 0 && !w->testAttribute(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(tr("What's This?"));
691         if (p.data()->exec(e->globalPos()) == wt) {
692             QHelpEvent e(QEvent::WhatsThis, w->rect().center(),
693                          w->mapToGlobal(w->rect().center()));
694             QCoreApplication::sendEvent(w, &e);
695         }
696         delete p.data();
697     }
698 #endif
699 }
700 #endif // QT_NO_CONTEXTMENU
701 
702 /*! \reimp */
keyPressEvent(QKeyEvent * e)703 void 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(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 */
closeEvent(QCloseEvent * e)740 void 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 
setVisible(bool visible)763 void QDialog::setVisible(bool visible)
764 {
765     Q_D(QDialog);
766     if (!testAttribute(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(Qt::WA_DontShowOnScreen)
773             && d->styleHint(QPlatformDialogHelper::DialogIsQtWindow).toBool();
774     Qt::WindowModality oldModality;
775     bool wasModalitySet;
776 
777     if (dontBlockWindows) {
778         oldModality = windowModality();
779         wasModalitySet = testAttribute(Qt::WA_SetWindowModality);
780         setWindowModality(Qt::NonModal);
781     }
782 
783     if (visible) {
784         if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden))
785             return;
786 
787         QWidget::setVisible(visible);
788 #if QT_DEPRECATED_SINCE(5, 13)
789 QT_WARNING_PUSH
790 QT_WARNING_DISABLE_DEPRECATED
791         showExtension(d->doShowExtension);
792 QT_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*>(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 *>(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(fw, &e);
830         }
831 
832 #ifndef QT_NO_ACCESSIBILITY
833         QAccessibleEvent event(this, QAccessible::DialogStart);
834         QAccessible::updateAccessibility(&event);
835 #endif
836 
837     } else {
838         if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(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);
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, wasModalitySet);
857     }
858 
859 #if QT_CONFIG(pushbutton)
860     const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
861     if (d->mainDef && isActiveWindow()
862         && theme->themeHint(QPlatformTheme::DialogSnapToDefaultButton).toBool())
863         QCursor::setPos(d->mainDef->mapToGlobal(d->mainDef->rect().center()));
864 #endif
865 }
866 
867 /*!\reimp */
showEvent(QShowEvent * event)868 void QDialog::showEvent(QShowEvent *event)
869 {
870     if (!event->spontaneous() && !testAttribute(Qt::WA_Moved)) {
871         Qt::WindowStates  state = windowState();
872         adjustPosition(parentWidget());
873         setAttribute(Qt::WA_Moved, false); // not really an explicit position
874         if (state != windowState())
875             setWindowState(state);
876     }
877 }
878 
879 /*! \internal */
adjustPosition(QWidget * w)880 void QDialog::adjustPosition(QWidget* w)
881 {
882 
883     if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme())
884         if (theme->themeHint(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(w);
893     } else if (QDesktopWidgetPrivate::isVirtualDesktop()) {
894         scrn = QDesktopWidgetPrivate::screenNumber(QCursor::pos());
895     } else {
896         scrn = QDesktopWidgetPrivate::screenNumber(this);
897     }
898     desk = QDesktopWidgetPrivate::availableGeometry(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(extraw, framew);
908             extrah = qMax(extrah, 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>(w->windowHandle()->property("_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(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 */
setOrientation(Qt::Orientation orientation)975 void 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 */
orientation() const992 Qt::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 */
setExtension(QWidget * extension)1012 void 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 */
extension() const1038 QWidget* 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 */
showExtension(bool showIt)1057 void QDialog::showExtension(bool showIt)
1058 {
1059     Q_D(QDialog);
1060     d->doShowExtension = showIt;
1061     if (!d->extension)
1062         return;
1063     if (!testAttribute(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(d->extension->minimumSize())
1076                  .boundedTo(d->extension->maximumSize()));
1077         if (d->orientation == Qt::Horizontal) {
1078             int h = qMax(height(), s.height());
1079             d->extension->setGeometry(width(), 0, s.width(), h);
1080             setFixedSize(width() + s.width(), h);
1081         } else {
1082             int w = qMax(width(), s.width());
1083             d->extension->setGeometry(0, height(), w, s.height());
1084             setFixedSize(w, 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(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 */
sizeHint() const1108 QSize 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(QWidget::sizeHint().height(),d->extension->sizeHint().height()));
1115         else
1116             return QSize(qMax(QWidget::sizeHint().width(), d->extension->sizeHint().width()),
1117                         QWidget::sizeHint().height());
1118     }
1119     return QWidget::sizeHint();
1120 }
1121 
1122 
1123 /*! \reimp */
minimumSizeHint() const1124 QSize 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(QWidget::minimumSizeHint().height(), d->extension->minimumSizeHint().height()));
1131         else
1132             return QSize(qMax(QWidget::minimumSizeHint().width(), 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 
setModal(bool modal)1153 void QDialog::setModal(bool modal)
1154 {
1155     setAttribute(Qt::WA_ShowModal, modal);
1156 }
1157 
1158 
isSizeGripEnabled() const1159 bool 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 
setSizeGripEnabled(bool enabled)1170 void 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 */
resizeEvent(QResizeEvent *)1203 void 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 
1259 QT_END_NAMESPACE
1260 #include "moc_qdialog.cpp"
1261