source: trunk/src/gui/dialogs/qwizard.cpp@ 1034

Last change on this file since 1034 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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