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

Last change on this file since 64 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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