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

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