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