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 This property cannot be set directly. To change the current page,
2393 call next(), back(), or restart().
2394
2395 By default, this property has a value of -1, indicating that no page is
2396 currently shown.
2397
2398 \sa currentPage()
2399*/
2400int QWizard::currentId() const
2401{
2402 Q_D(const QWizard);
2403 return d->current;
2404}
2405
2406/*!
2407 Sets the value of the field called \a name to \a value.
2408
2409 This function can be used to set fields on any page of the wizard.
2410
2411 \sa QWizardPage::registerField(), QWizardPage::setField(), field()
2412*/
2413void QWizard::setField(const QString &name, const QVariant &value)
2414{
2415 Q_D(QWizard);
2416
2417 int index = d->fieldIndexMap.value(key: name, defaultValue: -1);
2418 if (Q_UNLIKELY(index == -1)) {
2419 qWarning(msg: "QWizard::setField: No such field '%ls'", qUtf16Printable(name));
2420 return;
2421 }
2422
2423 const QWizardField &field = d->fields.at(i: index);
2424 if (Q_UNLIKELY(!field.object->setProperty(field.property, value)))
2425 qWarning(msg: "QWizard::setField: Couldn't write to property '%s'",
2426 field.property.constData());
2427}
2428
2429/*!
2430 Returns the value of the field called \a name.
2431
2432 This function can be used to access fields on any page of the wizard.
2433
2434 \sa QWizardPage::registerField(), QWizardPage::field(), setField()
2435*/
2436QVariant QWizard::field(const QString &name) const
2437{
2438 Q_D(const QWizard);
2439
2440 int index = d->fieldIndexMap.value(key: name, defaultValue: -1);
2441 if (Q_UNLIKELY(index == -1)) {
2442 qWarning(msg: "QWizard::field: No such field '%ls'", qUtf16Printable(name));
2443 return QVariant();
2444 }
2445
2446 const QWizardField &field = d->fields.at(i: index);
2447 return field.object->property(name: field.property);
2448}
2449
2450/*!
2451 \property QWizard::wizardStyle
2452 \brief the look and feel of the wizard
2453
2454 By default, QWizard uses the AeroStyle on a Windows Vista system with alpha compositing
2455 enabled, regardless of the current widget style. If this is not the case, the default
2456 wizard style depends on the current widget style as follows: MacStyle is the default if
2457 the current widget style is QMacStyle, ModernStyle is the default if the current widget
2458 style is QWindowsStyle, and ClassicStyle is the default in all other cases.
2459
2460 \sa {Wizard Look and Feel}, options
2461*/
2462void QWizard::setWizardStyle(WizardStyle style)
2463{
2464 Q_D(QWizard);
2465
2466 const bool styleChange = style != d->wizStyle;
2467
2468#if QT_CONFIG(style_windowsvista)
2469 const bool aeroStyleChange =
2470 d->vistaInitPending || d->vistaStateChanged || (styleChange && (style == AeroStyle || d->wizStyle == AeroStyle));
2471 d->vistaStateChanged = false;
2472 d->vistaInitPending = false;
2473#endif
2474
2475 if (styleChange
2476#if QT_CONFIG(style_windowsvista)
2477 || aeroStyleChange
2478#endif
2479 ) {
2480 d->disableUpdates();
2481 d->wizStyle = style;
2482 d->updateButtonTexts();
2483#if QT_CONFIG(style_windowsvista)
2484 if (aeroStyleChange) {
2485 //Send a resizeevent since the antiflicker widget probably needs a new size
2486 //because of the backbutton in the window title
2487 QResizeEvent ev(geometry().size(), geometry().size());
2488 QCoreApplication::sendEvent(this, &ev);
2489 }
2490#endif
2491 d->updateLayout();
2492 updateGeometry();
2493 d->enableUpdates();
2494#if QT_CONFIG(style_windowsvista)
2495 // Delay initialization when activating Aero style fails due to missing native window.
2496 if (aeroStyleChange && !d->handleAeroStyleChange() && d->wizStyle == AeroStyle)
2497 d->vistaInitPending = true;
2498#endif
2499 }
2500}
2501
2502QWizard::WizardStyle QWizard::wizardStyle() const
2503{
2504 Q_D(const QWizard);
2505 return d->wizStyle;
2506}
2507
2508/*!
2509 Sets the given \a option to be enabled if \a on is true;
2510 otherwise, clears the given \a option.
2511
2512 \sa options, testOption(), setWizardStyle()
2513*/
2514void QWizard::setOption(WizardOption option, bool on)
2515{
2516 Q_D(QWizard);
2517 if (!(d->opts & option) != !on)
2518 setOptions(d->opts ^ option);
2519}
2520
2521/*!
2522 Returns \c true if the given \a option is enabled; otherwise, returns
2523 false.
2524
2525 \sa options, setOption(), setWizardStyle()
2526*/
2527bool QWizard::testOption(WizardOption option) const
2528{
2529 Q_D(const QWizard);
2530 return (d->opts & option) != 0;
2531}
2532
2533/*!
2534 \property QWizard::options
2535 \brief the various options that affect the look and feel of the wizard
2536
2537 By default, the following options are set (depending on the platform):
2538
2539 \list
2540 \li Windows: HelpButtonOnRight.
2541 \li \macos: NoDefaultButton and NoCancelButton.
2542 \li X11 and QWS (Qt for Embedded Linux): none.
2543 \endlist
2544
2545 \sa wizardStyle
2546*/
2547void QWizard::setOptions(WizardOptions options)
2548{
2549 Q_D(QWizard);
2550
2551 WizardOptions changed = (options ^ d->opts);
2552 if (!changed)
2553 return;
2554
2555 d->disableUpdates();
2556
2557 d->opts = options;
2558 if ((changed & IndependentPages) && !(d->opts & IndependentPages))
2559 d->cleanupPagesNotInHistory();
2560
2561 if (changed & (NoDefaultButton | HaveHelpButton | HelpButtonOnRight | NoCancelButton
2562 | CancelButtonOnLeft | HaveCustomButton1 | HaveCustomButton2
2563 | HaveCustomButton3)) {
2564 d->updateButtonLayout();
2565 } else if (changed & (NoBackButtonOnStartPage | NoBackButtonOnLastPage
2566 | HaveNextButtonOnLastPage | HaveFinishButtonOnEarlyPages
2567 | DisabledBackButtonOnLastPage | NoCancelButtonOnLastPage)) {
2568 d->_q_updateButtonStates();
2569 }
2570
2571 d->enableUpdates();
2572 d->updateLayout();
2573}
2574
2575QWizard::WizardOptions QWizard::options() const
2576{
2577 Q_D(const QWizard);
2578 return d->opts;
2579}
2580
2581/*!
2582 Sets the text on button \a which to be \a text.
2583
2584 By default, the text on buttons depends on the wizardStyle. For
2585 example, on \macos, the \uicontrol Next button is called \uicontrol
2586 Continue.
2587
2588 To add extra buttons to the wizard (e.g., a \uicontrol Print button),
2589 one way is to call setButtonText() with CustomButton1,
2590 CustomButton2, or CustomButton3 to set their text, and make the
2591 buttons visible using the HaveCustomButton1, HaveCustomButton2,
2592 and/or HaveCustomButton3 options.
2593
2594 Button texts may also be set on a per-page basis using QWizardPage::setButtonText().
2595
2596 \sa setButton(), button(), setButtonLayout(), setOptions(), QWizardPage::setButtonText()
2597*/
2598void QWizard::setButtonText(WizardButton which, const QString &text)
2599{
2600 Q_D(QWizard);
2601
2602 if (!d->ensureButton(which))
2603 return;
2604
2605 d->buttonCustomTexts.insert(key: which, value: text);
2606
2607 if (!currentPage() || !currentPage()->d_func()->buttonCustomTexts.contains(key: which))
2608 d->btns[which]->setText(text);
2609}
2610
2611/*!
2612 Returns the text on button \a which.
2613
2614 If a text has ben set using setButtonText(), this text is returned.
2615
2616 By default, the text on buttons depends on the wizardStyle. For
2617 example, on \macos, the \uicontrol Next button is called \uicontrol
2618 Continue.
2619
2620 \sa button(), setButton(), setButtonText(), QWizardPage::buttonText(),
2621 QWizardPage::setButtonText()
2622*/
2623QString QWizard::buttonText(WizardButton which) const
2624{
2625 Q_D(const QWizard);
2626
2627 if (!d->ensureButton(which))
2628 return QString();
2629
2630 if (d->buttonCustomTexts.contains(key: which))
2631 return d->buttonCustomTexts.value(key: which);
2632
2633 const QString defText = buttonDefaultText(wstyle: d->wizStyle, which, wizardPrivate: d);
2634 if (!defText.isNull())
2635 return defText;
2636
2637 return d->btns[which]->text();
2638}
2639
2640/*!
2641 Sets the order in which buttons are displayed to \a layout, where
2642 \a layout is a list of \l{WizardButton}s.
2643
2644 The default layout depends on the options (e.g., whether
2645 HelpButtonOnRight) that are set. You can call this function if
2646 you need more control over the buttons' layout than what \l
2647 options already provides.
2648
2649 You can specify horizontal stretches in the layout using \l
2650 Stretch.
2651
2652 Example:
2653
2654 \snippet code/src_gui_dialogs_qwizard.cpp 1
2655
2656 \sa setButton(), setButtonText(), setOptions()
2657*/
2658void QWizard::setButtonLayout(const QList<WizardButton> &layout)
2659{
2660 Q_D(QWizard);
2661
2662 for (int i = 0; i < layout.size(); ++i) {
2663 WizardButton button1 = layout.at(i);
2664
2665 if (button1 == NoButton || button1 == Stretch)
2666 continue;
2667 if (!d->ensureButton(which: button1))
2668 return;
2669
2670 // O(n^2), but n is very small
2671 for (int j = 0; j < i; ++j) {
2672 WizardButton button2 = layout.at(i: j);
2673 if (Q_UNLIKELY(button2 == button1)) {
2674 qWarning(msg: "QWizard::setButtonLayout: Duplicate button in layout");
2675 return;
2676 }
2677 }
2678 }
2679
2680 d->buttonsHaveCustomLayout = true;
2681 d->buttonsCustomLayout = layout;
2682 d->updateButtonLayout();
2683}
2684
2685/*!
2686 Sets the button corresponding to role \a which to \a button.
2687
2688 To add extra buttons to the wizard (e.g., a \uicontrol Print button),
2689 one way is to call setButton() with CustomButton1 to
2690 CustomButton3, and make the buttons visible using the
2691 HaveCustomButton1 to HaveCustomButton3 options.
2692
2693 \sa setButtonText(), setButtonLayout(), options
2694*/
2695void QWizard::setButton(WizardButton which, QAbstractButton *button)
2696{
2697 Q_D(QWizard);
2698
2699 if (uint(which) >= NButtons || d->btns[which] == button)
2700 return;
2701
2702 if (QAbstractButton *oldButton = d->btns[which]) {
2703 d->buttonLayout->removeWidget(w: oldButton);
2704 delete oldButton;
2705 }
2706
2707 d->btns[which] = button;
2708 if (button) {
2709 button->setParent(d->antiFlickerWidget);
2710 d->buttonCustomTexts.insert(key: which, value: button->text());
2711 d->connectButton(which);
2712 } else {
2713 d->buttonCustomTexts.remove(key: which); // ### what about page-specific texts set for 'which'
2714 d->ensureButton(which); // (QWizardPage::setButtonText())? Clear them as well?
2715 }
2716
2717 d->updateButtonLayout();
2718}
2719
2720/*!
2721 Returns the button corresponding to role \a which.
2722
2723 \sa setButton(), setButtonText()
2724*/
2725QAbstractButton *QWizard::button(WizardButton which) const
2726{
2727 Q_D(const QWizard);
2728#if QT_CONFIG(style_windowsvista)
2729 if (d->wizStyle == AeroStyle && which == BackButton)
2730 return d->vistaHelper->backButton();
2731#endif
2732 if (!d->ensureButton(which))
2733 return nullptr;
2734 return d->btns[which];
2735}
2736
2737/*!
2738 \property QWizard::titleFormat
2739 \brief the text format used by page titles
2740
2741 The default format is Qt::AutoText.
2742
2743 \sa QWizardPage::title, subTitleFormat
2744*/
2745void QWizard::setTitleFormat(Qt::TextFormat format)
2746{
2747 Q_D(QWizard);
2748 d->titleFmt = format;
2749 d->updateLayout();
2750}
2751
2752Qt::TextFormat QWizard::titleFormat() const
2753{
2754 Q_D(const QWizard);
2755 return d->titleFmt;
2756}
2757
2758/*!
2759 \property QWizard::subTitleFormat
2760 \brief the text format used by page subtitles
2761
2762 The default format is Qt::AutoText.
2763
2764 \sa QWizardPage::title, titleFormat
2765*/
2766void QWizard::setSubTitleFormat(Qt::TextFormat format)
2767{
2768 Q_D(QWizard);
2769 d->subTitleFmt = format;
2770 d->updateLayout();
2771}
2772
2773Qt::TextFormat QWizard::subTitleFormat() const
2774{
2775 Q_D(const QWizard);
2776 return d->subTitleFmt;
2777}
2778
2779/*!
2780 Sets the pixmap for role \a which to \a pixmap.
2781
2782 The pixmaps are used by QWizard when displaying a page. Which
2783 pixmaps are actually used depend on the \l{Wizard Look and
2784 Feel}{wizard style}.
2785
2786 Pixmaps can also be set for a specific page using
2787 QWizardPage::setPixmap().
2788
2789 \sa QWizardPage::setPixmap(), {Elements of a Wizard Page}
2790*/
2791void QWizard::setPixmap(WizardPixmap which, const QPixmap &pixmap)
2792{
2793 Q_D(QWizard);
2794 Q_ASSERT(uint(which) < NPixmaps);
2795 d->defaultPixmaps[which] = pixmap;
2796 d->updatePixmap(which);
2797}
2798
2799/*!
2800 Returns the pixmap set for role \a which.
2801
2802 By default, the only pixmap that is set is the BackgroundPixmap on
2803 \macos.
2804
2805 \sa QWizardPage::pixmap(), {Elements of a Wizard Page}
2806*/
2807QPixmap QWizard::pixmap(WizardPixmap which) const
2808{
2809 Q_D(const QWizard);
2810 Q_ASSERT(uint(which) < NPixmaps);
2811#ifdef Q_OS_MACOS
2812 if (which == BackgroundPixmap && d->defaultPixmaps[BackgroundPixmap].isNull())
2813 d->defaultPixmaps[BackgroundPixmap] = d->findDefaultBackgroundPixmap();
2814#endif
2815 return d->defaultPixmaps[which];
2816}
2817
2818/*!
2819 Sets the default property for \a className to be \a property,
2820 and the associated change signal to be \a changedSignal.
2821
2822 The default property is used when an instance of \a className (or
2823 of one of its subclasses) is passed to
2824 QWizardPage::registerField() and no property is specified.
2825
2826 QWizard knows the most common Qt widgets. For these (or their
2827 subclasses), you don't need to specify a \a property or a \a
2828 changedSignal. The table below lists these widgets:
2829
2830 \table
2831 \header \li Widget \li Property \li Change Notification Signal
2832 \row \li QAbstractButton \li bool \l{QAbstractButton::}{checked} \li \l{QAbstractButton::}{toggled()}
2833 \row \li QAbstractSlider \li int \l{QAbstractSlider::}{value} \li \l{QAbstractSlider::}{valueChanged()}
2834 \row \li QComboBox \li int \l{QComboBox::}{currentIndex} \li \l{QComboBox::}{currentIndexChanged()}
2835 \row \li QDateTimeEdit \li QDateTime \l{QDateTimeEdit::}{dateTime} \li \l{QDateTimeEdit::}{dateTimeChanged()}
2836 \row \li QLineEdit \li QString \l{QLineEdit::}{text} \li \l{QLineEdit::}{textChanged()}
2837 \row \li QListWidget \li int \l{QListWidget::}{currentRow} \li \l{QListWidget::}{currentRowChanged()}
2838 \row \li QSpinBox \li int \l{QSpinBox::}{value} \li \l{QSpinBox::}{valueChanged()}
2839 \endtable
2840
2841 \sa QWizardPage::registerField()
2842*/
2843void QWizard::setDefaultProperty(const char *className, const char *property,
2844 const char *changedSignal)
2845{
2846 Q_D(QWizard);
2847 for (int i = d->defaultPropertyTable.size() - 1; i >= 0; --i) {
2848 if (qstrcmp(str1: d->defaultPropertyTable.at(i).className, str2: className) == 0) {
2849 d->defaultPropertyTable.remove(i);
2850 break;
2851 }
2852 }
2853 d->defaultPropertyTable.append(t: QWizardDefaultProperty(className, property, changedSignal));
2854}
2855
2856/*!
2857 Sets the given \a widget to be shown on the left side of the wizard.
2858 For styles which use the WatermarkPixmap (ClassicStyle and ModernStyle)
2859 the side widget is displayed on top of the watermark, for other styles
2860 or when the watermark is not provided the side widget is displayed
2861 on the left side of the wizard.
2862
2863 Passing \nullptr shows no side widget.
2864
2865 When the \a widget is not \nullptr the wizard reparents it.
2866
2867 Any previous side widget is hidden.
2868
2869 You may call setSideWidget() with the same widget at different
2870 times.
2871
2872 All widgets set here will be deleted by the wizard when it is
2873 destroyed unless you separately reparent the widget after setting
2874 some other side widget (or \nullptr).
2875
2876 By default, no side widget is present.
2877*/
2878void QWizard::setSideWidget(QWidget *widget)
2879{
2880 Q_D(QWizard);
2881
2882 d->sideWidget = widget;
2883 if (d->watermarkLabel) {
2884 d->watermarkLabel->setSideWidget(widget);
2885 d->updateLayout();
2886 }
2887}
2888
2889/*!
2890 Returns the widget on the left side of the wizard or \nullptr.
2891
2892 By default, no side widget is present.
2893*/
2894QWidget *QWizard::sideWidget() const
2895{
2896 Q_D(const QWizard);
2897
2898 return d->sideWidget;
2899}
2900
2901/*!
2902 \reimp
2903*/
2904void QWizard::setVisible(bool visible)
2905{
2906 Q_D(QWizard);
2907 if (visible) {
2908 if (d->current == -1)
2909 restart();
2910 }
2911 QDialog::setVisible(visible);
2912}
2913
2914/*!
2915 \reimp
2916*/
2917QSize QWizard::sizeHint() const
2918{
2919 Q_D(const QWizard);
2920 QSize result = d->mainLayout->totalSizeHint();
2921 QSize extra(500, 360);
2922 if (d->wizStyle == MacStyle && d->current != -1) {
2923 QSize pixmap(currentPage()->pixmap(which: BackgroundPixmap).size());
2924 extra.setWidth(616);
2925 if (!pixmap.isNull()) {
2926 extra.setHeight(pixmap.height());
2927
2928 /*
2929 The width isn't always reliable as a size hint, as
2930 some wizard backgrounds just cover the leftmost area.
2931 Use a rule of thumb to determine if the width is
2932 reliable or not.
2933 */
2934 if (pixmap.width() >= pixmap.height())
2935 extra.setWidth(pixmap.width());
2936 }
2937 }
2938 return result.expandedTo(otherSize: extra);
2939}
2940
2941/*!
2942 \fn void QWizard::currentIdChanged(int id)
2943
2944 This signal is emitted when the current page changes, with the new
2945 current \a id.
2946
2947 \sa currentId(), currentPage()
2948*/
2949
2950/*!
2951 \fn void QWizard::pageAdded(int id)
2952
2953 This signal is emitted whenever a page is added to the
2954 wizard. The page's \a id is passed as parameter.
2955
2956 \sa addPage(), setPage(), startId()
2957*/
2958
2959/*!
2960 \fn void QWizard::pageRemoved(int id)
2961
2962 This signal is emitted whenever a page is removed from the
2963 wizard. The page's \a id is passed as parameter.
2964
2965 \sa removePage(), startId()
2966*/
2967
2968/*!
2969 \fn void QWizard::helpRequested()
2970
2971 This signal is emitted when the user clicks the \uicontrol Help button.
2972
2973 By default, no \uicontrol Help button is shown. Call
2974 setOption(HaveHelpButton, true) to have one.
2975
2976 Example:
2977
2978 \snippet dialogs/licensewizard/licensewizard.cpp 0
2979 \dots
2980 \snippet dialogs/licensewizard/licensewizard.cpp 5
2981 \snippet dialogs/licensewizard/licensewizard.cpp 7
2982 \dots
2983 \snippet dialogs/licensewizard/licensewizard.cpp 8
2984 \codeline
2985 \snippet dialogs/licensewizard/licensewizard.cpp 10
2986 \dots
2987 \snippet dialogs/licensewizard/licensewizard.cpp 12
2988 \codeline
2989 \snippet dialogs/licensewizard/licensewizard.cpp 14
2990 \codeline
2991 \snippet dialogs/licensewizard/licensewizard.cpp 15
2992
2993 \sa customButtonClicked()
2994*/
2995
2996/*!
2997 \fn void QWizard::customButtonClicked(int which)
2998
2999 This signal is emitted when the user clicks a custom button. \a
3000 which can be CustomButton1, CustomButton2, or CustomButton3.
3001
3002 By default, no custom button is shown. Call setOption() with
3003 HaveCustomButton1, HaveCustomButton2, or HaveCustomButton3 to have
3004 one, and use setButtonText() or setButton() to configure it.
3005
3006 \sa helpRequested()
3007*/
3008
3009/*!
3010 Goes back to the previous page.
3011
3012 This is equivalent to pressing the \uicontrol Back button.
3013
3014 \sa next(), accept(), reject(), restart()
3015*/
3016void QWizard::back()
3017{
3018 Q_D(QWizard);
3019 int n = d->history.size() - 2;
3020 if (n < 0)
3021 return;
3022 d->switchToPage(newId: d->history.at(i: n), direction: QWizardPrivate::Backward);
3023}
3024
3025/*!
3026 Advances to the next page.
3027
3028 This is equivalent to pressing the \uicontrol Next or \uicontrol Commit button.
3029
3030 \sa nextId(), back(), accept(), reject(), restart()
3031*/
3032void QWizard::next()
3033{
3034 Q_D(QWizard);
3035
3036 if (d->current == -1)
3037 return;
3038
3039 if (validateCurrentPage()) {
3040 int next = nextId();
3041 if (next != -1) {
3042 if (Q_UNLIKELY(d->history.contains(next))) {
3043 qWarning(msg: "QWizard::next: Page %d already met", next);
3044 return;
3045 }
3046 if (Q_UNLIKELY(!d->pageMap.contains(next))) {
3047 qWarning(msg: "QWizard::next: No such page %d", next);
3048 return;
3049 }
3050 d->switchToPage(newId: next, direction: QWizardPrivate::Forward);
3051 }
3052 }
3053}
3054
3055/*!
3056 Sets currentId to \a id, without visiting the pages between currentId and \a id.
3057
3058 Returns without page change, if
3059 \list
3060 \li wizard holds no pages
3061 \li current page is invalid
3062 \li given page equals currentId()
3063 \li given page is out of range
3064 \endlist
3065
3066 Note: If pages have been forward skipped and \a id is 0, page visiting history
3067 will be deleted
3068*/
3069
3070void QWizard::setCurrentId(int id)
3071{
3072 Q_D(QWizard);
3073
3074 if (d->current == -1)
3075 return;
3076
3077 if (currentId() == id)
3078 return;
3079
3080 if (!validateCurrentPage())
3081 return;
3082
3083 if (id < 0 || Q_UNLIKELY(!d->pageMap.contains(id))) {
3084 qWarning(msg: "QWizard::setCurrentId: No such page: %d", id);
3085 return;
3086 }
3087
3088 d->switchToPage(newId: id, direction: (id < currentId()) ? QWizardPrivate::Backward : QWizardPrivate::Forward);
3089}
3090
3091/*!
3092 Restarts the wizard at the start page. This function is called automatically when the
3093 wizard is shown.
3094
3095 \sa startId()
3096*/
3097void QWizard::restart()
3098{
3099 Q_D(QWizard);
3100 d->disableUpdates();
3101 d->reset();
3102 d->switchToPage(newId: startId(), direction: QWizardPrivate::Forward);
3103 d->enableUpdates();
3104}
3105
3106/*!
3107 \reimp
3108*/
3109bool QWizard::event(QEvent *event)
3110{
3111 Q_D(QWizard);
3112 if (event->type() == QEvent::StyleChange) { // Propagate style
3113 d->setStyle(style());
3114 d->updateLayout();
3115 } else if (event->type() == QEvent::PaletteChange) { // Emitted on theme change
3116 d->updatePalette();
3117 }
3118#if QT_CONFIG(style_windowsvista)
3119 else if (event->type() == QEvent::Show && d->vistaInitPending) {
3120 d->vistaInitPending = false;
3121 d->wizStyle = AeroStyle;
3122 d->handleAeroStyleChange();
3123 }
3124 else if (d->isVistaThemeEnabled()) {
3125 if (event->type() == QEvent::Resize
3126 || event->type() == QEvent::LayoutDirectionChange) {
3127 const int buttonLeft = (layoutDirection() == Qt::RightToLeft
3128 ? width() - d->vistaHelper->backButton()->sizeHint().width()
3129 : 0);
3130
3131 d->vistaHelper->backButton()->move(buttonLeft,
3132 d->vistaHelper->backButton()->y());
3133 }
3134
3135 d->vistaHelper->mouseEvent(event);
3136 }
3137#endif
3138 return QDialog::event(event);
3139}
3140
3141/*!
3142 \reimp
3143*/
3144void QWizard::resizeEvent(QResizeEvent *event)
3145{
3146 Q_D(QWizard);
3147 int heightOffset = 0;
3148#if QT_CONFIG(style_windowsvista)
3149 if (d->isVistaThemeEnabled()) {
3150 heightOffset = d->vistaHelper->topOffset(this);
3151 heightOffset += d->vistaHelper->titleBarSize();
3152 }
3153#endif
3154 d->antiFlickerWidget->resize(w: event->size().width(), h: event->size().height() - heightOffset);
3155#if QT_CONFIG(style_windowsvista)
3156 if (d->isVistaThemeEnabled())
3157 d->vistaHelper->resizeEvent(event);
3158#endif
3159 QDialog::resizeEvent(event);
3160}
3161
3162/*!
3163 \reimp
3164*/
3165void QWizard::paintEvent(QPaintEvent * event)
3166{
3167 Q_D(QWizard);
3168 if (d->wizStyle == MacStyle && currentPage()) {
3169 QPixmap backgroundPixmap = currentPage()->pixmap(which: BackgroundPixmap);
3170 if (backgroundPixmap.isNull())
3171 return;
3172
3173 QStylePainter painter(this);
3174 painter.drawPixmap(x: 0, y: (height() - backgroundPixmap.height()) / 2, pm: backgroundPixmap);
3175 }
3176#if QT_CONFIG(style_windowsvista)
3177 else if (d->isVistaThemeEnabled()) {
3178 d->vistaHelper->paintEvent(event);
3179 }
3180#else
3181 Q_UNUSED(event);
3182#endif
3183}
3184
3185#if defined(Q_OS_WIN) || defined(Q_QDOC)
3186/*!
3187 \reimp
3188*/
3189bool QWizard::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
3190{
3191#if QT_CONFIG(style_windowsvista)
3192 Q_D(QWizard);
3193 if (d->isVistaThemeEnabled() && eventType == "windows_generic_MSG") {
3194 MSG *windowsMessage = static_cast<MSG *>(message);
3195 const bool winEventResult = d->vistaHelper->handleWinEvent(windowsMessage, result);
3196 if (d->vistaDirty) {
3197 // QTBUG-78300: When Qt::AA_NativeWindows is set, delay further
3198 // window creation until after the platform window creation events.
3199 if (windowsMessage->message == WM_GETICON) {
3200 d->vistaStateChanged = true;
3201 d->vistaDirty = false;
3202 setWizardStyle(AeroStyle);
3203 }
3204 }
3205 return winEventResult;
3206 } else {
3207 return QDialog::nativeEvent(eventType, message, result);
3208 }
3209#else
3210 return QDialog::nativeEvent(eventType, message, result);
3211#endif
3212}
3213#endif
3214
3215/*!
3216 \reimp
3217*/
3218void QWizard::done(int result)
3219{
3220 Q_D(QWizard);
3221 // canceling leaves the wizard in a known state
3222 if (result == Rejected) {
3223 d->reset();
3224 } else {
3225 if (!validateCurrentPage())
3226 return;
3227 }
3228 QDialog::done(result);
3229}
3230
3231/*!
3232 \fn void QWizard::initializePage(int id)
3233
3234 This virtual function is called by QWizard to prepare page \a id
3235 just before it is shown either as a result of QWizard::restart()
3236 being called, or as a result of the user clicking \uicontrol Next. (However, if the \l
3237 QWizard::IndependentPages option is set, this function is only
3238 called the first time the page is shown.)
3239
3240 By reimplementing this function, you can ensure that the page's
3241 fields are properly initialized based on fields from previous
3242 pages.
3243
3244 The default implementation calls QWizardPage::initializePage() on
3245 page(\a id).
3246
3247 \sa QWizardPage::initializePage(), cleanupPage()
3248*/
3249void QWizard::initializePage(int theid)
3250{
3251 QWizardPage *page = this->page(theid);
3252 if (page)
3253 page->initializePage();
3254}
3255
3256/*!
3257 \fn void QWizard::cleanupPage(int id)
3258
3259 This virtual function is called by QWizard to clean up page \a id just before the
3260 user leaves it by clicking \uicontrol Back (unless the \l QWizard::IndependentPages option is set).
3261
3262 The default implementation calls QWizardPage::cleanupPage() on
3263 page(\a id).
3264
3265 \sa QWizardPage::cleanupPage(), initializePage()
3266*/
3267void QWizard::cleanupPage(int theid)
3268{
3269 QWizardPage *page = this->page(theid);
3270 if (page)
3271 page->cleanupPage();
3272}
3273
3274/*!
3275 This virtual function is called by QWizard when the user clicks
3276 \uicontrol Next or \uicontrol Finish to perform some last-minute validation.
3277 If it returns \c true, the next page is shown (or the wizard
3278 finishes); otherwise, the current page stays up.
3279
3280 The default implementation calls QWizardPage::validatePage() on
3281 the currentPage().
3282
3283 When possible, it is usually better style to disable the \uicontrol
3284 Next or \uicontrol Finish button (by specifying \l{mandatory fields} or
3285 by reimplementing QWizardPage::isComplete()) than to reimplement
3286 validateCurrentPage().
3287
3288 \sa QWizardPage::validatePage(), currentPage()
3289*/
3290bool QWizard::validateCurrentPage()
3291{
3292 QWizardPage *page = currentPage();
3293 if (!page)
3294 return true;
3295
3296 return page->validatePage();
3297}
3298
3299/*!
3300 This virtual function is called by QWizard to find out which page
3301 to show when the user clicks the \uicontrol Next button.
3302
3303 The return value is the ID of the next page, or -1 if no page follows.
3304
3305 The default implementation calls QWizardPage::nextId() on the
3306 currentPage().
3307
3308 By reimplementing this function, you can specify a dynamic page
3309 order.
3310
3311 \sa QWizardPage::nextId(), currentPage()
3312*/
3313int QWizard::nextId() const
3314{
3315 const QWizardPage *page = currentPage();
3316 if (!page)
3317 return -1;
3318
3319 return page->nextId();
3320}
3321
3322/*!
3323 \class QWizardPage
3324 \brief The QWizardPage class is the base class for wizard pages.
3325
3326 \inmodule QtWidgets
3327
3328 QWizard represents a wizard. Each page is a QWizardPage. When
3329 you create your own wizards, you can use QWizardPage directly,
3330 or you can subclass it for more control.
3331
3332 A page has the following attributes, which are rendered by
3333 QWizard: a \l title, a \l subTitle, and a \l{setPixmap()}{set of
3334 pixmaps}. See \l{Elements of a Wizard Page} for details. Once a
3335 page is added to the wizard (using QWizard::addPage() or
3336 QWizard::setPage()), wizard() returns a pointer to the
3337 associated QWizard object.
3338
3339 Page provides five virtual functions that can be reimplemented to
3340 provide custom behavior:
3341
3342 \list
3343 \li initializePage() is called to initialize the page's contents
3344 when the user clicks the wizard's \uicontrol Next button. If you
3345 want to derive the page's default from what the user entered
3346 on previous pages, this is the function to reimplement.
3347 \li cleanupPage() is called to reset the page's contents when the
3348 user clicks the wizard's \uicontrol Back button.
3349 \li validatePage() validates the page when the user clicks \uicontrol
3350 Next or \uicontrol Finish. It is often used to show an error message
3351 if the user has entered incomplete or invalid information.
3352 \li nextId() returns the ID of the next page. It is useful when
3353 \l{creating non-linear wizards}, which allow different
3354 traversal paths based on the information provided by the user.
3355 \li isComplete() is called to determine whether the \uicontrol Next
3356 and/or \uicontrol Finish button should be enabled or disabled. If
3357 you reimplement isComplete(), also make sure that
3358 completeChanged() is emitted whenever the complete state
3359 changes.
3360 \endlist
3361
3362 Normally, the \uicontrol Next button and the \uicontrol Finish button of a
3363 wizard are mutually exclusive. If isFinalPage() returns \c true, \uicontrol
3364 Finish is available; otherwise, \uicontrol Next is available. By
3365 default, isFinalPage() is true only when nextId() returns -1. If
3366 you want to show \uicontrol Next and \uicontrol Final simultaneously for a
3367 page (letting the user perform an "early finish"), call
3368 setFinalPage(true) on that page. For wizards that support early
3369 finishes, you might also want to set the
3370 \l{QWizard::}{HaveNextButtonOnLastPage} and
3371 \l{QWizard::}{HaveFinishButtonOnEarlyPages} options on the
3372 wizard.
3373
3374 In many wizards, the contents of a page may affect the default
3375 values of the fields of a later page. To make it easy to
3376 communicate between pages, QWizard supports a \l{Registering and
3377 Using Fields}{"field" mechanism} that allows you to register a
3378 field (e.g., a QLineEdit) on a page and to access its value from
3379 any page. Fields are global to the entire wizard and make it easy
3380 for any single page to access information stored by another page,
3381 without having to put all the logic in QWizard or having the
3382 pages know explicitly about each other. Fields are registered
3383 using registerField() and can be accessed at any time using
3384 field() and setField().
3385
3386 \sa QWizard, {Trivial Wizard Example}, {License Wizard Example}
3387*/
3388
3389/*!
3390 Constructs a wizard page with the given \a parent.
3391
3392 When the page is inserted into a wizard using QWizard::addPage()
3393 or QWizard::setPage(), the parent is automatically set to be the
3394 wizard.
3395
3396 \sa wizard()
3397*/
3398QWizardPage::QWizardPage(QWidget *parent)
3399 : QWidget(*new QWizardPagePrivate, parent, { })
3400{
3401 connect(sender: this, SIGNAL(completeChanged()), receiver: this, SLOT(_q_updateCachedCompleteState()));
3402}
3403
3404/*!
3405 Destructor.
3406*/
3407QWizardPage::~QWizardPage()
3408{
3409}
3410
3411/*!
3412 \property QWizardPage::title
3413 \brief the title of the page
3414
3415 The title is shown by the QWizard, above the actual page. All
3416 pages should have a title.
3417
3418 The title may be plain text or HTML, depending on the value of the
3419 \l{QWizard::titleFormat} property.
3420
3421 By default, this property contains an empty string.
3422
3423 \sa subTitle, {Elements of a Wizard Page}
3424*/
3425void QWizardPage::setTitle(const QString &title)
3426{
3427 Q_D(QWizardPage);
3428 d->title = title;
3429 if (d->wizard && d->wizard->currentPage() == this)
3430 d->wizard->d_func()->updateLayout();
3431}
3432
3433QString QWizardPage::title() const
3434{
3435 Q_D(const QWizardPage);
3436 return d->title;
3437}
3438
3439/*!
3440 \property QWizardPage::subTitle
3441 \brief the subtitle of the page
3442
3443 The subtitle is shown by the QWizard, between the title and the
3444 actual page. Subtitles are optional. In
3445 \l{QWizard::ClassicStyle}{ClassicStyle} and
3446 \l{QWizard::ModernStyle}{ModernStyle}, using subtitles is
3447 necessary to make the header appear. In
3448 \l{QWizard::MacStyle}{MacStyle}, the subtitle is shown as a text
3449 label just above the actual page.
3450
3451 The subtitle may be plain text or HTML, depending on the value of
3452 the \l{QWizard::subTitleFormat} property.
3453
3454 By default, this property contains an empty string.
3455
3456 \sa title, QWizard::IgnoreSubTitles, {Elements of a Wizard Page}
3457*/
3458void QWizardPage::setSubTitle(const QString &subTitle)
3459{
3460 Q_D(QWizardPage);
3461 d->subTitle = subTitle;
3462 if (d->wizard && d->wizard->currentPage() == this)
3463 d->wizard->d_func()->updateLayout();
3464}
3465
3466QString QWizardPage::subTitle() const
3467{
3468 Q_D(const QWizardPage);
3469 return d->subTitle;
3470}
3471
3472/*!
3473 Sets the pixmap for role \a which to \a pixmap.
3474
3475 The pixmaps are used by QWizard when displaying a page. Which
3476 pixmaps are actually used depend on the \l{Wizard Look and
3477 Feel}{wizard style}.
3478
3479 Pixmaps can also be set for the entire wizard using
3480 QWizard::setPixmap(), in which case they apply for all pages that
3481 don't specify a pixmap.
3482
3483 \sa QWizard::setPixmap(), {Elements of a Wizard Page}
3484*/
3485void QWizardPage::setPixmap(QWizard::WizardPixmap which, const QPixmap &pixmap)
3486{
3487 Q_D(QWizardPage);
3488 Q_ASSERT(uint(which) < QWizard::NPixmaps);
3489 d->pixmaps[which] = pixmap;
3490 if (d->wizard && d->wizard->currentPage() == this)
3491 d->wizard->d_func()->updatePixmap(which);
3492}
3493
3494/*!
3495 Returns the pixmap set for role \a which.
3496
3497 Pixmaps can also be set for the entire wizard using
3498 QWizard::setPixmap(), in which case they apply for all pages that
3499 don't specify a pixmap.
3500
3501 \sa QWizard::pixmap(), {Elements of a Wizard Page}
3502*/
3503QPixmap QWizardPage::pixmap(QWizard::WizardPixmap which) const
3504{
3505 Q_D(const QWizardPage);
3506 Q_ASSERT(uint(which) < QWizard::NPixmaps);
3507
3508 const QPixmap &pixmap = d->pixmaps[which];
3509 if (!pixmap.isNull())
3510 return pixmap;
3511
3512 if (wizard())
3513 return wizard()->pixmap(which);
3514
3515 return pixmap;
3516}
3517
3518/*!
3519 This virtual function is called by QWizard::initializePage() to
3520 prepare the page just before it is shown either as a result of QWizard::restart()
3521 being called, or as a result of the user clicking \uicontrol Next.
3522 (However, if the \l QWizard::IndependentPages option is set, this function is only
3523 called the first time the page is shown.)
3524
3525 By reimplementing this function, you can ensure that the page's
3526 fields are properly initialized based on fields from previous
3527 pages. For example:
3528
3529 \snippet dialogs/licensewizard/licensewizard.cpp 27
3530
3531 The default implementation does nothing.
3532
3533 \sa QWizard::initializePage(), cleanupPage(), QWizard::IndependentPages
3534*/
3535void QWizardPage::initializePage()
3536{
3537}
3538
3539/*!
3540 This virtual function is called by QWizard::cleanupPage() when
3541 the user leaves the page by clicking \uicontrol Back (unless the \l QWizard::IndependentPages
3542 option is set).
3543
3544 The default implementation resets the page's fields to their
3545 original values (the values they had before initializePage() was
3546 called).
3547
3548 \sa QWizard::cleanupPage(), initializePage(), QWizard::IndependentPages
3549*/
3550void QWizardPage::cleanupPage()
3551{
3552 Q_D(QWizardPage);
3553 if (d->wizard) {
3554 const QList<QWizardField> &fields = d->wizard->d_func()->fields;
3555 for (const auto &field : fields) {
3556 if (field.page == this)
3557 field.object->setProperty(name: field.property, value: field.initialValue);
3558 }
3559 }
3560}
3561
3562/*!
3563 This virtual function is called by QWizard::validateCurrentPage()
3564 when the user clicks \uicontrol Next or \uicontrol Finish to perform some
3565 last-minute validation. If it returns \c true, the next page is shown
3566 (or the wizard finishes); otherwise, the current page stays up.
3567
3568 The default implementation returns \c true.
3569
3570 When possible, it is usually better style to disable the \uicontrol
3571 Next or \uicontrol Finish button (by specifying \l{mandatory fields} or
3572 reimplementing isComplete()) than to reimplement validatePage().
3573
3574 \sa QWizard::validateCurrentPage(), isComplete()
3575*/
3576bool QWizardPage::validatePage()
3577{
3578 return true;
3579}
3580
3581/*!
3582 This virtual function is called by QWizard to determine whether
3583 the \uicontrol Next or \uicontrol Finish button should be enabled or
3584 disabled.
3585
3586 The default implementation returns \c true if all \l{mandatory
3587 fields} are filled; otherwise, it returns \c false.
3588
3589 If you reimplement this function, make sure to emit completeChanged(),
3590 from the rest of your implementation, whenever the value of isComplete()
3591 changes. This ensures that QWizard updates the enabled or disabled state of
3592 its buttons. An example of the reimplementation is
3593 available \l{http://doc.qt.io/archives/qq/qq22-qwizard.html#validatebeforeitstoolate}
3594 {here}.
3595
3596 \sa completeChanged(), isFinalPage()
3597*/
3598bool QWizardPage::isComplete() const
3599{
3600 Q_D(const QWizardPage);
3601
3602 if (!d->wizard)
3603 return true;
3604
3605 const QList<QWizardField> &wizardFields = d->wizard->d_func()->fields;
3606 const auto end = wizardFields.crend();
3607 for (auto it = wizardFields.crbegin(); it != end; ++it) {
3608 const QWizardField &field = *it;
3609 if (field.page == this && field.mandatory) {
3610 QVariant value = field.object->property(name: field.property);
3611 if (value == field.initialValue)
3612 return false;
3613
3614#if QT_CONFIG(lineedit)
3615 if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(object: field.object)) {
3616 if (!lineEdit->hasAcceptableInput())
3617 return false;
3618 }
3619#endif
3620#if QT_CONFIG(spinbox)
3621 if (QAbstractSpinBox *spinBox = qobject_cast<QAbstractSpinBox *>(object: field.object)) {
3622 if (!spinBox->hasAcceptableInput())
3623 return false;
3624 }
3625#endif
3626 }
3627 }
3628 return true;
3629}
3630
3631/*!
3632 Explicitly sets this page to be final if \a finalPage is true.
3633
3634 After calling setFinalPage(true), isFinalPage() returns \c true and the \uicontrol
3635 Finish button is visible (and enabled if isComplete() returns
3636 true).
3637
3638 After calling setFinalPage(false), isFinalPage() returns \c true if
3639 nextId() returns -1; otherwise, it returns \c false.
3640
3641 \sa isComplete(), QWizard::HaveFinishButtonOnEarlyPages
3642*/
3643void QWizardPage::setFinalPage(bool finalPage)
3644{
3645 Q_D(QWizardPage);
3646 d->explicitlyFinal = finalPage;
3647 QWizard *wizard = this->wizard();
3648 if (wizard && wizard->currentPage() == this)
3649 wizard->d_func()->updateCurrentPage();
3650}
3651
3652/*!
3653 This function is called by QWizard to determine whether the \uicontrol
3654 Finish button should be shown for this page or not.
3655
3656 By default, it returns \c true if there is no next page
3657 (i.e., nextId() returns -1); otherwise, it returns \c false.
3658
3659 By explicitly calling setFinalPage(true), you can let the user perform an
3660 "early finish".
3661
3662 \sa isComplete(), QWizard::HaveFinishButtonOnEarlyPages
3663*/
3664bool QWizardPage::isFinalPage() const
3665{
3666 Q_D(const QWizardPage);
3667 if (d->explicitlyFinal)
3668 return true;
3669
3670 QWizard *wizard = this->wizard();
3671 if (wizard && wizard->currentPage() == this) {
3672 // try to use the QWizard implementation if possible
3673 return wizard->nextId() == -1;
3674 } else {
3675 return nextId() == -1;
3676 }
3677}
3678
3679/*!
3680 Sets this page to be a commit page if \a commitPage is true; otherwise,
3681 sets it to be a normal page.
3682
3683 A commit page is a page that represents an action which cannot be undone
3684 by clicking \uicontrol Back or \uicontrol Cancel.
3685
3686 A \uicontrol Commit button replaces the \uicontrol Next button on a commit page. Clicking this
3687 button simply calls QWizard::next() just like clicking \uicontrol Next does.
3688
3689 A page entered directly from a commit page has its \uicontrol Back button disabled.
3690
3691 \sa isCommitPage()
3692*/
3693void QWizardPage::setCommitPage(bool commitPage)
3694{
3695 Q_D(QWizardPage);
3696 d->commit = commitPage;
3697 QWizard *wizard = this->wizard();
3698 if (wizard && wizard->currentPage() == this)
3699 wizard->d_func()->updateCurrentPage();
3700}
3701
3702/*!
3703 Returns \c true if this page is a commit page; otherwise returns \c false.
3704
3705 \sa setCommitPage()
3706*/
3707bool QWizardPage::isCommitPage() const
3708{
3709 Q_D(const QWizardPage);
3710 return d->commit;
3711}
3712
3713/*!
3714 Sets the text on button \a which to be \a text on this page.
3715
3716 By default, the text on buttons depends on the QWizard::wizardStyle,
3717 but may be redefined for the wizard as a whole using QWizard::setButtonText().
3718
3719 \sa buttonText(), QWizard::setButtonText(), QWizard::buttonText()
3720*/
3721void QWizardPage::setButtonText(QWizard::WizardButton which, const QString &text)
3722{
3723 Q_D(QWizardPage);
3724 d->buttonCustomTexts.insert(key: which, value: text);
3725 if (wizard() && wizard()->currentPage() == this && wizard()->d_func()->btns[which])
3726 wizard()->d_func()->btns[which]->setText(text);
3727}
3728
3729/*!
3730 Returns the text on button \a which on this page.
3731
3732 If a text has ben set using setButtonText(), this text is returned.
3733 Otherwise, if a text has been set using QWizard::setButtonText(),
3734 this text is returned.
3735
3736 By default, the text on buttons depends on the QWizard::wizardStyle.
3737 For example, on \macos, the \uicontrol Next button is called \uicontrol
3738 Continue.
3739
3740 \sa setButtonText(), QWizard::buttonText(), QWizard::setButtonText()
3741*/
3742QString QWizardPage::buttonText(QWizard::WizardButton which) const
3743{
3744 Q_D(const QWizardPage);
3745
3746 if (d->buttonCustomTexts.contains(key: which))
3747 return d->buttonCustomTexts.value(key: which);
3748
3749 if (wizard())
3750 return wizard()->buttonText(which);
3751
3752 return QString();
3753}
3754
3755/*!
3756 This virtual function is called by QWizard::nextId() to find
3757 out which page to show when the user clicks the \uicontrol Next button.
3758
3759 The return value is the ID of the next page, or -1 if no page follows.
3760
3761 By default, this function returns the lowest ID greater than the ID
3762 of the current page, or -1 if there is no such ID.
3763
3764 By reimplementing this function, you can specify a dynamic page
3765 order. For example:
3766
3767 \snippet dialogs/licensewizard/licensewizard.cpp 18
3768
3769 \sa QWizard::nextId()
3770*/
3771int QWizardPage::nextId() const
3772{
3773 Q_D(const QWizardPage);
3774
3775 if (!d->wizard)
3776 return -1;
3777
3778 bool foundCurrentPage = false;
3779
3780 const QWizardPrivate::PageMap &pageMap = d->wizard->d_func()->pageMap;
3781 QWizardPrivate::PageMap::const_iterator i = pageMap.constBegin();
3782 QWizardPrivate::PageMap::const_iterator end = pageMap.constEnd();
3783
3784 for (; i != end; ++i) {
3785 if (i.value() == this) {
3786 foundCurrentPage = true;
3787 } else if (foundCurrentPage) {
3788 return i.key();
3789 }
3790 }
3791 return -1;
3792}
3793
3794/*!
3795 \fn void QWizardPage::completeChanged()
3796
3797 This signal is emitted whenever the complete state of the page
3798 (i.e., the value of isComplete()) changes.
3799
3800 If you reimplement isComplete(), make sure to emit
3801 completeChanged() whenever the value of isComplete() changes, to
3802 ensure that QWizard updates the enabled or disabled state of its
3803 buttons.
3804
3805 \sa isComplete()
3806*/
3807
3808/*!
3809 Sets the value of the field called \a name to \a value.
3810
3811 This function can be used to set fields on any page of the wizard.
3812 It is equivalent to calling
3813 wizard()->\l{QWizard::setField()}{setField(\a name, \a value)}.
3814
3815 \sa QWizard::setField(), field(), registerField()
3816*/
3817void QWizardPage::setField(const QString &name, const QVariant &value)
3818{
3819 Q_D(QWizardPage);
3820 if (!d->wizard)
3821 return;
3822 d->wizard->setField(name, value);
3823}
3824
3825/*!
3826 Returns the value of the field called \a name.
3827
3828 This function can be used to access fields on any page of the
3829 wizard. It is equivalent to calling
3830 wizard()->\l{QWizard::field()}{field(\a name)}.
3831
3832 Example:
3833
3834 \snippet dialogs/licensewizard/licensewizard.cpp accessField
3835
3836 \sa QWizard::field(), setField(), registerField()
3837*/
3838QVariant QWizardPage::field(const QString &name) const
3839{
3840 Q_D(const QWizardPage);
3841 if (!d->wizard)
3842 return QVariant();
3843 return d->wizard->field(name);
3844}
3845
3846/*!
3847 Creates a field called \a name associated with the given \a
3848 property of the given \a widget. From then on, that property
3849 becomes accessible using field() and setField().
3850
3851 Fields are global to the entire wizard and make it easy for any
3852 single page to access information stored by another page, without
3853 having to put all the logic in QWizard or having the pages know
3854 explicitly about each other.
3855
3856 If \a name ends with an asterisk (\c *), the field is a mandatory
3857 field. When a page has mandatory fields, the \uicontrol Next and/or
3858 \uicontrol Finish buttons are enabled only when all mandatory fields
3859 are filled. This requires a \a changedSignal to be specified, to
3860 tell QWizard to recheck the value stored by the mandatory field.
3861
3862 QWizard knows the most common Qt widgets. For these (or their
3863 subclasses), you don't need to specify a \a property or a \a
3864 changedSignal. The table below lists these widgets:
3865
3866 \table
3867 \header \li Widget \li Property \li Change Notification Signal
3868 \row \li QAbstractButton \li bool \l{QAbstractButton::}{checked} \li \l{QAbstractButton::}{toggled()}
3869 \row \li QAbstractSlider \li int \l{QAbstractSlider::}{value} \li \l{QAbstractSlider::}{valueChanged()}
3870 \row \li QComboBox \li int \l{QComboBox::}{currentIndex} \li \l{QComboBox::}{currentIndexChanged()}
3871 \row \li QDateTimeEdit \li QDateTime \l{QDateTimeEdit::}{dateTime} \li \l{QDateTimeEdit::}{dateTimeChanged()}
3872 \row \li QLineEdit \li QString \l{QLineEdit::}{text} \li \l{QLineEdit::}{textChanged()}
3873 \row \li QListWidget \li int \l{QListWidget::}{currentRow} \li \l{QListWidget::}{currentRowChanged()}
3874 \row \li QSpinBox \li int \l{QSpinBox::}{value} \li \l{QSpinBox::}{valueChanged()}
3875 \endtable
3876
3877 You can use QWizard::setDefaultProperty() to add entries to this
3878 table or to override existing entries.
3879
3880 To consider a field "filled", QWizard simply checks that their
3881 current value doesn't equal their original value (the value they
3882 had before initializePage() was called). For QLineEdit, it also
3883 checks that
3884 \l{QLineEdit::hasAcceptableInput()}{hasAcceptableInput()} returns
3885 true, to honor any validator or mask.
3886
3887 QWizard's mandatory field mechanism is provided for convenience.
3888 It can be bypassed by reimplementing QWizardPage::isComplete().
3889
3890 \sa field(), setField(), QWizard::setDefaultProperty()
3891*/
3892void QWizardPage::registerField(const QString &name, QWidget *widget, const char *property,
3893 const char *changedSignal)
3894{
3895 Q_D(QWizardPage);
3896 QWizardField field(this, name, widget, property, changedSignal);
3897 if (d->wizard) {
3898 d->wizard->d_func()->addField(field);
3899 } else {
3900 d->pendingFields += field;
3901 }
3902}
3903
3904/*!
3905 Returns the wizard associated with this page, or \nullptr if this page
3906 hasn't been inserted into a QWizard yet.
3907
3908 \sa QWizard::addPage(), QWizard::setPage()
3909*/
3910QWizard *QWizardPage::wizard() const
3911{
3912 Q_D(const QWizardPage);
3913 return d->wizard;
3914}
3915
3916QT_END_NAMESPACE
3917
3918#include "moc_qwizard.cpp"
3919

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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