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