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 | |
77 | QT_BEGIN_NAMESPACE |
78 | |
79 | static 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 | |
108 | QDialogPrivate::~QDialogPrivate() |
109 | { |
110 | delete m_platformHelper; |
111 | } |
112 | |
113 | QPlatformDialogHelper *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 | |
135 | bool 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 | */ |
151 | void 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 | */ |
169 | void 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 | |
181 | QWindow *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 | |
191 | bool 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 | |
205 | QVariant 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 | |
212 | void 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 | |
396 | QDialog::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 | */ |
406 | QDialog::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 | |
415 | QDialog::~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) |
434 | void 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 | */ |
459 | void 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 | */ |
470 | void 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 | |
480 | void 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 | */ |
505 | int 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 | */ |
519 | void 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 | */ |
533 | void 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 | |
573 | int 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 | |
632 | void 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 | |
645 | void 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 | |
656 | void QDialog::reject() |
657 | { |
658 | done(Rejected); |
659 | } |
660 | |
661 | /*! \reimp */ |
662 | bool 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 */ |
673 | void QDialog::(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 */ |
701 | void 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 */ |
738 | void 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 | |
761 | void 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 */ |
860 | void 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 */ |
872 | void 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 = 0, = 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 */ |
953 | QSize 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 */ |
969 | QSize 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 | |
998 | void QDialog::setModal(bool modal) |
999 | { |
1000 | setAttribute(Qt::WA_ShowModal, modal); |
1001 | } |
1002 | |
1003 | |
1004 | bool 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 | |
1015 | void 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 */ |
1048 | void 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 | |
1104 | QT_END_NAMESPACE |
1105 | #include "moc_qdialog.cpp" |
1106 | |