1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qwizard.h"
5#include <QtWidgets/private/qtwidgetsglobal_p.h>
6
7#if QT_CONFIG(spinbox)
8#include "qabstractspinbox.h"
9#endif
10#include "qalgorithms.h"
11#include "qapplication.h"
12#include "qboxlayout.h"
13#include "qlayoutitem.h"
14#include "qevent.h"
15#include "qframe.h"
16#include "qlabel.h"
17#if QT_CONFIG(lineedit)
18#include "qlineedit.h"
19#endif
20#include <qpointer.h>
21#include "qstylepainter.h"
22#include "qwindow.h"
23#include "qpushbutton.h"
24#include "qset.h"
25#if QT_CONFIG(shortcut)
26# include "qshortcut.h"
27#endif
28#include "qstyle.h"
29#include "qstyleoption.h"
30#include "qvarlengtharray.h"
31#if defined(Q_OS_MACOS)
32#include <AppKit/AppKit.h>
33#include <QtGui/private/qcoregraphics_p.h>
34#elif QT_CONFIG(style_windowsvista)
35#include "qwizard_win_p.h"
36#endif
37
38#include "private/qdialog_p.h"
39#include <qdebug.h>
40
41#include <string.h> // for memset()
42#include <algorithm>
43
44QT_BEGIN_NAMESPACE
45
46using namespace Qt::StringLiterals;
47
48// These fudge terms were needed a few places to obtain pixel-perfect results
49const int GapBetweenLogoAndRightEdge = 5;
50const int ModernHeaderTopMargin = 2;
51const int ClassicHMargin = 4;
52const int MacButtonTopMargin = 13;
53const int MacLayoutLeftMargin = 20;
54//const int MacLayoutTopMargin = 14; // Unused. Save some space and avoid warning.
55const int MacLayoutRightMargin = 20;
56const int MacLayoutBottomMargin = 17;
57
58static void changeSpacerSize(QLayout *layout, int index, int width, int height)
59{
60 QSpacerItem *spacer = layout->itemAt(index)->spacerItem();
61 if (!spacer)
62 return;
63 spacer->changeSize(w: width, h: height);
64}
65
66static QWidget *iWantTheFocus(QWidget *ancestor)
67{
68 const int MaxIterations = 100;
69
70 QWidget *candidate = ancestor;
71 for (int i = 0; i < MaxIterations; ++i) {
72 candidate = candidate->nextInFocusChain();
73 if (!candidate)
74 break;
75
76 if (candidate->focusPolicy() & Qt::TabFocus) {
77 if (candidate != ancestor && ancestor->isAncestorOf(child: candidate))
78 return candidate;
79 }
80 }
81 return nullptr;
82}
83
84static bool objectInheritsXAndXIsCloserThanY(const QObject *object, const QByteArray &classX,
85 const QByteArray &classY)
86{
87 const QMetaObject *metaObject = object->metaObject();
88 while (metaObject) {
89 if (metaObject->className() == classX)
90 return true;
91 if (metaObject->className() == classY)
92 return false;
93 metaObject = metaObject->superClass();
94 }
95 return false;
96}
97
98const struct {
99 const char className[16];
100 const char property[13];
101} fallbackProperties[] = {
102 // If you modify this list, make sure to update the documentation (and the auto test)
103 { .className: "QAbstractButton", .property: "checked" },
104 { .className: "QAbstractSlider", .property: "value" },
105 { .className: "QComboBox", .property: "currentIndex" },
106 { .className: "QDateTimeEdit", .property: "dateTime" },
107 { .className: "QLineEdit", .property: "text" },
108 { .className: "QListWidget", .property: "currentRow" },
109 { .className: "QSpinBox", .property: "value" },
110};
111const size_t NFallbackDefaultProperties = sizeof fallbackProperties / sizeof *fallbackProperties;
112
113static const char *changed_signal(int which)
114{
115 // since it might expand to a runtime function call (to
116 // qFlagLocations()), we cannot store the result of SIGNAL() in a
117 // character array and expect it to be statically initialized. To
118 // avoid the relocations caused by a char pointer table, use a
119 // switch statement:
120 switch (which) {
121 case 0: return SIGNAL(toggled(bool));
122 case 1: return SIGNAL(valueChanged(int));
123 case 2: return SIGNAL(currentIndexChanged(int));
124 case 3: return SIGNAL(dateTimeChanged(QDateTime));
125 case 4: return SIGNAL(textChanged(QString));
126 case 5: return SIGNAL(currentRowChanged(int));
127 case 6: return SIGNAL(valueChanged(int));
128 };
129 static_assert(7 == NFallbackDefaultProperties);
130 Q_UNREACHABLE_RETURN(nullptr);
131}
132
133class QWizardDefaultProperty
134{
135public:
136 QByteArray className;
137 QByteArray property;
138 QByteArray changedSignal;
139
140 inline QWizardDefaultProperty() {}
141 inline QWizardDefaultProperty(const char *className, const char *property,
142 const char *changedSignal)
143 : className(className), property(property), changedSignal(changedSignal) {}
144};
145Q_DECLARE_TYPEINFO(QWizardDefaultProperty, Q_RELOCATABLE_TYPE);
146
147class QWizardField
148{
149public:
150 inline QWizardField() {}
151 QWizardField(QWizardPage *page, const QString &spec, QObject *object, const char *property,
152 const char *changedSignal);
153
154 void resolve(const QList<QWizardDefaultProperty> &defaultPropertyTable);
155 void findProperty(const QWizardDefaultProperty *properties, int propertyCount);
156
157 QWizardPage *page;
158 QString name;
159 bool mandatory;
160 QObject *object;
161 QByteArray property;
162 QByteArray changedSignal;
163 QVariant initialValue;
164};
165Q_DECLARE_TYPEINFO(QWizardField, Q_RELOCATABLE_TYPE);
166
167QWizardField::QWizardField(QWizardPage *page, const QString &spec, QObject *object,
168 const char *property, const char *changedSignal)
169 : page(page), name(spec), mandatory(false), object(object), property(property),
170 changedSignal(changedSignal)
171{
172 if (name.endsWith(c: u'*')) {
173 name.chop(n: 1);
174 mandatory = true;
175 }
176}
177
178void QWizardField::resolve(const QList<QWizardDefaultProperty> &defaultPropertyTable)
179{
180 if (property.isEmpty())
181 findProperty(properties: defaultPropertyTable.constData(), propertyCount: defaultPropertyTable.size());
182 initialValue = object->property(name: property);
183}
184
185void QWizardField::findProperty(const QWizardDefaultProperty *properties, int propertyCount)
186{
187 QByteArray className;
188
189 for (int i = 0; i < propertyCount; ++i) {
190 if (objectInheritsXAndXIsCloserThanY(object, classX: properties[i].className, classY: className)) {
191 className = properties[i].className;
192 property = properties[i].property;
193 changedSignal = properties[i].changedSignal;
194 }
195 }
196}
197
198class QWizardLayoutInfo
199{
200public:
201 int topLevelMarginLeft = -1;
202 int topLevelMarginRight = -1;
203 int topLevelMarginTop = -1;
204 int topLevelMarginBottom = -1;
205 int childMarginLeft = -1;
206 int childMarginRight = -1;
207 int childMarginTop = -1;
208 int childMarginBottom = -1;
209 int hspacing = -1;
210 int vspacing = -1;
211 int buttonSpacing = -1;
212 QWizard::WizardStyle wizStyle = QWizard::ClassicStyle;
213 bool header = false;
214 bool watermark = false;
215 bool title = false;
216 bool subTitle = false;
217 bool extension = false;
218 bool sideWidget = false;
219
220 bool operator==(const QWizardLayoutInfo &other) const;
221 inline bool operator!=(const QWizardLayoutInfo &other) const { return !operator==(other); }
222};
223
224bool QWizardLayoutInfo::operator==(const QWizardLayoutInfo &other) const
225{
226 return topLevelMarginLeft == other.topLevelMarginLeft
227 && topLevelMarginRight == other.topLevelMarginRight
228 && topLevelMarginTop == other.topLevelMarginTop
229 && topLevelMarginBottom == other.topLevelMarginBottom
230 && childMarginLeft == other.childMarginLeft
231 && childMarginRight == other.childMarginRight
232 && childMarginTop == other.childMarginTop
233 && childMarginBottom == other.childMarginBottom
234 && hspacing == other.hspacing
235 && vspacing == other.vspacing
236 && buttonSpacing == other.buttonSpacing
237 && wizStyle == other.wizStyle
238 && header == other.header
239 && watermark == other.watermark
240 && title == other.title
241 && subTitle == other.subTitle
242 && extension == other.extension
243 && sideWidget == other.sideWidget;
244}
245
246class QWizardHeader : public QWidget
247{
248public:
249 enum RulerType { Ruler };
250
251 inline QWizardHeader(RulerType /* ruler */, QWidget *parent = nullptr)
252 : QWidget(parent) { setFixedHeight(2); }
253 QWizardHeader(QWidget *parent = nullptr);
254
255 void setup(const QWizardLayoutInfo &info, const QString &title,
256 const QString &subTitle, const QPixmap &, const QPixmap &banner,
257 Qt::TextFormat titleFormat, Qt::TextFormat subTitleFormat);
258
259protected:
260 void paintEvent(QPaintEvent *event) override;
261#if QT_CONFIG(style_windowsvista)
262private:
263 bool vistaDisabled() const;
264#endif
265private:
266 QLabel *titleLabel;
267 QLabel *subTitleLabel;
268 QLabel *logoLabel;
269 QGridLayout *layout;
270 QPixmap bannerPixmap;
271};
272
273QWizardHeader::QWizardHeader(QWidget *parent)
274 : QWidget(parent)
275{
276 setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Fixed);
277 setBackgroundRole(QPalette::Base);
278
279 titleLabel = new QLabel(this);
280 titleLabel->setBackgroundRole(QPalette::Base);
281
282 subTitleLabel = new QLabel(this);
283 subTitleLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
284 subTitleLabel->setWordWrap(true);
285
286 logoLabel = new QLabel(this);
287
288 QFont font = titleLabel->font();
289 font.setBold(true);
290 titleLabel->setFont(font);
291
292 layout = new QGridLayout(this);
293 layout->setContentsMargins(QMargins());
294 layout->setSpacing(0);
295
296 layout->setRowMinimumHeight(row: 3, minSize: 1);
297 layout->setRowStretch(row: 4, stretch: 1);
298
299 layout->setColumnStretch(column: 2, stretch: 1);
300 layout->setColumnMinimumWidth(column: 4, minSize: 2 * GapBetweenLogoAndRightEdge);
301 layout->setColumnMinimumWidth(column: 6, minSize: GapBetweenLogoAndRightEdge);
302
303 layout->addWidget(titleLabel, row: 2, column: 1, rowSpan: 1, columnSpan: 2);
304 layout->addWidget(subTitleLabel, row: 4, column: 2);
305 layout->addWidget(logoLabel, row: 1, column: 5, rowSpan: 5, columnSpan: 1);
306}
307
308#if QT_CONFIG(style_windowsvista)
309bool QWizardHeader::vistaDisabled() const
310{
311 bool styleDisabled = false;
312 QWizard *wiz = parentWidget() ? qobject_cast <QWizard *>(parentWidget()->parentWidget()) : 0;
313 if (wiz) {
314 // Designer doesn't support the Vista style for Wizards. This property is used to turn
315 // off the Vista style.
316 const QVariant v = wiz->property("_q_wizard_vista_off");
317 styleDisabled = v.isValid() && v.toBool();
318 }
319 return styleDisabled;
320}
321#endif
322
323void QWizardHeader::setup(const QWizardLayoutInfo &info, const QString &title,
324 const QString &subTitle, const QPixmap &, const QPixmap &banner,
325 Qt::TextFormat titleFormat, Qt::TextFormat subTitleFormat)
326{
327 bool modern = ((info.wizStyle == QWizard::ModernStyle)
328#if QT_CONFIG(style_windowsvista)
329 || vistaDisabled()
330#endif
331 );
332
333 layout->setRowMinimumHeight(row: 0, minSize: modern ? ModernHeaderTopMargin : 0);
334 layout->setRowMinimumHeight(row: 1, minSize: modern ? info.topLevelMarginTop - ModernHeaderTopMargin - 1 : 0);
335 layout->setRowMinimumHeight(row: 6, minSize: (modern ? 3 : GapBetweenLogoAndRightEdge) + 2);
336
337 int minColumnWidth0 = modern ? info.topLevelMarginLeft + info.topLevelMarginRight : 0;
338 int minColumnWidth1 = modern ? info.topLevelMarginLeft + info.topLevelMarginRight + 1
339 : info.topLevelMarginLeft + ClassicHMargin;
340 layout->setColumnMinimumWidth(column: 0, minSize: minColumnWidth0);
341 layout->setColumnMinimumWidth(column: 1, minSize: minColumnWidth1);
342
343 titleLabel->setTextFormat(titleFormat);
344 titleLabel->setText(title);
345 logoLabel->setPixmap(logo);
346
347 subTitleLabel->setTextFormat(subTitleFormat);
348 subTitleLabel->setText("Pq\nPq"_L1);
349 int desiredSubTitleHeight = subTitleLabel->sizeHint().height();
350 subTitleLabel->setText(subTitle);
351
352 if (modern) {
353 bannerPixmap = banner;
354 } else {
355 bannerPixmap = QPixmap();
356 }
357
358 if (bannerPixmap.isNull()) {
359 /*
360 There is no widthForHeight() function, so we simulate it with a loop.
361 */
362 int candidateSubTitleWidth = qMin(a: 512, b: 2 * QGuiApplication::primaryScreen()->virtualGeometry().width() / 3);
363 int delta = candidateSubTitleWidth >> 1;
364 while (delta > 0) {
365 if (subTitleLabel->heightForWidth(candidateSubTitleWidth - delta)
366 <= desiredSubTitleHeight)
367 candidateSubTitleWidth -= delta;
368 delta >>= 1;
369 }
370
371 subTitleLabel->setMinimumSize(minw: candidateSubTitleWidth, minh: desiredSubTitleHeight);
372
373 QSize size = layout->totalMinimumSize();
374 setMinimumSize(size);
375 setMaximumSize(QWIDGETSIZE_MAX, maxh: size.height());
376 } else {
377 subTitleLabel->setMinimumSize(minw: 0, minh: 0);
378 setFixedSize(banner.size() + QSize(0, 2));
379 }
380 updateGeometry();
381}
382
383void QWizardHeader::paintEvent(QPaintEvent * /* event */)
384{
385 QStylePainter painter(this);
386 painter.drawPixmap(x: 0, y: 0, pm: bannerPixmap);
387
388 int x = width() - 2;
389 int y = height() - 2;
390 const QPalette &pal = palette();
391 painter.setPen(pal.mid().color());
392 painter.drawLine(x1: 0, y1: y, x2: x, y2: y);
393 painter.setPen(pal.base().color());
394 painter.drawPoint(x: x + 1, y);
395 painter.drawLine(x1: 0, y1: y + 1, x2: x + 1, y2: y + 1);
396}
397
398// We save one vtable by basing QWizardRuler on QWizardHeader
399class QWizardRuler : public QWizardHeader
400{
401public:
402 inline QWizardRuler(QWidget *parent = nullptr)
403 : QWizardHeader(Ruler, parent) {}
404};
405
406class QWatermarkLabel : public QLabel
407{
408public:
409 QWatermarkLabel(QWidget *parent, QWidget *sideWidget) : QLabel(parent), m_sideWidget(sideWidget) {
410 m_layout = new QVBoxLayout(this);
411 if (m_sideWidget)
412 m_layout->addWidget(m_sideWidget);
413 }
414
415 QSize minimumSizeHint() const override {
416 if (!pixmap().isNull())
417 return pixmap().deviceIndependentSize().toSize();
418 return QFrame::minimumSizeHint();
419 }
420
421 void setSideWidget(QWidget *widget) {
422 if (m_sideWidget == widget)
423 return;
424 if (m_sideWidget) {
425 m_layout->removeWidget(w: m_sideWidget);
426 m_sideWidget->hide();
427 }
428 m_sideWidget = widget;
429 if (m_sideWidget)
430 m_layout->addWidget(m_sideWidget);
431 }
432 QWidget *sideWidget() const {
433 return m_sideWidget;
434 }
435private:
436 QVBoxLayout *m_layout;
437 QWidget *m_sideWidget;
438};
439
440class QWizardPagePrivate : public QWidgetPrivate
441{
442 Q_DECLARE_PUBLIC(QWizardPage)
443
444public:
445 enum TriState { Tri_Unknown = -1, Tri_False, Tri_True };
446
447 bool cachedIsComplete() const;
448 void _q_maybeEmitCompleteChanged();
449 void _q_updateCachedCompleteState();
450
451 QWizard *wizard = nullptr;
452 QString title;
453 QString subTitle;
454 QPixmap pixmaps[QWizard::NPixmaps];
455 QList<QWizardField> pendingFields;
456 mutable TriState completeState = Tri_Unknown;
457 bool explicitlyFinal = false;
458 bool commit = false;
459 bool initialized = false;
460 QMap<int, QString> buttonCustomTexts;
461};
462
463bool QWizardPagePrivate::cachedIsComplete() const
464{
465 Q_Q(const QWizardPage);
466 if (completeState == Tri_Unknown)
467 completeState = q->isComplete() ? Tri_True : Tri_False;
468 return completeState == Tri_True;
469}
470
471void QWizardPagePrivate::_q_maybeEmitCompleteChanged()
472{
473 Q_Q(QWizardPage);
474 TriState newState = q->isComplete() ? Tri_True : Tri_False;
475 if (newState != completeState)
476 emit q->completeChanged();
477}
478
479void QWizardPagePrivate::_q_updateCachedCompleteState()
480{
481 Q_Q(QWizardPage);
482 completeState = q->isComplete() ? Tri_True : Tri_False;
483}
484
485class QWizardAntiFlickerWidget : public QWidget
486{
487public:
488#if QT_CONFIG(style_windowsvista)
489 QWizardPrivate *wizardPrivate;
490 QWizardAntiFlickerWidget(QWizard *wizard, QWizardPrivate *wizardPrivate)
491 : QWidget(wizard)
492 , wizardPrivate(wizardPrivate) {}
493protected:
494 void paintEvent(QPaintEvent *) override;
495#else
496 QWizardAntiFlickerWidget(QWizard *wizard, QWizardPrivate *)
497 : QWidget(wizard)
498 {}
499#endif
500};
501
502class QWizardPrivate : public QDialogPrivate
503{
504 Q_DECLARE_PUBLIC(QWizard)
505
506public:
507 typedef QMap<int, QWizardPage *> PageMap;
508
509 enum Direction {
510 Backward,
511 Forward
512 };
513
514 void init();
515 void reset();
516 void cleanupPagesNotInHistory();
517 void addField(const QWizardField &field);
518 void removeFieldAt(int index);
519 void switchToPage(int newId, Direction direction);
520 QWizardLayoutInfo layoutInfoForCurrentPage();
521 void recreateLayout(const QWizardLayoutInfo &info);
522 void updateLayout();
523 void updatePalette();
524 void updateMinMaxSizes(const QWizardLayoutInfo &info);
525 void updateCurrentPage();
526 bool ensureButton(QWizard::WizardButton which) const;
527 void connectButton(QWizard::WizardButton which) const;
528 void updateButtonTexts();
529 void updateButtonLayout();
530 void setButtonLayout(const QWizard::WizardButton *array, int size);
531 bool buttonLayoutContains(QWizard::WizardButton which);
532 void updatePixmap(QWizard::WizardPixmap which);
533#if QT_CONFIG(style_windowsvista)
534 bool vistaDisabled() const;
535 bool handleAeroStyleChange();
536#endif
537 bool isVistaThemeEnabled() const;
538 void disableUpdates();
539 void enableUpdates();
540 void _q_emitCustomButtonClicked();
541 void _q_updateButtonStates();
542 void _q_handleFieldObjectDestroyed(QObject *);
543 void setStyle(QStyle *style);
544#ifdef Q_OS_MACOS
545 static QPixmap findDefaultBackgroundPixmap();
546#endif
547
548 PageMap pageMap;
549 QList<QWizardField> fields;
550 QMap<QString, int> fieldIndexMap;
551 QList<QWizardDefaultProperty> defaultPropertyTable;
552 QList<int> history;
553 int start = -1;
554 bool startSetByUser = false;
555 int current = -1;
556 bool canContinue = false;
557 bool canFinish = false;
558 QWizardLayoutInfo layoutInfo;
559 int disableUpdatesCount = 0;
560
561 QWizard::WizardStyle wizStyle = QWizard::ClassicStyle;
562 QWizard::WizardOptions opts;
563 QMap<int, QString> buttonCustomTexts;
564 bool buttonsHaveCustomLayout = false;
565 QList<QWizard::WizardButton> buttonsCustomLayout;
566 Qt::TextFormat titleFmt = Qt::AutoText;
567 Qt::TextFormat subTitleFmt = Qt::AutoText;
568 mutable QPixmap defaultPixmaps[QWizard::NPixmaps];
569
570 union {
571 // keep in sync with QWizard::WizardButton
572 mutable struct {
573 QAbstractButton *back;
574 QAbstractButton *next;
575 QAbstractButton *commit;
576 QAbstractButton *finish;
577 QAbstractButton *cancel;
578 QAbstractButton *help;
579 } btn;
580 mutable QAbstractButton *btns[QWizard::NButtons];
581 };
582 QWizardAntiFlickerWidget *antiFlickerWidget = nullptr;
583 QWidget *placeholderWidget1 = nullptr;
584 QWidget *placeholderWidget2 = nullptr;
585 QWizardHeader *headerWidget = nullptr;
586 QWatermarkLabel *watermarkLabel = nullptr;
587 QWidget *sideWidget = nullptr;
588 QFrame *pageFrame = nullptr;
589 QLabel *titleLabel = nullptr;
590 QLabel *subTitleLabel = nullptr;
591 QWizardRuler *bottomRuler = nullptr;
592
593 QVBoxLayout *pageVBoxLayout = nullptr;
594 QHBoxLayout *buttonLayout = nullptr;
595 QGridLayout *mainLayout = nullptr;
596
597#if QT_CONFIG(style_windowsvista)
598 QVistaHelper *vistaHelper = nullptr;
599# if QT_CONFIG(shortcut)
600 QPointer<QShortcut> vistaNextShortcut;
601# endif
602 bool vistaInitPending = true;
603 bool vistaDirty = true;
604 bool vistaStateChanged = false;
605 bool inHandleAeroStyleChange = false;
606#endif
607 int minimumWidth = 0;
608 int minimumHeight = 0;
609 int maximumWidth = QWIDGETSIZE_MAX;
610 int maximumHeight = QWIDGETSIZE_MAX;
611};
612
613static QString buttonDefaultText(int wstyle, int which, const QWizardPrivate *wizardPrivate)
614{
615#if !QT_CONFIG(style_windowsvista)
616 Q_UNUSED(wizardPrivate);
617#endif
618 const bool macStyle = (wstyle == QWizard::MacStyle);
619 switch (which) {
620 case QWizard::BackButton:
621 return macStyle ? QWizard::tr(s: "Go Back") : QWizard::tr(s: "< &Back");
622 case QWizard::NextButton:
623 if (macStyle)
624 return QWizard::tr(s: "Continue");
625 else
626 return wizardPrivate->isVistaThemeEnabled()
627 ? QWizard::tr(s: "&Next") : QWizard::tr(s: "&Next >");
628 case QWizard::CommitButton:
629 return QWizard::tr(s: "Commit");
630 case QWizard::FinishButton:
631 return macStyle ? QWizard::tr(s: "Done") : QWizard::tr(s: "&Finish");
632 case QWizard::CancelButton:
633 return QWizard::tr(s: "Cancel");
634 case QWizard::HelpButton:
635 return macStyle ? QWizard::tr(s: "Help") : QWizard::tr(s: "&Help");
636 default:
637 return QString();
638 }
639}
640
641void QWizardPrivate::init()
642{
643 Q_Q(QWizard);
644
645 std::fill(btns, btns + QWizard::NButtons, nullptr);
646
647 antiFlickerWidget = new QWizardAntiFlickerWidget(q, this);
648 wizStyle = QWizard::WizardStyle(q->style()->styleHint(stylehint: QStyle::SH_WizardStyle, opt: nullptr, widget: q));
649 if (wizStyle == QWizard::MacStyle) {
650 opts = (QWizard::NoDefaultButton | QWizard::NoCancelButton);
651 } else if (wizStyle == QWizard::ModernStyle) {
652 opts = QWizard::HelpButtonOnRight;
653 }
654
655#if QT_CONFIG(style_windowsvista)
656 vistaHelper = new QVistaHelper(q);
657#endif
658
659 // create these buttons right away; create the other buttons as necessary
660 ensureButton(which: QWizard::BackButton);
661 ensureButton(which: QWizard::NextButton);
662 ensureButton(which: QWizard::CommitButton);
663 ensureButton(which: QWizard::FinishButton);
664
665 pageFrame = new QFrame(antiFlickerWidget);
666 pageFrame->setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Preferred);
667
668 pageVBoxLayout = new QVBoxLayout(pageFrame);
669 pageVBoxLayout->setSpacing(0);
670 pageVBoxLayout->addSpacing(size: 0);
671 QSpacerItem *spacerItem = new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding);
672 pageVBoxLayout->addItem(spacerItem);
673
674 buttonLayout = new QHBoxLayout;
675 mainLayout = new QGridLayout(antiFlickerWidget);
676 mainLayout->setSizeConstraint(QLayout::SetNoConstraint);
677
678 updateButtonLayout();
679
680 defaultPropertyTable.reserve(size: NFallbackDefaultProperties);
681 for (uint i = 0; i < NFallbackDefaultProperties; ++i)
682 defaultPropertyTable.append(t: QWizardDefaultProperty(fallbackProperties[i].className,
683 fallbackProperties[i].property,
684 changed_signal(which: i)));
685}
686
687void QWizardPrivate::reset()
688{
689 Q_Q(QWizard);
690 if (current != -1) {
691 q->currentPage()->hide();
692 cleanupPagesNotInHistory();
693 const auto end = history.crend();
694 for (auto it = history.crbegin(); it != end; ++it)
695 q->cleanupPage(id: *it);
696 history.clear();
697 for (QWizardPage *page : std::as_const(t&: pageMap))
698 page->d_func()->initialized = false;
699
700 current = -1;
701 emit q->currentIdChanged(id: -1);
702 }
703}
704
705void QWizardPrivate::cleanupPagesNotInHistory()
706{
707 Q_Q(QWizard);
708
709 for (auto it = pageMap.begin(), end = pageMap.end(); it != end; ++it) {
710 const auto idx = it.key();
711 const auto page = it.value()->d_func();
712 if (page->initialized && !history.contains(t: idx)) {
713 q->cleanupPage(id: idx);
714 page->initialized = false;
715 }
716 }
717}
718
719void QWizardPrivate::addField(const QWizardField &field)
720{
721 Q_Q(QWizard);
722
723 QWizardField myField = field;
724 myField.resolve(defaultPropertyTable);
725
726 if (Q_UNLIKELY(fieldIndexMap.contains(myField.name))) {
727 qWarning(msg: "QWizardPage::addField: Duplicate field '%ls'", qUtf16Printable(myField.name));
728 return;
729 }
730
731 fieldIndexMap.insert(key: myField.name, value: fields.size());
732 fields += myField;
733 if (myField.mandatory && !myField.changedSignal.isEmpty())
734 QObject::connect(sender: myField.object, signal: myField.changedSignal,
735 receiver: myField.page, SLOT(_q_maybeEmitCompleteChanged()));
736 QObject::connect(
737 sender: myField.object, SIGNAL(destroyed(QObject*)), receiver: q,
738 SLOT(_q_handleFieldObjectDestroyed(QObject*)));
739}
740
741void QWizardPrivate::removeFieldAt(int index)
742{
743 Q_Q(QWizard);
744
745 const QWizardField &field = fields.at(i: index);
746 fieldIndexMap.remove(key: field.name);
747 if (field.mandatory && !field.changedSignal.isEmpty())
748 QObject::disconnect(sender: field.object, signal: field.changedSignal,
749 receiver: field.page, SLOT(_q_maybeEmitCompleteChanged()));
750 QObject::disconnect(
751 sender: field.object, SIGNAL(destroyed(QObject*)), receiver: q,
752 SLOT(_q_handleFieldObjectDestroyed(QObject*)));
753 fields.remove(i: index);
754}
755
756void QWizardPrivate::switchToPage(int newId, Direction direction)
757{
758 Q_Q(QWizard);
759
760 disableUpdates();
761
762 int oldId = current;
763 if (QWizardPage *oldPage = q->currentPage()) {
764 oldPage->hide();
765
766 if (direction == Backward) {
767 if (!(opts & QWizard::IndependentPages)) {
768 q->cleanupPage(id: oldId);
769 oldPage->d_func()->initialized = false;
770 }
771 Q_ASSERT(history.constLast() == oldId);
772 history.removeLast();
773 Q_ASSERT(history.constLast() == newId);
774 }
775 }
776
777 current = newId;
778
779 QWizardPage *newPage = q->currentPage();
780 if (newPage) {
781 if (direction == Forward) {
782 if (!newPage->d_func()->initialized) {
783 newPage->d_func()->initialized = true;
784 q->initializePage(id: current);
785 }
786 history.append(t: current);
787 }
788 newPage->show();
789 }
790
791 canContinue = (q->nextId() != -1);
792 canFinish = (newPage && newPage->isFinalPage());
793
794 _q_updateButtonStates();
795 updateButtonTexts();
796
797 const QWizard::WizardButton nextOrCommit =
798 newPage && newPage->isCommitPage() ? QWizard::CommitButton : QWizard::NextButton;
799 QAbstractButton *nextOrFinishButton =
800 btns[canContinue ? nextOrCommit : QWizard::FinishButton];
801 QWidget *candidate = nullptr;
802
803 /*
804 If there is no default button and the Next or Finish button
805 is enabled, give focus directly to it as a convenience to the
806 user. This is the normal case on OS X.
807
808 Otherwise, give the focus to the new page's first child that
809 can handle it. If there is no such child, give the focus to
810 Next or Finish.
811 */
812 if ((opts & QWizard::NoDefaultButton) && nextOrFinishButton->isEnabled()) {
813 candidate = nextOrFinishButton;
814 } else if (newPage) {
815 candidate = iWantTheFocus(ancestor: newPage);
816 }
817 if (!candidate)
818 candidate = nextOrFinishButton;
819 candidate->setFocus();
820
821 if (wizStyle == QWizard::MacStyle)
822 q->updateGeometry();
823
824 enableUpdates();
825 updateLayout();
826 updatePalette();
827
828 emit q->currentIdChanged(id: current);
829}
830
831// keep in sync with QWizard::WizardButton
832static const char * buttonSlots(QWizard::WizardButton which)
833{
834 switch (which) {
835 case QWizard::BackButton:
836 return SLOT(back());
837 case QWizard::NextButton:
838 case QWizard::CommitButton:
839 return SLOT(next());
840 case QWizard::FinishButton:
841 return SLOT(accept());
842 case QWizard::CancelButton:
843 return SLOT(reject());
844 case QWizard::HelpButton:
845 return SIGNAL(helpRequested());
846 case QWizard::CustomButton1:
847 case QWizard::CustomButton2:
848 case QWizard::CustomButton3:
849 case QWizard::Stretch:
850 case QWizard::NoButton:
851 Q_UNREACHABLE();
852 };
853 return nullptr;
854};
855
856QWizardLayoutInfo QWizardPrivate::layoutInfoForCurrentPage()
857{
858 Q_Q(QWizard);
859 QStyle *style = q->style();
860
861 QWizardLayoutInfo info;
862
863 QStyleOption option;
864 option.initFrom(w: q);
865 const int layoutHorizontalSpacing = style->pixelMetric(metric: QStyle::PM_LayoutHorizontalSpacing, option: &option, widget: q);
866 info.topLevelMarginLeft = style->pixelMetric(metric: QStyle::PM_LayoutLeftMargin, option: nullptr, widget: q);
867 info.topLevelMarginRight = style->pixelMetric(metric: QStyle::PM_LayoutRightMargin, option: nullptr, widget: q);
868 info.topLevelMarginTop = style->pixelMetric(metric: QStyle::PM_LayoutTopMargin, option: nullptr, widget: q);
869 info.topLevelMarginBottom = style->pixelMetric(metric: QStyle::PM_LayoutBottomMargin, option: nullptr, widget: q);
870 info.childMarginLeft = style->pixelMetric(metric: QStyle::PM_LayoutLeftMargin, option: nullptr, widget: titleLabel);
871 info.childMarginRight = style->pixelMetric(metric: QStyle::PM_LayoutRightMargin, option: nullptr, widget: titleLabel);
872 info.childMarginTop = style->pixelMetric(metric: QStyle::PM_LayoutTopMargin, option: nullptr, widget: titleLabel);
873 info.childMarginBottom = style->pixelMetric(metric: QStyle::PM_LayoutBottomMargin, option: nullptr, widget: titleLabel);
874 info.hspacing = (layoutHorizontalSpacing == -1)
875 ? style->layoutSpacing(control1: QSizePolicy::DefaultType, control2: QSizePolicy::DefaultType, orientation: Qt::Horizontal)
876 : layoutHorizontalSpacing;
877 info.vspacing = style->pixelMetric(metric: QStyle::PM_LayoutVerticalSpacing, option: &option, widget: q);
878 info.buttonSpacing = (layoutHorizontalSpacing == -1)
879 ? style->layoutSpacing(control1: QSizePolicy::PushButton, control2: QSizePolicy::PushButton, orientation: Qt::Horizontal)
880 : layoutHorizontalSpacing;
881
882 if (wizStyle == QWizard::MacStyle)
883 info.buttonSpacing = 12;
884
885 info.wizStyle = wizStyle;
886 if (info.wizStyle == QWizard::AeroStyle
887#if QT_CONFIG(style_windowsvista)
888 && vistaDisabled()
889#endif
890 )
891 info.wizStyle = QWizard::ModernStyle;
892
893 QString titleText;
894 QString subTitleText;
895 QPixmap backgroundPixmap;
896 QPixmap watermarkPixmap;
897
898 if (QWizardPage *page = q->currentPage()) {
899 titleText = page->title();
900 subTitleText = page->subTitle();
901 backgroundPixmap = page->pixmap(which: QWizard::BackgroundPixmap);
902 watermarkPixmap = page->pixmap(which: QWizard::WatermarkPixmap);
903 }
904
905 info.header = (info.wizStyle == QWizard::ClassicStyle || info.wizStyle == QWizard::ModernStyle)
906 && !(opts & QWizard::IgnoreSubTitles) && !subTitleText.isEmpty();
907 info.sideWidget = sideWidget;
908 info.watermark = (info.wizStyle != QWizard::MacStyle) && (info.wizStyle != QWizard::AeroStyle)
909 && !watermarkPixmap.isNull();
910 info.title = !info.header && !titleText.isEmpty();
911 info.subTitle = !(opts & QWizard::IgnoreSubTitles) && !info.header && !subTitleText.isEmpty();
912 info.extension = (info.watermark || info.sideWidget) && (opts & QWizard::ExtendedWatermarkPixmap);
913
914 return info;
915}
916
917void QWizardPrivate::recreateLayout(const QWizardLayoutInfo &info)
918{
919 Q_Q(QWizard);
920
921 /*
922 Start by undoing the main layout.
923 */
924 for (int i = mainLayout->count() - 1; i >= 0; --i) {
925 QLayoutItem *item = mainLayout->takeAt(index: i);
926 if (item->layout()) {
927 item->layout()->setParent(nullptr);
928 } else {
929 delete item;
930 }
931 }
932 for (int i = mainLayout->columnCount() - 1; i >= 0; --i)
933 mainLayout->setColumnMinimumWidth(column: i, minSize: 0);
934 for (int i = mainLayout->rowCount() - 1; i >= 0; --i)
935 mainLayout->setRowMinimumHeight(row: i, minSize: 0);
936
937 /*
938 Now, recreate it.
939 */
940
941 bool mac = (info.wizStyle == QWizard::MacStyle);
942 bool classic = (info.wizStyle == QWizard::ClassicStyle);
943 bool modern = (info.wizStyle == QWizard::ModernStyle);
944 bool aero = (info.wizStyle == QWizard::AeroStyle);
945 int deltaMarginLeft = info.topLevelMarginLeft - info.childMarginLeft;
946 int deltaMarginRight = info.topLevelMarginRight - info.childMarginRight;
947 int deltaMarginTop = info.topLevelMarginTop - info.childMarginTop;
948 int deltaMarginBottom = info.topLevelMarginBottom - info.childMarginBottom;
949 int deltaVSpacing = info.topLevelMarginBottom - info.vspacing;
950
951 int row = 0;
952 int numColumns;
953 if (mac) {
954 numColumns = 3;
955 } else if (info.watermark || info.sideWidget) {
956 numColumns = 2;
957 } else {
958 numColumns = 1;
959 }
960 int pageColumn = qMin(a: 1, b: numColumns - 1);
961
962 if (mac) {
963 mainLayout->setContentsMargins(QMargins());
964 mainLayout->setSpacing(0);
965 buttonLayout->setContentsMargins(left: MacLayoutLeftMargin, top: MacButtonTopMargin, right: MacLayoutRightMargin, bottom: MacLayoutBottomMargin);
966 pageVBoxLayout->setContentsMargins(left: 7, top: 7, right: 7, bottom: 7);
967 } else {
968 if (modern) {
969 mainLayout->setContentsMargins(QMargins());
970 mainLayout->setSpacing(0);
971 pageVBoxLayout->setContentsMargins(left: deltaMarginLeft, top: deltaMarginTop,
972 right: deltaMarginRight, bottom: deltaMarginBottom);
973 buttonLayout->setContentsMargins(left: info.topLevelMarginLeft, top: info.topLevelMarginTop,
974 right: info.topLevelMarginRight, bottom: info.topLevelMarginBottom);
975 } else {
976 mainLayout->setContentsMargins(left: info.topLevelMarginLeft, top: info.topLevelMarginTop,
977 right: info.topLevelMarginRight, bottom: info.topLevelMarginBottom);
978 mainLayout->setHorizontalSpacing(info.hspacing);
979 mainLayout->setVerticalSpacing(info.vspacing);
980 pageVBoxLayout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
981 buttonLayout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
982 }
983 }
984 buttonLayout->setSpacing(info.buttonSpacing);
985
986 if (info.header) {
987 if (!headerWidget)
988 headerWidget = new QWizardHeader(antiFlickerWidget);
989 headerWidget->setAutoFillBackground(modern);
990 mainLayout->addWidget(headerWidget, row: row++, column: 0, rowSpan: 1, columnSpan: numColumns);
991 }
992 if (headerWidget)
993 headerWidget->setVisible(info.header);
994
995 int watermarkStartRow = row;
996
997 if (mac)
998 mainLayout->setRowMinimumHeight(row: row++, minSize: 10);
999
1000 if (info.title) {
1001 if (!titleLabel) {
1002 titleLabel = new QLabel(antiFlickerWidget);
1003 titleLabel->setBackgroundRole(QPalette::Base);
1004 titleLabel->setWordWrap(true);
1005 }
1006
1007 QFont titleFont = q->font();
1008 titleFont.setPointSize(titleFont.pointSize() + (mac ? 3 : 4));
1009 titleFont.setBold(true);
1010 titleLabel->setPalette(QPalette());
1011
1012 if (aero) {
1013 // ### hardcoded for now:
1014 titleFont = QFont("Segoe UI"_L1, 12);
1015 QPalette pal(titleLabel->palette());
1016 pal.setColor(acr: QPalette::Text, acolor: QColor(0x00, 0x33, 0x99));
1017 titleLabel->setPalette(pal);
1018 }
1019
1020 titleLabel->setFont(titleFont);
1021 const int aeroTitleIndent = 25; // ### hardcoded for now - should be calculated somehow
1022 if (aero)
1023 titleLabel->setIndent(aeroTitleIndent);
1024 else if (mac)
1025 titleLabel->setIndent(2);
1026 else if (classic)
1027 titleLabel->setIndent(info.childMarginLeft);
1028 else
1029 titleLabel->setIndent(info.topLevelMarginLeft);
1030 if (modern) {
1031 if (!placeholderWidget1) {
1032 placeholderWidget1 = new QWidget(antiFlickerWidget);
1033 placeholderWidget1->setBackgroundRole(QPalette::Base);
1034 }
1035 placeholderWidget1->setFixedHeight(info.topLevelMarginLeft + 2);
1036 mainLayout->addWidget(placeholderWidget1, row: row++, column: pageColumn);
1037 }
1038 mainLayout->addWidget(titleLabel, row: row++, column: pageColumn);
1039 if (modern) {
1040 if (!placeholderWidget2) {
1041 placeholderWidget2 = new QWidget(antiFlickerWidget);
1042 placeholderWidget2->setBackgroundRole(QPalette::Base);
1043 }
1044 placeholderWidget2->setFixedHeight(5);
1045 mainLayout->addWidget(placeholderWidget2, row: row++, column: pageColumn);
1046 }
1047 if (mac)
1048 mainLayout->setRowMinimumHeight(row: row++, minSize: 7);
1049 }
1050 if (placeholderWidget1)
1051 placeholderWidget1->setVisible(info.title && modern);
1052 if (placeholderWidget2)
1053 placeholderWidget2->setVisible(info.title && modern);
1054
1055 if (info.subTitle) {
1056 if (!subTitleLabel) {
1057 subTitleLabel = new QLabel(pageFrame);
1058 subTitleLabel->setWordWrap(true);
1059
1060 subTitleLabel->setContentsMargins(left: info.childMarginLeft , top: 0,
1061 right: info.childMarginRight , bottom: 0);
1062
1063 pageVBoxLayout->insertWidget(index: 1, widget: subTitleLabel);
1064 }
1065 }
1066
1067 // ### try to replace with margin.
1068 changeSpacerSize(layout: pageVBoxLayout, index: 0, width: 0, height: info.subTitle ? info.childMarginLeft : 0);
1069
1070 int hMargin = mac ? 1 : 0;
1071 int vMargin = hMargin;
1072
1073 pageFrame->setFrameStyle(mac ? (QFrame::Box | QFrame::Raised) : QFrame::NoFrame);
1074 pageFrame->setLineWidth(0);
1075 pageFrame->setMidLineWidth(hMargin);
1076
1077 if (info.header) {
1078 if (modern) {
1079 hMargin = info.topLevelMarginLeft;
1080 vMargin = deltaMarginBottom;
1081 } else if (classic) {
1082 hMargin = deltaMarginLeft + ClassicHMargin;
1083 vMargin = 0;
1084 }
1085 }
1086
1087 if (aero) {
1088 int leftMargin = 18; // ### hardcoded for now - should be calculated somehow
1089 int topMargin = vMargin;
1090 int rightMargin = hMargin; // ### for now
1091 int bottomMargin = vMargin;
1092 pageFrame->setContentsMargins(left: leftMargin, top: topMargin, right: rightMargin, bottom: bottomMargin);
1093 } else {
1094 pageFrame->setContentsMargins(left: hMargin, top: vMargin, right: hMargin, bottom: vMargin);
1095 }
1096
1097 if ((info.watermark || info.sideWidget) && !watermarkLabel) {
1098 watermarkLabel = new QWatermarkLabel(antiFlickerWidget, sideWidget);
1099 watermarkLabel->setBackgroundRole(QPalette::Base);
1100 watermarkLabel->setMinimumHeight(1);
1101 watermarkLabel->setSizePolicy(hor: QSizePolicy::Fixed, ver: QSizePolicy::Expanding);
1102 watermarkLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
1103 }
1104
1105 //bool wasSemiTransparent = pageFrame->testAttribute(Qt::WA_SetPalette);
1106 const bool wasSemiTransparent =
1107 pageFrame->palette().brush(cr: QPalette::Window).color().alpha() < 255
1108 || pageFrame->palette().brush(cr: QPalette::Base).color().alpha() < 255;
1109 if (mac) {
1110 pageFrame->setAutoFillBackground(true);
1111 antiFlickerWidget->setAutoFillBackground(false);
1112 } else {
1113 if (wasSemiTransparent)
1114 pageFrame->setPalette(QPalette());
1115
1116 bool baseBackground = (modern && !info.header); // ### TAG1
1117 pageFrame->setBackgroundRole(baseBackground ? QPalette::Base : QPalette::Window);
1118
1119 if (titleLabel)
1120 titleLabel->setAutoFillBackground(baseBackground);
1121 pageFrame->setAutoFillBackground(baseBackground);
1122 if (watermarkLabel)
1123 watermarkLabel->setAutoFillBackground(baseBackground);
1124 if (placeholderWidget1)
1125 placeholderWidget1->setAutoFillBackground(baseBackground);
1126 if (placeholderWidget2)
1127 placeholderWidget2->setAutoFillBackground(baseBackground);
1128
1129 if (aero) {
1130 QPalette pal = pageFrame->palette();
1131 pal.setBrush(acr: QPalette::Window, abrush: QColor(255, 255, 255));
1132 pageFrame->setPalette(pal);
1133 pageFrame->setAutoFillBackground(true);
1134 pal = antiFlickerWidget->palette();
1135 pal.setBrush(acr: QPalette::Window, abrush: QColor(255, 255, 255));
1136 antiFlickerWidget->setPalette(pal);
1137 antiFlickerWidget->setAutoFillBackground(true);
1138 }
1139 }
1140
1141 mainLayout->addWidget(pageFrame, row: row++, column: pageColumn);
1142
1143 int watermarkEndRow = row;
1144 if (classic)
1145 mainLayout->setRowMinimumHeight(row: row++, minSize: deltaVSpacing);
1146
1147 if (aero) {
1148 buttonLayout->setContentsMargins(left: 9, top: 9, right: 9, bottom: 9);
1149 mainLayout->setContentsMargins(left: 0, top: 11, right: 0, bottom: 0);
1150 }
1151
1152 int buttonStartColumn = info.extension ? 1 : 0;
1153 int buttonNumColumns = info.extension ? 1 : numColumns;
1154
1155 if (classic || modern) {
1156 if (!bottomRuler)
1157 bottomRuler = new QWizardRuler(antiFlickerWidget);
1158 mainLayout->addWidget(bottomRuler, row: row++, column: buttonStartColumn, rowSpan: 1, columnSpan: buttonNumColumns);
1159 }
1160
1161 if (classic)
1162 mainLayout->setRowMinimumHeight(row: row++, minSize: deltaVSpacing);
1163
1164 mainLayout->addLayout(buttonLayout, row: row++, column: buttonStartColumn, rowSpan: 1, columnSpan: buttonNumColumns);
1165
1166 if (info.watermark || info.sideWidget) {
1167 if (info.extension)
1168 watermarkEndRow = row;
1169 mainLayout->addWidget(watermarkLabel, row: watermarkStartRow, column: 0,
1170 rowSpan: watermarkEndRow - watermarkStartRow, columnSpan: 1);
1171 }
1172
1173 mainLayout->setColumnMinimumWidth(column: 0, minSize: mac && !info.watermark ? 181 : 0);
1174 if (mac)
1175 mainLayout->setColumnMinimumWidth(column: 2, minSize: 21);
1176
1177 if (headerWidget)
1178 headerWidget->setVisible(info.header);
1179 if (titleLabel)
1180 titleLabel->setVisible(info.title);
1181 if (subTitleLabel)
1182 subTitleLabel->setVisible(info.subTitle);
1183 if (bottomRuler)
1184 bottomRuler->setVisible(classic || modern);
1185 if (watermarkLabel)
1186 watermarkLabel->setVisible(info.watermark || info.sideWidget);
1187
1188 layoutInfo = info;
1189}
1190
1191void QWizardPrivate::updateLayout()
1192{
1193 Q_Q(QWizard);
1194
1195 disableUpdates();
1196
1197 QWizardLayoutInfo info = layoutInfoForCurrentPage();
1198 if (layoutInfo != info)
1199 recreateLayout(info);
1200 QWizardPage *page = q->currentPage();
1201
1202 // If the page can expand vertically, let it stretch "infinitely" more
1203 // than the QSpacerItem at the bottom. Otherwise, let the QSpacerItem
1204 // stretch "infinitely" more than the page. Change the bottom item's
1205 // policy accordingly. The case that the page has no layout is basically
1206 // for Designer, only.
1207 if (page) {
1208 bool expandPage = !page->layout();
1209 if (!expandPage) {
1210 const QLayoutItem *pageItem = pageVBoxLayout->itemAt(pageVBoxLayout->indexOf(page));
1211 expandPage = pageItem->expandingDirections() & Qt::Vertical;
1212 }
1213 QSpacerItem *bottomSpacer = pageVBoxLayout->itemAt(pageVBoxLayout->count() - 1)->spacerItem();
1214 Q_ASSERT(bottomSpacer);
1215 bottomSpacer->changeSize(w: 0, h: 0, hData: QSizePolicy::Ignored, vData: expandPage ? QSizePolicy::Ignored : QSizePolicy::MinimumExpanding);
1216 pageVBoxLayout->invalidate();
1217 }
1218
1219 if (info.header) {
1220 Q_ASSERT(page);
1221 headerWidget->setup(info, title: page->title(), subTitle: page->subTitle(),
1222 logo: page->pixmap(which: QWizard::LogoPixmap), banner: page->pixmap(which: QWizard::BannerPixmap),
1223 titleFormat: titleFmt, subTitleFormat: subTitleFmt);
1224 }
1225
1226 if (info.watermark || info.sideWidget) {
1227 QPixmap pix;
1228 if (info.watermark) {
1229 if (page)
1230 pix = page->pixmap(which: QWizard::WatermarkPixmap);
1231 else
1232 pix = q->pixmap(which: QWizard::WatermarkPixmap);
1233 }
1234 watermarkLabel->setPixmap(pix); // in case there is no watermark and we show the side widget we need to clear the watermark
1235 }
1236
1237 if (info.title) {
1238 Q_ASSERT(page);
1239 titleLabel->setTextFormat(titleFmt);
1240 titleLabel->setText(page->title());
1241 }
1242 if (info.subTitle) {
1243 Q_ASSERT(page);
1244 subTitleLabel->setTextFormat(subTitleFmt);
1245 subTitleLabel->setText(page->subTitle());
1246 }
1247
1248 enableUpdates();
1249 updateMinMaxSizes(info);
1250}
1251
1252void QWizardPrivate::updatePalette() {
1253 if (wizStyle == QWizard::MacStyle) {
1254 // This is required to ensure visual semitransparency when
1255 // switching from ModernStyle to MacStyle.
1256 // See TAG1 in recreateLayout
1257 // This additionally ensures that the colors are correct
1258 // when the theme is changed.
1259
1260 // we should base the new palette on the default one
1261 // so theme colors will be correct
1262 QPalette newPalette = QApplication::palette(pageFrame);
1263
1264 QColor windowColor = newPalette.brush(cr: QPalette::Window).color();
1265 windowColor.setAlpha(153);
1266 newPalette.setBrush(acr: QPalette::Window, abrush: windowColor);
1267
1268 QColor baseColor = newPalette.brush(cr: QPalette::Base).color();
1269 baseColor.setAlpha(153);
1270 newPalette.setBrush(acr: QPalette::Base, abrush: baseColor);
1271
1272 pageFrame->setPalette(newPalette);
1273 }
1274}
1275
1276void QWizardPrivate::updateMinMaxSizes(const QWizardLayoutInfo &info)
1277{
1278 Q_Q(QWizard);
1279
1280 int extraHeight = 0;
1281#if QT_CONFIG(style_windowsvista)
1282 if (isVistaThemeEnabled())
1283 extraHeight = vistaHelper->titleBarSize() + vistaHelper->topOffset(q);
1284#endif
1285 QSize minimumSize = mainLayout->totalMinimumSize() + QSize(0, extraHeight);
1286 QSize maximumSize = mainLayout->totalMaximumSize();
1287 if (info.header && headerWidget->maximumWidth() != QWIDGETSIZE_MAX) {
1288 minimumSize.setWidth(headerWidget->maximumWidth());
1289 maximumSize.setWidth(headerWidget->maximumWidth());
1290 }
1291 if (info.watermark && !info.sideWidget) {
1292 minimumSize.setHeight(mainLayout->totalSizeHint().height());
1293 }
1294 if (q->minimumWidth() == minimumWidth) {
1295 minimumWidth = minimumSize.width();
1296 q->setMinimumWidth(minimumWidth);
1297 }
1298 if (q->minimumHeight() == minimumHeight) {
1299 minimumHeight = minimumSize.height();
1300 q->setMinimumHeight(minimumHeight);
1301 }
1302 if (q->maximumWidth() == maximumWidth) {
1303 maximumWidth = maximumSize.width();
1304 q->setMaximumWidth(maximumWidth);
1305 }
1306 if (q->maximumHeight() == maximumHeight) {
1307 maximumHeight = maximumSize.height();
1308 q->setMaximumHeight(maximumHeight);
1309 }
1310}
1311
1312void QWizardPrivate::updateCurrentPage()
1313{
1314 Q_Q(QWizard);
1315 if (q->currentPage()) {
1316 canContinue = (q->nextId() != -1);
1317 canFinish = q->currentPage()->isFinalPage();
1318 } else {
1319 canContinue = false;
1320 canFinish = false;
1321 }
1322 _q_updateButtonStates();
1323 updateButtonTexts();
1324}
1325
1326static QString object_name_for_button(QWizard::WizardButton which)
1327{
1328 switch (which) {
1329 case QWizard::CommitButton:
1330 return u"qt_wizard_commit"_s;
1331 case QWizard::FinishButton:
1332 return u"qt_wizard_finish"_s;
1333 case QWizard::CancelButton:
1334 return u"qt_wizard_cancel"_s;
1335 case QWizard::BackButton:
1336 case QWizard::NextButton:
1337 case QWizard::HelpButton:
1338 case QWizard::CustomButton1:
1339 case QWizard::CustomButton2:
1340 case QWizard::CustomButton3:
1341 // Make navigation buttons detectable as passive interactor in designer
1342 return "__qt__passive_wizardbutton"_L1 + QString::number(which);
1343 case QWizard::Stretch:
1344 case QWizard::NoButton:
1345 //case QWizard::NStandardButtons:
1346 //case QWizard::NButtons:
1347 ;
1348 }
1349 Q_UNREACHABLE_RETURN(QString());
1350}
1351
1352bool QWizardPrivate::ensureButton(QWizard::WizardButton which) const
1353{
1354 Q_Q(const QWizard);
1355 if (uint(which) >= QWizard::NButtons)
1356 return false;
1357
1358 if (!btns[which]) {
1359 QPushButton *pushButton = new QPushButton(antiFlickerWidget);
1360 QStyle *style = q->style();
1361 if (style != QApplication::style()) // Propagate style
1362 pushButton->setStyle(style);
1363 pushButton->setObjectName(object_name_for_button(which));
1364#ifdef Q_OS_MACOS
1365 pushButton->setAutoDefault(false);
1366#endif
1367 pushButton->hide();
1368#ifdef Q_CC_HPACC
1369 const_cast<QWizardPrivate *>(this)->btns[which] = pushButton;
1370#else
1371 btns[which] = pushButton;
1372#endif
1373 if (which < QWizard::NStandardButtons)
1374 pushButton->setText(buttonDefaultText(wstyle: wizStyle, which, wizardPrivate: this));
1375
1376 connectButton(which);
1377 }
1378 return true;
1379}
1380
1381void QWizardPrivate::connectButton(QWizard::WizardButton which) const
1382{
1383 Q_Q(const QWizard);
1384 if (which < QWizard::NStandardButtons) {
1385 QObject::connect(sender: btns[which], SIGNAL(clicked()), receiver: q, member: buttonSlots(which));
1386 } else {
1387 QObject::connect(sender: btns[which], SIGNAL(clicked()), receiver: q, SLOT(_q_emitCustomButtonClicked()));
1388 }
1389}
1390
1391void QWizardPrivate::updateButtonTexts()
1392{
1393 Q_Q(QWizard);
1394 for (int i = 0; i < QWizard::NButtons; ++i) {
1395 if (btns[i]) {
1396 if (q->currentPage() && (q->currentPage()->d_func()->buttonCustomTexts.contains(key: i)))
1397 btns[i]->setText(q->currentPage()->d_func()->buttonCustomTexts.value(key: i));
1398 else if (buttonCustomTexts.contains(key: i))
1399 btns[i]->setText(buttonCustomTexts.value(key: i));
1400 else if (i < QWizard::NStandardButtons)
1401 btns[i]->setText(buttonDefaultText(wstyle: wizStyle, which: i, wizardPrivate: this));
1402 }
1403 }
1404 // Vista: Add shortcut for 'next'. Note: native dialogs use ALT-Right
1405 // even in RTL mode, so do the same, even if it might be counter-intuitive.
1406 // The shortcut for 'back' is set in class QVistaBackButton.
1407#if QT_CONFIG(shortcut) && QT_CONFIG(style_windowsvista)
1408 if (btns[QWizard::NextButton] && isVistaThemeEnabled()) {
1409 if (vistaNextShortcut.isNull()) {
1410 vistaNextShortcut =
1411 new QShortcut(QKeySequence(Qt::ALT | Qt::Key_Right),
1412 btns[QWizard::NextButton], SLOT(animateClick()));
1413 }
1414 } else {
1415 delete vistaNextShortcut;
1416 }
1417#endif // shortcut && style_windowsvista
1418}
1419
1420void QWizardPrivate::updateButtonLayout()
1421{
1422 if (buttonsHaveCustomLayout) {
1423 QVarLengthArray<QWizard::WizardButton, QWizard::NButtons> array{
1424 buttonsCustomLayout.cbegin(), buttonsCustomLayout.cend()};
1425 setButtonLayout(array: array.constData(), size: int(array.size()));
1426 } else {
1427 // Positions:
1428 // Help Stretch Custom1 Custom2 Custom3 Cancel Back Next Commit Finish Cancel Help
1429
1430 const int ArraySize = 12;
1431 QWizard::WizardButton array[ArraySize];
1432 memset(s: array, c: -1, n: sizeof(array));
1433 Q_ASSERT(array[0] == QWizard::NoButton);
1434
1435 if (opts & QWizard::HaveHelpButton) {
1436 int i = (opts & QWizard::HelpButtonOnRight) ? 11 : 0;
1437 array[i] = QWizard::HelpButton;
1438 }
1439 array[1] = QWizard::Stretch;
1440 if (opts & QWizard::HaveCustomButton1)
1441 array[2] = QWizard::CustomButton1;
1442 if (opts & QWizard::HaveCustomButton2)
1443 array[3] = QWizard::CustomButton2;
1444 if (opts & QWizard::HaveCustomButton3)
1445 array[4] = QWizard::CustomButton3;
1446
1447 if (!(opts & QWizard::NoCancelButton)) {
1448 int i = (opts & QWizard::CancelButtonOnLeft) ? 5 : 10;
1449 array[i] = QWizard::CancelButton;
1450 }
1451 array[6] = QWizard::BackButton;
1452 array[7] = QWizard::NextButton;
1453 array[8] = QWizard::CommitButton;
1454 array[9] = QWizard::FinishButton;
1455
1456 setButtonLayout(array, size: ArraySize);
1457 }
1458}
1459
1460void QWizardPrivate::setButtonLayout(const QWizard::WizardButton *array, int size)
1461{
1462 QWidget *prev = pageFrame;
1463
1464 for (int i = buttonLayout->count() - 1; i >= 0; --i) {
1465 QLayoutItem *item = buttonLayout->takeAt(i);
1466 if (QWidget *widget = item->widget())
1467 widget->hide();
1468 delete item;
1469 }
1470
1471 for (int i = 0; i < size; ++i) {
1472 QWizard::WizardButton which = array[i];
1473 if (which == QWizard::Stretch) {
1474 buttonLayout->addStretch(stretch: 1);
1475 } else if (which != QWizard::NoButton) {
1476 ensureButton(which);
1477 buttonLayout->addWidget(btns[which]);
1478
1479 // Back, Next, Commit, and Finish are handled in _q_updateButtonStates()
1480 if (which != QWizard::BackButton && which != QWizard::NextButton
1481 && which != QWizard::CommitButton && which != QWizard::FinishButton)
1482 btns[which]->show();
1483
1484 if (prev)
1485 QWidget::setTabOrder(prev, btns[which]);
1486 prev = btns[which];
1487 }
1488 }
1489
1490 _q_updateButtonStates();
1491}
1492
1493bool QWizardPrivate::buttonLayoutContains(QWizard::WizardButton which)
1494{
1495 return !buttonsHaveCustomLayout || buttonsCustomLayout.contains(t: which);
1496}
1497
1498void QWizardPrivate::updatePixmap(QWizard::WizardPixmap which)
1499{
1500 Q_Q(QWizard);
1501 if (which == QWizard::BackgroundPixmap) {
1502 if (wizStyle == QWizard::MacStyle) {
1503 q->update();
1504 q->updateGeometry();
1505 }
1506 } else {
1507 updateLayout();
1508 }
1509}
1510
1511#if QT_CONFIG(style_windowsvista)
1512bool QWizardPrivate::vistaDisabled() const
1513{
1514 Q_Q(const QWizard);
1515 const QVariant v = q->property("_q_wizard_vista_off");
1516 return v.isValid() && v.toBool();
1517}
1518
1519bool QWizardPrivate::handleAeroStyleChange()
1520{
1521 Q_Q(QWizard);
1522
1523 if (inHandleAeroStyleChange)
1524 return false; // prevent recursion
1525 // For top-level wizards, we need the platform window handle for the
1526 // DWM changes. Delay aero initialization to the show event handling if
1527 // it does not exist. If we are a child, skip DWM and just make room by
1528 // moving the antiFlickerWidget.
1529 const bool isWindow = q->isWindow();
1530 if (isWindow && (!q->windowHandle() || !q->windowHandle()->handle()))
1531 return false;
1532 inHandleAeroStyleChange = true;
1533
1534 vistaHelper->disconnectBackButton();
1535 q->removeEventFilter(vistaHelper);
1536
1537 bool vistaMargins = false;
1538
1539 if (isVistaThemeEnabled()) {
1540 const int topOffset = vistaHelper->topOffset(q);
1541 const int topPadding = vistaHelper->topPadding(q);
1542 if (isWindow) {
1543 vistaHelper->setDWMTitleBar(QVistaHelper::ExtendedTitleBar);
1544 q->installEventFilter(vistaHelper);
1545 }
1546 q->setMouseTracking(true);
1547 antiFlickerWidget->move(0, vistaHelper->titleBarSize() + topOffset);
1548 vistaHelper->backButton()->move(
1549 0, topOffset // ### should ideally work without the '+ 1'
1550 - qMin(topOffset, topPadding + 1));
1551 vistaMargins = true;
1552 vistaHelper->backButton()->show();
1553 if (isWindow)
1554 vistaHelper->setTitleBarIconAndCaptionVisible(false);
1555 QObject::connect(
1556 vistaHelper->backButton(), SIGNAL(clicked()), q, buttonSlots(QWizard::BackButton));
1557 vistaHelper->backButton()->show();
1558 } else {
1559 q->setMouseTracking(true); // ### original value possibly different
1560#ifndef QT_NO_CURSOR
1561 q->unsetCursor(); // ### ditto
1562#endif
1563 antiFlickerWidget->move(0, 0);
1564 vistaHelper->hideBackButton();
1565 if (isWindow)
1566 vistaHelper->setTitleBarIconAndCaptionVisible(true);
1567 }
1568
1569 _q_updateButtonStates();
1570
1571 vistaHelper->updateCustomMargins(vistaMargins);
1572
1573 inHandleAeroStyleChange = false;
1574 return true;
1575}
1576#endif
1577
1578bool QWizardPrivate::isVistaThemeEnabled() const
1579{
1580#if QT_CONFIG(style_windowsvista)
1581 return wizStyle == QWizard::AeroStyle && !vistaDisabled();
1582#else
1583 return false;
1584#endif
1585}
1586
1587void QWizardPrivate::disableUpdates()
1588{
1589 Q_Q(QWizard);
1590 if (disableUpdatesCount++ == 0) {
1591 q->setUpdatesEnabled(false);
1592 antiFlickerWidget->hide();
1593 }
1594}
1595
1596void QWizardPrivate::enableUpdates()
1597{
1598 Q_Q(QWizard);
1599 if (--disableUpdatesCount == 0) {
1600 antiFlickerWidget->show();
1601 q->setUpdatesEnabled(true);
1602 }
1603}
1604
1605void QWizardPrivate::_q_emitCustomButtonClicked()
1606{
1607 Q_Q(QWizard);
1608 QObject *button = q->sender();
1609 for (int i = QWizard::NStandardButtons; i < QWizard::NButtons; ++i) {
1610 if (btns[i] == button) {
1611 emit q->customButtonClicked(which: QWizard::WizardButton(i));
1612 break;
1613 }
1614 }
1615}
1616
1617void QWizardPrivate::_q_updateButtonStates()
1618{
1619 Q_Q(QWizard);
1620
1621 disableUpdates();
1622
1623 const QWizardPage *page = q->currentPage();
1624 bool complete = page && page->isComplete();
1625
1626 btn.back->setEnabled(history.size() > 1
1627 && !q->page(id: history.at(i: history.size() - 2))->isCommitPage()
1628 && (!canFinish || !(opts & QWizard::DisabledBackButtonOnLastPage)));
1629 btn.next->setEnabled(canContinue && complete);
1630 btn.commit->setEnabled(canContinue && complete);
1631 btn.finish->setEnabled(canFinish && complete);
1632
1633 const bool backButtonVisible = buttonLayoutContains(which: QWizard::BackButton)
1634 && (history.size() > 1 || !(opts & QWizard::NoBackButtonOnStartPage))
1635 && (canContinue || !(opts & QWizard::NoBackButtonOnLastPage));
1636 bool commitPage = page && page->isCommitPage();
1637 btn.back->setVisible(backButtonVisible);
1638 btn.next->setVisible(buttonLayoutContains(which: QWizard::NextButton) && !commitPage
1639 && (canContinue || (opts & QWizard::HaveNextButtonOnLastPage)));
1640 btn.commit->setVisible(buttonLayoutContains(which: QWizard::CommitButton) && commitPage
1641 && canContinue);
1642 btn.finish->setVisible(buttonLayoutContains(which: QWizard::FinishButton)
1643 && (canFinish || (opts & QWizard::HaveFinishButtonOnEarlyPages)));
1644
1645 if (!(opts & QWizard::NoCancelButton))
1646 btn.cancel->setVisible(buttonLayoutContains(which: QWizard::CancelButton)
1647 && (canContinue || !(opts & QWizard::NoCancelButtonOnLastPage)));
1648
1649 bool useDefault = !(opts & QWizard::NoDefaultButton);
1650 if (QPushButton *nextPush = qobject_cast<QPushButton *>(object: btn.next))
1651 nextPush->setDefault(canContinue && useDefault && !commitPage);
1652 if (QPushButton *commitPush = qobject_cast<QPushButton *>(object: btn.commit))
1653 commitPush->setDefault(canContinue && useDefault && commitPage);
1654 if (QPushButton *finishPush = qobject_cast<QPushButton *>(object: btn.finish))
1655 finishPush->setDefault(!canContinue && useDefault);
1656
1657#if QT_CONFIG(style_windowsvista)
1658 if (isVistaThemeEnabled()) {
1659 vistaHelper->backButton()->setEnabled(btn.back->isEnabled());
1660 vistaHelper->backButton()->setVisible(backButtonVisible);
1661 btn.back->setVisible(false);
1662 }
1663#endif
1664
1665 enableUpdates();
1666}
1667
1668void QWizardPrivate::_q_handleFieldObjectDestroyed(QObject *object)
1669{
1670 int destroyed_index = -1;
1671 QList<QWizardField>::iterator it = fields.begin();
1672 while (it != fields.end()) {
1673 const QWizardField &field = *it;
1674 if (field.object == object) {
1675 destroyed_index = fieldIndexMap.value(key: field.name, defaultValue: -1);
1676 fieldIndexMap.remove(key: field.name);
1677 it = fields.erase(pos: it);
1678 } else {
1679 ++it;
1680 }
1681 }
1682 if (destroyed_index != -1) {
1683 QMap<QString, int>::iterator it2 = fieldIndexMap.begin();
1684 while (it2 != fieldIndexMap.end()) {
1685 int index = it2.value();
1686 if (index > destroyed_index) {
1687 QString field_name = it2.key();
1688 fieldIndexMap.insert(key: field_name, value: index-1);
1689 }
1690 ++it2;
1691 }
1692 }
1693}
1694
1695void QWizardPrivate::setStyle(QStyle *style)
1696{
1697 for (int i = 0; i < QWizard::NButtons; i++)
1698 if (btns[i])
1699 btns[i]->setStyle(style);
1700 const PageMap::const_iterator pcend = pageMap.constEnd();
1701 for (PageMap::const_iterator it = pageMap.constBegin(); it != pcend; ++it)
1702 it.value()->setStyle(style);
1703}
1704
1705#ifdef Q_OS_MACOS
1706QPixmap QWizardPrivate::findDefaultBackgroundPixmap()
1707{
1708 auto *keyboardAssistantURL = [NSWorkspace.sharedWorkspace
1709 URLForApplicationWithBundleIdentifier:@"com.apple.KeyboardSetupAssistant"];
1710 auto *keyboardAssistantBundle = [NSBundle bundleWithURL:keyboardAssistantURL];
1711 auto *assistantBackground = [keyboardAssistantBundle imageForResource:@"Background"];
1712 auto size = QSizeF::fromCGSize(assistantBackground.size);
1713 static const QSizeF expectedSize(242, 414);
1714 if (size == expectedSize)
1715 return qt_mac_toQPixmap(assistantBackground, size);
1716
1717 return QPixmap();
1718}
1719#endif
1720
1721#if QT_CONFIG(style_windowsvista)
1722void QWizardAntiFlickerWidget::paintEvent(QPaintEvent *)
1723{
1724 if (wizardPrivate->isVistaThemeEnabled()) {
1725 int leftMargin, topMargin, rightMargin, bottomMargin;
1726 wizardPrivate->buttonLayout->getContentsMargins(
1727 &leftMargin, &topMargin, &rightMargin, &bottomMargin);
1728 const int buttonLayoutTop = wizardPrivate->buttonLayout->contentsRect().top() - topMargin;
1729 QPainter painter(this);
1730 const QBrush brush(QColor(240, 240, 240)); // ### hardcoded for now
1731 painter.fillRect(0, buttonLayoutTop, width(), height() - buttonLayoutTop, brush);
1732 painter.setPen(QPen(QBrush(QColor(223, 223, 223)), 0)); // ### hardcoded for now
1733 painter.drawLine(0, buttonLayoutTop, width(), buttonLayoutTop);
1734 }
1735}
1736#endif
1737
1738/*!
1739 \class QWizard
1740 \brief The QWizard class provides a framework for wizards.
1741
1742 \inmodule QtWidgets
1743
1744 A wizard (also called an assistant on \macos) is a special type
1745 of input dialog that consists of a sequence of pages. A wizard's
1746 purpose is to guide the user through a process step by step.
1747 Wizards are useful for complex or infrequent tasks that users may
1748 find difficult to learn.
1749
1750 QWizard inherits QDialog and represents a wizard. Each page is a
1751 QWizardPage (a QWidget subclass). To create your own wizards, you
1752 can use these classes directly, or you can subclass them for more
1753 control.
1754
1755 \section1 A Trivial Example
1756
1757 The following example illustrates how to create wizard pages and
1758 add them to a wizard. For more advanced examples, see the
1759 \l{dialogs/licensewizard}{License Wizard}.
1760
1761 \snippet dialogs/trivialwizard/trivialwizard.cpp 1
1762 \snippet dialogs/trivialwizard/trivialwizard.cpp 3
1763 \dots
1764 \snippet dialogs/trivialwizard/trivialwizard.cpp 4
1765 \codeline
1766 \snippet dialogs/trivialwizard/trivialwizard.cpp 5
1767 \snippet dialogs/trivialwizard/trivialwizard.cpp 7
1768 \dots
1769 \snippet dialogs/trivialwizard/trivialwizard.cpp 8
1770 \codeline
1771 \snippet dialogs/trivialwizard/trivialwizard.cpp 10
1772
1773 \section1 Wizard Look and Feel
1774
1775 QWizard supports four wizard looks:
1776
1777 \list
1778 \li ClassicStyle
1779 \li ModernStyle
1780 \li MacStyle
1781 \li AeroStyle
1782 \endlist
1783
1784 You can explicitly set the look to use using setWizardStyle()
1785 (e.g., if you want the same look on all platforms).
1786
1787 \table
1788 \header \li ClassicStyle
1789 \li ModernStyle
1790 \li MacStyle
1791 \li AeroStyle
1792 \row \li \inlineimage qtwizard-classic1.png
1793 \li \inlineimage qtwizard-modern1.png
1794 \li \inlineimage qtwizard-mac1.png
1795 \li \inlineimage qtwizard-aero1.png
1796 \row \li \inlineimage qtwizard-classic2.png
1797 \li \inlineimage qtwizard-modern2.png
1798 \li \inlineimage qtwizard-mac2.png
1799 \li \inlineimage qtwizard-aero2.png
1800 \endtable
1801
1802 Note: AeroStyle has effect only on a Windows Vista system with alpha compositing enabled.
1803 ModernStyle is used as a fallback when this condition is not met.
1804
1805 In addition to the wizard style, there are several options that
1806 control the look and feel of the wizard. These can be set using
1807 setOption() or setOptions(). For example, HaveHelpButton makes
1808 QWizard show a \uicontrol Help button along with the other wizard
1809 buttons.
1810
1811 You can even change the order of the wizard buttons to any
1812 arbitrary order using setButtonLayout(), and you can add up to
1813 three custom buttons (e.g., a \uicontrol Print button) to the button
1814 row. This is achieved by calling setButton() or setButtonText()
1815 with CustomButton1, CustomButton2, or CustomButton3 to set up the
1816 button, and by enabling the HaveCustomButton1, HaveCustomButton2,
1817 or HaveCustomButton3 options. Whenever the user clicks a custom
1818 button, customButtonClicked() is emitted. For example:
1819
1820 \snippet dialogs/licensewizard/licensewizard.cpp 29
1821
1822 \section1 Elements of a Wizard Page
1823
1824 Wizards consist of a sequence of \l{QWizardPage}s. At any time,
1825 only one page is shown. A page has the following attributes:
1826
1827 \list
1828 \li A \l{QWizardPage::}{title}.
1829 \li A \l{QWizardPage::}{subTitle}.
1830 \li A set of pixmaps, which may or may not be honored, depending
1831 on the wizard's style:
1832 \list
1833 \li WatermarkPixmap (used by ClassicStyle and ModernStyle)
1834 \li BannerPixmap (used by ModernStyle)
1835 \li LogoPixmap (used by ClassicStyle and ModernStyle)
1836 \li BackgroundPixmap (used by MacStyle)
1837 \endlist
1838 \endlist
1839
1840 The diagram belows shows how QWizard renders these attributes,
1841 assuming they are all present and ModernStyle is used:
1842
1843 \image qtwizard-nonmacpage.png
1844
1845 When a \l{QWizardPage::}{subTitle} is set, QWizard displays it
1846 in a header, in which case it also uses the BannerPixmap and the
1847 LogoPixmap to decorate the header. The WatermarkPixmap is
1848 displayed on the left side, below the header. At the bottom,
1849 there is a row of buttons allowing the user to navigate through
1850 the pages.
1851
1852 The page itself (the \l{QWizardPage} widget) occupies the area
1853 between the header, the watermark, and the button row. Typically,
1854 the page is a QWizardPage on which a QGridLayout is installed,
1855 with standard child widgets (\l{QLabel}s, \l{QLineEdit}s, etc.).
1856
1857 If the wizard's style is MacStyle, the page looks radically
1858 different:
1859
1860 \image qtwizard-macpage.png
1861
1862 The watermark, banner, and logo pixmaps are ignored by the
1863 MacStyle. If the BackgroundPixmap is set, it is used as the
1864 background for the wizard; otherwise, a default "assistant" image
1865 is used.
1866
1867 The title and subtitle are set by calling
1868 QWizardPage::setTitle() and QWizardPage::setSubTitle() on the
1869 individual pages. They may be plain text or HTML (see titleFormat
1870 and subTitleFormat). The pixmaps can be set globally for the
1871 entire wizard using setPixmap(), or on a per-page basis using
1872 QWizardPage::setPixmap().
1873
1874 \target field mechanism
1875 \section1 Registering and Using Fields
1876
1877 In many wizards, the contents of a page may affect the default
1878 values of the fields of a later page. To make it easy to
1879 communicate between pages, QWizard supports a "field" mechanism
1880 that allows you to register a field (e.g., a QLineEdit) on a page
1881 and to access its value from any page. It is also possible to
1882 specify mandatory fields (i.e., fields that must be filled before
1883 the user can advance to the next page).
1884
1885 To register a field, call QWizardPage::registerField() field.
1886 For example:
1887
1888 \snippet dialogs/licensewizard/licensewizard.cpp 21
1889
1890 The above code registers three fields, \c className, \c
1891 baseClass, and \c qobjectMacro, which are associated with three
1892 child widgets. The asterisk (\c *) next to \c className denotes a
1893 mandatory field.
1894
1895 \target initialize page
1896 The fields of any page are accessible from any other page. For
1897 example:
1898
1899 \snippet dialogs/licensewizard/licensewizard.cpp 27
1900
1901 Here, we call QWizardPage::field() to access the contents of the
1902 \c details.email field (which was defined in the \c DetailsPage)
1903 and use it to initialize the \c ConclusionPage. The field's
1904 contents is returned as a QVariant.
1905
1906 When we create a field using QWizardPage::registerField(), we
1907 pass a unique field name and a widget. We can also provide a Qt
1908 property name and a "changed" signal (a signal that is emitted
1909 when the property changes) as third and fourth arguments;
1910 however, this is not necessary for the most common Qt widgets,
1911 such as QLineEdit, QCheckBox, and QComboBox, because QWizard
1912 knows which properties to look for.
1913
1914 \target mandatory fields
1915
1916 If an asterisk (\c *) is appended to the name when the property
1917 is registered, the field is a \e{mandatory field}. When a page has
1918 mandatory fields, the \uicontrol Next and/or \uicontrol Finish buttons are
1919 enabled only when all mandatory fields are filled.
1920
1921 To consider a field "filled", QWizard simply checks that the
1922 field's current value doesn't equal the original value (the value
1923 it had when initializePage() was called). For QLineEdit and
1924 QAbstractSpinBox subclasses, QWizard also checks that
1925 \l{QLineEdit::hasAcceptableInput()}{hasAcceptableInput()} returns
1926 true, to honor any validator or mask.
1927
1928 QWizard's mandatory field mechanism is provided for convenience.
1929 A more powerful (but also more cumbersome) alternative is to
1930 reimplement QWizardPage::isComplete() and to emit the
1931 QWizardPage::completeChanged() signal whenever the page becomes
1932 complete or incomplete.
1933
1934 The enabled/disabled state of the \uicontrol Next and/or \uicontrol Finish
1935 buttons is one way to perform validation on the user input.
1936 Another way is to reimplement validateCurrentPage() (or
1937 QWizardPage::validatePage()) to perform some last-minute
1938 validation (and show an error message if the user has entered
1939 incomplete or invalid information). If the function returns \c true,
1940 the next page is shown (or the wizard finishes); otherwise, the
1941 current page stays up.
1942
1943 \section1 Creating Linear Wizards
1944
1945 Most wizards have a linear structure, with page 1 followed by
1946 page 2 and so on until the last page. The \l{dialogs/trivialwizard}
1947 {Trivial Wizard} example is such a wizard. With QWizard, linear wizards
1948 are created by instantiating the \l{QWizardPage}s and inserting
1949 them using addPage(). By default, the pages are shown in the
1950 order in which they were added. For example:
1951
1952 \snippet dialogs/trivialwizard/trivialwizard.cpp linearAddPage
1953
1954 When a page is about to be shown, QWizard calls initializePage()
1955 (which in turn calls QWizardPage::initializePage()) to fill the
1956 page with default values. By default, this function does nothing,
1957 but it can be reimplemented to initialize the page's contents
1958 based on other pages' fields (see the \l{initialize page}{example
1959 above}).
1960
1961 If the user presses \uicontrol Back, cleanupPage() is called (which in
1962 turn calls QWizardPage::cleanupPage()). The default
1963 implementation resets the page's fields to their original values
1964 (the values they had before initializePage() was called). If you
1965 want the \uicontrol Back button to be non-destructive and keep the
1966 values entered by the user, simply enable the IndependentPages
1967 option.
1968
1969 \section1 Creating Non-Linear Wizards
1970
1971 Some wizards are more complex in that they allow different
1972 traversal paths based on the information provided by the user.
1973 The \l{dialogs/licensewizard}{License Wizard} example illustrates this.
1974 It provides several wizard pages; depending on which options are
1975 selected, the user can reach different pages.
1976
1977 \image licensewizard-flow.png
1978
1979 In complex wizards, pages are identified by IDs. These IDs are
1980 typically defined using an enum. For example:
1981
1982 \snippet dialogs/licensewizard/licensewizard.h 0
1983 \dots
1984 \snippet dialogs/licensewizard/licensewizard.h 2
1985 \dots
1986 \snippet dialogs/licensewizard/licensewizard.h 3
1987
1988 The pages are inserted using setPage(), which takes an ID and an
1989 instance of QWizardPage (or of a subclass):
1990
1991 \snippet dialogs/licensewizard/licensewizard.cpp 1
1992 \dots
1993 \snippet dialogs/licensewizard/licensewizard.cpp 8
1994
1995 By default, the pages are shown in increasing ID order. To
1996 provide a dynamic order that depends on the options chosen by the
1997 user, we must reimplement QWizardPage::nextId(). For example:
1998
1999 \snippet dialogs/licensewizard/licensewizard.cpp 18
2000 \codeline
2001 \snippet dialogs/licensewizard/licensewizard.cpp 23
2002 \codeline
2003 \snippet dialogs/licensewizard/licensewizard.cpp 24
2004 \codeline
2005 \snippet dialogs/licensewizard/licensewizard.cpp 25
2006 \codeline
2007 \snippet dialogs/licensewizard/licensewizard.cpp 26
2008
2009 It would also be possible to put all the logic in one place, in a
2010 QWizard::nextId() reimplementation. For example:
2011
2012 \snippet code/src_gui_dialogs_qwizard.cpp 0
2013
2014 To start at another page than the page with the lowest ID, call
2015 setStartId().
2016
2017 To test whether a page has been visited or not, call
2018 hasVisitedPage(). For example:
2019
2020 \snippet dialogs/licensewizard/licensewizard.cpp 27
2021
2022 \sa QWizardPage, {Trivial Wizard Example}, {License Wizard Example}
2023*/
2024
2025/*!
2026 \enum QWizard::WizardButton
2027
2028 This enum specifies the buttons in a wizard.
2029
2030 \value BackButton The \uicontrol Back button (\uicontrol {Go Back} on \macos)
2031 \value NextButton The \uicontrol Next button (\uicontrol Continue on \macos)
2032 \value CommitButton The \uicontrol Commit button
2033 \value FinishButton The \uicontrol Finish button (\uicontrol Done on \macos)
2034 \value CancelButton The \uicontrol Cancel button (see also NoCancelButton)
2035 \value HelpButton The \uicontrol Help button (see also HaveHelpButton)
2036 \value CustomButton1 The first user-defined button (see also HaveCustomButton1)
2037 \value CustomButton2 The second user-defined button (see also HaveCustomButton2)
2038 \value CustomButton3 The third user-defined button (see also HaveCustomButton3)
2039
2040 The following value is only useful when calling setButtonLayout():
2041
2042 \value Stretch A horizontal stretch in the button layout
2043
2044 \omitvalue NoButton
2045 \omitvalue NStandardButtons
2046 \omitvalue NButtons
2047
2048 \sa setButton(), setButtonText(), setButtonLayout(), customButtonClicked()
2049*/
2050
2051/*!
2052 \enum QWizard::WizardPixmap
2053
2054 This enum specifies the pixmaps that can be associated with a page.
2055
2056 \value WatermarkPixmap The tall pixmap on the left side of a ClassicStyle or ModernStyle page
2057 \value LogoPixmap The small pixmap on the right side of a ClassicStyle or ModernStyle page header
2058 \value BannerPixmap The pixmap that occupies the background of a ModernStyle page header
2059 \value BackgroundPixmap The pixmap that occupies the background of a MacStyle wizard
2060
2061 \omitvalue NPixmaps
2062
2063 \sa setPixmap(), QWizardPage::setPixmap(), {Elements of a Wizard Page}
2064*/
2065
2066/*!
2067 \enum QWizard::WizardStyle
2068
2069 This enum specifies the different looks supported by QWizard.
2070
2071 \value ClassicStyle Classic Windows look
2072 \value ModernStyle Modern Windows look
2073 \value MacStyle \macos look
2074 \value AeroStyle Windows Aero look
2075
2076 \omitvalue NStyles
2077
2078 \sa setWizardStyle(), WizardOption, {Wizard Look and Feel}
2079*/
2080
2081/*!
2082 \enum QWizard::WizardOption
2083
2084 This enum specifies various options that affect the look and feel
2085 of a wizard.
2086
2087 \value IndependentPages The pages are independent of each other
2088 (i.e., they don't derive values from each
2089 other).
2090 \value IgnoreSubTitles Don't show any subtitles, even if they are set.
2091 \value ExtendedWatermarkPixmap Extend any WatermarkPixmap all the
2092 way down to the window's edge.
2093 \value NoDefaultButton Don't make the \uicontrol Next or \uicontrol Finish button the
2094 dialog's \l{QPushButton::setDefault()}{default button}.
2095 \value NoBackButtonOnStartPage Don't show the \uicontrol Back button on the start page.
2096 \value NoBackButtonOnLastPage Don't show the \uicontrol Back button on the last page.
2097 \value DisabledBackButtonOnLastPage Disable the \uicontrol Back button on the last page.
2098 \value HaveNextButtonOnLastPage Show the (disabled) \uicontrol Next button on the last page.
2099 \value HaveFinishButtonOnEarlyPages Show the (disabled) \uicontrol Finish button on non-final pages.
2100 \value NoCancelButton Don't show the \uicontrol Cancel button.
2101 \value CancelButtonOnLeft Put the \uicontrol Cancel button on the left of \uicontrol Back (rather than on
2102 the right of \uicontrol Finish or \uicontrol Next).
2103 \value HaveHelpButton Show the \uicontrol Help button.
2104 \value HelpButtonOnRight Put the \uicontrol Help button on the far right of the button layout
2105 (rather than on the far left).
2106 \value HaveCustomButton1 Show the first user-defined button (CustomButton1).
2107 \value HaveCustomButton2 Show the second user-defined button (CustomButton2).
2108 \value HaveCustomButton3 Show the third user-defined button (CustomButton3).
2109 \value NoCancelButtonOnLastPage Don't show the \uicontrol Cancel button on the last page.
2110
2111 \sa setOptions(), setOption(), testOption()
2112*/
2113
2114/*!
2115 Constructs a wizard with the given \a parent and window \a flags.
2116
2117 \sa parent(), windowFlags()
2118*/
2119QWizard::QWizard(QWidget *parent, Qt::WindowFlags flags)
2120 : QDialog(*new QWizardPrivate, parent, flags)
2121{
2122 Q_D(QWizard);
2123 d->init();
2124}
2125
2126/*!
2127 Destroys the wizard and its pages, releasing any allocated resources.
2128*/
2129QWizard::~QWizard()
2130{
2131 Q_D(QWizard);
2132 delete d->buttonLayout;
2133}
2134
2135/*!
2136 Adds the given \a page to the wizard, and returns the page's ID.
2137
2138 The ID is guaranteed to be larger than any other ID in the
2139 QWizard so far.
2140
2141 \sa setPage(), page(), pageAdded()
2142*/
2143int QWizard::addPage(QWizardPage *page)
2144{
2145 Q_D(QWizard);
2146 int theid = 0;
2147 if (!d->pageMap.isEmpty())
2148 theid = d->pageMap.lastKey() + 1;
2149 setPage(id: theid, page);
2150 return theid;
2151}
2152
2153/*!
2154 \fn void QWizard::setPage(int id, QWizardPage *page)
2155
2156 Adds the given \a page to the wizard with the given \a id.
2157
2158 \note Adding a page may influence the value of the startId property
2159 in case it was not set explicitly.
2160
2161 \sa addPage(), page(), pageAdded()
2162*/
2163void QWizard::setPage(int theid, QWizardPage *page)
2164{
2165 Q_D(QWizard);
2166
2167 if (Q_UNLIKELY(!page)) {
2168 qWarning(msg: "QWizard::setPage: Cannot insert null page");
2169 return;
2170 }
2171
2172 if (Q_UNLIKELY(theid == -1)) {
2173 qWarning(msg: "QWizard::setPage: Cannot insert page with ID -1");
2174 return;
2175 }
2176
2177 if (Q_UNLIKELY(d->pageMap.contains(theid))) {
2178 qWarning(msg: "QWizard::setPage: Page with duplicate ID %d ignored", theid);
2179 return;
2180 }
2181
2182 page->setParent(d->pageFrame);
2183
2184 QList<QWizardField> &pendingFields = page->d_func()->pendingFields;
2185 for (const auto &field : std::as_const(t&: pendingFields))
2186 d->addField(field);
2187 pendingFields.clear();
2188
2189 connect(sender: page, SIGNAL(completeChanged()), receiver: this, SLOT(_q_updateButtonStates()));
2190
2191 d->pageMap.insert(key: theid, value: page);
2192 page->d_func()->wizard = this;
2193
2194 int n = d->pageVBoxLayout->count();
2195
2196 // disable layout to prevent layout updates while adding
2197 bool pageVBoxLayoutEnabled = d->pageVBoxLayout->isEnabled();
2198 d->pageVBoxLayout->setEnabled(false);
2199
2200 d->pageVBoxLayout->insertWidget(index: n - 1, widget: page);
2201
2202 // hide new page and reset layout to old status
2203 page->hide();
2204 d->pageVBoxLayout->setEnabled(pageVBoxLayoutEnabled);
2205
2206 if (!d->startSetByUser && d->pageMap.constBegin().key() == theid)
2207 d->start = theid;
2208 emit pageAdded(id: theid);
2209}
2210
2211/*!
2212 Removes the page with the given \a id. cleanupPage() will be called if necessary.
2213
2214 \note Removing a page may influence the value of the startId property.
2215
2216 \sa addPage(), setPage(), pageRemoved(), startId()
2217*/
2218void QWizard::removePage(int id)
2219{
2220 Q_D(QWizard);
2221
2222 QWizardPage *removedPage = nullptr;
2223
2224 // update startItem accordingly
2225 if (d->pageMap.size() > 0) { // only if we have any pages
2226 if (d->start == id) {
2227 const int firstId = d->pageMap.constBegin().key();
2228 if (firstId == id) {
2229 if (d->pageMap.size() > 1)
2230 d->start = (++d->pageMap.constBegin()).key(); // secondId
2231 else
2232 d->start = -1; // removing the last page
2233 } else { // startSetByUser has to be "true" here
2234 d->start = firstId;
2235 }
2236 d->startSetByUser = false;
2237 }
2238 }
2239
2240 if (d->pageMap.contains(key: id))
2241 emit pageRemoved(id);
2242
2243 if (!d->history.contains(t: id)) {
2244 // Case 1: removing a page not in the history
2245 removedPage = d->pageMap.take(key: id);
2246 d->updateCurrentPage();
2247 } else if (id != d->current) {
2248 // Case 2: removing a page in the history before the current page
2249 removedPage = d->pageMap.take(key: id);
2250 d->history.removeOne(t: id);
2251 d->_q_updateButtonStates();
2252 } else if (d->history.size() == 1) {
2253 // Case 3: removing the current page which is the first (and only) one in the history
2254 d->reset();
2255 removedPage = d->pageMap.take(key: id);
2256 if (d->pageMap.isEmpty())
2257 d->updateCurrentPage();
2258 else
2259 restart();
2260 } else {
2261 // Case 4: removing the current page which is not the first one in the history
2262 back();
2263 removedPage = d->pageMap.take(key: id);
2264 d->updateCurrentPage();
2265 }
2266
2267 if (removedPage) {
2268 if (removedPage->d_func()->initialized) {
2269 cleanupPage(id);
2270 removedPage->d_func()->initialized = false;
2271 }
2272
2273 d->pageVBoxLayout->removeWidget(w: removedPage);
2274
2275 for (int i = d->fields.size() - 1; i >= 0; --i) {
2276 if (d->fields.at(i).page == removedPage) {
2277 removedPage->d_func()->pendingFields += d->fields.at(i);
2278 d->removeFieldAt(index: i);
2279 }
2280 }
2281 }
2282}
2283
2284/*!
2285 \fn QWizardPage *QWizard::page(int id) const
2286
2287 Returns the page with the given \a id, or \nullptr if there is no
2288 such page.
2289
2290 \sa addPage(), setPage()
2291*/
2292QWizardPage *QWizard::page(int theid) const
2293{
2294 Q_D(const QWizard);
2295 return d->pageMap.value(key: theid);
2296}
2297
2298/*!
2299 \fn bool QWizard::hasVisitedPage(int id) const
2300
2301 Returns \c true if the page history contains page \a id; otherwise,
2302 returns \c false.
2303
2304 Pressing \uicontrol Back marks the current page as "unvisited" again.
2305
2306 \sa visitedIds()
2307*/
2308bool QWizard::hasVisitedPage(int theid) const
2309{
2310 Q_D(const QWizard);
2311 return d->history.contains(t: theid);
2312}
2313
2314/*!
2315 \since 5.15
2316
2317 Returns the list of IDs of visited pages, in the order in which the pages
2318 were visited.
2319
2320 \sa hasVisitedPage()
2321*/
2322QList<int> QWizard::visitedIds() const
2323{
2324 Q_D(const QWizard);
2325 return d->history;
2326}
2327
2328/*!
2329 Returns the list of page IDs.
2330*/
2331QList<int> QWizard::pageIds() const
2332{
2333 Q_D(const QWizard);
2334 return d->pageMap.keys();
2335}
2336
2337/*!
2338 \property QWizard::startId
2339 \brief the ID of the first page
2340
2341 If this property isn't explicitly set, this property defaults to
2342 the lowest page ID in this wizard, or -1 if no page has been
2343 inserted yet.
2344
2345 \sa restart(), nextId()
2346*/
2347void QWizard::setStartId(int theid)
2348{
2349 Q_D(QWizard);
2350 int newStart = theid;
2351 if (theid == -1)
2352 newStart = d->pageMap.size() ? d->pageMap.constBegin().key() : -1;
2353
2354 if (d->start == newStart) {
2355 d->startSetByUser = theid != -1;
2356 return;
2357 }
2358
2359 if (Q_UNLIKELY(!d->pageMap.contains(newStart))) {
2360 qWarning(msg: "QWizard::setStartId: Invalid page ID %d", newStart);
2361 return;
2362 }
2363 d->start = newStart;
2364 d->startSetByUser = theid != -1;
2365}
2366
2367int QWizard::startId() const
2368{
2369 Q_D(const QWizard);
2370 return d->start;
2371}
2372
2373/*!
2374 Returns a pointer to the current page, or \nullptr if there is no
2375 current page (e.g., before the wizard is shown).
2376
2377 This is equivalent to calling page(currentId()).
2378
2379 \sa page(), currentId(), restart()
2380*/
2381QWizardPage *QWizard::currentPage() const
2382{
2383 Q_D(const QWizard);
2384 return page(theid: d->current);
2385}
2386
2387/*!
2388 \property QWizard::currentId
2389 \brief the ID of the current page
2390
2391 By default, this property has a value of -1, indicating that no page is
2392 currently shown.
2393
2394 \sa currentPage()
2395*/
2396int QWizard::currentId() const
2397{
2398 Q_D(const QWizard);
2399 return d->current;
2400}
2401
2402/*!
2403 Sets the value of the field called \a name to \a value.
2404
2405 This function can be used to set fields on any page of the wizard.
2406
2407 \sa QWizardPage::registerField(), QWizardPage::setField(), field()
2408*/
2409void QWizard::setField(const QString &name, const QVariant &value)
2410{
2411 Q_D(QWizard);
2412
2413 int index = d->fieldIndexMap.value(key: name, defaultValue: -1);
2414 if (Q_UNLIKELY(index == -1)) {
2415 qWarning(msg: "QWizard::setField: No such field '%ls'", qUtf16Printable(name));
2416 return;
2417 }
2418
2419 const QWizardField &field = d->fields.at(i: index);
2420 if (Q_UNLIKELY(!field.object->setProperty(field.property, value)))
2421 qWarning(msg: "QWizard::setField: Couldn't write to property '%s'",
2422 field.property.constData());
2423}
2424
2425/*!
2426 Returns the value of the field called \a name.
2427
2428 This function can be used to access fields on any page of the wizard.
2429
2430 \sa QWizardPage::registerField(), QWizardPage::field(), setField()
2431*/
2432QVariant QWizard::field(const QString &name) const
2433{
2434 Q_D(const QWizard);
2435
2436 int index = d->fieldIndexMap.value(key: name, defaultValue: -1);
2437 if (Q_UNLIKELY(index == -1)) {
2438 qWarning(msg: "QWizard::field: No such field '%ls'", qUtf16Printable(name));
2439 return QVariant();
2440 }
2441
2442 const QWizardField &field = d->fields.at(i: index);
2443 return field.object->property(name: field.property);
2444}
2445
2446/*!
2447 \property QWizard::wizardStyle
2448 \brief the look and feel of the wizard
2449
2450 By default, QWizard uses the AeroStyle on a Windows Vista system with alpha compositing
2451 enabled, regardless of the current widget style. If this is not the case, the default
2452 wizard style depends on the current widget style as follows: MacStyle is the default if
2453 the current widget style is QMacStyle, ModernStyle is the default if the current widget
2454 style is QWindowsStyle, and ClassicStyle is the default in all other cases.
2455
2456 \sa {Wizard Look and Feel}, options
2457*/
2458void QWizard::setWizardStyle(WizardStyle style)
2459{
2460 Q_D(QWizard);
2461
2462 const bool styleChange = style != d->wizStyle;
2463
2464#if QT_CONFIG(style_windowsvista)
2465 const bool aeroStyleChange =
2466 d->vistaInitPending || d->vistaStateChanged || (styleChange && (style == AeroStyle || d->wizStyle == AeroStyle));
2467 d->vistaStateChanged = false;
2468 d->vistaInitPending = false;
2469#endif
2470
2471 if (styleChange
2472#if QT_CONFIG(style_windowsvista)
2473 || aeroStyleChange
2474#endif
2475 ) {
2476 d->disableUpdates();
2477 d->wizStyle = style;
2478 d->updateButtonTexts();
2479#if QT_CONFIG(style_windowsvista)
2480 if (aeroStyleChange) {
2481 //Send a resizeevent since the antiflicker widget probably needs a new size
2482 //because of the backbutton in the window title
2483 QResizeEvent ev(geometry().size(), geometry().size());
2484 QCoreApplication::sendEvent(this, &ev);
2485 }
2486#endif
2487 d->updateLayout();
2488 updateGeometry();
2489 d->enableUpdates();
2490#if QT_CONFIG(style_windowsvista)
2491 // Delay initialization when activating Aero style fails due to missing native window.
2492 if (aeroStyleChange && !d->handleAeroStyleChange() && d->wizStyle == AeroStyle)
2493 d->vistaInitPending = true;
2494#endif
2495 }
2496}
2497
2498QWizard::WizardStyle QWizard::wizardStyle() const
2499{
2500 Q_D(const QWizard);
2501 return d->wizStyle;
2502}
2503
2504/*!
2505 Sets the given \a option to be enabled if \a on is true;
2506 otherwise, clears the given \a option.
2507
2508 \sa options, testOption(), setWizardStyle()
2509*/
2510void QWizard::setOption(WizardOption option, bool on)
2511{
2512 Q_D(QWizard);
2513 if (!(d->opts & option) != !on)
2514 setOptions(d->opts ^ option);
2515}
2516
2517/*!
2518 Returns \c true if the given \a option is enabled; otherwise, returns
2519 false.
2520
2521 \sa options, setOption(), setWizardStyle()
2522*/
2523bool QWizard::testOption(WizardOption option) const
2524{
2525 Q_D(const QWizard);
2526 return (d->opts & option) != 0;
2527}
2528
2529/*!
2530 \property QWizard::options
2531 \brief the various options that affect the look and feel of the wizard
2532
2533 By default, the following options are set (depending on the platform):
2534
2535 \list
2536 \li Windows: HelpButtonOnRight.
2537 \li \macos: NoDefaultButton and NoCancelButton.
2538 \li X11 and QWS (Qt for Embedded Linux): none.
2539 \endlist
2540
2541 \sa wizardStyle
2542*/
2543void QWizard::setOptions(WizardOptions options)
2544{
2545 Q_D(QWizard);
2546
2547 WizardOptions changed = (options ^ d->opts);
2548 if (!changed)
2549 return;
2550
2551 d->disableUpdates();
2552
2553 d->opts = options;
2554 if ((changed & IndependentPages) && !(d->opts & IndependentPages))
2555 d->cleanupPagesNotInHistory();
2556
2557 if (changed & (NoDefaultButton | HaveHelpButton | HelpButtonOnRight | NoCancelButton
2558 | CancelButtonOnLeft | HaveCustomButton1 | HaveCustomButton2
2559 | HaveCustomButton3)) {
2560 d->updateButtonLayout();
2561 } else if (changed & (NoBackButtonOnStartPage | NoBackButtonOnLastPage
2562 | HaveNextButtonOnLastPage | HaveFinishButtonOnEarlyPages
2563 | DisabledBackButtonOnLastPage | NoCancelButtonOnLastPage)) {
2564 d->_q_updateButtonStates();
2565 }
2566
2567 d->enableUpdates();
2568 d->updateLayout();
2569}
2570
2571QWizard::WizardOptions QWizard::options() const
2572{
2573 Q_D(const QWizard);
2574 return d->opts;
2575}
2576
2577/*!
2578 Sets the text on button \a which to be \a text.
2579
2580 By default, the text on buttons depends on the wizardStyle. For
2581 example, on \macos, the \uicontrol Next button is called \uicontrol
2582 Continue.
2583
2584 To add extra buttons to the wizard (e.g., a \uicontrol Print button),
2585 one way is to call setButtonText() with CustomButton1,
2586 CustomButton2, or CustomButton3 to set their text, and make the
2587 buttons visible using the HaveCustomButton1, HaveCustomButton2,
2588 and/or HaveCustomButton3 options.
2589
2590 Button texts may also be set on a per-page basis using QWizardPage::setButtonText().
2591
2592 \sa setButton(), button(), setButtonLayout(), setOptions(), QWizardPage::setButtonText()
2593*/
2594void QWizard::setButtonText(WizardButton which, const QString &text)
2595{
2596 Q_D(QWizard);
2597
2598 if (!d->ensureButton(which))
2599 return;
2600
2601 d->buttonCustomTexts.insert(key: which, value: text);
2602
2603 if (!currentPage() || !currentPage()->d_func()->buttonCustomTexts.contains(key: which))
2604 d->btns[which]->setText(text);
2605}
2606
2607/*!
2608 Returns the text on button \a which.
2609
2610 If a text has ben set using setButtonText(), this text is returned.
2611
2612 By default, the text on buttons depends on the wizardStyle. For
2613 example, on \macos, the \uicontrol Next button is called \uicontrol
2614 Continue.
2615
2616 \sa button(), setButton(), setButtonText(), QWizardPage::buttonText(),
2617 QWizardPage::setButtonText()
2618*/
2619QString QWizard::buttonText(WizardButton which) const
2620{
2621 Q_D(const QWizard);
2622
2623 if (!d->ensureButton(which))
2624 return QString();
2625
2626 if (d->buttonCustomTexts.contains(key: which))
2627 return d->buttonCustomTexts.value(key: which);
2628
2629 const QString defText = buttonDefaultText(wstyle: d->wizStyle, which, wizardPrivate: d);
2630 if (!defText.isNull())
2631 return defText;
2632
2633 return d->btns[which]->text();
2634}
2635
2636/*!
2637 Sets the order in which buttons are displayed to \a layout, where
2638 \a layout is a list of \l{WizardButton}s.
2639
2640 The default layout depends on the options (e.g., whether
2641 HelpButtonOnRight) that are set. You can call this function if
2642 you need more control over the buttons' layout than what \l
2643 options already provides.
2644
2645 You can specify horizontal stretches in the layout using \l
2646 Stretch.
2647
2648 Example:
2649
2650 \snippet code/src_gui_dialogs_qwizard.cpp 1
2651
2652 \sa setButton(), setButtonText(), setOptions()
2653*/
2654void QWizard::setButtonLayout(const QList<WizardButton> &layout)
2655{
2656 Q_D(QWizard);
2657
2658 for (int i = 0; i < layout.size(); ++i) {
2659 WizardButton button1 = layout.at(i);
2660
2661 if (button1 == NoButton || button1 == Stretch)
2662 continue;
2663 if (!d->ensureButton(which: button1))
2664 return;
2665
2666 // O(n^2), but n is very small
2667 for (int j = 0; j < i; ++j) {
2668 WizardButton button2 = layout.at(i: j);
2669 if (Q_UNLIKELY(button2 == button1)) {
2670 qWarning(msg: "QWizard::setButtonLayout: Duplicate button in layout");
2671 return;
2672 }
2673 }
2674 }
2675
2676 d->buttonsHaveCustomLayout = true;
2677 d->buttonsCustomLayout = layout;
2678 d->updateButtonLayout();
2679}
2680
2681/*!
2682 Sets the button corresponding to role \a which to \a button.
2683
2684 To add extra buttons to the wizard (e.g., a \uicontrol Print button),
2685 one way is to call setButton() with CustomButton1 to
2686 CustomButton3, and make the buttons visible using the
2687 HaveCustomButton1 to HaveCustomButton3 options.
2688
2689 \sa setButtonText(), setButtonLayout(), options
2690*/
2691void QWizard::setButton(WizardButton which, QAbstractButton *button)
2692{
2693 Q_D(QWizard);
2694
2695 if (uint(which) >= NButtons || d->btns[which] == button)
2696 return;
2697
2698 if (QAbstractButton *oldButton = d->btns[which]) {
2699 d->buttonLayout->removeWidget(w: oldButton);
2700 delete oldButton;
2701 }
2702
2703 d->btns[which] = button;
2704 if (button) {
2705 button->setParent(d->antiFlickerWidget);
2706 d->buttonCustomTexts.insert(key: which, value: button->text());
2707 d->connectButton(which);
2708 } else {
2709 d->buttonCustomTexts.remove(key: which); // ### what about page-specific texts set for 'which'
2710 d->ensureButton(which); // (QWizardPage::setButtonText())? Clear them as well?
2711 }
2712
2713 d->updateButtonLayout();
2714}
2715
2716/*!
2717 Returns the button corresponding to role \a which.
2718
2719 \sa setButton(), setButtonText()
2720*/
2721QAbstractButton *QWizard::button(WizardButton which) const
2722{
2723 Q_D(const QWizard);
2724#if QT_CONFIG(style_windowsvista)
2725 if (d->wizStyle == AeroStyle && which == BackButton)
2726 return d->vistaHelper->backButton();
2727#endif
2728 if (!d->ensureButton(which))
2729 return nullptr;
2730 return d->btns[which];
2731}
2732
2733/*!
2734 \property QWizard::titleFormat
2735 \brief the text format used by page titles
2736
2737 The default format is Qt::AutoText.
2738
2739 \sa QWizardPage::title, subTitleFormat
2740*/
2741void QWizard::setTitleFormat(Qt::TextFormat format)
2742{
2743 Q_D(QWizard);
2744 d->titleFmt = format;
2745 d->updateLayout();
2746}
2747
2748Qt::TextFormat QWizard::titleFormat() const
2749{
2750 Q_D(const QWizard);
2751 return d->titleFmt;
2752}
2753
2754/*!
2755 \property QWizard::subTitleFormat
2756 \brief the text format used by page subtitles
2757
2758 The default format is Qt::AutoText.
2759
2760 \sa QWizardPage::title, titleFormat
2761*/
2762void QWizard::setSubTitleFormat(Qt::TextFormat format)
2763{
2764 Q_D(QWizard);
2765 d->subTitleFmt = format;
2766 d->updateLayout();
2767}
2768
2769Qt::TextFormat QWizard::subTitleFormat() const
2770{
2771 Q_D(const QWizard);
2772 return d->subTitleFmt;
2773}
2774
2775/*!
2776 Sets the pixmap for role \a which to \a pixmap.
2777
2778 The pixmaps are used by QWizard when displaying a page. Which
2779 pixmaps are actually used depend on the \l{Wizard Look and
2780 Feel}{wizard style}.
2781
2782 Pixmaps can also be set for a specific page using
2783 QWizardPage::setPixmap().
2784
2785 \sa QWizardPage::setPixmap(), {Elements of a Wizard Page}
2786*/
2787void QWizard::setPixmap(WizardPixmap which, const QPixmap &pixmap)
2788{
2789 Q_D(QWizard);
2790 Q_ASSERT(uint(which) < NPixmaps);
2791 d->defaultPixmaps[which] = pixmap;
2792 d->updatePixmap(which);
2793}
2794
2795/*!
2796 Returns the pixmap set for role \a which.
2797
2798 By default, the only pixmap that is set is the BackgroundPixmap on
2799 \macos.
2800
2801 \sa QWizardPage::pixmap(), {Elements of a Wizard Page}
2802*/
2803QPixmap QWizard::pixmap(WizardPixmap which) const
2804{
2805 Q_D(const QWizard);
2806 Q_ASSERT(uint(which) < NPixmaps);
2807#ifdef Q_OS_MACOS
2808 if (which == BackgroundPixmap && d->defaultPixmaps[BackgroundPixmap].isNull())
2809 d->defaultPixmaps[BackgroundPixmap] = d->findDefaultBackgroundPixmap();
2810#endif
2811 return d->defaultPixmaps[which];
2812}
2813
2814/*!
2815 Sets the default property for \a className to be \a property,
2816 and the associated change signal to be \a changedSignal.
2817
2818 The default property is used when an instance of \a className (or
2819 of one of its subclasses) is passed to
2820 QWizardPage::registerField() and no property is specified.
2821
2822 QWizard knows the most common Qt widgets. For these (or their
2823 subclasses), you don't need to specify a \a property or a \a
2824 changedSignal. The table below lists these widgets:
2825
2826 \table
2827 \header \li Widget \li Property \li Change Notification Signal
2828 \row \li QAbstractButton \li bool \l{QAbstractButton::}{checked} \li \l{QAbstractButton::}{toggled()}
2829 \row \li QAbstractSlider \li int \l{QAbstractSlider::}{value} \li \l{QAbstractSlider::}{valueChanged()}
2830 \row \li QComboBox \li int \l{QComboBox::}{currentIndex} \li \l{QComboBox::}{currentIndexChanged()}
2831 \row \li QDateTimeEdit \li QDateTime \l{QDateTimeEdit::}{dateTime} \li \l{QDateTimeEdit::}{dateTimeChanged()}
2832 \row \li QLineEdit \li QString \l{QLineEdit::}{text} \li \l{QLineEdit::}{textChanged()}
2833 \row \li QListWidget \li int \l{QListWidget::}{currentRow} \li \l{QListWidget::}{currentRowChanged()}
2834 \row \li QSpinBox \li int \l{QSpinBox::}{value} \li \l{QSpinBox::}{valueChanged()}
2835 \endtable
2836
2837 \sa QWizardPage::registerField()
2838*/
2839void QWizard::setDefaultProperty(const char *className, const char *property,
2840 const char *changedSignal)
2841{
2842 Q_D(QWizard);
2843 for (int i = d->defaultPropertyTable.size() - 1; i >= 0; --i) {
2844 if (qstrcmp(str1: d->defaultPropertyTable.at(i).className, str2: className) == 0) {
2845 d->defaultPropertyTable.remove(i);
2846 break;
2847 }
2848 }
2849 d->defaultPropertyTable.append(t: QWizardDefaultProperty(className, property, changedSignal));
2850}
2851
2852/*!
2853 Sets the given \a widget to be shown on the left side of the wizard.
2854 For styles which use the WatermarkPixmap (ClassicStyle and ModernStyle)
2855 the side widget is displayed on top of the watermark, for other styles
2856 or when the watermark is not provided the side widget is displayed
2857 on the left side of the wizard.
2858
2859 Passing \nullptr shows no side widget.
2860
2861 When the \a widget is not \nullptr the wizard reparents it.
2862
2863 Any previous side widget is hidden.
2864
2865 You may call setSideWidget() with the same widget at different
2866 times.
2867
2868 All widgets set here will be deleted by the wizard when it is
2869 destroyed unless you separately reparent the widget after setting
2870 some other side widget (or \nullptr).
2871
2872 By default, no side widget is present.
2873*/
2874void QWizard::setSideWidget(QWidget *widget)
2875{
2876 Q_D(QWizard);
2877
2878 d->sideWidget = widget;
2879 if (d->watermarkLabel) {
2880 d->watermarkLabel->setSideWidget(widget);
2881 d->updateLayout();
2882 }
2883}
2884
2885/*!
2886 Returns the widget on the left side of the wizard or \nullptr.
2887
2888 By default, no side widget is present.
2889*/
2890QWidget *QWizard::sideWidget() const
2891{
2892 Q_D(const QWizard);
2893
2894 return d->sideWidget;
2895}
2896
2897/*!
2898 \reimp
2899*/
2900void QWizard::setVisible(bool visible)
2901{
2902 Q_D(QWizard);
2903 if (visible) {
2904 if (d->current == -1)
2905 restart();
2906 }
2907 QDialog::setVisible(visible);
2908}
2909
2910/*!
2911 \reimp
2912*/
2913QSize QWizard::sizeHint() const
2914{
2915 Q_D(const QWizard);
2916 QSize result = d->mainLayout->totalSizeHint();
2917 QSize extra(500, 360);
2918 if (d->wizStyle == MacStyle && d->current != -1) {
2919 QSize pixmap(currentPage()->pixmap(which: BackgroundPixmap).size());
2920 extra.setWidth(616);
2921 if (!pixmap.isNull()) {
2922 extra.setHeight(pixmap.height());
2923
2924 /*
2925 The width isn't always reliable as a size hint, as
2926 some wizard backgrounds just cover the leftmost area.
2927 Use a rule of thumb to determine if the width is
2928 reliable or not.
2929 */
2930 if (pixmap.width() >= pixmap.height())
2931 extra.setWidth(pixmap.width());
2932 }
2933 }
2934 return result.expandedTo(otherSize: extra);
2935}
2936
2937/*!
2938 \fn void QWizard::currentIdChanged(int id)
2939
2940 This signal is emitted when the current page changes, with the new
2941 current \a id.
2942
2943 \sa currentId(), currentPage()
2944*/
2945
2946/*!
2947 \fn void QWizard::pageAdded(int id)
2948
2949 This signal is emitted whenever a page is added to the
2950 wizard. The page's \a id is passed as parameter.
2951
2952 \sa addPage(), setPage(), startId()
2953*/
2954
2955/*!
2956 \fn void QWizard::pageRemoved(int id)
2957
2958 This signal is emitted whenever a page is removed from the
2959 wizard. The page's \a id is passed as parameter.
2960
2961 \sa removePage(), startId()
2962*/
2963
2964/*!
2965 \fn void QWizard::helpRequested()
2966
2967 This signal is emitted when the user clicks the \uicontrol Help button.
2968
2969 By default, no \uicontrol Help button is shown. Call
2970 setOption(HaveHelpButton, true) to have one.
2971
2972 Example:
2973
2974 \snippet dialogs/licensewizard/licensewizard.cpp 0
2975 \dots
2976 \snippet dialogs/licensewizard/licensewizard.cpp 5
2977 \snippet dialogs/licensewizard/licensewizard.cpp 7
2978 \dots
2979 \snippet dialogs/licensewizard/licensewizard.cpp 8
2980 \codeline
2981 \snippet dialogs/licensewizard/licensewizard.cpp 10
2982 \dots
2983 \snippet dialogs/licensewizard/licensewizard.cpp 12
2984 \codeline
2985 \snippet dialogs/licensewizard/licensewizard.cpp 14
2986 \codeline
2987 \snippet dialogs/licensewizard/licensewizard.cpp 15
2988
2989 \sa customButtonClicked()
2990*/
2991
2992/*!
2993 \fn void QWizard::customButtonClicked(int which)
2994
2995 This signal is emitted when the user clicks a custom button. \a
2996 which can be CustomButton1, CustomButton2, or CustomButton3.
2997
2998 By default, no custom button is shown. Call setOption() with
2999 HaveCustomButton1, HaveCustomButton2, or HaveCustomButton3 to have
3000 one, and use setButtonText() or setButton() to configure it.
3001
3002 \sa helpRequested()
3003*/
3004
3005/*!
3006 Goes back to the previous page.
3007
3008 This is equivalent to pressing the \uicontrol Back button.
3009
3010 \sa next(), accept(), reject(), restart()
3011*/
3012void QWizard::back()
3013{
3014 Q_D(QWizard);
3015 int n = d->history.size() - 2;
3016 if (n < 0)
3017 return;
3018 d->switchToPage(newId: d->history.at(i: n), direction: QWizardPrivate::Backward);
3019}
3020
3021/*!
3022 Advances to the next page.
3023
3024 This is equivalent to pressing the \uicontrol Next or \uicontrol Commit button.
3025
3026 \sa nextId(), back(), accept(), reject(), restart()
3027*/
3028void QWizard::next()
3029{
3030 Q_D(QWizard);
3031
3032 if (d->current == -1)
3033 return;
3034
3035 if (validateCurrentPage()) {
3036 int next = nextId();
3037 if (next != -1) {
3038 if (Q_UNLIKELY(d->history.contains(next))) {
3039 qWarning(msg: "QWizard::next: Page %d already met", next);
3040 return;
3041 }
3042 if (Q_UNLIKELY(!d->pageMap.contains(next))) {
3043 qWarning(msg: "QWizard::next: No such page %d", next);
3044 return;
3045 }
3046 d->switchToPage(newId: next, direction: QWizardPrivate::Forward);
3047 }
3048 }
3049}
3050
3051/*!
3052 Sets currentId to \a id, without visiting the pages between currentId and \a id.
3053
3054 Returns without page change, if
3055 \list
3056 \li wizard holds no pages
3057 \li current page is invalid
3058 \li given page equals currentId()
3059 \li given page is out of range
3060 \endlist
3061
3062 Note: If pages have been forward skipped and \a id is 0, page visiting history
3063 will be deleted
3064*/
3065
3066void QWizard::setCurrentId(int id)
3067{
3068 Q_D(QWizard);
3069
3070 if (d->current == -1)
3071 return;
3072
3073 if (currentId() == id)
3074 return;
3075
3076 if (!validateCurrentPage())
3077 return;
3078
3079 if (id < 0 || Q_UNLIKELY(!d->pageMap.contains(id))) {
3080 qWarning(msg: "QWizard::setCurrentId: No such page: %d", id);
3081 return;
3082 }
3083
3084 d->switchToPage(newId: id, direction: (id < currentId()) ? QWizardPrivate::Backward : QWizardPrivate::Forward);
3085}
3086
3087/*!
3088 Restarts the wizard at the start page. This function is called automatically when the
3089 wizard is shown.
3090
3091 \sa startId()
3092*/
3093void QWizard::restart()
3094{
3095 Q_D(QWizard);
3096 d->disableUpdates();
3097 d->reset();
3098 d->switchToPage(newId: startId(), direction: QWizardPrivate::Forward);
3099 d->enableUpdates();
3100}
3101
3102/*!
3103 \reimp
3104*/
3105bool QWizard::event(QEvent *event)
3106{
3107 Q_D(QWizard);
3108 if (event->type() == QEvent::StyleChange) { // Propagate style
3109 d->setStyle(style());
3110 d->updateLayout();
3111 } else if (event->type() == QEvent::PaletteChange) { // Emitted on theme change
3112 d->updatePalette();
3113 }
3114#if QT_CONFIG(style_windowsvista)
3115 else if (event->type() == QEvent::Show && d->vistaInitPending) {
3116 d->vistaInitPending = false;
3117 d->wizStyle = AeroStyle;
3118 d->handleAeroStyleChange();
3119 }
3120 else if (d->isVistaThemeEnabled()) {
3121 if (event->type() == QEvent::Resize
3122 || event->type() == QEvent::LayoutDirectionChange) {
3123 const int buttonLeft = (layoutDirection() == Qt::RightToLeft
3124 ? width() - d->vistaHelper->backButton()->sizeHint().width()
3125 : 0);
3126
3127 d->vistaHelper->backButton()->move(buttonLeft,
3128 d->vistaHelper->backButton()->y());
3129 }
3130
3131 d->vistaHelper->mouseEvent(event);
3132 }
3133#endif
3134 return QDialog::event(event);
3135}
3136
3137/*!
3138 \reimp
3139*/
3140void QWizard::resizeEvent(QResizeEvent *event)
3141{
3142 Q_D(QWizard);
3143 int heightOffset = 0;
3144#if QT_CONFIG(style_windowsvista)
3145 if (d->isVistaThemeEnabled()) {
3146 heightOffset = d->vistaHelper->topOffset(this);
3147 heightOffset += d->vistaHelper->titleBarSize();
3148 }
3149#endif
3150 d->antiFlickerWidget->resize(w: event->size().width(), h: event->size().height() - heightOffset);
3151#if QT_CONFIG(style_windowsvista)
3152 if (d->isVistaThemeEnabled())
3153 d->vistaHelper->resizeEvent(event);
3154#endif
3155 QDialog::resizeEvent(event);
3156}
3157
3158/*!
3159 \reimp
3160*/
3161void QWizard::paintEvent(QPaintEvent * event)
3162{
3163 Q_D(QWizard);
3164 if (d->wizStyle == MacStyle && currentPage()) {
3165 QPixmap backgroundPixmap = currentPage()->pixmap(which: BackgroundPixmap);
3166 if (backgroundPixmap.isNull())
3167 return;
3168
3169 QStylePainter painter(this);
3170 painter.drawPixmap(x: 0, y: (height() - backgroundPixmap.height()) / 2, pm: backgroundPixmap);
3171 }
3172#if QT_CONFIG(style_windowsvista)
3173 else if (d->isVistaThemeEnabled()) {
3174 d->vistaHelper->paintEvent(event);
3175 }
3176#else
3177 Q_UNUSED(event);
3178#endif
3179}
3180
3181#if defined(Q_OS_WIN) || defined(Q_QDOC)
3182/*!
3183 \reimp
3184*/
3185bool QWizard::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
3186{
3187#if QT_CONFIG(style_windowsvista)
3188 Q_D(QWizard);
3189 if (d->isVistaThemeEnabled() && eventType == "windows_generic_MSG") {
3190 MSG *windowsMessage = static_cast<MSG *>(message);
3191 const bool winEventResult = d->vistaHelper->handleWinEvent(windowsMessage, result);
3192 if (d->vistaDirty) {
3193 // QTBUG-78300: When Qt::AA_NativeWindows is set, delay further
3194 // window creation until after the platform window creation events.
3195 if (windowsMessage->message == WM_GETICON) {
3196 d->vistaStateChanged = true;
3197 d->vistaDirty = false;
3198 setWizardStyle(AeroStyle);
3199 }
3200 }
3201 return winEventResult;
3202 } else {
3203 return QDialog::nativeEvent(eventType, message, result);
3204 }
3205#else
3206 return QDialog::nativeEvent(eventType, message, result);
3207#endif
3208}
3209#endif
3210
3211/*!
3212 \reimp
3213*/
3214void QWizard::done(int result)
3215{
3216 Q_D(QWizard);
3217 // canceling leaves the wizard in a known state
3218 if (result == Rejected) {
3219 d->reset();
3220 } else {
3221 if (!validateCurrentPage())
3222 return;
3223 }
3224 QDialog::done(result);
3225}
3226
3227/*!
3228 \fn void QWizard::initializePage(int id)
3229
3230 This virtual function is called by QWizard to prepare page \a id
3231 just before it is shown either as a result of QWizard::restart()
3232 being called, or as a result of the user clicking \uicontrol Next. (However, if the \l
3233 QWizard::IndependentPages option is set, this function is only
3234 called the first time the page is shown.)
3235
3236 By reimplementing this function, you can ensure that the page's
3237 fields are properly initialized based on fields from previous
3238 pages.
3239
3240 The default implementation calls QWizardPage::initializePage() on
3241 page(\a id).
3242
3243 \sa QWizardPage::initializePage(), cleanupPage()
3244*/
3245void QWizard::initializePage(int theid)
3246{
3247 QWizardPage *page = this->page(theid);
3248 if (page)
3249 page->initializePage();
3250}
3251
3252/*!
3253 \fn void QWizard::cleanupPage(int id)
3254
3255 This virtual function is called by QWizard to clean up page \a id just before the
3256 user leaves it by clicking \uicontrol Back (unless the \l QWizard::IndependentPages option is set).
3257
3258 The default implementation calls QWizardPage::cleanupPage() on
3259 page(\a id).
3260
3261 \sa QWizardPage::cleanupPage(), initializePage()
3262*/
3263void QWizard::cleanupPage(int theid)
3264{
3265 QWizardPage *page = this->page(theid);
3266 if (page)
3267 page->cleanupPage();
3268}
3269
3270/*!
3271 This virtual function is called by QWizard when the user clicks
3272 \uicontrol Next or \uicontrol Finish to perform some last-minute validation.
3273 If it returns \c true, the next page is shown (or the wizard
3274 finishes); otherwise, the current page stays up.
3275
3276 The default implementation calls QWizardPage::validatePage() on
3277 the currentPage().
3278
3279 When possible, it is usually better style to disable the \uicontrol
3280 Next or \uicontrol Finish button (by specifying \l{mandatory fields} or
3281 by reimplementing QWizardPage::isComplete()) than to reimplement
3282 validateCurrentPage().
3283
3284 \sa QWizardPage::validatePage(), currentPage()
3285*/
3286bool QWizard::validateCurrentPage()
3287{
3288 QWizardPage *page = currentPage();
3289 if (!page)
3290 return true;
3291
3292 return page->validatePage();
3293}
3294
3295/*!
3296 This virtual function is called by QWizard to find out which page
3297 to show when the user clicks the \uicontrol Next button.
3298
3299 The return value is the ID of the next page, or -1 if no page follows.
3300
3301 The default implementation calls QWizardPage::nextId() on the
3302 currentPage().
3303
3304 By reimplementing this function, you can specify a dynamic page
3305 order.
3306
3307 \sa QWizardPage::nextId(), currentPage()
3308*/
3309int QWizard::nextId() const
3310{
3311 const QWizardPage *page = currentPage();
3312 if (!page)
3313 return -1;
3314
3315 return page->nextId();
3316}
3317
3318/*!
3319 \class QWizardPage
3320 \brief The QWizardPage class is the base class for wizard pages.
3321
3322 \inmodule QtWidgets
3323
3324 QWizard represents a wizard. Each page is a QWizardPage. When
3325 you create your own wizards, you can use QWizardPage directly,
3326 or you can subclass it for more control.
3327
3328 A page has the following attributes, which are rendered by
3329 QWizard: a \l title, a \l subTitle, and a \l{setPixmap()}{set of
3330 pixmaps}. See \l{Elements of a Wizard Page} for details. Once a
3331 page is added to the wizard (using QWizard::addPage() or
3332 QWizard::setPage()), wizard() returns a pointer to the
3333 associated QWizard object.
3334
3335 Page provides five virtual functions that can be reimplemented to
3336 provide custom behavior:
3337
3338 \list
3339 \li initializePage() is called to initialize the page's contents
3340 when the user clicks the wizard's \uicontrol Next button. If you
3341 want to derive the page's default from what the user entered
3342 on previous pages, this is the function to reimplement.
3343 \li cleanupPage() is called to reset the page's contents when the
3344 user clicks the wizard's \uicontrol Back button.
3345 \li validatePage() validates the page when the user clicks \uicontrol
3346 Next or \uicontrol Finish. It is often used to show an error message
3347 if the user has entered incomplete or invalid information.
3348 \li nextId() returns the ID of the next page. It is useful when
3349 \l{creating non-linear wizards}, which allow different
3350 traversal paths based on the information provided by the user.
3351 \li isComplete() is called to determine whether the \uicontrol Next
3352 and/or \uicontrol Finish button should be enabled or disabled. If
3353 you reimplement isComplete(), also make sure that
3354 completeChanged() is emitted whenever the complete state
3355 changes.
3356 \endlist
3357
3358 Normally, the \uicontrol Next button and the \uicontrol Finish button of a
3359 wizard are mutually exclusive. If isFinalPage() returns \c true, \uicontrol
3360 Finish is available; otherwise, \uicontrol Next is available. By
3361 default, isFinalPage() is true only when nextId() returns -1. If
3362 you want to show \uicontrol Next and \uicontrol Final simultaneously for a
3363 page (letting the user perform an "early finish"), call
3364 setFinalPage(true) on that page. For wizards that support early
3365 finishes, you might also want to set the
3366 \l{QWizard::}{HaveNextButtonOnLastPage} and
3367 \l{QWizard::}{HaveFinishButtonOnEarlyPages} options on the
3368 wizard.
3369
3370 In many wizards, the contents of a page may affect the default
3371 values of the fields of a later page. To make it easy to
3372 communicate between pages, QWizard supports a \l{Registering and
3373 Using Fields}{"field" mechanism} that allows you to register a
3374 field (e.g., a QLineEdit) on a page and to access its value from
3375 any page. Fields are global to the entire wizard and make it easy
3376 for any single page to access information stored by another page,
3377 without having to put all the logic in QWizard or having the
3378 pages know explicitly about each other. Fields are registered
3379 using registerField() and can be accessed at any time using
3380 field() and setField().
3381
3382 \sa QWizard, {Trivial Wizard Example}, {License Wizard Example}
3383*/
3384
3385/*!
3386 Constructs a wizard page with the given \a parent.
3387
3388 When the page is inserted into a wizard using QWizard::addPage()
3389 or QWizard::setPage(), the parent is automatically set to be the
3390 wizard.
3391
3392 \sa wizard()
3393*/
3394QWizardPage::QWizardPage(QWidget *parent)
3395 : QWidget(*new QWizardPagePrivate, parent, { })
3396{
3397 connect(sender: this, SIGNAL(completeChanged()), receiver: this, SLOT(_q_updateCachedCompleteState()));
3398}
3399
3400/*!
3401 Destructor.
3402*/
3403QWizardPage::~QWizardPage()
3404{
3405}
3406
3407/*!
3408 \property QWizardPage::title
3409 \brief the title of the page
3410
3411 The title is shown by the QWizard, above the actual page. All
3412 pages should have a title.
3413
3414 The title may be plain text or HTML, depending on the value of the
3415 \l{QWizard::titleFormat} property.
3416
3417 By default, this property contains an empty string.
3418
3419 \sa subTitle, {Elements of a Wizard Page}
3420*/
3421void QWizardPage::setTitle(const QString &title)
3422{
3423 Q_D(QWizardPage);
3424 d->title = title;
3425 if (d->wizard && d->wizard->currentPage() == this)
3426 d->wizard->d_func()->updateLayout();
3427}
3428
3429QString QWizardPage::title() const
3430{
3431 Q_D(const QWizardPage);
3432 return d->title;
3433}
3434
3435/*!
3436 \property QWizardPage::subTitle
3437 \brief the subtitle of the page
3438
3439 The subtitle is shown by the QWizard, between the title and the
3440 actual page. Subtitles are optional. In
3441 \l{QWizard::ClassicStyle}{ClassicStyle} and
3442 \l{QWizard::ModernStyle}{ModernStyle}, using subtitles is
3443 necessary to make the header appear. In
3444 \l{QWizard::MacStyle}{MacStyle}, the subtitle is shown as a text
3445 label just above the actual page.
3446
3447 The subtitle may be plain text or HTML, depending on the value of
3448 the \l{QWizard::subTitleFormat} property.
3449
3450 By default, this property contains an empty string.
3451
3452 \sa title, QWizard::IgnoreSubTitles, {Elements of a Wizard Page}
3453*/
3454void QWizardPage::setSubTitle(const QString &subTitle)
3455{
3456 Q_D(QWizardPage);
3457 d->subTitle = subTitle;
3458 if (d->wizard && d->wizard->currentPage() == this)
3459 d->wizard->d_func()->updateLayout();
3460}
3461
3462QString QWizardPage::subTitle() const
3463{
3464 Q_D(const QWizardPage);
3465 return d->subTitle;
3466}
3467
3468/*!
3469 Sets the pixmap for role \a which to \a pixmap.
3470
3471 The pixmaps are used by QWizard when displaying a page. Which
3472 pixmaps are actually used depend on the \l{Wizard Look and
3473 Feel}{wizard style}.
3474
3475 Pixmaps can also be set for the entire wizard using
3476 QWizard::setPixmap(), in which case they apply for all pages that
3477 don't specify a pixmap.
3478
3479 \sa QWizard::setPixmap(), {Elements of a Wizard Page}
3480*/
3481void QWizardPage::setPixmap(QWizard::WizardPixmap which, const QPixmap &pixmap)
3482{
3483 Q_D(QWizardPage);
3484 Q_ASSERT(uint(which) < QWizard::NPixmaps);
3485 d->pixmaps[which] = pixmap;
3486 if (d->wizard && d->wizard->currentPage() == this)
3487 d->wizard->d_func()->updatePixmap(which);
3488}
3489
3490/*!
3491 Returns the pixmap set for role \a which.
3492
3493 Pixmaps can also be set for the entire wizard using
3494 QWizard::setPixmap(), in which case they apply for all pages that
3495 don't specify a pixmap.
3496
3497 \sa QWizard::pixmap(), {Elements of a Wizard Page}
3498*/
3499QPixmap QWizardPage::pixmap(QWizard::WizardPixmap which) const
3500{
3501 Q_D(const QWizardPage);
3502 Q_ASSERT(uint(which) < QWizard::NPixmaps);
3503
3504 const QPixmap &pixmap = d->pixmaps[which];
3505 if (!pixmap.isNull())
3506 return pixmap;
3507
3508 if (wizard())
3509 return wizard()->pixmap(which);
3510
3511 return pixmap;
3512}
3513
3514/*!
3515 This virtual function is called by QWizard::initializePage() to
3516 prepare the page just before it is shown either as a result of QWizard::restart()
3517 being called, or as a result of the user clicking \uicontrol Next.
3518 (However, if the \l QWizard::IndependentPages option is set, this function is only
3519 called the first time the page is shown.)
3520
3521 By reimplementing this function, you can ensure that the page's
3522 fields are properly initialized based on fields from previous
3523 pages. For example:
3524
3525 \snippet dialogs/licensewizard/licensewizard.cpp 27
3526
3527 The default implementation does nothing.
3528
3529 \sa QWizard::initializePage(), cleanupPage(), QWizard::IndependentPages
3530*/
3531void QWizardPage::initializePage()
3532{
3533}
3534
3535/*!
3536 This virtual function is called by QWizard::cleanupPage() when
3537 the user leaves the page by clicking \uicontrol Back (unless the \l QWizard::IndependentPages
3538 option is set).
3539
3540 The default implementation resets the page's fields to their
3541 original values (the values they had before initializePage() was
3542 called).
3543
3544 \sa QWizard::cleanupPage(), initializePage(), QWizard::IndependentPages
3545*/
3546void QWizardPage::cleanupPage()
3547{
3548 Q_D(QWizardPage);
3549 if (d->wizard) {
3550 const QList<QWizardField> &fields = d->wizard->d_func()->fields;
3551 for (const auto &field : fields) {
3552 if (field.page == this)
3553 field.object->setProperty(name: field.property, value: field.initialValue);
3554 }
3555 }
3556}
3557
3558/*!
3559 This virtual function is called by QWizard::validateCurrentPage()
3560 when the user clicks \uicontrol Next or \uicontrol Finish to perform some
3561 last-minute validation. If it returns \c true, the next page is shown
3562 (or the wizard finishes); otherwise, the current page stays up.
3563
3564 The default implementation returns \c true.
3565
3566 When possible, it is usually better style to disable the \uicontrol
3567 Next or \uicontrol Finish button (by specifying \l{mandatory fields} or
3568 reimplementing isComplete()) than to reimplement validatePage().
3569
3570 \sa QWizard::validateCurrentPage(), isComplete()
3571*/
3572bool QWizardPage::validatePage()
3573{
3574 return true;
3575}
3576
3577/*!
3578 This virtual function is called by QWizard to determine whether
3579 the \uicontrol Next or \uicontrol Finish button should be enabled or
3580 disabled.
3581
3582 The default implementation returns \c true if all \l{mandatory
3583 fields} are filled; otherwise, it returns \c false.
3584
3585 If you reimplement this function, make sure to emit completeChanged(),
3586 from the rest of your implementation, whenever the value of isComplete()
3587 changes. This ensures that QWizard updates the enabled or disabled state of
3588 its buttons. An example of the reimplementation is
3589 available \l{http://doc.qt.io/archives/qq/qq22-qwizard.html#validatebeforeitstoolate}
3590 {here}.
3591
3592 \sa completeChanged(), isFinalPage()
3593*/
3594bool QWizardPage::isComplete() const
3595{
3596 Q_D(const QWizardPage);
3597
3598 if (!d->wizard)
3599 return true;
3600
3601 const QList<QWizardField> &wizardFields = d->wizard->d_func()->fields;
3602 const auto end = wizardFields.crend();
3603 for (auto it = wizardFields.crbegin(); it != end; ++it) {
3604 const QWizardField &field = *it;
3605 if (field.page == this && field.mandatory) {
3606 QVariant value = field.object->property(name: field.property);
3607 if (value == field.initialValue)
3608 return false;
3609
3610#if QT_CONFIG(lineedit)
3611 if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(object: field.object)) {
3612 if (!lineEdit->hasAcceptableInput())
3613 return false;
3614 }
3615#endif
3616#if QT_CONFIG(spinbox)
3617 if (QAbstractSpinBox *spinBox = qobject_cast<QAbstractSpinBox *>(object: field.object)) {
3618 if (!spinBox->hasAcceptableInput())
3619 return false;
3620 }
3621#endif
3622 }
3623 }
3624 return true;
3625}
3626
3627/*!
3628 Explicitly sets this page to be final if \a finalPage is true.
3629
3630 After calling setFinalPage(true), isFinalPage() returns \c true and the \uicontrol
3631 Finish button is visible (and enabled if isComplete() returns
3632 true).
3633
3634 After calling setFinalPage(false), isFinalPage() returns \c true if
3635 nextId() returns -1; otherwise, it returns \c false.
3636
3637 \sa isComplete(), QWizard::HaveFinishButtonOnEarlyPages
3638*/
3639void QWizardPage::setFinalPage(bool finalPage)
3640{
3641 Q_D(QWizardPage);
3642 d->explicitlyFinal = finalPage;
3643 QWizard *wizard = this->wizard();
3644 if (wizard && wizard->currentPage() == this)
3645 wizard->d_func()->updateCurrentPage();
3646}
3647
3648/*!
3649 This function is called by QWizard to determine whether the \uicontrol
3650 Finish button should be shown for this page or not.
3651
3652 By default, it returns \c true if there is no next page
3653 (i.e., nextId() returns -1); otherwise, it returns \c false.
3654
3655 By explicitly calling setFinalPage(true), you can let the user perform an
3656 "early finish".
3657
3658 \sa isComplete(), QWizard::HaveFinishButtonOnEarlyPages
3659*/
3660bool QWizardPage::isFinalPage() const
3661{
3662 Q_D(const QWizardPage);
3663 if (d->explicitlyFinal)
3664 return true;
3665
3666 QWizard *wizard = this->wizard();
3667 if (wizard && wizard->currentPage() == this) {
3668 // try to use the QWizard implementation if possible
3669 return wizard->nextId() == -1;
3670 } else {
3671 return nextId() == -1;
3672 }
3673}
3674
3675/*!
3676 Sets this page to be a commit page if \a commitPage is true; otherwise,
3677 sets it to be a normal page.
3678
3679 A commit page is a page that represents an action which cannot be undone
3680 by clicking \uicontrol Back or \uicontrol Cancel.
3681
3682 A \uicontrol Commit button replaces the \uicontrol Next button on a commit page. Clicking this
3683 button simply calls QWizard::next() just like clicking \uicontrol Next does.
3684
3685 A page entered directly from a commit page has its \uicontrol Back button disabled.
3686
3687 \sa isCommitPage()
3688*/
3689void QWizardPage::setCommitPage(bool commitPage)
3690{
3691 Q_D(QWizardPage);
3692 d->commit = commitPage;
3693 QWizard *wizard = this->wizard();
3694 if (wizard && wizard->currentPage() == this)
3695 wizard->d_func()->updateCurrentPage();
3696}
3697
3698/*!
3699 Returns \c true if this page is a commit page; otherwise returns \c false.
3700
3701 \sa setCommitPage()
3702*/
3703bool QWizardPage::isCommitPage() const
3704{
3705 Q_D(const QWizardPage);
3706 return d->commit;
3707}
3708
3709/*!
3710 Sets the text on button \a which to be \a text on this page.
3711
3712 By default, the text on buttons depends on the QWizard::wizardStyle,
3713 but may be redefined for the wizard as a whole using QWizard::setButtonText().
3714
3715 \sa buttonText(), QWizard::setButtonText(), QWizard::buttonText()
3716*/
3717void QWizardPage::setButtonText(QWizard::WizardButton which, const QString &text)
3718{
3719 Q_D(QWizardPage);
3720 d->buttonCustomTexts.insert(key: which, value: text);
3721 if (wizard() && wizard()->currentPage() == this && wizard()->d_func()->btns[which])
3722 wizard()->d_func()->btns[which]->setText(text);
3723}
3724
3725/*!
3726 Returns the text on button \a which on this page.
3727
3728 If a text has ben set using setButtonText(), this text is returned.
3729 Otherwise, if a text has been set using QWizard::setButtonText(),
3730 this text is returned.
3731
3732 By default, the text on buttons depends on the QWizard::wizardStyle.
3733 For example, on \macos, the \uicontrol Next button is called \uicontrol
3734 Continue.
3735
3736 \sa setButtonText(), QWizard::buttonText(), QWizard::setButtonText()
3737*/
3738QString QWizardPage::buttonText(QWizard::WizardButton which) const
3739{
3740 Q_D(const QWizardPage);
3741
3742 if (d->buttonCustomTexts.contains(key: which))
3743 return d->buttonCustomTexts.value(key: which);
3744
3745 if (wizard())
3746 return wizard()->buttonText(which);
3747
3748 return QString();
3749}
3750
3751/*!
3752 This virtual function is called by QWizard::nextId() to find
3753 out which page to show when the user clicks the \uicontrol Next button.
3754
3755 The return value is the ID of the next page, or -1 if no page follows.
3756
3757 By default, this function returns the lowest ID greater than the ID
3758 of the current page, or -1 if there is no such ID.
3759
3760 By reimplementing this function, you can specify a dynamic page
3761 order. For example:
3762
3763 \snippet dialogs/licensewizard/licensewizard.cpp 18
3764
3765 \sa QWizard::nextId()
3766*/
3767int QWizardPage::nextId() const
3768{
3769 Q_D(const QWizardPage);
3770
3771 if (!d->wizard)
3772 return -1;
3773
3774 bool foundCurrentPage = false;
3775
3776 const QWizardPrivate::PageMap &pageMap = d->wizard->d_func()->pageMap;
3777 QWizardPrivate::PageMap::const_iterator i = pageMap.constBegin();
3778 QWizardPrivate::PageMap::const_iterator end = pageMap.constEnd();
3779
3780 for (; i != end; ++i) {
3781 if (i.value() == this) {
3782 foundCurrentPage = true;
3783 } else if (foundCurrentPage) {
3784 return i.key();
3785 }
3786 }
3787 return -1;
3788}
3789
3790/*!
3791 \fn void QWizardPage::completeChanged()
3792
3793 This signal is emitted whenever the complete state of the page
3794 (i.e., the value of isComplete()) changes.
3795
3796 If you reimplement isComplete(), make sure to emit
3797 completeChanged() whenever the value of isComplete() changes, to
3798 ensure that QWizard updates the enabled or disabled state of its
3799 buttons.
3800
3801 \sa isComplete()
3802*/
3803
3804/*!
3805 Sets the value of the field called \a name to \a value.
3806
3807 This function can be used to set fields on any page of the wizard.
3808 It is equivalent to calling
3809 wizard()->\l{QWizard::setField()}{setField(\a name, \a value)}.
3810
3811 \sa QWizard::setField(), field(), registerField()
3812*/
3813void QWizardPage::setField(const QString &name, const QVariant &value)
3814{
3815 Q_D(QWizardPage);
3816 if (!d->wizard)
3817 return;
3818 d->wizard->setField(name, value);
3819}
3820
3821/*!
3822 Returns the value of the field called \a name.
3823
3824 This function can be used to access fields on any page of the
3825 wizard. It is equivalent to calling
3826 wizard()->\l{QWizard::field()}{field(\a name)}.
3827
3828 Example:
3829
3830 \snippet dialogs/licensewizard/licensewizard.cpp accessField
3831
3832 \sa QWizard::field(), setField(), registerField()
3833*/
3834QVariant QWizardPage::field(const QString &name) const
3835{
3836 Q_D(const QWizardPage);
3837 if (!d->wizard)
3838 return QVariant();
3839 return d->wizard->field(name);
3840}
3841
3842/*!
3843 Creates a field called \a name associated with the given \a
3844 property of the given \a widget. From then on, that property
3845 becomes accessible using field() and setField().
3846
3847 Fields are global to the entire wizard and make it easy for any
3848 single page to access information stored by another page, without
3849 having to put all the logic in QWizard or having the pages know
3850 explicitly about each other.
3851
3852 If \a name ends with an asterisk (\c *), the field is a mandatory
3853 field. When a page has mandatory fields, the \uicontrol Next and/or
3854 \uicontrol Finish buttons are enabled only when all mandatory fields
3855 are filled. This requires a \a changedSignal to be specified, to
3856 tell QWizard to recheck the value stored by the mandatory field.
3857
3858 QWizard knows the most common Qt widgets. For these (or their
3859 subclasses), you don't need to specify a \a property or a \a
3860 changedSignal. The table below lists these widgets:
3861
3862 \table
3863 \header \li Widget \li Property \li Change Notification Signal
3864 \row \li QAbstractButton \li bool \l{QAbstractButton::}{checked} \li \l{QAbstractButton::}{toggled()}
3865 \row \li QAbstractSlider \li int \l{QAbstractSlider::}{value} \li \l{QAbstractSlider::}{valueChanged()}
3866 \row \li QComboBox \li int \l{QComboBox::}{currentIndex} \li \l{QComboBox::}{currentIndexChanged()}
3867 \row \li QDateTimeEdit \li QDateTime \l{QDateTimeEdit::}{dateTime} \li \l{QDateTimeEdit::}{dateTimeChanged()}
3868 \row \li QLineEdit \li QString \l{QLineEdit::}{text} \li \l{QLineEdit::}{textChanged()}
3869 \row \li QListWidget \li int \l{QListWidget::}{currentRow} \li \l{QListWidget::}{currentRowChanged()}
3870 \row \li QSpinBox \li int \l{QSpinBox::}{value} \li \l{QSpinBox::}{valueChanged()}
3871 \endtable
3872
3873 You can use QWizard::setDefaultProperty() to add entries to this
3874 table or to override existing entries.
3875
3876 To consider a field "filled", QWizard simply checks that their
3877 current value doesn't equal their original value (the value they
3878 had before initializePage() was called). For QLineEdit, it also
3879 checks that
3880 \l{QLineEdit::hasAcceptableInput()}{hasAcceptableInput()} returns
3881 true, to honor any validator or mask.
3882
3883 QWizard's mandatory field mechanism is provided for convenience.
3884 It can be bypassed by reimplementing QWizardPage::isComplete().
3885
3886 \sa field(), setField(), QWizard::setDefaultProperty()
3887*/
3888void QWizardPage::registerField(const QString &name, QWidget *widget, const char *property,
3889 const char *changedSignal)
3890{
3891 Q_D(QWizardPage);
3892 QWizardField field(this, name, widget, property, changedSignal);
3893 if (d->wizard) {
3894 d->wizard->d_func()->addField(field);
3895 } else {
3896 d->pendingFields += field;
3897 }
3898}
3899
3900/*!
3901 Returns the wizard associated with this page, or \nullptr if this page
3902 hasn't been inserted into a QWizard yet.
3903
3904 \sa QWizard::addPage(), QWizard::setPage()
3905*/
3906QWizard *QWizardPage::wizard() const
3907{
3908 Q_D(const QWizardPage);
3909 return d->wizard;
3910}
3911
3912QT_END_NAMESPACE
3913
3914#include "moc_qwizard.cpp"
3915

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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