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, widget: q);
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, widget: q);
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 \brief The QWizard class provides a framework for wizards.
1742
1743 \inmodule QtWidgets
1744
1745 A wizard (also called an assistant on \macos) is a special type
1746 of input dialog that consists of a sequence of pages. A wizard's
1747 purpose is to guide the user through a process step by step.
1748 Wizards are useful for complex or infrequent tasks that users may
1749 find difficult to learn.
1750
1751 QWizard inherits QDialog and represents a wizard. Each page is a
1752 QWizardPage (a QWidget subclass). To create your own wizards, you
1753 can use these classes directly, or you can subclass them for more
1754 control.
1755
1756 \section1 A Trivial Example
1757
1758 The following example illustrates how to create wizard pages and
1759 add them to a wizard. For more advanced examples, see the
1760 \l{dialogs/licensewizard}{License Wizard}.
1761
1762 \snippet dialogs/trivialwizard/trivialwizard.cpp 1
1763 \snippet dialogs/trivialwizard/trivialwizard.cpp 3
1764 \dots
1765 \snippet dialogs/trivialwizard/trivialwizard.cpp 4
1766 \codeline
1767 \snippet dialogs/trivialwizard/trivialwizard.cpp 5
1768 \snippet dialogs/trivialwizard/trivialwizard.cpp 7
1769 \dots
1770 \snippet dialogs/trivialwizard/trivialwizard.cpp 8
1771 \codeline
1772 \snippet dialogs/trivialwizard/trivialwizard.cpp 10
1773
1774 \section1 Wizard Look and Feel
1775
1776 QWizard supports four wizard looks:
1777
1778 \list
1779 \li ClassicStyle
1780 \li ModernStyle
1781 \li MacStyle
1782 \li AeroStyle
1783 \endlist
1784
1785 You can explicitly set the look to use using setWizardStyle()
1786 (e.g., if you want the same look on all platforms).
1787
1788 \table
1789 \header \li ClassicStyle
1790 \li ModernStyle
1791 \li MacStyle
1792 \li AeroStyle
1793 \row \li \inlineimage qtwizard-classic1.png
1794 \li \inlineimage qtwizard-modern1.png
1795 \li \inlineimage qtwizard-mac1.png
1796 \li \inlineimage qtwizard-aero1.png
1797 \row \li \inlineimage qtwizard-classic2.png
1798 \li \inlineimage qtwizard-modern2.png
1799 \li \inlineimage qtwizard-mac2.png
1800 \li \inlineimage qtwizard-aero2.png
1801 \endtable
1802
1803 Note: AeroStyle has effect only on a Windows Vista system with alpha compositing enabled.
1804 ModernStyle is used as a fallback when this condition is not met.
1805
1806 In addition to the wizard style, there are several options that
1807 control the look and feel of the wizard. These can be set using
1808 setOption() or setOptions(). For example, HaveHelpButton makes
1809 QWizard show a \uicontrol Help button along with the other wizard
1810 buttons.
1811
1812 You can even change the order of the wizard buttons to any
1813 arbitrary order using setButtonLayout(), and you can add up to
1814 three custom buttons (e.g., a \uicontrol Print button) to the button
1815 row. This is achieved by calling setButton() or setButtonText()
1816 with CustomButton1, CustomButton2, or CustomButton3 to set up the
1817 button, and by enabling the HaveCustomButton1, HaveCustomButton2,
1818 or HaveCustomButton3 options. Whenever the user clicks a custom
1819 button, customButtonClicked() is emitted. For example:
1820
1821 \snippet dialogs/licensewizard/licensewizard.cpp 29
1822
1823 \section1 Elements of a Wizard Page
1824
1825 Wizards consist of a sequence of \l{QWizardPage}s. At any time,
1826 only one page is shown. A page has the following attributes:
1827
1828 \list
1829 \li A \l{QWizardPage::}{title}.
1830 \li A \l{QWizardPage::}{subTitle}.
1831 \li A set of pixmaps, which may or may not be honored, depending
1832 on the wizard's style:
1833 \list
1834 \li WatermarkPixmap (used by ClassicStyle and ModernStyle)
1835 \li BannerPixmap (used by ModernStyle)
1836 \li LogoPixmap (used by ClassicStyle and ModernStyle)
1837 \li BackgroundPixmap (used by MacStyle)
1838 \endlist
1839 \endlist
1840
1841 The diagram belows shows how QWizard renders these attributes,
1842 assuming they are all present and ModernStyle is used:
1843
1844 \image qtwizard-nonmacpage.png
1845
1846 When a \l{QWizardPage::}{subTitle} is set, QWizard displays it
1847 in a header, in which case it also uses the BannerPixmap and the
1848 LogoPixmap to decorate the header. The WatermarkPixmap is
1849 displayed on the left side, below the header. At the bottom,
1850 there is a row of buttons allowing the user to navigate through
1851 the pages.
1852
1853 The page itself (the \l{QWizardPage} widget) occupies the area
1854 between the header, the watermark, and the button row. Typically,
1855 the page is a QWizardPage on which a QGridLayout is installed,
1856 with standard child widgets (\l{QLabel}s, \l{QLineEdit}s, etc.).
1857
1858 If the wizard's style is MacStyle, the page looks radically
1859 different:
1860
1861 \image qtwizard-macpage.png
1862
1863 The watermark, banner, and logo pixmaps are ignored by the
1864 MacStyle. If the BackgroundPixmap is set, it is used as the
1865 background for the wizard; otherwise, a default "assistant" image
1866 is used.
1867
1868 The title and subtitle are set by calling
1869 QWizardPage::setTitle() and QWizardPage::setSubTitle() on the
1870 individual pages. They may be plain text or HTML (see titleFormat
1871 and subTitleFormat). The pixmaps can be set globally for the
1872 entire wizard using setPixmap(), or on a per-page basis using
1873 QWizardPage::setPixmap().
1874
1875 \target field mechanism
1876 \section1 Registering and Using Fields
1877
1878 In many wizards, the contents of a page may affect the default
1879 values of the fields of a later page. To make it easy to
1880 communicate between pages, QWizard supports a "field" mechanism
1881 that allows you to register a field (e.g., a QLineEdit) on a page
1882 and to access its value from any page. It is also possible to
1883 specify mandatory fields (i.e., fields that must be filled before
1884 the user can advance to the next page).
1885
1886 To register a field, call QWizardPage::registerField() field.
1887 For example:
1888
1889 \snippet dialogs/licensewizard/licensewizard.cpp 21
1890
1891 The above code registers three fields, \c className, \c
1892 baseClass, and \c qobjectMacro, which are associated with three
1893 child widgets. The asterisk (\c *) next to \c className denotes a
1894 mandatory field.
1895
1896 \target initialize page
1897 The fields of any page are accessible from any other page. For
1898 example:
1899
1900 \snippet dialogs/licensewizard/licensewizard.cpp 27
1901
1902 Here, we call QWizardPage::field() to access the contents of the
1903 \c details.email field (which was defined in the \c DetailsPage)
1904 and use it to initialize the \c ConclusionPage. The field's
1905 contents is returned as a QVariant.
1906
1907 When we create a field using QWizardPage::registerField(), we
1908 pass a unique field name and a widget. We can also provide a Qt
1909 property name and a "changed" signal (a signal that is emitted
1910 when the property changes) as third and fourth arguments;
1911 however, this is not necessary for the most common Qt widgets,
1912 such as QLineEdit, QCheckBox, and QComboBox, because QWizard
1913 knows which properties to look for.
1914
1915 \target mandatory fields
1916
1917 If an asterisk (\c *) is appended to the name when the property
1918 is registered, the field is a \e{mandatory field}. When a page has
1919 mandatory fields, the \uicontrol Next and/or \uicontrol Finish buttons are
1920 enabled only when all mandatory fields are filled.
1921
1922 To consider a field "filled", QWizard simply checks that the
1923 field's current value doesn't equal the original value (the value
1924 it had when initializePage() was called). For QLineEdit and
1925 QAbstractSpinBox subclasses, QWizard also checks that
1926 \l{QLineEdit::hasAcceptableInput()}{hasAcceptableInput()} returns
1927 true, to honor any validator or mask.
1928
1929 QWizard's mandatory field mechanism is provided for convenience.
1930 A more powerful (but also more cumbersome) alternative is to
1931 reimplement QWizardPage::isComplete() and to emit the
1932 QWizardPage::completeChanged() signal whenever the page becomes
1933 complete or incomplete.
1934
1935 The enabled/disabled state of the \uicontrol Next and/or \uicontrol Finish
1936 buttons is one way to perform validation on the user input.
1937 Another way is to reimplement validateCurrentPage() (or
1938 QWizardPage::validatePage()) to perform some last-minute
1939 validation (and show an error message if the user has entered
1940 incomplete or invalid information). If the function returns \c true,
1941 the next page is shown (or the wizard finishes); otherwise, the
1942 current page stays up.
1943
1944 \section1 Creating Linear Wizards
1945
1946 Most wizards have a linear structure, with page 1 followed by
1947 page 2 and so on until the last page. The \l{dialogs/trivialwizard}
1948 {Trivial Wizard} example is such a wizard. With QWizard, linear wizards
1949 are created by instantiating the \l{QWizardPage}s and inserting
1950 them using addPage(). By default, the pages are shown in the
1951 order in which they were added. For example:
1952
1953 \snippet dialogs/trivialwizard/trivialwizard.cpp linearAddPage
1954
1955 When a page is about to be shown, QWizard calls initializePage()
1956 (which in turn calls QWizardPage::initializePage()) to fill the
1957 page with default values. By default, this function does nothing,
1958 but it can be reimplemented to initialize the page's contents
1959 based on other pages' fields (see the \l{initialize page}{example
1960 above}).
1961
1962 If the user presses \uicontrol Back, cleanupPage() is called (which in
1963 turn calls QWizardPage::cleanupPage()). The default
1964 implementation resets the page's fields to their original values
1965 (the values they had before initializePage() was called). If you
1966 want the \uicontrol Back button to be non-destructive and keep the
1967 values entered by the user, simply enable the IndependentPages
1968 option.
1969
1970 \section1 Creating Non-Linear Wizards
1971
1972 Some wizards are more complex in that they allow different
1973 traversal paths based on the information provided by the user.
1974 The \l{dialogs/licensewizard}{License Wizard} example illustrates this.
1975 It provides several wizard pages; depending on which options are
1976 selected, the user can reach different pages.
1977
1978 \image licensewizard-flow.png
1979
1980 In complex wizards, pages are identified by IDs. These IDs are
1981 typically defined using an enum. For example:
1982
1983 \snippet dialogs/licensewizard/licensewizard.h 0
1984 \dots
1985 \snippet dialogs/licensewizard/licensewizard.h 2
1986 \dots
1987 \snippet dialogs/licensewizard/licensewizard.h 3
1988
1989 The pages are inserted using setPage(), which takes an ID and an
1990 instance of QWizardPage (or of a subclass):
1991
1992 \snippet dialogs/licensewizard/licensewizard.cpp 1
1993 \dots
1994 \snippet dialogs/licensewizard/licensewizard.cpp 8
1995
1996 By default, the pages are shown in increasing ID order. To
1997 provide a dynamic order that depends on the options chosen by the
1998 user, we must reimplement QWizardPage::nextId(). For example:
1999
2000 \snippet dialogs/licensewizard/licensewizard.cpp 18
2001 \codeline
2002 \snippet dialogs/licensewizard/licensewizard.cpp 23
2003 \codeline
2004 \snippet dialogs/licensewizard/licensewizard.cpp 24
2005 \codeline
2006 \snippet dialogs/licensewizard/licensewizard.cpp 25
2007 \codeline
2008 \snippet dialogs/licensewizard/licensewizard.cpp 26
2009
2010 It would also be possible to put all the logic in one place, in a
2011 QWizard::nextId() reimplementation. For example:
2012
2013 \snippet code/src_gui_dialogs_qwizard.cpp 0
2014
2015 To start at another page than the page with the lowest ID, call
2016 setStartId().
2017
2018 To test whether a page has been visited or not, call
2019 hasVisitedPage(). For example:
2020
2021 \snippet dialogs/licensewizard/licensewizard.cpp 27
2022
2023 \sa QWizardPage, {Trivial Wizard Example}, {License Wizard Example}
2024*/
2025
2026/*!
2027 \enum QWizard::WizardButton
2028
2029 This enum specifies the buttons in a wizard.
2030
2031 \value BackButton The \uicontrol Back button (\uicontrol {Go Back} on \macos)
2032 \value NextButton The \uicontrol Next button (\uicontrol Continue on \macos)
2033 \value CommitButton The \uicontrol Commit button
2034 \value FinishButton The \uicontrol Finish button (\uicontrol Done on \macos)
2035 \value CancelButton The \uicontrol Cancel button (see also NoCancelButton)
2036 \value HelpButton The \uicontrol Help button (see also HaveHelpButton)
2037 \value CustomButton1 The first user-defined button (see also HaveCustomButton1)
2038 \value CustomButton2 The second user-defined button (see also HaveCustomButton2)
2039 \value CustomButton3 The third user-defined button (see also HaveCustomButton3)
2040
2041 The following value is only useful when calling setButtonLayout():
2042
2043 \value Stretch A horizontal stretch in the button layout
2044
2045 \omitvalue NoButton
2046 \omitvalue NStandardButtons
2047 \omitvalue NButtons
2048
2049 \sa setButton(), setButtonText(), setButtonLayout(), customButtonClicked()
2050*/
2051
2052/*!
2053 \enum QWizard::WizardPixmap
2054
2055 This enum specifies the pixmaps that can be associated with a page.
2056
2057 \value WatermarkPixmap The tall pixmap on the left side of a ClassicStyle or ModernStyle page
2058 \value LogoPixmap The small pixmap on the right side of a ClassicStyle or ModernStyle page header
2059 \value BannerPixmap The pixmap that occupies the background of a ModernStyle page header
2060 \value BackgroundPixmap The pixmap that occupies the background of a MacStyle wizard
2061
2062 \omitvalue NPixmaps
2063
2064 \sa setPixmap(), QWizardPage::setPixmap(), {Elements of a Wizard Page}
2065*/
2066
2067/*!
2068 \enum QWizard::WizardStyle
2069
2070 This enum specifies the different looks supported by QWizard.
2071
2072 \value ClassicStyle Classic Windows look
2073 \value ModernStyle Modern Windows look
2074 \value MacStyle \macos look
2075 \value AeroStyle Windows Aero look
2076
2077 \omitvalue NStyles
2078
2079 \sa setWizardStyle(), WizardOption, {Wizard Look and Feel}
2080*/
2081
2082/*!
2083 \enum QWizard::WizardOption
2084
2085 This enum specifies various options that affect the look and feel
2086 of a wizard.
2087
2088 \value IndependentPages The pages are independent of each other
2089 (i.e., they don't derive values from each
2090 other).
2091 \value IgnoreSubTitles Don't show any subtitles, even if they are set.
2092 \value ExtendedWatermarkPixmap Extend any WatermarkPixmap all the
2093 way down to the window's edge.
2094 \value NoDefaultButton Don't make the \uicontrol Next or \uicontrol Finish button the
2095 dialog's \l{QPushButton::setDefault()}{default button}.
2096 \value NoBackButtonOnStartPage Don't show the \uicontrol Back button on the start page.
2097 \value NoBackButtonOnLastPage Don't show the \uicontrol Back button on the last page.
2098 \value DisabledBackButtonOnLastPage Disable the \uicontrol Back button on the last page.
2099 \value HaveNextButtonOnLastPage Show the (disabled) \uicontrol Next button on the last page.
2100 \value HaveFinishButtonOnEarlyPages Show the (disabled) \uicontrol Finish button on non-final pages.
2101 \value NoCancelButton Don't show the \uicontrol Cancel button.
2102 \value CancelButtonOnLeft Put the \uicontrol Cancel button on the left of \uicontrol Back (rather than on
2103 the right of \uicontrol Finish or \uicontrol Next).
2104 \value HaveHelpButton Show the \uicontrol Help button.
2105 \value HelpButtonOnRight Put the \uicontrol Help button on the far right of the button layout
2106 (rather than on the far left).
2107 \value HaveCustomButton1 Show the first user-defined button (CustomButton1).
2108 \value HaveCustomButton2 Show the second user-defined button (CustomButton2).
2109 \value HaveCustomButton3 Show the third user-defined button (CustomButton3).
2110 \value NoCancelButtonOnLastPage Don't show the \uicontrol Cancel button on the last page.
2111
2112 \sa setOptions(), setOption(), testOption()
2113*/
2114
2115/*!
2116 Constructs a wizard with the given \a parent and window \a flags.
2117
2118 \sa parent(), windowFlags()
2119*/
2120QWizard::QWizard(QWidget *parent, Qt::WindowFlags flags)
2121 : QDialog(*new QWizardPrivate, parent, flags)
2122{
2123 Q_D(QWizard);
2124 d->init();
2125}
2126
2127/*!
2128 Destroys the wizard and its pages, releasing any allocated resources.
2129*/
2130QWizard::~QWizard()
2131{
2132 Q_D(QWizard);
2133 delete d->buttonLayout;
2134}
2135
2136/*!
2137 Adds the given \a page to the wizard, and returns the page's ID.
2138
2139 The ID is guaranteed to be larger than any other ID in the
2140 QWizard so far.
2141
2142 \sa setPage(), page(), pageAdded()
2143*/
2144int QWizard::addPage(QWizardPage *page)
2145{
2146 Q_D(QWizard);
2147 int theid = 0;
2148 if (!d->pageMap.isEmpty())
2149 theid = d->pageMap.lastKey() + 1;
2150 setPage(id: theid, page);
2151 return theid;
2152}
2153
2154/*!
2155 \fn void QWizard::setPage(int id, QWizardPage *page)
2156
2157 Adds the given \a page to the wizard with the given \a id.
2158
2159 \note Adding a page may influence the value of the startId property
2160 in case it was not set explicitly.
2161
2162 \sa addPage(), page(), pageAdded()
2163*/
2164void QWizard::setPage(int theid, QWizardPage *page)
2165{
2166 Q_D(QWizard);
2167
2168 if (Q_UNLIKELY(!page)) {
2169 qWarning(msg: "QWizard::setPage: Cannot insert null page");
2170 return;
2171 }
2172
2173 if (Q_UNLIKELY(theid == -1)) {
2174 qWarning(msg: "QWizard::setPage: Cannot insert page with ID -1");
2175 return;
2176 }
2177
2178 if (Q_UNLIKELY(d->pageMap.contains(theid))) {
2179 qWarning(msg: "QWizard::setPage: Page with duplicate ID %d ignored", theid);
2180 return;
2181 }
2182
2183 page->setParent(d->pageFrame);
2184
2185 QList<QWizardField> &pendingFields = page->d_func()->pendingFields;
2186 for (const auto &field : std::as_const(t&: pendingFields))
2187 d->addField(field);
2188 pendingFields.clear();
2189
2190 connect(sender: page, SIGNAL(completeChanged()), receiver: this, SLOT(_q_updateButtonStates()));
2191
2192 d->pageMap.insert(key: theid, value: page);
2193 page->d_func()->wizard = this;
2194
2195 int n = d->pageVBoxLayout->count();
2196
2197 // disable layout to prevent layout updates while adding
2198 bool pageVBoxLayoutEnabled = d->pageVBoxLayout->isEnabled();
2199 d->pageVBoxLayout->setEnabled(false);
2200
2201 d->pageVBoxLayout->insertWidget(index: n - 1, widget: page);
2202
2203 // hide new page and reset layout to old status
2204 page->hide();
2205 d->pageVBoxLayout->setEnabled(pageVBoxLayoutEnabled);
2206
2207 if (!d->startSetByUser && d->pageMap.constBegin().key() == theid)
2208 d->start = theid;
2209 emit pageAdded(id: theid);
2210}
2211
2212/*!
2213 Removes the page with the given \a id. cleanupPage() will be called if necessary.
2214
2215 \note Removing a page may influence the value of the startId property.
2216
2217 \sa addPage(), setPage(), pageRemoved(), startId()
2218*/
2219void QWizard::removePage(int id)
2220{
2221 Q_D(QWizard);
2222
2223 QWizardPage *removedPage = nullptr;
2224
2225 // update startItem accordingly
2226 if (d->pageMap.size() > 0) { // only if we have any pages
2227 if (d->start == id) {
2228 const int firstId = d->pageMap.constBegin().key();
2229 if (firstId == id) {
2230 if (d->pageMap.size() > 1)
2231 d->start = (++d->pageMap.constBegin()).key(); // secondId
2232 else
2233 d->start = -1; // removing the last page
2234 } else { // startSetByUser has to be "true" here
2235 d->start = firstId;
2236 }
2237 d->startSetByUser = false;
2238 }
2239 }
2240
2241 if (d->pageMap.contains(key: id))
2242 emit pageRemoved(id);
2243
2244 if (!d->history.contains(t: id)) {
2245 // Case 1: removing a page not in the history
2246 removedPage = d->pageMap.take(key: id);
2247 d->updateCurrentPage();
2248 } else if (id != d->current) {
2249 // Case 2: removing a page in the history before the current page
2250 removedPage = d->pageMap.take(key: id);
2251 d->history.removeOne(t: id);
2252 d->_q_updateButtonStates();
2253 } else if (d->history.size() == 1) {
2254 // Case 3: removing the current page which is the first (and only) one in the history
2255 d->reset();
2256 removedPage = d->pageMap.take(key: id);
2257 if (d->pageMap.isEmpty())
2258 d->updateCurrentPage();
2259 else
2260 restart();
2261 } else {
2262 // Case 4: removing the current page which is not the first one in the history
2263 back();
2264 removedPage = d->pageMap.take(key: id);
2265 d->updateCurrentPage();
2266 }
2267
2268 if (removedPage) {
2269 if (removedPage->d_func()->initialized) {
2270 cleanupPage(id);
2271 removedPage->d_func()->initialized = false;
2272 }
2273
2274 d->pageVBoxLayout->removeWidget(w: removedPage);
2275
2276 for (int i = d->fields.size() - 1; i >= 0; --i) {
2277 if (d->fields.at(i).page == removedPage) {
2278 removedPage->d_func()->pendingFields += d->fields.at(i);
2279 d->removeFieldAt(index: i);
2280 }
2281 }
2282 }
2283}
2284
2285/*!
2286 \fn QWizardPage *QWizard::page(int id) const
2287
2288 Returns the page with the given \a id, or \nullptr if there is no
2289 such page.
2290
2291 \sa addPage(), setPage()
2292*/
2293QWizardPage *QWizard::page(int theid) const
2294{
2295 Q_D(const QWizard);
2296 return d->pageMap.value(key: theid);
2297}
2298
2299/*!
2300 \fn bool QWizard::hasVisitedPage(int id) const
2301
2302 Returns \c true if the page history contains page \a id; otherwise,
2303 returns \c false.
2304
2305 Pressing \uicontrol Back marks the current page as "unvisited" again.
2306
2307 \sa visitedIds()
2308*/
2309bool QWizard::hasVisitedPage(int theid) const
2310{
2311 Q_D(const QWizard);
2312 return d->history.contains(t: theid);
2313}
2314
2315/*!
2316 \since 5.15
2317
2318 Returns the list of IDs of visited pages, in the order in which the pages
2319 were visited.
2320
2321 \sa hasVisitedPage()
2322*/
2323QList<int> QWizard::visitedIds() const
2324{
2325 Q_D(const QWizard);
2326 return d->history;
2327}
2328
2329/*!
2330 Returns the list of page IDs.
2331*/
2332QList<int> QWizard::pageIds() const
2333{
2334 Q_D(const QWizard);
2335 return d->pageMap.keys();
2336}
2337
2338/*!
2339 \property QWizard::startId
2340 \brief the ID of the first page
2341
2342 If this property isn't explicitly set, this property defaults to
2343 the lowest page ID in this wizard, or -1 if no page has been
2344 inserted yet.
2345
2346 \sa restart(), nextId()
2347*/
2348void QWizard::setStartId(int theid)
2349{
2350 Q_D(QWizard);
2351 int newStart = theid;
2352 if (theid == -1)
2353 newStart = d->pageMap.size() ? d->pageMap.constBegin().key() : -1;
2354
2355 if (d->start == newStart) {
2356 d->startSetByUser = theid != -1;
2357 return;
2358 }
2359
2360 if (Q_UNLIKELY(!d->pageMap.contains(newStart))) {
2361 qWarning(msg: "QWizard::setStartId: Invalid page ID %d", newStart);
2362 return;
2363 }
2364 d->start = newStart;
2365 d->startSetByUser = theid != -1;
2366}
2367
2368int QWizard::startId() const
2369{
2370 Q_D(const QWizard);
2371 return d->start;
2372}
2373
2374/*!
2375 Returns a pointer to the current page, or \nullptr if there is no
2376 current page (e.g., before the wizard is shown).
2377
2378 This is equivalent to calling page(currentId()).
2379
2380 \sa page(), currentId(), restart()
2381*/
2382QWizardPage *QWizard::currentPage() const
2383{
2384 Q_D(const QWizard);
2385 return page(theid: d->current);
2386}
2387
2388/*!
2389 \property QWizard::currentId
2390 \brief the ID of the current page
2391
2392 By default, this property has a value of -1, indicating that no page is
2393 currently shown.
2394
2395 \sa currentPage()
2396*/
2397int QWizard::currentId() const
2398{
2399 Q_D(const QWizard);
2400 return d->current;
2401}
2402
2403/*!
2404 Sets the value of the field called \a name to \a value.
2405
2406 This function can be used to set fields on any page of the wizard.
2407
2408 \sa QWizardPage::registerField(), QWizardPage::setField(), field()
2409*/
2410void QWizard::setField(const QString &name, const QVariant &value)
2411{
2412 Q_D(QWizard);
2413
2414 int index = d->fieldIndexMap.value(key: name, defaultValue: -1);
2415 if (Q_UNLIKELY(index == -1)) {
2416 qWarning(msg: "QWizard::setField: No such field '%ls'", qUtf16Printable(name));
2417 return;
2418 }
2419
2420 const QWizardField &field = d->fields.at(i: index);
2421 if (Q_UNLIKELY(!field.object->setProperty(field.property, value)))
2422 qWarning(msg: "QWizard::setField: Couldn't write to property '%s'",
2423 field.property.constData());
2424}
2425
2426/*!
2427 Returns the value of the field called \a name.
2428
2429 This function can be used to access fields on any page of the wizard.
2430
2431 \sa QWizardPage::registerField(), QWizardPage::field(), setField()
2432*/
2433QVariant QWizard::field(const QString &name) const
2434{
2435 Q_D(const QWizard);
2436
2437 int index = d->fieldIndexMap.value(key: name, defaultValue: -1);
2438 if (Q_UNLIKELY(index == -1)) {
2439 qWarning(msg: "QWizard::field: No such field '%ls'", qUtf16Printable(name));
2440 return QVariant();
2441 }
2442
2443 const QWizardField &field = d->fields.at(i: index);
2444 return field.object->property(name: field.property);
2445}
2446
2447/*!
2448 \property QWizard::wizardStyle
2449 \brief the look and feel of the wizard
2450
2451 By default, QWizard uses the AeroStyle on a Windows Vista system with alpha compositing
2452 enabled, regardless of the current widget style. If this is not the case, the default
2453 wizard style depends on the current widget style as follows: MacStyle is the default if
2454 the current widget style is QMacStyle, ModernStyle is the default if the current widget
2455 style is QWindowsStyle, and ClassicStyle is the default in all other cases.
2456
2457 \sa {Wizard Look and Feel}, options
2458*/
2459void QWizard::setWizardStyle(WizardStyle style)
2460{
2461 Q_D(QWizard);
2462
2463 const bool styleChange = style != d->wizStyle;
2464
2465#if QT_CONFIG(style_windowsvista)
2466 const bool aeroStyleChange =
2467 d->vistaInitPending || d->vistaStateChanged || (styleChange && (style == AeroStyle || d->wizStyle == AeroStyle));
2468 d->vistaStateChanged = false;
2469 d->vistaInitPending = false;
2470#endif
2471
2472 if (styleChange
2473#if QT_CONFIG(style_windowsvista)
2474 || aeroStyleChange
2475#endif
2476 ) {
2477 d->disableUpdates();
2478 d->wizStyle = style;
2479 d->updateButtonTexts();
2480#if QT_CONFIG(style_windowsvista)
2481 if (aeroStyleChange) {
2482 //Send a resizeevent since the antiflicker widget probably needs a new size
2483 //because of the backbutton in the window title
2484 QResizeEvent ev(geometry().size(), geometry().size());
2485 QCoreApplication::sendEvent(this, &ev);
2486 }
2487#endif
2488 d->updateLayout();
2489 updateGeometry();
2490 d->enableUpdates();
2491#if QT_CONFIG(style_windowsvista)
2492 // Delay initialization when activating Aero style fails due to missing native window.
2493 if (aeroStyleChange && !d->handleAeroStyleChange() && d->wizStyle == AeroStyle)
2494 d->vistaInitPending = true;
2495#endif
2496 }
2497}
2498
2499QWizard::WizardStyle QWizard::wizardStyle() const
2500{
2501 Q_D(const QWizard);
2502 return d->wizStyle;
2503}
2504
2505/*!
2506 Sets the given \a option to be enabled if \a on is true;
2507 otherwise, clears the given \a option.
2508
2509 \sa options, testOption(), setWizardStyle()
2510*/
2511void QWizard::setOption(WizardOption option, bool on)
2512{
2513 Q_D(QWizard);
2514 if (!(d->opts & option) != !on)
2515 setOptions(d->opts ^ option);
2516}
2517
2518/*!
2519 Returns \c true if the given \a option is enabled; otherwise, returns
2520 false.
2521
2522 \sa options, setOption(), setWizardStyle()
2523*/
2524bool QWizard::testOption(WizardOption option) const
2525{
2526 Q_D(const QWizard);
2527 return (d->opts & option) != 0;
2528}
2529
2530/*!
2531 \property QWizard::options
2532 \brief the various options that affect the look and feel of the wizard
2533
2534 By default, the following options are set (depending on the platform):
2535
2536 \list
2537 \li Windows: HelpButtonOnRight.
2538 \li \macos: NoDefaultButton and NoCancelButton.
2539 \li X11 and QWS (Qt for Embedded Linux): none.
2540 \endlist
2541
2542 \sa wizardStyle
2543*/
2544void QWizard::setOptions(WizardOptions options)
2545{
2546 Q_D(QWizard);
2547
2548 WizardOptions changed = (options ^ d->opts);
2549 if (!changed)
2550 return;
2551
2552 d->disableUpdates();
2553
2554 d->opts = options;
2555 if ((changed & IndependentPages) && !(d->opts & IndependentPages))
2556 d->cleanupPagesNotInHistory();
2557
2558 if (changed & (NoDefaultButton | HaveHelpButton | HelpButtonOnRight | NoCancelButton
2559 | CancelButtonOnLeft | HaveCustomButton1 | HaveCustomButton2
2560 | HaveCustomButton3)) {
2561 d->updateButtonLayout();
2562 } else if (changed & (NoBackButtonOnStartPage | NoBackButtonOnLastPage
2563 | HaveNextButtonOnLastPage | HaveFinishButtonOnEarlyPages
2564 | DisabledBackButtonOnLastPage | NoCancelButtonOnLastPage)) {
2565 d->_q_updateButtonStates();
2566 }
2567
2568 d->enableUpdates();
2569 d->updateLayout();
2570}
2571
2572QWizard::WizardOptions QWizard::options() const
2573{
2574 Q_D(const QWizard);
2575 return d->opts;
2576}
2577
2578/*!
2579 Sets the text on button \a which to be \a text.
2580
2581 By default, the text on buttons depends on the wizardStyle. For
2582 example, on \macos, the \uicontrol Next button is called \uicontrol
2583 Continue.
2584
2585 To add extra buttons to the wizard (e.g., a \uicontrol Print button),
2586 one way is to call setButtonText() with CustomButton1,
2587 CustomButton2, or CustomButton3 to set their text, and make the
2588 buttons visible using the HaveCustomButton1, HaveCustomButton2,
2589 and/or HaveCustomButton3 options.
2590
2591 Button texts may also be set on a per-page basis using QWizardPage::setButtonText().
2592
2593 \sa setButton(), button(), setButtonLayout(), setOptions(), QWizardPage::setButtonText()
2594*/
2595void QWizard::setButtonText(WizardButton which, const QString &text)
2596{
2597 Q_D(QWizard);
2598
2599 if (!d->ensureButton(which))
2600 return;
2601
2602 d->buttonCustomTexts.insert(key: which, value: text);
2603
2604 if (!currentPage() || !currentPage()->d_func()->buttonCustomTexts.contains(key: which))
2605 d->btns[which]->setText(text);
2606}
2607
2608/*!
2609 Returns the text on button \a which.
2610
2611 If a text has ben set using setButtonText(), this text is returned.
2612
2613 By default, the text on buttons depends on the wizardStyle. For
2614 example, on \macos, the \uicontrol Next button is called \uicontrol
2615 Continue.
2616
2617 \sa button(), setButton(), setButtonText(), QWizardPage::buttonText(),
2618 QWizardPage::setButtonText()
2619*/
2620QString QWizard::buttonText(WizardButton which) const
2621{
2622 Q_D(const QWizard);
2623
2624 if (!d->ensureButton(which))
2625 return QString();
2626
2627 if (d->buttonCustomTexts.contains(key: which))
2628 return d->buttonCustomTexts.value(key: which);
2629
2630 const QString defText = buttonDefaultText(wstyle: d->wizStyle, which, wizardPrivate: d);
2631 if (!defText.isNull())
2632 return defText;
2633
2634 return d->btns[which]->text();
2635}
2636
2637/*!
2638 Sets the order in which buttons are displayed to \a layout, where
2639 \a layout is a list of \l{WizardButton}s.
2640
2641 The default layout depends on the options (e.g., whether
2642 HelpButtonOnRight) that are set. You can call this function if
2643 you need more control over the buttons' layout than what \l
2644 options already provides.
2645
2646 You can specify horizontal stretches in the layout using \l
2647 Stretch.
2648
2649 Example:
2650
2651 \snippet code/src_gui_dialogs_qwizard.cpp 1
2652
2653 \sa setButton(), setButtonText(), setOptions()
2654*/
2655void QWizard::setButtonLayout(const QList<WizardButton> &layout)
2656{
2657 Q_D(QWizard);
2658
2659 for (int i = 0; i < layout.size(); ++i) {
2660 WizardButton button1 = layout.at(i);
2661
2662 if (button1 == NoButton || button1 == Stretch)
2663 continue;
2664 if (!d->ensureButton(which: button1))
2665 return;
2666
2667 // O(n^2), but n is very small
2668 for (int j = 0; j < i; ++j) {
2669 WizardButton button2 = layout.at(i: j);
2670 if (Q_UNLIKELY(button2 == button1)) {
2671 qWarning(msg: "QWizard::setButtonLayout: Duplicate button in layout");
2672 return;
2673 }
2674 }
2675 }
2676
2677 d->buttonsHaveCustomLayout = true;
2678 d->buttonsCustomLayout = layout;
2679 d->updateButtonLayout();
2680}
2681
2682/*!
2683 Sets the button corresponding to role \a which to \a button.
2684
2685 To add extra buttons to the wizard (e.g., a \uicontrol Print button),
2686 one way is to call setButton() with CustomButton1 to
2687 CustomButton3, and make the buttons visible using the
2688 HaveCustomButton1 to HaveCustomButton3 options.
2689
2690 \sa setButtonText(), setButtonLayout(), options
2691*/
2692void QWizard::setButton(WizardButton which, QAbstractButton *button)
2693{
2694 Q_D(QWizard);
2695
2696 if (uint(which) >= NButtons || d->btns[which] == button)
2697 return;
2698
2699 if (QAbstractButton *oldButton = d->btns[which]) {
2700 d->buttonLayout->removeWidget(w: oldButton);
2701 delete oldButton;
2702 }
2703
2704 d->btns[which] = button;
2705 if (button) {
2706 button->setParent(d->antiFlickerWidget);
2707 d->buttonCustomTexts.insert(key: which, value: button->text());
2708 d->connectButton(which);
2709 } else {
2710 d->buttonCustomTexts.remove(key: which); // ### what about page-specific texts set for 'which'
2711 d->ensureButton(which); // (QWizardPage::setButtonText())? Clear them as well?
2712 }
2713
2714 d->updateButtonLayout();
2715}
2716
2717/*!
2718 Returns the button corresponding to role \a which.
2719
2720 \sa setButton(), setButtonText()
2721*/
2722QAbstractButton *QWizard::button(WizardButton which) const
2723{
2724 Q_D(const QWizard);
2725#if QT_CONFIG(style_windowsvista)
2726 if (d->wizStyle == AeroStyle && which == BackButton)
2727 return d->vistaHelper->backButton();
2728#endif
2729 if (!d->ensureButton(which))
2730 return nullptr;
2731 return d->btns[which];
2732}
2733
2734/*!
2735 \property QWizard::titleFormat
2736 \brief the text format used by page titles
2737
2738 The default format is Qt::AutoText.
2739
2740 \sa QWizardPage::title, subTitleFormat
2741*/
2742void QWizard::setTitleFormat(Qt::TextFormat format)
2743{
2744 Q_D(QWizard);
2745 d->titleFmt = format;
2746 d->updateLayout();
2747}
2748
2749Qt::TextFormat QWizard::titleFormat() const
2750{
2751 Q_D(const QWizard);
2752 return d->titleFmt;
2753}
2754
2755/*!
2756 \property QWizard::subTitleFormat
2757 \brief the text format used by page subtitles
2758
2759 The default format is Qt::AutoText.
2760
2761 \sa QWizardPage::title, titleFormat
2762*/
2763void QWizard::setSubTitleFormat(Qt::TextFormat format)
2764{
2765 Q_D(QWizard);
2766 d->subTitleFmt = format;
2767 d->updateLayout();
2768}
2769
2770Qt::TextFormat QWizard::subTitleFormat() const
2771{
2772 Q_D(const QWizard);
2773 return d->subTitleFmt;
2774}
2775
2776/*!
2777 Sets the pixmap for role \a which to \a pixmap.
2778
2779 The pixmaps are used by QWizard when displaying a page. Which
2780 pixmaps are actually used depend on the \l{Wizard Look and
2781 Feel}{wizard style}.
2782
2783 Pixmaps can also be set for a specific page using
2784 QWizardPage::setPixmap().
2785
2786 \sa QWizardPage::setPixmap(), {Elements of a Wizard Page}
2787*/
2788void QWizard::setPixmap(WizardPixmap which, const QPixmap &pixmap)
2789{
2790 Q_D(QWizard);
2791 Q_ASSERT(uint(which) < NPixmaps);
2792 d->defaultPixmaps[which] = pixmap;
2793 d->updatePixmap(which);
2794}
2795
2796/*!
2797 Returns the pixmap set for role \a which.
2798
2799 By default, the only pixmap that is set is the BackgroundPixmap on
2800 \macos.
2801
2802 \sa QWizardPage::pixmap(), {Elements of a Wizard Page}
2803*/
2804QPixmap QWizard::pixmap(WizardPixmap which) const
2805{
2806 Q_D(const QWizard);
2807 Q_ASSERT(uint(which) < NPixmaps);
2808#ifdef Q_OS_MACOS
2809 if (which == BackgroundPixmap && d->defaultPixmaps[BackgroundPixmap].isNull())
2810 d->defaultPixmaps[BackgroundPixmap] = d->findDefaultBackgroundPixmap();
2811#endif
2812 return d->defaultPixmaps[which];
2813}
2814
2815/*!
2816 Sets the default property for \a className to be \a property,
2817 and the associated change signal to be \a changedSignal.
2818
2819 The default property is used when an instance of \a className (or
2820 of one of its subclasses) is passed to
2821 QWizardPage::registerField() and no property is specified.
2822
2823 QWizard knows the most common Qt widgets. For these (or their
2824 subclasses), you don't need to specify a \a property or a \a
2825 changedSignal. The table below lists these widgets:
2826
2827 \table
2828 \header \li Widget \li Property \li Change Notification Signal
2829 \row \li QAbstractButton \li bool \l{QAbstractButton::}{checked} \li \l{QAbstractButton::}{toggled()}
2830 \row \li QAbstractSlider \li int \l{QAbstractSlider::}{value} \li \l{QAbstractSlider::}{valueChanged()}
2831 \row \li QComboBox \li int \l{QComboBox::}{currentIndex} \li \l{QComboBox::}{currentIndexChanged()}
2832 \row \li QDateTimeEdit \li QDateTime \l{QDateTimeEdit::}{dateTime} \li \l{QDateTimeEdit::}{dateTimeChanged()}
2833 \row \li QLineEdit \li QString \l{QLineEdit::}{text} \li \l{QLineEdit::}{textChanged()}
2834 \row \li QListWidget \li int \l{QListWidget::}{currentRow} \li \l{QListWidget::}{currentRowChanged()}
2835 \row \li QSpinBox \li int \l{QSpinBox::}{value} \li \l{QSpinBox::}{valueChanged()}
2836 \endtable
2837
2838 \sa QWizardPage::registerField()
2839*/
2840void QWizard::setDefaultProperty(const char *className, const char *property,
2841 const char *changedSignal)
2842{
2843 Q_D(QWizard);
2844 for (int i = d->defaultPropertyTable.size() - 1; i >= 0; --i) {
2845 if (qstrcmp(str1: d->defaultPropertyTable.at(i).className, str2: className) == 0) {
2846 d->defaultPropertyTable.remove(i);
2847 break;
2848 }
2849 }
2850 d->defaultPropertyTable.append(t: QWizardDefaultProperty(className, property, changedSignal));
2851}
2852
2853/*!
2854 Sets the given \a widget to be shown on the left side of the wizard.
2855 For styles which use the WatermarkPixmap (ClassicStyle and ModernStyle)
2856 the side widget is displayed on top of the watermark, for other styles
2857 or when the watermark is not provided the side widget is displayed
2858 on the left side of the wizard.
2859
2860 Passing \nullptr shows no side widget.
2861
2862 When the \a widget is not \nullptr the wizard reparents it.
2863
2864 Any previous side widget is hidden.
2865
2866 You may call setSideWidget() with the same widget at different
2867 times.
2868
2869 All widgets set here will be deleted by the wizard when it is
2870 destroyed unless you separately reparent the widget after setting
2871 some other side widget (or \nullptr).
2872
2873 By default, no side widget is present.
2874*/
2875void QWizard::setSideWidget(QWidget *widget)
2876{
2877 Q_D(QWizard);
2878
2879 d->sideWidget = widget;
2880 if (d->watermarkLabel) {
2881 d->watermarkLabel->setSideWidget(widget);
2882 d->updateLayout();
2883 }
2884}
2885
2886/*!
2887 Returns the widget on the left side of the wizard or \nullptr.
2888
2889 By default, no side widget is present.
2890*/
2891QWidget *QWizard::sideWidget() const
2892{
2893 Q_D(const QWizard);
2894
2895 return d->sideWidget;
2896}
2897
2898/*!
2899 \reimp
2900*/
2901void QWizard::setVisible(bool visible)
2902{
2903 Q_D(QWizard);
2904 if (visible) {
2905 if (d->current == -1)
2906 restart();
2907 }
2908 QDialog::setVisible(visible);
2909}
2910
2911/*!
2912 \reimp
2913*/
2914QSize QWizard::sizeHint() const
2915{
2916 Q_D(const QWizard);
2917 QSize result = d->mainLayout->totalSizeHint();
2918 QSize extra(500, 360);
2919 if (d->wizStyle == MacStyle && d->current != -1) {
2920 QSize pixmap(currentPage()->pixmap(which: BackgroundPixmap).size());
2921 extra.setWidth(616);
2922 if (!pixmap.isNull()) {
2923 extra.setHeight(pixmap.height());
2924
2925 /*
2926 The width isn't always reliable as a size hint, as
2927 some wizard backgrounds just cover the leftmost area.
2928 Use a rule of thumb to determine if the width is
2929 reliable or not.
2930 */
2931 if (pixmap.width() >= pixmap.height())
2932 extra.setWidth(pixmap.width());
2933 }
2934 }
2935 return result.expandedTo(otherSize: extra);
2936}
2937
2938/*!
2939 \fn void QWizard::currentIdChanged(int id)
2940
2941 This signal is emitted when the current page changes, with the new
2942 current \a id.
2943
2944 \sa currentId(), currentPage()
2945*/
2946
2947/*!
2948 \fn void QWizard::pageAdded(int id)
2949
2950 This signal is emitted whenever a page is added to the
2951 wizard. The page's \a id is passed as parameter.
2952
2953 \sa addPage(), setPage(), startId()
2954*/
2955
2956/*!
2957 \fn void QWizard::pageRemoved(int id)
2958
2959 This signal is emitted whenever a page is removed from the
2960 wizard. The page's \a id is passed as parameter.
2961
2962 \sa removePage(), startId()
2963*/
2964
2965/*!
2966 \fn void QWizard::helpRequested()
2967
2968 This signal is emitted when the user clicks the \uicontrol Help button.
2969
2970 By default, no \uicontrol Help button is shown. Call
2971 setOption(HaveHelpButton, true) to have one.
2972
2973 Example:
2974
2975 \snippet dialogs/licensewizard/licensewizard.cpp 0
2976 \dots
2977 \snippet dialogs/licensewizard/licensewizard.cpp 5
2978 \snippet dialogs/licensewizard/licensewizard.cpp 7
2979 \dots
2980 \snippet dialogs/licensewizard/licensewizard.cpp 8
2981 \codeline
2982 \snippet dialogs/licensewizard/licensewizard.cpp 10
2983 \dots
2984 \snippet dialogs/licensewizard/licensewizard.cpp 12
2985 \codeline
2986 \snippet dialogs/licensewizard/licensewizard.cpp 14
2987 \codeline
2988 \snippet dialogs/licensewizard/licensewizard.cpp 15
2989
2990 \sa customButtonClicked()
2991*/
2992
2993/*!
2994 \fn void QWizard::customButtonClicked(int which)
2995
2996 This signal is emitted when the user clicks a custom button. \a
2997 which can be CustomButton1, CustomButton2, or CustomButton3.
2998
2999 By default, no custom button is shown. Call setOption() with
3000 HaveCustomButton1, HaveCustomButton2, or HaveCustomButton3 to have
3001 one, and use setButtonText() or setButton() to configure it.
3002
3003 \sa helpRequested()
3004*/
3005
3006/*!
3007 Goes back to the previous page.
3008
3009 This is equivalent to pressing the \uicontrol Back button.
3010
3011 \sa next(), accept(), reject(), restart()
3012*/
3013void QWizard::back()
3014{
3015 Q_D(QWizard);
3016 int n = d->history.size() - 2;
3017 if (n < 0)
3018 return;
3019 d->switchToPage(newId: d->history.at(i: n), direction: QWizardPrivate::Backward);
3020}
3021
3022/*!
3023 Advances to the next page.
3024
3025 This is equivalent to pressing the \uicontrol Next or \uicontrol Commit button.
3026
3027 \sa nextId(), back(), accept(), reject(), restart()
3028*/
3029void QWizard::next()
3030{
3031 Q_D(QWizard);
3032
3033 if (d->current == -1)
3034 return;
3035
3036 if (validateCurrentPage()) {
3037 int next = nextId();
3038 if (next != -1) {
3039 if (Q_UNLIKELY(d->history.contains(next))) {
3040 qWarning(msg: "QWizard::next: Page %d already met", next);
3041 return;
3042 }
3043 if (Q_UNLIKELY(!d->pageMap.contains(next))) {
3044 qWarning(msg: "QWizard::next: No such page %d", next);
3045 return;
3046 }
3047 d->switchToPage(newId: next, direction: QWizardPrivate::Forward);
3048 }
3049 }
3050}
3051
3052/*!
3053 Sets currentId to \a id, without visiting the pages between currentId and \a id.
3054
3055 Returns without page change, if
3056 \list
3057 \li wizard holds no pages
3058 \li current page is invalid
3059 \li given page equals currentId()
3060 \li given page is out of range
3061 \endlist
3062
3063 Note: If pages have been forward skipped and \a id is 0, page visiting history
3064 will be deleted
3065*/
3066
3067void QWizard::setCurrentId(int id)
3068{
3069 Q_D(QWizard);
3070
3071 if (d->current == -1)
3072 return;
3073
3074 if (currentId() == id)
3075 return;
3076
3077 if (!validateCurrentPage())
3078 return;
3079
3080 if (id < 0 || Q_UNLIKELY(!d->pageMap.contains(id))) {
3081 qWarning(msg: "QWizard::setCurrentId: No such page: %d", id);
3082 return;
3083 }
3084
3085 d->switchToPage(newId: id, direction: (id < currentId()) ? QWizardPrivate::Backward : QWizardPrivate::Forward);
3086}
3087
3088/*!
3089 Restarts the wizard at the start page. This function is called automatically when the
3090 wizard is shown.
3091
3092 \sa startId()
3093*/
3094void QWizard::restart()
3095{
3096 Q_D(QWizard);
3097 d->disableUpdates();
3098 d->reset();
3099 d->switchToPage(newId: startId(), direction: QWizardPrivate::Forward);
3100 d->enableUpdates();
3101}
3102
3103/*!
3104 \reimp
3105*/
3106bool QWizard::event(QEvent *event)
3107{
3108 Q_D(QWizard);
3109 if (event->type() == QEvent::StyleChange) { // Propagate style
3110 d->setStyle(style());
3111 d->updateLayout();
3112 } else if (event->type() == QEvent::PaletteChange) { // Emitted on theme change
3113 d->updatePalette();
3114 }
3115#if QT_CONFIG(style_windowsvista)
3116 else if (event->type() == QEvent::Show && d->vistaInitPending) {
3117 d->vistaInitPending = false;
3118 d->wizStyle = AeroStyle;
3119 d->handleAeroStyleChange();
3120 }
3121 else if (d->isVistaThemeEnabled()) {
3122 if (event->type() == QEvent::Resize
3123 || event->type() == QEvent::LayoutDirectionChange) {
3124 const int buttonLeft = (layoutDirection() == Qt::RightToLeft
3125 ? width() - d->vistaHelper->backButton()->sizeHint().width()
3126 : 0);
3127
3128 d->vistaHelper->backButton()->move(buttonLeft,
3129 d->vistaHelper->backButton()->y());
3130 }
3131
3132 d->vistaHelper->mouseEvent(event);
3133 }
3134#endif
3135 return QDialog::event(event);
3136}
3137
3138/*!
3139 \reimp
3140*/
3141void QWizard::resizeEvent(QResizeEvent *event)
3142{
3143 Q_D(QWizard);
3144 int heightOffset = 0;
3145#if QT_CONFIG(style_windowsvista)
3146 if (d->isVistaThemeEnabled()) {
3147 heightOffset = d->vistaHelper->topOffset(this);
3148 heightOffset += d->vistaHelper->titleBarSize();
3149 }
3150#endif
3151 d->antiFlickerWidget->resize(w: event->size().width(), h: event->size().height() - heightOffset);
3152#if QT_CONFIG(style_windowsvista)
3153 if (d->isVistaThemeEnabled())
3154 d->vistaHelper->resizeEvent(event);
3155#endif
3156 QDialog::resizeEvent(event);
3157}
3158
3159/*!
3160 \reimp
3161*/
3162void QWizard::paintEvent(QPaintEvent * event)
3163{
3164 Q_D(QWizard);
3165 if (d->wizStyle == MacStyle && currentPage()) {
3166 QPixmap backgroundPixmap = currentPage()->pixmap(which: BackgroundPixmap);
3167 if (backgroundPixmap.isNull())
3168 return;
3169
3170 QStylePainter painter(this);
3171 painter.drawPixmap(x: 0, y: (height() - backgroundPixmap.height()) / 2, pm: backgroundPixmap);
3172 }
3173#if QT_CONFIG(style_windowsvista)
3174 else if (d->isVistaThemeEnabled()) {
3175 d->vistaHelper->paintEvent(event);
3176 }
3177#else
3178 Q_UNUSED(event);
3179#endif
3180}
3181
3182#if defined(Q_OS_WIN) || defined(Q_QDOC)
3183/*!
3184 \reimp
3185*/
3186bool QWizard::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
3187{
3188#if QT_CONFIG(style_windowsvista)
3189 Q_D(QWizard);
3190 if (d->isVistaThemeEnabled() && eventType == "windows_generic_MSG") {
3191 MSG *windowsMessage = static_cast<MSG *>(message);
3192 const bool winEventResult = d->vistaHelper->handleWinEvent(windowsMessage, result);
3193 if (d->vistaDirty) {
3194 // QTBUG-78300: When Qt::AA_NativeWindows is set, delay further
3195 // window creation until after the platform window creation events.
3196 if (windowsMessage->message == WM_GETICON) {
3197 d->vistaStateChanged = true;
3198 d->vistaDirty = false;
3199 setWizardStyle(AeroStyle);
3200 }
3201 }
3202 return winEventResult;
3203 } else {
3204 return QDialog::nativeEvent(eventType, message, result);
3205 }
3206#else
3207 return QDialog::nativeEvent(eventType, message, result);
3208#endif
3209}
3210#endif
3211
3212/*!
3213 \reimp
3214*/
3215void QWizard::done(int result)
3216{
3217 Q_D(QWizard);
3218 // canceling leaves the wizard in a known state
3219 if (result == Rejected) {
3220 d->reset();
3221 } else {
3222 if (!validateCurrentPage())
3223 return;
3224 }
3225 QDialog::done(result);
3226}
3227
3228/*!
3229 \fn void QWizard::initializePage(int id)
3230
3231 This virtual function is called by QWizard to prepare page \a id
3232 just before it is shown either as a result of QWizard::restart()
3233 being called, or as a result of the user clicking \uicontrol Next. (However, if the \l
3234 QWizard::IndependentPages option is set, this function is only
3235 called the first time the page is shown.)
3236
3237 By reimplementing this function, you can ensure that the page's
3238 fields are properly initialized based on fields from previous
3239 pages.
3240
3241 The default implementation calls QWizardPage::initializePage() on
3242 page(\a id).
3243
3244 \sa QWizardPage::initializePage(), cleanupPage()
3245*/
3246void QWizard::initializePage(int theid)
3247{
3248 QWizardPage *page = this->page(theid);
3249 if (page)
3250 page->initializePage();
3251}
3252
3253/*!
3254 \fn void QWizard::cleanupPage(int id)
3255
3256 This virtual function is called by QWizard to clean up page \a id just before the
3257 user leaves it by clicking \uicontrol Back (unless the \l QWizard::IndependentPages option is set).
3258
3259 The default implementation calls QWizardPage::cleanupPage() on
3260 page(\a id).
3261
3262 \sa QWizardPage::cleanupPage(), initializePage()
3263*/
3264void QWizard::cleanupPage(int theid)
3265{
3266 QWizardPage *page = this->page(theid);
3267 if (page)
3268 page->cleanupPage();
3269}
3270
3271/*!
3272 This virtual function is called by QWizard when the user clicks
3273 \uicontrol Next or \uicontrol Finish to perform some last-minute validation.
3274 If it returns \c true, the next page is shown (or the wizard
3275 finishes); otherwise, the current page stays up.
3276
3277 The default implementation calls QWizardPage::validatePage() on
3278 the currentPage().
3279
3280 When possible, it is usually better style to disable the \uicontrol
3281 Next or \uicontrol Finish button (by specifying \l{mandatory fields} or
3282 by reimplementing QWizardPage::isComplete()) than to reimplement
3283 validateCurrentPage().
3284
3285 \sa QWizardPage::validatePage(), currentPage()
3286*/
3287bool QWizard::validateCurrentPage()
3288{
3289 QWizardPage *page = currentPage();
3290 if (!page)
3291 return true;
3292
3293 return page->validatePage();
3294}
3295
3296/*!
3297 This virtual function is called by QWizard to find out which page
3298 to show when the user clicks the \uicontrol Next button.
3299
3300 The return value is the ID of the next page, or -1 if no page follows.
3301
3302 The default implementation calls QWizardPage::nextId() on the
3303 currentPage().
3304
3305 By reimplementing this function, you can specify a dynamic page
3306 order.
3307
3308 \sa QWizardPage::nextId(), currentPage()
3309*/
3310int QWizard::nextId() const
3311{
3312 const QWizardPage *page = currentPage();
3313 if (!page)
3314 return -1;
3315
3316 return page->nextId();
3317}
3318
3319/*!
3320 \class QWizardPage
3321 \brief The QWizardPage class is the base class for wizard pages.
3322
3323 \inmodule QtWidgets
3324
3325 QWizard represents a wizard. Each page is a QWizardPage. When
3326 you create your own wizards, you can use QWizardPage directly,
3327 or you can subclass it for more control.
3328
3329 A page has the following attributes, which are rendered by
3330 QWizard: a \l title, a \l subTitle, and a \l{setPixmap()}{set of
3331 pixmaps}. See \l{Elements of a Wizard Page} for details. Once a
3332 page is added to the wizard (using QWizard::addPage() or
3333 QWizard::setPage()), wizard() returns a pointer to the
3334 associated QWizard object.
3335
3336 Page provides five virtual functions that can be reimplemented to
3337 provide custom behavior:
3338
3339 \list
3340 \li initializePage() is called to initialize the page's contents
3341 when the user clicks the wizard's \uicontrol Next button. If you
3342 want to derive the page's default from what the user entered
3343 on previous pages, this is the function to reimplement.
3344 \li cleanupPage() is called to reset the page's contents when the
3345 user clicks the wizard's \uicontrol Back button.
3346 \li validatePage() validates the page when the user clicks \uicontrol
3347 Next or \uicontrol Finish. It is often used to show an error message
3348 if the user has entered incomplete or invalid information.
3349 \li nextId() returns the ID of the next page. It is useful when
3350 \l{creating non-linear wizards}, which allow different
3351 traversal paths based on the information provided by the user.
3352 \li isComplete() is called to determine whether the \uicontrol Next
3353 and/or \uicontrol Finish button should be enabled or disabled. If
3354 you reimplement isComplete(), also make sure that
3355 completeChanged() is emitted whenever the complete state
3356 changes.
3357 \endlist
3358
3359 Normally, the \uicontrol Next button and the \uicontrol Finish button of a
3360 wizard are mutually exclusive. If isFinalPage() returns \c true, \uicontrol
3361 Finish is available; otherwise, \uicontrol Next is available. By
3362 default, isFinalPage() is true only when nextId() returns -1. If
3363 you want to show \uicontrol Next and \uicontrol Final simultaneously for a
3364 page (letting the user perform an "early finish"), call
3365 setFinalPage(true) on that page. For wizards that support early
3366 finishes, you might also want to set the
3367 \l{QWizard::}{HaveNextButtonOnLastPage} and
3368 \l{QWizard::}{HaveFinishButtonOnEarlyPages} options on the
3369 wizard.
3370
3371 In many wizards, the contents of a page may affect the default
3372 values of the fields of a later page. To make it easy to
3373 communicate between pages, QWizard supports a \l{Registering and
3374 Using Fields}{"field" mechanism} that allows you to register a
3375 field (e.g., a QLineEdit) on a page and to access its value from
3376 any page. Fields are global to the entire wizard and make it easy
3377 for any single page to access information stored by another page,
3378 without having to put all the logic in QWizard or having the
3379 pages know explicitly about each other. Fields are registered
3380 using registerField() and can be accessed at any time using
3381 field() and setField().
3382
3383 \sa QWizard, {Trivial Wizard Example}, {License Wizard Example}
3384*/
3385
3386/*!
3387 Constructs a wizard page with the given \a parent.
3388
3389 When the page is inserted into a wizard using QWizard::addPage()
3390 or QWizard::setPage(), the parent is automatically set to be the
3391 wizard.
3392
3393 \sa wizard()
3394*/
3395QWizardPage::QWizardPage(QWidget *parent)
3396 : QWidget(*new QWizardPagePrivate, parent, { })
3397{
3398 connect(sender: this, SIGNAL(completeChanged()), receiver: this, SLOT(_q_updateCachedCompleteState()));
3399}
3400
3401/*!
3402 Destructor.
3403*/
3404QWizardPage::~QWizardPage()
3405{
3406}
3407
3408/*!
3409 \property QWizardPage::title
3410 \brief the title of the page
3411
3412 The title is shown by the QWizard, above the actual page. All
3413 pages should have a title.
3414
3415 The title may be plain text or HTML, depending on the value of the
3416 \l{QWizard::titleFormat} property.
3417
3418 By default, this property contains an empty string.
3419
3420 \sa subTitle, {Elements of a Wizard Page}
3421*/
3422void QWizardPage::setTitle(const QString &title)
3423{
3424 Q_D(QWizardPage);
3425 d->title = title;
3426 if (d->wizard && d->wizard->currentPage() == this)
3427 d->wizard->d_func()->updateLayout();
3428}
3429
3430QString QWizardPage::title() const
3431{
3432 Q_D(const QWizardPage);
3433 return d->title;
3434}
3435
3436/*!
3437 \property QWizardPage::subTitle
3438 \brief the subtitle of the page
3439
3440 The subtitle is shown by the QWizard, between the title and the
3441 actual page. Subtitles are optional. In
3442 \l{QWizard::ClassicStyle}{ClassicStyle} and
3443 \l{QWizard::ModernStyle}{ModernStyle}, using subtitles is
3444 necessary to make the header appear. In
3445 \l{QWizard::MacStyle}{MacStyle}, the subtitle is shown as a text
3446 label just above the actual page.
3447
3448 The subtitle may be plain text or HTML, depending on the value of
3449 the \l{QWizard::subTitleFormat} property.
3450
3451 By default, this property contains an empty string.
3452
3453 \sa title, QWizard::IgnoreSubTitles, {Elements of a Wizard Page}
3454*/
3455void QWizardPage::setSubTitle(const QString &subTitle)
3456{
3457 Q_D(QWizardPage);
3458 d->subTitle = subTitle;
3459 if (d->wizard && d->wizard->currentPage() == this)
3460 d->wizard->d_func()->updateLayout();
3461}
3462
3463QString QWizardPage::subTitle() const
3464{
3465 Q_D(const QWizardPage);
3466 return d->subTitle;
3467}
3468
3469/*!
3470 Sets the pixmap for role \a which to \a pixmap.
3471
3472 The pixmaps are used by QWizard when displaying a page. Which
3473 pixmaps are actually used depend on the \l{Wizard Look and
3474 Feel}{wizard style}.
3475
3476 Pixmaps can also be set for the entire wizard using
3477 QWizard::setPixmap(), in which case they apply for all pages that
3478 don't specify a pixmap.
3479
3480 \sa QWizard::setPixmap(), {Elements of a Wizard Page}
3481*/
3482void QWizardPage::setPixmap(QWizard::WizardPixmap which, const QPixmap &pixmap)
3483{
3484 Q_D(QWizardPage);
3485 Q_ASSERT(uint(which) < QWizard::NPixmaps);
3486 d->pixmaps[which] = pixmap;
3487 if (d->wizard && d->wizard->currentPage() == this)
3488 d->wizard->d_func()->updatePixmap(which);
3489}
3490
3491/*!
3492 Returns the pixmap set for role \a which.
3493
3494 Pixmaps can also be set for the entire wizard using
3495 QWizard::setPixmap(), in which case they apply for all pages that
3496 don't specify a pixmap.
3497
3498 \sa QWizard::pixmap(), {Elements of a Wizard Page}
3499*/
3500QPixmap QWizardPage::pixmap(QWizard::WizardPixmap which) const
3501{
3502 Q_D(const QWizardPage);
3503 Q_ASSERT(uint(which) < QWizard::NPixmaps);
3504
3505 const QPixmap &pixmap = d->pixmaps[which];
3506 if (!pixmap.isNull())
3507 return pixmap;
3508
3509 if (wizard())
3510 return wizard()->pixmap(which);
3511
3512 return pixmap;
3513}
3514
3515/*!
3516 This virtual function is called by QWizard::initializePage() to
3517 prepare the page just before it is shown either as a result of QWizard::restart()
3518 being called, or as a result of the user clicking \uicontrol Next.
3519 (However, if the \l QWizard::IndependentPages option is set, this function is only
3520 called the first time the page is shown.)
3521
3522 By reimplementing this function, you can ensure that the page's
3523 fields are properly initialized based on fields from previous
3524 pages. For example:
3525
3526 \snippet dialogs/licensewizard/licensewizard.cpp 27
3527
3528 The default implementation does nothing.
3529
3530 \sa QWizard::initializePage(), cleanupPage(), QWizard::IndependentPages
3531*/
3532void QWizardPage::initializePage()
3533{
3534}
3535
3536/*!
3537 This virtual function is called by QWizard::cleanupPage() when
3538 the user leaves the page by clicking \uicontrol Back (unless the \l QWizard::IndependentPages
3539 option is set).
3540
3541 The default implementation resets the page's fields to their
3542 original values (the values they had before initializePage() was
3543 called).
3544
3545 \sa QWizard::cleanupPage(), initializePage(), QWizard::IndependentPages
3546*/
3547void QWizardPage::cleanupPage()
3548{
3549 Q_D(QWizardPage);
3550 if (d->wizard) {
3551 const QList<QWizardField> &fields = d->wizard->d_func()->fields;
3552 for (const auto &field : fields) {
3553 if (field.page == this)
3554 field.object->setProperty(name: field.property, value: field.initialValue);
3555 }
3556 }
3557}
3558
3559/*!
3560 This virtual function is called by QWizard::validateCurrentPage()
3561 when the user clicks \uicontrol Next or \uicontrol Finish to perform some
3562 last-minute validation. If it returns \c true, the next page is shown
3563 (or the wizard finishes); otherwise, the current page stays up.
3564
3565 The default implementation returns \c true.
3566
3567 When possible, it is usually better style to disable the \uicontrol
3568 Next or \uicontrol Finish button (by specifying \l{mandatory fields} or
3569 reimplementing isComplete()) than to reimplement validatePage().
3570
3571 \sa QWizard::validateCurrentPage(), isComplete()
3572*/
3573bool QWizardPage::validatePage()
3574{
3575 return true;
3576}
3577
3578/*!
3579 This virtual function is called by QWizard to determine whether
3580 the \uicontrol Next or \uicontrol Finish button should be enabled or
3581 disabled.
3582
3583 The default implementation returns \c true if all \l{mandatory
3584 fields} are filled; otherwise, it returns \c false.
3585
3586 If you reimplement this function, make sure to emit completeChanged(),
3587 from the rest of your implementation, whenever the value of isComplete()
3588 changes. This ensures that QWizard updates the enabled or disabled state of
3589 its buttons. An example of the reimplementation is
3590 available \l{http://doc.qt.io/archives/qq/qq22-qwizard.html#validatebeforeitstoolate}
3591 {here}.
3592
3593 \sa completeChanged(), isFinalPage()
3594*/
3595bool QWizardPage::isComplete() const
3596{
3597 Q_D(const QWizardPage);
3598
3599 if (!d->wizard)
3600 return true;
3601
3602 const QList<QWizardField> &wizardFields = d->wizard->d_func()->fields;
3603 const auto end = wizardFields.crend();
3604 for (auto it = wizardFields.crbegin(); it != end; ++it) {
3605 const QWizardField &field = *it;
3606 if (field.page == this && field.mandatory) {
3607 QVariant value = field.object->property(name: field.property);
3608 if (value == field.initialValue)
3609 return false;
3610
3611#if QT_CONFIG(lineedit)
3612 if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(object: field.object)) {
3613 if (!lineEdit->hasAcceptableInput())
3614 return false;
3615 }
3616#endif
3617#if QT_CONFIG(spinbox)
3618 if (QAbstractSpinBox *spinBox = qobject_cast<QAbstractSpinBox *>(object: field.object)) {
3619 if (!spinBox->hasAcceptableInput())
3620 return false;
3621 }
3622#endif
3623 }
3624 }
3625 return true;
3626}
3627
3628/*!
3629 Explicitly sets this page to be final if \a finalPage is true.
3630
3631 After calling setFinalPage(true), isFinalPage() returns \c true and the \uicontrol
3632 Finish button is visible (and enabled if isComplete() returns
3633 true).
3634
3635 After calling setFinalPage(false), isFinalPage() returns \c true if
3636 nextId() returns -1; otherwise, it returns \c false.
3637
3638 \sa isComplete(), QWizard::HaveFinishButtonOnEarlyPages
3639*/
3640void QWizardPage::setFinalPage(bool finalPage)
3641{
3642 Q_D(QWizardPage);
3643 d->explicitlyFinal = finalPage;
3644 QWizard *wizard = this->wizard();
3645 if (wizard && wizard->currentPage() == this)
3646 wizard->d_func()->updateCurrentPage();
3647}
3648
3649/*!
3650 This function is called by QWizard to determine whether the \uicontrol
3651 Finish button should be shown for this page or not.
3652
3653 By default, it returns \c true if there is no next page
3654 (i.e., nextId() returns -1); otherwise, it returns \c false.
3655
3656 By explicitly calling setFinalPage(true), you can let the user perform an
3657 "early finish".
3658
3659 \sa isComplete(), QWizard::HaveFinishButtonOnEarlyPages
3660*/
3661bool QWizardPage::isFinalPage() const
3662{
3663 Q_D(const QWizardPage);
3664 if (d->explicitlyFinal)
3665 return true;
3666
3667 QWizard *wizard = this->wizard();
3668 if (wizard && wizard->currentPage() == this) {
3669 // try to use the QWizard implementation if possible
3670 return wizard->nextId() == -1;
3671 } else {
3672 return nextId() == -1;
3673 }
3674}
3675
3676/*!
3677 Sets this page to be a commit page if \a commitPage is true; otherwise,
3678 sets it to be a normal page.
3679
3680 A commit page is a page that represents an action which cannot be undone
3681 by clicking \uicontrol Back or \uicontrol Cancel.
3682
3683 A \uicontrol Commit button replaces the \uicontrol Next button on a commit page. Clicking this
3684 button simply calls QWizard::next() just like clicking \uicontrol Next does.
3685
3686 A page entered directly from a commit page has its \uicontrol Back button disabled.
3687
3688 \sa isCommitPage()
3689*/
3690void QWizardPage::setCommitPage(bool commitPage)
3691{
3692 Q_D(QWizardPage);
3693 d->commit = commitPage;
3694 QWizard *wizard = this->wizard();
3695 if (wizard && wizard->currentPage() == this)
3696 wizard->d_func()->updateCurrentPage();
3697}
3698
3699/*!
3700 Returns \c true if this page is a commit page; otherwise returns \c false.
3701
3702 \sa setCommitPage()
3703*/
3704bool QWizardPage::isCommitPage() const
3705{
3706 Q_D(const QWizardPage);
3707 return d->commit;
3708}
3709
3710/*!
3711 Sets the text on button \a which to be \a text on this page.
3712
3713 By default, the text on buttons depends on the QWizard::wizardStyle,
3714 but may be redefined for the wizard as a whole using QWizard::setButtonText().
3715
3716 \sa buttonText(), QWizard::setButtonText(), QWizard::buttonText()
3717*/
3718void QWizardPage::setButtonText(QWizard::WizardButton which, const QString &text)
3719{
3720 Q_D(QWizardPage);
3721 d->buttonCustomTexts.insert(key: which, value: text);
3722 if (wizard() && wizard()->currentPage() == this && wizard()->d_func()->btns[which])
3723 wizard()->d_func()->btns[which]->setText(text);
3724}
3725
3726/*!
3727 Returns the text on button \a which on this page.
3728
3729 If a text has ben set using setButtonText(), this text is returned.
3730 Otherwise, if a text has been set using QWizard::setButtonText(),
3731 this text is returned.
3732
3733 By default, the text on buttons depends on the QWizard::wizardStyle.
3734 For example, on \macos, the \uicontrol Next button is called \uicontrol
3735 Continue.
3736
3737 \sa setButtonText(), QWizard::buttonText(), QWizard::setButtonText()
3738*/
3739QString QWizardPage::buttonText(QWizard::WizardButton which) const
3740{
3741 Q_D(const QWizardPage);
3742
3743 if (d->buttonCustomTexts.contains(key: which))
3744 return d->buttonCustomTexts.value(key: which);
3745
3746 if (wizard())
3747 return wizard()->buttonText(which);
3748
3749 return QString();
3750}
3751
3752/*!
3753 This virtual function is called by QWizard::nextId() to find
3754 out which page to show when the user clicks the \uicontrol Next button.
3755
3756 The return value is the ID of the next page, or -1 if no page follows.
3757
3758 By default, this function returns the lowest ID greater than the ID
3759 of the current page, or -1 if there is no such ID.
3760
3761 By reimplementing this function, you can specify a dynamic page
3762 order. For example:
3763
3764 \snippet dialogs/licensewizard/licensewizard.cpp 18
3765
3766 \sa QWizard::nextId()
3767*/
3768int QWizardPage::nextId() const
3769{
3770 Q_D(const QWizardPage);
3771
3772 if (!d->wizard)
3773 return -1;
3774
3775 bool foundCurrentPage = false;
3776
3777 const QWizardPrivate::PageMap &pageMap = d->wizard->d_func()->pageMap;
3778 QWizardPrivate::PageMap::const_iterator i = pageMap.constBegin();
3779 QWizardPrivate::PageMap::const_iterator end = pageMap.constEnd();
3780
3781 for (; i != end; ++i) {
3782 if (i.value() == this) {
3783 foundCurrentPage = true;
3784 } else if (foundCurrentPage) {
3785 return i.key();
3786 }
3787 }
3788 return -1;
3789}
3790
3791/*!
3792 \fn void QWizardPage::completeChanged()
3793
3794 This signal is emitted whenever the complete state of the page
3795 (i.e., the value of isComplete()) changes.
3796
3797 If you reimplement isComplete(), make sure to emit
3798 completeChanged() whenever the value of isComplete() changes, to
3799 ensure that QWizard updates the enabled or disabled state of its
3800 buttons.
3801
3802 \sa isComplete()
3803*/
3804
3805/*!
3806 Sets the value of the field called \a name to \a value.
3807
3808 This function can be used to set fields on any page of the wizard.
3809 It is equivalent to calling
3810 wizard()->\l{QWizard::setField()}{setField(\a name, \a value)}.
3811
3812 \sa QWizard::setField(), field(), registerField()
3813*/
3814void QWizardPage::setField(const QString &name, const QVariant &value)
3815{
3816 Q_D(QWizardPage);
3817 if (!d->wizard)
3818 return;
3819 d->wizard->setField(name, value);
3820}
3821
3822/*!
3823 Returns the value of the field called \a name.
3824
3825 This function can be used to access fields on any page of the
3826 wizard. It is equivalent to calling
3827 wizard()->\l{QWizard::field()}{field(\a name)}.
3828
3829 Example:
3830
3831 \snippet dialogs/licensewizard/licensewizard.cpp accessField
3832
3833 \sa QWizard::field(), setField(), registerField()
3834*/
3835QVariant QWizardPage::field(const QString &name) const
3836{
3837 Q_D(const QWizardPage);
3838 if (!d->wizard)
3839 return QVariant();
3840 return d->wizard->field(name);
3841}
3842
3843/*!
3844 Creates a field called \a name associated with the given \a
3845 property of the given \a widget. From then on, that property
3846 becomes accessible using field() and setField().
3847
3848 Fields are global to the entire wizard and make it easy for any
3849 single page to access information stored by another page, without
3850 having to put all the logic in QWizard or having the pages know
3851 explicitly about each other.
3852
3853 If \a name ends with an asterisk (\c *), the field is a mandatory
3854 field. When a page has mandatory fields, the \uicontrol Next and/or
3855 \uicontrol Finish buttons are enabled only when all mandatory fields
3856 are filled. This requires a \a changedSignal to be specified, to
3857 tell QWizard to recheck the value stored by the mandatory field.
3858
3859 QWizard knows the most common Qt widgets. For these (or their
3860 subclasses), you don't need to specify a \a property or a \a
3861 changedSignal. The table below lists these widgets:
3862
3863 \table
3864 \header \li Widget \li Property \li Change Notification Signal
3865 \row \li QAbstractButton \li bool \l{QAbstractButton::}{checked} \li \l{QAbstractButton::}{toggled()}
3866 \row \li QAbstractSlider \li int \l{QAbstractSlider::}{value} \li \l{QAbstractSlider::}{valueChanged()}
3867 \row \li QComboBox \li int \l{QComboBox::}{currentIndex} \li \l{QComboBox::}{currentIndexChanged()}
3868 \row \li QDateTimeEdit \li QDateTime \l{QDateTimeEdit::}{dateTime} \li \l{QDateTimeEdit::}{dateTimeChanged()}
3869 \row \li QLineEdit \li QString \l{QLineEdit::}{text} \li \l{QLineEdit::}{textChanged()}
3870 \row \li QListWidget \li int \l{QListWidget::}{currentRow} \li \l{QListWidget::}{currentRowChanged()}
3871 \row \li QSpinBox \li int \l{QSpinBox::}{value} \li \l{QSpinBox::}{valueChanged()}
3872 \endtable
3873
3874 You can use QWizard::setDefaultProperty() to add entries to this
3875 table or to override existing entries.
3876
3877 To consider a field "filled", QWizard simply checks that their
3878 current value doesn't equal their original value (the value they
3879 had before initializePage() was called). For QLineEdit, it also
3880 checks that
3881 \l{QLineEdit::hasAcceptableInput()}{hasAcceptableInput()} returns
3882 true, to honor any validator or mask.
3883
3884 QWizard's mandatory field mechanism is provided for convenience.
3885 It can be bypassed by reimplementing QWizardPage::isComplete().
3886
3887 \sa field(), setField(), QWizard::setDefaultProperty()
3888*/
3889void QWizardPage::registerField(const QString &name, QWidget *widget, const char *property,
3890 const char *changedSignal)
3891{
3892 Q_D(QWizardPage);
3893 QWizardField field(this, name, widget, property, changedSignal);
3894 if (d->wizard) {
3895 d->wizard->d_func()->addField(field);
3896 } else {
3897 d->pendingFields += field;
3898 }
3899}
3900
3901/*!
3902 Returns the wizard associated with this page, or \nullptr if this page
3903 hasn't been inserted into a QWizard yet.
3904
3905 \sa QWizard::addPage(), QWizard::setPage()
3906*/
3907QWizard *QWizardPage::wizard() const
3908{
3909 Q_D(const QWizardPage);
3910 return d->wizard;
3911}
3912
3913QT_END_NAMESPACE
3914
3915#include "moc_qwizard.cpp"
3916

Provided by KDAB

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

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