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