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

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