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

Last change on this file since 561 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

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