OSDN Git Service

da050b29fd4510762a0ba79d1a2a8d5a25b9a40b
[qt-creator-jp/qt-creator-jp.git] / src / libs / utils / wizard.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "wizard.h"
35
36 #include <QtCore/QMap>
37 #include <QtCore/QHash>
38 #include <QtCore/QVariant>
39
40 #include <QtGui/QLabel>
41 #include <QtGui/QVBoxLayout>
42 #include <QtGui/QHBoxLayout>
43 #include <QtGui/QStyle>
44
45 /*! \class Utils::Wizard
46
47   \brief A wizard with a progress bar on the left.
48
49   Informs the user about the progress.
50 */
51
52 namespace Utils {
53
54 class ProgressItemWidget : public QWidget
55 {
56     Q_OBJECT
57 public:
58     ProgressItemWidget(const QPixmap &indicatorPixmap, const QString &title, QWidget *parent = 0)
59         : QWidget(parent),
60         m_indicatorVisible(false),
61         m_indicatorPixmap(indicatorPixmap)
62     {
63         m_indicatorLabel = new QLabel(this);
64         m_indicatorLabel->setFixedSize(m_indicatorPixmap.size());
65         m_titleLabel = new QLabel(title, this);
66         QHBoxLayout *l = new QHBoxLayout(this);
67         l->setMargin(0);
68         l->addWidget(m_indicatorLabel);
69         l->addWidget(m_titleLabel);
70     }
71     void setIndicatorVisible(bool visible) {
72         if (m_indicatorVisible == visible)
73             return;
74         m_indicatorVisible = visible;
75         if (m_indicatorVisible)
76             m_indicatorLabel->setPixmap(m_indicatorPixmap);
77         else
78             m_indicatorLabel->setPixmap(QPixmap());
79     }
80     void setTitle(const QString &title) {
81         m_titleLabel->setText(title);
82     }
83     void setWordWrap(bool wrap) {
84         m_titleLabel->setWordWrap(wrap);
85     }
86
87 private:
88     bool m_indicatorVisible;
89     QPixmap m_indicatorPixmap;
90     QLabel *m_indicatorLabel;
91     QLabel *m_titleLabel;
92 };
93
94 class LinearProgressWidget : public QWidget
95 {
96     Q_OBJECT
97 public:
98     LinearProgressWidget(WizardProgress *progress, QWidget *parent = 0);
99
100 private slots:
101     void slotItemAdded(WizardProgressItem *item);
102     void slotItemRemoved(WizardProgressItem *item);
103     void slotItemChanged(WizardProgressItem *item);
104     void slotNextItemsChanged(WizardProgressItem *item, const QList<WizardProgressItem *> &nextItems);
105     void slotNextShownItemChanged(WizardProgressItem *item, WizardProgressItem *nextItem);
106     void slotStartItemChanged(WizardProgressItem *item);
107     void slotCurrentItemChanged(WizardProgressItem *item);
108
109 private:
110     void recreateLayout();
111     void updateProgress();
112     void disableUpdates();
113     void enableUpdates();
114
115     QVBoxLayout *m_mainLayout;
116     QVBoxLayout *m_itemWidgetLayout;
117     WizardProgress *m_wizardProgress;
118     QMap<WizardProgressItem *, ProgressItemWidget *> m_itemToItemWidget;
119     QMap<ProgressItemWidget *, WizardProgressItem *> m_itemWidgetToItem;
120     QList<WizardProgressItem *> m_visibleItems;
121     ProgressItemWidget *m_dotsItemWidget;
122     int m_disableUpdatesCount;
123     QPixmap m_indicatorPixmap;
124 };
125
126 LinearProgressWidget::LinearProgressWidget(WizardProgress *progress, QWidget *parent)
127     :
128     QWidget(parent),
129     m_dotsItemWidget(0),
130     m_disableUpdatesCount(0)
131 {
132     m_indicatorPixmap = QIcon::fromTheme(QLatin1String("go-next"), QIcon(QLatin1String(":/utils/images/arrow.png"))).pixmap(16);
133     m_wizardProgress = progress;
134     m_mainLayout = new QVBoxLayout(this);
135     m_itemWidgetLayout = new QVBoxLayout();
136     QSpacerItem *spacer = new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::Expanding);
137     m_mainLayout->addLayout(m_itemWidgetLayout);
138     m_mainLayout->addSpacerItem(spacer);
139
140     m_dotsItemWidget = new ProgressItemWidget(m_indicatorPixmap, tr("..."), this);
141     m_dotsItemWidget->setVisible(false);
142     m_dotsItemWidget->setEnabled(false);
143
144     connect(m_wizardProgress, SIGNAL(itemAdded(WizardProgressItem *)),
145             this, SLOT(slotItemAdded(WizardProgressItem *)));
146     connect(m_wizardProgress, SIGNAL(itemRemoved(WizardProgressItem *)),
147             this, SLOT(slotItemRemoved(WizardProgressItem *)));
148     connect(m_wizardProgress, SIGNAL(itemChanged(WizardProgressItem *)),
149             this, SLOT(slotItemChanged(WizardProgressItem *)));
150     connect(m_wizardProgress, SIGNAL(nextItemsChanged(WizardProgressItem *, const QList<WizardProgressItem *> &)),
151             this, SLOT(slotNextItemsChanged(WizardProgressItem *, const QList<WizardProgressItem *> &)));
152     connect(m_wizardProgress, SIGNAL(nextShownItemChanged(WizardProgressItem *, WizardProgressItem *)),
153             this, SLOT(slotNextShownItemChanged(WizardProgressItem *, WizardProgressItem *)));
154     connect(m_wizardProgress, SIGNAL(startItemChanged(WizardProgressItem *)),
155             this, SLOT(slotStartItemChanged(WizardProgressItem *)));
156     connect(m_wizardProgress, SIGNAL(currentItemChanged(WizardProgressItem *)),
157             this, SLOT(slotCurrentItemChanged(WizardProgressItem *)));
158
159     QList<WizardProgressItem *> items = m_wizardProgress->items();
160     for (int i = 0; i < items.count(); i++)
161         slotItemAdded(items.at(i));
162     recreateLayout();
163
164     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
165 }
166
167 void LinearProgressWidget::slotItemAdded(WizardProgressItem *item)
168 {
169     ProgressItemWidget *itemWidget = new ProgressItemWidget(m_indicatorPixmap, item->title(), this);
170     itemWidget->setVisible(false);
171     itemWidget->setWordWrap(item->titleWordWrap());
172     m_itemToItemWidget.insert(item, itemWidget);
173     m_itemWidgetToItem.insert(itemWidget, item);
174 }
175
176 void LinearProgressWidget::slotItemRemoved(WizardProgressItem *item)
177 {
178     ProgressItemWidget *itemWidget = m_itemToItemWidget.value(item);
179     if (!itemWidget)
180         return;
181
182     m_itemWidgetToItem.remove(itemWidget);
183     m_itemToItemWidget.remove(item);
184
185     recreateLayout();
186
187     delete itemWidget;
188 }
189
190 void LinearProgressWidget::slotItemChanged(WizardProgressItem *item)
191 {
192     ProgressItemWidget *itemWidget = m_itemToItemWidget.value(item);
193     if (!itemWidget)
194         return;
195
196     itemWidget->setTitle(item->title());
197     itemWidget->setWordWrap(item->titleWordWrap());
198 }
199
200 void LinearProgressWidget::slotNextItemsChanged(WizardProgressItem *item, const QList<WizardProgressItem *> &nextItems)
201 {
202     Q_UNUSED(nextItems)
203     if (m_visibleItems.contains(item))
204         recreateLayout();
205 }
206
207 void LinearProgressWidget::slotNextShownItemChanged(WizardProgressItem *item, WizardProgressItem *nextItem)
208 {
209     Q_UNUSED(nextItem)
210     if (m_visibleItems.contains(item))
211         recreateLayout();
212 }
213
214 void LinearProgressWidget::slotStartItemChanged(WizardProgressItem *item)
215 {
216     Q_UNUSED(item)
217     recreateLayout();
218 }
219
220 void LinearProgressWidget::slotCurrentItemChanged(WizardProgressItem *item)
221 {
222     Q_UNUSED(item)
223     if (m_wizardProgress->directlyReachableItems() == m_visibleItems)
224         updateProgress();
225     else
226         recreateLayout();
227 }
228
229 void LinearProgressWidget::recreateLayout()
230 {
231     disableUpdates();
232
233     QMap<WizardProgressItem *, ProgressItemWidget *>::ConstIterator it = m_itemToItemWidget.constBegin();
234     QMap<WizardProgressItem *, ProgressItemWidget *>::ConstIterator itEnd = m_itemToItemWidget.constEnd();
235     while (it != itEnd) {
236         it.value()->setVisible(false);
237         ++it;
238     }
239     m_dotsItemWidget->setVisible(false);
240
241     for (int i = m_itemWidgetLayout->count() - 1; i >= 0; --i) {
242         QLayoutItem *item = m_itemWidgetLayout->takeAt(i);
243         delete item;
244     }
245
246     m_visibleItems = m_wizardProgress->directlyReachableItems();
247     for (int i = 0; i < m_visibleItems.count(); i++) {
248         ProgressItemWidget *itemWidget = m_itemToItemWidget.value(m_visibleItems.at(i));
249         m_itemWidgetLayout->addWidget(itemWidget);
250         itemWidget->setVisible(true);
251     }
252
253     if (!m_wizardProgress->isFinalItemDirectlyReachable()) {
254         m_itemWidgetLayout->addWidget(m_dotsItemWidget);
255         m_dotsItemWidget->setVisible(true);
256     }
257
258     enableUpdates();
259     updateProgress();
260 }
261
262 void LinearProgressWidget::updateProgress()
263 {
264     disableUpdates();
265
266     QList<WizardProgressItem *> visitedItems = m_wizardProgress->visitedItems();
267
268     QMap<WizardProgressItem *, ProgressItemWidget *>::ConstIterator it = m_itemToItemWidget.constBegin();
269     QMap<WizardProgressItem *, ProgressItemWidget *>::ConstIterator itEnd = m_itemToItemWidget.constEnd();
270     while (it != itEnd) {
271         WizardProgressItem *item = it.key();
272         ProgressItemWidget *itemWidget = it.value();
273         itemWidget->setEnabled(visitedItems.contains(item));
274         itemWidget->setIndicatorVisible(false);
275         ++it;
276     }
277
278     WizardProgressItem *currentItem = m_wizardProgress->currentItem();
279     ProgressItemWidget *currentItemWidget = m_itemToItemWidget.value(currentItem);
280     if (currentItemWidget)
281         currentItemWidget->setIndicatorVisible(true);
282
283     enableUpdates();
284 }
285
286 void LinearProgressWidget::disableUpdates()
287 {
288     if (m_disableUpdatesCount++ == 0) {
289         setUpdatesEnabled(false);
290         hide();
291     }
292 }
293
294 void LinearProgressWidget::enableUpdates()
295 {
296     if (--m_disableUpdatesCount == 0) {
297         show();
298         setUpdatesEnabled(true);
299     }
300 }
301
302 class WizardPrivate
303 {
304     Wizard *q_ptr;
305     Q_DECLARE_PUBLIC(Wizard)
306
307 public:
308     WizardPrivate()
309         :
310         m_automaticProgressCreation(true),
311         m_wizardProgress(0)
312     {
313     }
314     bool m_automaticProgressCreation;
315     WizardProgress *m_wizardProgress;
316 };
317
318 Wizard::Wizard(QWidget *parent, Qt::WindowFlags flags) :
319     QWizard(parent, flags), d_ptr(new WizardPrivate)
320 {
321     d_ptr->q_ptr = this;
322     d_ptr->m_wizardProgress = new WizardProgress(this);
323     connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(_q_currentPageChanged(int)));
324     connect(this, SIGNAL(pageAdded(int)), this, SLOT(_q_pageAdded(int)));
325     connect(this, SIGNAL(pageRemoved(int)), this, SLOT(_q_pageRemoved(int)));
326     setSideWidget(new LinearProgressWidget(d_ptr->m_wizardProgress, this));
327 }
328
329 Wizard::~Wizard()
330 {
331     delete d_ptr;
332 }
333
334 bool Wizard::isAutomaticProgressCreationEnabled() const
335 {
336     Q_D(const Wizard);
337
338     return d->m_automaticProgressCreation;
339 }
340
341 void Wizard::setAutomaticProgressCreationEnabled(bool enabled)
342 {
343     Q_D(Wizard);
344
345     d->m_automaticProgressCreation = enabled;
346 }
347
348 void Wizard::setStartId(int pageId)
349 {
350     Q_D(Wizard);
351
352     QWizard::setStartId(pageId);
353     d->m_wizardProgress->setStartPage(startId());
354 }
355
356 WizardProgress *Wizard::wizardProgress() const
357 {
358     Q_D(const Wizard);
359
360     return d->m_wizardProgress;
361 }
362
363 bool Wizard::validateCurrentPage()
364 {
365     emit nextClicked();
366     return QWizard::validateCurrentPage();
367 }
368
369 void Wizard::_q_currentPageChanged(int pageId)
370 {
371     Q_D(Wizard);
372
373     d->m_wizardProgress->setCurrentPage(pageId);
374 }
375
376 void Wizard::_q_pageAdded(int pageId)
377 {
378     Q_D(Wizard);
379
380     if (!d->m_automaticProgressCreation)
381         return;
382
383     WizardProgressItem *item = d->m_wizardProgress->addItem(page(pageId)->title());
384     item->addPage(pageId);
385     d->m_wizardProgress->setStartPage(startId());
386     if (!d->m_wizardProgress->startItem())
387         return;
388
389     QList<int> pages = pageIds();
390     int index = pages.indexOf(pageId);
391     int prevId = -1;
392     int nextId = -1;
393     if (index > 0)
394         prevId = pages.at(index - 1);
395     if (index < pages.count() - 1)
396         nextId = pages.at(index + 1);
397
398     WizardProgressItem *prevItem = 0;
399     WizardProgressItem *nextItem = 0;
400
401     if (prevId >= 0)
402         prevItem = d->m_wizardProgress->item(prevId);
403     if (nextId >= 0)
404         nextItem = d->m_wizardProgress->item(nextId);
405
406     if (prevItem)
407         prevItem->setNextItems(QList<WizardProgressItem *>() << item);
408     if (nextItem)
409         item->setNextItems(QList<WizardProgressItem *>() << nextItem);
410 }
411
412 void Wizard::_q_pageRemoved(int pageId)
413 {
414     Q_D(Wizard);
415
416     if (!d->m_automaticProgressCreation)
417         return;
418
419     WizardProgressItem *item = d->m_wizardProgress->item(pageId);
420     d->m_wizardProgress->removePage(pageId);
421     d->m_wizardProgress->setStartPage(startId());
422
423     if (!item->pages().isEmpty())
424         return;
425
426     QList<int> pages = pageIds();
427     int index = pages.indexOf(pageId);
428     int prevId = -1;
429     int nextId = -1;
430     if (index > 0)
431         prevId = pages.at(index - 1);
432     if (index < pages.count() - 1)
433         nextId = pages.at(index + 1);
434
435     WizardProgressItem *prevItem = 0;
436     WizardProgressItem *nextItem = 0;
437
438     if (prevId >= 0)
439         prevItem = d->m_wizardProgress->item(prevId);
440     if (nextId >= 0)
441         nextItem = d->m_wizardProgress->item(nextId);
442
443     if (prevItem && nextItem) {
444         QList<WizardProgressItem *> nextItems = prevItem->nextItems();
445         nextItems.removeOne(item);
446         if (!nextItems.contains(nextItem))
447             nextItems.append(nextItem);
448         prevItem->setNextItems(nextItems);
449     }
450     d->m_wizardProgress->removeItem(item);
451 }
452
453
454
455
456 class WizardProgressPrivate
457 {
458     WizardProgress *q_ptr;
459     Q_DECLARE_PUBLIC(WizardProgress)
460
461 public:
462     WizardProgressPrivate()
463         :
464         m_currentItem(0),
465         m_startItem(0)
466     {
467     }
468
469     bool isNextItem(WizardProgressItem *item, WizardProgressItem *nextItem) const;
470     // if multiple paths are possible the empty list is returned
471     QList<WizardProgressItem *> singlePathBetween(WizardProgressItem *fromItem, WizardProgressItem *toItem) const;
472     void updateReachableItems();
473
474     QMap<int, WizardProgressItem *> m_pageToItem;
475     QMap<WizardProgressItem *, WizardProgressItem *> m_itemToItem;
476
477     QList<WizardProgressItem *> m_items;
478
479     QList<WizardProgressItem *> m_visitedItems;
480     QList<WizardProgressItem *> m_reachableItems;
481
482     WizardProgressItem *m_currentItem;
483     WizardProgressItem *m_startItem;
484 };
485
486 class WizardProgressItemPrivate
487 {
488     WizardProgressItem *q_ptr;
489     Q_DECLARE_PUBLIC(WizardProgressItem)
490 public:
491     QString m_title;
492     bool m_titleWordWrap;
493     WizardProgress *m_wizardProgress;
494     QList<int> m_pages;
495     QList<WizardProgressItem *> m_nextItems;
496     QList<WizardProgressItem *> m_prevItems;
497     WizardProgressItem *m_nextShownItem;
498 };
499
500 bool WizardProgressPrivate::isNextItem(WizardProgressItem *item, WizardProgressItem *nextItem) const
501 {
502     QHash<WizardProgressItem *, bool> visitedItems;
503     QList<WizardProgressItem *> workingItems = item->nextItems();
504     while (!workingItems.isEmpty()) {
505         WizardProgressItem *workingItem = workingItems.takeFirst();
506         if (workingItem == nextItem)
507             return true;
508         if (visitedItems.contains(workingItem))
509             continue;
510         visitedItems.insert(workingItem, true);
511         workingItems += workingItem->nextItems();
512     }
513     return false;
514 }
515
516 QList<WizardProgressItem *> WizardProgressPrivate::singlePathBetween(WizardProgressItem *fromItem, WizardProgressItem *toItem) const
517 {
518     WizardProgressItem *item = fromItem;
519     if (!item)
520         item = m_startItem;
521     if (!item)
522         return QList<WizardProgressItem *>();
523
524     // Optimization. It is workaround for case A->B, B->C, A->C where "from" is A and "to" is C.
525     // When we had X->A in addition and "from" was X and "to" was C, this would not work
526     // (it should return the shortest path which would be X->A->C).
527     if (item->nextItems().contains(toItem))
528         return QList<WizardProgressItem *>() << toItem;
529
530     QHash<WizardProgressItem *, QHash<WizardProgressItem *, bool> > visitedItemsToParents;
531     QList<QPair<WizardProgressItem *, WizardProgressItem *> > workingItems; // next to prev item
532
533     QList<WizardProgressItem *> items = item->nextItems();
534     for (int i = 0; i < items.count(); i++)
535         workingItems.append(qMakePair(items.at(i), item));
536
537     while (!workingItems.isEmpty()) {
538         QPair<WizardProgressItem *, WizardProgressItem *> workingItem = workingItems.takeFirst();
539
540         QHash<WizardProgressItem *, bool> &parents = visitedItemsToParents[workingItem.first];
541         parents.insert(workingItem.second, true);
542         if (parents.count() > 1)
543             continue;
544
545         QList<WizardProgressItem *> items = workingItem.first->nextItems();
546         for (int i = 0; i < items.count(); i++)
547             workingItems.append(qMakePair(items.at(i), workingItem.first));
548     }
549
550     QList<WizardProgressItem *> path;
551
552     WizardProgressItem *it = toItem;
553     QHash<WizardProgressItem *, QHash<WizardProgressItem *, bool> >::ConstIterator itItem = visitedItemsToParents.constFind(it);
554     QHash<WizardProgressItem *, QHash<WizardProgressItem *, bool> >::ConstIterator itEnd = visitedItemsToParents.constEnd();
555     while (itItem != itEnd) {
556         path.prepend(itItem.key());
557         if (itItem.value().count() != 1)
558             return QList<WizardProgressItem *>();
559         it = itItem.value().constBegin().key();
560         if (it == item) {
561             return path;
562         }
563         itItem = visitedItemsToParents.constFind(it);
564     }
565     return QList<WizardProgressItem *>();
566 }
567
568 void WizardProgressPrivate::updateReachableItems()
569 {
570     m_reachableItems = m_visitedItems;
571     WizardProgressItem *item = 0;
572     if (m_visitedItems.count() > 0)
573         item = m_visitedItems.last();
574     if (!item) {
575         item = m_startItem;
576         m_reachableItems.append(item);
577     }
578     if (!item)
579         return;
580     while (item->nextShownItem()) {
581         item = item->nextShownItem();
582         m_reachableItems.append(item);
583     }
584 }
585
586
587 WizardProgress::WizardProgress(QObject *parent)
588     : QObject(parent), d_ptr(new WizardProgressPrivate)
589 {
590     d_ptr->q_ptr = this;
591 }
592
593 WizardProgress::~WizardProgress()
594 {
595     Q_D(WizardProgress);
596
597     QMap<WizardProgressItem *, WizardProgressItem *>::ConstIterator it = d->m_itemToItem.constBegin();
598     QMap<WizardProgressItem *, WizardProgressItem *>::ConstIterator itEnd = d->m_itemToItem.constEnd();
599     while (it != itEnd) {
600         delete it.key();
601         ++it;
602     }
603     delete d_ptr;
604 }
605
606 WizardProgressItem *WizardProgress::addItem(const QString &title)
607 {
608     Q_D(WizardProgress);
609
610     WizardProgressItem *item = new WizardProgressItem(this, title);
611     d->m_itemToItem.insert(item, item);
612     emit itemAdded(item);
613     return item;
614 }
615
616 void WizardProgress::removeItem(WizardProgressItem *item)
617 {
618     Q_D(WizardProgress);
619
620     QMap<WizardProgressItem *, WizardProgressItem *>::iterator it = d->m_itemToItem.find(item);
621     if (it == d->m_itemToItem.end()) {
622         qWarning("WizardProgress::removePage: Item is not a part of the wizard");
623         return;
624     }
625
626     // remove item from prev items
627     QList<WizardProgressItem *> prevItems = item->d_ptr->m_prevItems;
628     for (int i = 0; i < prevItems.count(); i++) {
629         WizardProgressItem *prevItem = prevItems.at(i);
630         prevItem->d_ptr->m_nextItems.removeOne(item);
631     }
632
633     // remove item from next items
634     QList<WizardProgressItem *> nextItems = item->d_ptr->m_nextItems;
635     for (int i = 0; i < nextItems.count(); i++) {
636         WizardProgressItem *nextItem = nextItems.at(i);
637         nextItem->d_ptr->m_prevItems.removeOne(item);
638     }
639
640     // update history
641     int idx = d->m_visitedItems.indexOf(item);
642     if (idx >= 0)
643         d->m_visitedItems.removeAt(idx);
644
645     // update reachable items
646     d->updateReachableItems();
647
648     emit itemRemoved(item);
649
650     QList<int> pages = item->pages();
651     for (int i = 0; i < pages.count(); i++)
652         d->m_pageToItem.remove(pages.at(i));
653     d->m_itemToItem.erase(it);
654     delete item;
655 }
656
657 void WizardProgress::removePage(int pageId)
658 {
659     Q_D(WizardProgress);
660
661     QMap<int, WizardProgressItem *>::iterator it = d->m_pageToItem.find(pageId);
662     if (it == d->m_pageToItem.end()) {
663         qWarning("WizardProgress::removePage: page is not a part of the wizard");
664         return;
665     }
666     WizardProgressItem *item = it.value();
667     d->m_pageToItem.erase(it);
668     item->d_ptr->m_pages.removeOne(pageId);
669 }
670
671 QList<int> WizardProgress::pages(WizardProgressItem *item) const
672 {
673     return item->pages();
674 }
675
676 WizardProgressItem *WizardProgress::item(int pageId) const
677 {
678     Q_D(const WizardProgress);
679
680     return d->m_pageToItem.value(pageId);
681 }
682
683 WizardProgressItem *WizardProgress::currentItem() const
684 {
685     Q_D(const WizardProgress);
686
687     return d->m_currentItem;
688 }
689
690 QList<WizardProgressItem *> WizardProgress::items() const
691 {
692     Q_D(const WizardProgress);
693
694     return d->m_itemToItem.keys();
695 }
696
697 WizardProgressItem *WizardProgress::startItem() const
698 {
699     Q_D(const WizardProgress);
700
701     return d->m_startItem;
702 }
703
704 QList<WizardProgressItem *> WizardProgress::visitedItems() const
705 {
706     Q_D(const WizardProgress);
707
708     return d->m_visitedItems;
709 }
710
711 QList<WizardProgressItem *> WizardProgress::directlyReachableItems() const
712 {
713     Q_D(const WizardProgress);
714
715     return d->m_reachableItems;
716 }
717
718 bool WizardProgress::isFinalItemDirectlyReachable() const
719 {
720     Q_D(const WizardProgress);
721
722     if (d->m_reachableItems.isEmpty())
723         return false;
724
725     return d->m_reachableItems.last()->isFinalItem();
726 }
727
728 void WizardProgress::setCurrentPage(int pageId)
729 {
730     Q_D(WizardProgress);
731
732     if (pageId < 0) { // reset history
733         d->m_currentItem = 0;
734         d->m_visitedItems.clear();
735         d->m_reachableItems.clear();
736         d->updateReachableItems();
737         return;
738     }
739
740     WizardProgressItem *item = d->m_pageToItem.value(pageId);
741     if (!item) {
742         qWarning("WizardProgress::setCurrentPage: page is not mapped to any wizard progress item");
743         return;
744     }
745
746     if (d->m_currentItem == item) // nothing changes
747         return;
748
749     const bool currentStartItem = !d->m_currentItem && d->m_startItem && d->m_startItem == item;
750
751     // Check if item is reachable with the provided history or with the next items.
752     const QList<WizardProgressItem *> singleItemPath = d->singlePathBetween(d->m_currentItem, item);
753     const int prevItemIndex = d->m_visitedItems.indexOf(item);
754
755     if (singleItemPath.isEmpty() && prevItemIndex < 0 && !currentStartItem) {
756         qWarning("WizardProgress::setCurrentPage: new current item is not directly reachable from the old current item");
757         return;
758     }
759
760     // Update the history accordingly.
761     if (prevItemIndex >= 0) {
762         while (prevItemIndex + 1 < d->m_visitedItems.count())
763             d->m_visitedItems.removeLast();
764     } else {
765         if ((!d->m_currentItem && d->m_startItem && !singleItemPath.isEmpty()) || currentStartItem)
766             d->m_visitedItems += d->m_startItem;
767         d->m_visitedItems += singleItemPath;
768     }
769
770     d->m_currentItem = item;
771
772     // Update reachable items accordingly.
773     d->updateReachableItems();
774
775     emit currentItemChanged(item);
776 }
777
778 void WizardProgress::setStartPage(int pageId)
779 {
780     Q_D(WizardProgress);
781
782     WizardProgressItem *item = d->m_pageToItem.value(pageId);
783     if (!item) {
784         qWarning("WizardProgress::setStartPage: page is not mapped to any wizard progress item");
785         return;
786     }
787
788     d->m_startItem = item;
789     d->updateReachableItems();
790
791     emit startItemChanged(item);
792 }
793
794 WizardProgressItem::WizardProgressItem(WizardProgress *progress, const QString &title)
795     : d_ptr(new WizardProgressItemPrivate)
796 {
797     d_ptr->q_ptr = this;
798     d_ptr->m_title = title;
799     d_ptr->m_titleWordWrap = false;
800     d_ptr->m_wizardProgress = progress;
801     d_ptr->m_nextShownItem = 0;
802 }
803
804 WizardProgressItem::~WizardProgressItem()
805 {
806     delete d_ptr;
807 }
808
809 void WizardProgressItem::addPage(int pageId)
810 {
811     Q_D(WizardProgressItem);
812
813     if (d->m_wizardProgress->d_ptr->m_pageToItem.contains(pageId)) {
814         qWarning("WizardProgress::addPage: Page is already added to the item");
815         return;
816     }
817     d->m_pages.append(pageId);
818     d->m_wizardProgress->d_ptr->m_pageToItem.insert(pageId, this);
819 }
820
821 QList<int> WizardProgressItem::pages() const
822 {
823     Q_D(const WizardProgressItem);
824
825     return d->m_pages;
826 }
827
828 void WizardProgressItem::setNextItems(const QList<WizardProgressItem *> &items)
829 {
830     Q_D(WizardProgressItem);
831
832     // check if we create cycle
833     for (int i = 0; i < items.count(); i++) {
834         WizardProgressItem *nextItem = items.at(i);
835         if (nextItem == this || d->m_wizardProgress->d_ptr->isNextItem(nextItem, this)) {
836             qWarning("WizardProgress::setNextItems: Setting one of the next items would create a cycle");
837             return;
838         }
839     }
840
841     if (d->m_nextItems == items) // nothing changes
842         return;
843
844     if (!items.contains(d->m_nextShownItem))
845         setNextShownItem(0);
846
847     // update prev items (remove this item from the old next items)
848     for (int i = 0; i < d->m_nextItems.count(); i++) {
849         WizardProgressItem *nextItem = d->m_nextItems.at(i);
850         nextItem->d_ptr->m_prevItems.removeOne(this);
851     }
852
853     d->m_nextItems = items;
854
855     // update prev items (add this item to the new next items)
856     for (int i = 0; i < d->m_nextItems.count(); i++) {
857         WizardProgressItem *nextItem = d->m_nextItems.at(i);
858         nextItem->d_ptr->m_prevItems.append(this);
859     }
860
861     d->m_wizardProgress->d_ptr->updateReachableItems();
862
863     emit d->m_wizardProgress->nextItemsChanged(this, items);
864
865     if (items.count() == 1)
866         setNextShownItem(items.first());
867 }
868
869 QList<WizardProgressItem *> WizardProgressItem::nextItems() const
870 {
871     Q_D(const WizardProgressItem);
872
873     return d->m_nextItems;
874 }
875
876 void WizardProgressItem::setNextShownItem(WizardProgressItem *item)
877 {
878     Q_D(WizardProgressItem);
879
880     if (d->m_nextShownItem == item) // nothing changes
881         return;
882
883     if (item && !d->m_nextItems.contains(item)) // the "item" is not a one of next items
884         return;
885
886     d->m_nextShownItem = item;
887
888     d->m_wizardProgress->d_ptr->updateReachableItems();
889
890     emit d->m_wizardProgress->nextShownItemChanged(this, item);
891 }
892
893 WizardProgressItem *WizardProgressItem::nextShownItem() const
894 {
895     Q_D(const WizardProgressItem);
896
897     return d->m_nextShownItem;
898 }
899
900 bool WizardProgressItem::isFinalItem() const
901 {
902     return nextItems().isEmpty();
903 }
904
905 void WizardProgressItem::setTitle(const QString &title)
906 {
907     Q_D(WizardProgressItem);
908
909     d->m_title = title;
910     emit d->m_wizardProgress->itemChanged(this);
911 }
912
913 QString WizardProgressItem::title() const
914 {
915     Q_D(const WizardProgressItem);
916
917     return d->m_title;
918 }
919
920 void WizardProgressItem::setTitleWordWrap(bool wrap)
921 {
922     Q_D(WizardProgressItem);
923
924     d->m_titleWordWrap = wrap;
925     emit d->m_wizardProgress->itemChanged(this);
926 }
927
928 bool WizardProgressItem::titleWordWrap() const
929 {
930     Q_D(const WizardProgressItem);
931
932     return d->m_titleWordWrap;
933 }
934
935 } // namespace Utils
936
937 #include "wizard.moc"
938 #include "moc_wizard.cpp"
939