summaryrefslogtreecommitdiff
path: root/src/libs/utils/wizard.cpp
diff options
context:
space:
mode:
authorJarek Kobus <jkobus@trolltech.com>2010-03-29 16:17:53 +0200
committerJarek Kobus <jkobus@trolltech.com>2010-03-29 16:19:04 +0200
commitb790e04e51b2ba2e122520c41b4f8ef8cddf46ad (patch)
tree6ba11477f70d3036ed0795c8a6cd37ce6f65341c /src/libs/utils/wizard.cpp
parenta699e6d56afd928c01f35c2343c9400e26377265 (diff)
downloadqt-creator-b790e04e51b2ba2e122520c41b4f8ef8cddf46ad.tar.gz
Introduce a new Wizard class, capable of showing progress items.
Diffstat (limited to 'src/libs/utils/wizard.cpp')
-rw-r--r--src/libs/utils/wizard.cpp869
1 files changed, 869 insertions, 0 deletions
diff --git a/src/libs/utils/wizard.cpp b/src/libs/utils/wizard.cpp
new file mode 100644
index 0000000000..3c04e9385f
--- /dev/null
+++ b/src/libs/utils/wizard.cpp
@@ -0,0 +1,869 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "wizard.h"
+#include <QLabel>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QMap>
+#include <QHash>
+
+namespace Utils {
+
+class ProgressItemWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ ProgressItemWidget(const QPixmap &indicatorPixmap, const QString &title, QWidget *parent = 0)
+ : QWidget(parent),
+ m_indicatorVisible(false),
+ m_indicatorPixmap(indicatorPixmap)
+ {
+ m_indicatorLabel = new QLabel(this);
+ m_indicatorLabel->setFixedSize(m_indicatorPixmap.size());
+ m_titleLabel = new QLabel(title, this);
+ QHBoxLayout *l = new QHBoxLayout(this);
+ l->setMargin(0);
+ l->addWidget(m_indicatorLabel);
+ l->addWidget(m_titleLabel);
+ }
+ void setIndicatorVisible(bool visible) {
+ if (m_indicatorVisible == visible)
+ return;
+ m_indicatorVisible = visible;
+ if (m_indicatorVisible)
+ m_indicatorLabel->setPixmap(m_indicatorPixmap);
+ else
+ m_indicatorLabel->setPixmap(QPixmap());
+ }
+ void setTitle(const QString &title) {
+ m_titleLabel->setText(title);
+ }
+ void setWordWrap(bool wrap) {
+ m_titleLabel->setWordWrap(wrap);
+ }
+
+private:
+ bool m_indicatorVisible;
+ QPixmap m_indicatorPixmap;
+ QLabel *m_indicatorLabel;
+ QLabel *m_titleLabel;
+};
+
+class LinearProgressWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ LinearProgressWidget(WizardProgress *progress, QWidget *parent = 0);
+
+private slots:
+ void slotItemAdded(WizardProgressItem *item);
+ void slotItemRemoved(WizardProgressItem *item);
+ void slotItemChanged(WizardProgressItem *item);
+ void slotNextItemsChanged(WizardProgressItem *item, const QList<WizardProgressItem *> &nextItems);
+ void slotStartItemChanged(WizardProgressItem *item);
+ void slotCurrentItemChanged(WizardProgressItem *item);
+
+private:
+ void recreateLayout();
+ void updateProgress();
+ void disableUpdates();
+ void enableUpdates();
+
+ QVBoxLayout *m_mainLayout;
+ QVBoxLayout *m_itemWidgetLayout;
+ WizardProgress *m_wizardProgress;
+ QMap<WizardProgressItem *, ProgressItemWidget *> m_itemToItemWidget;
+ QMap<ProgressItemWidget *, WizardProgressItem *> m_itemWidgetToItem;
+ QList<WizardProgressItem *> m_visibleItems;
+ ProgressItemWidget *m_dotsItemWidget;
+ int m_disableUpdatesCount;
+ QPixmap m_indicatorPixmap;
+};
+
+LinearProgressWidget::LinearProgressWidget(WizardProgress *progress, QWidget *parent)
+ :
+ QWidget(parent),
+ m_dotsItemWidget(0),
+ m_disableUpdatesCount(0),
+ m_indicatorPixmap(QLatin1String(":/trolltech/styles/commonstyle/images/right-16.png"))
+{
+ m_wizardProgress = progress;
+ m_mainLayout = new QVBoxLayout(this);
+ m_itemWidgetLayout = new QVBoxLayout();
+ QSpacerItem *spacer = new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::Expanding);
+ m_mainLayout->addLayout(m_itemWidgetLayout);
+ m_mainLayout->addSpacerItem(spacer);
+
+ m_dotsItemWidget = new ProgressItemWidget(m_indicatorPixmap, tr("..."), this);
+ m_dotsItemWidget->setVisible(false);
+ m_dotsItemWidget->setEnabled(false);
+
+ connect(m_wizardProgress, SIGNAL(itemAdded(WizardProgressItem *)),
+ this, SLOT(slotItemAdded(WizardProgressItem *)));
+ connect(m_wizardProgress, SIGNAL(itemRemoved(WizardProgressItem *)),
+ this, SLOT(slotItemRemoved(WizardProgressItem *)));
+ connect(m_wizardProgress, SIGNAL(itemChanged(WizardProgressItem *)),
+ this, SLOT(slotItemChanged(WizardProgressItem *)));
+ connect(m_wizardProgress, SIGNAL(nextItemsChanged(WizardProgressItem *, const QList<WizardProgressItem *> &)),
+ this, SLOT(slotNextItemsChanged(WizardProgressItem *, const QList<WizardProgressItem *> &)));
+ connect(m_wizardProgress, SIGNAL(startItemChanged(WizardProgressItem *)),
+ this, SLOT(slotStartItemChanged(WizardProgressItem *)));
+ connect(m_wizardProgress, SIGNAL(currentItemChanged(WizardProgressItem *)),
+ this, SLOT(slotCurrentItemChanged(WizardProgressItem *)));
+
+ QList<WizardProgressItem *> items = m_wizardProgress->items();
+ for (int i = 0; i < items.count(); i++)
+ slotItemAdded(items.at(i));
+ recreateLayout();
+
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+}
+
+void LinearProgressWidget::slotItemAdded(WizardProgressItem *item)
+{
+ ProgressItemWidget *itemWidget = new ProgressItemWidget(m_indicatorPixmap, item->title(), this);
+ itemWidget->setVisible(false);
+ itemWidget->setWordWrap(item->titleWordWrap());
+ m_itemToItemWidget.insert(item, itemWidget);
+ m_itemWidgetToItem.insert(itemWidget, item);
+}
+
+void LinearProgressWidget::slotItemRemoved(WizardProgressItem *item)
+{
+ ProgressItemWidget *itemWidget = m_itemToItemWidget.value(item);
+ if (!itemWidget)
+ return;
+
+ m_itemWidgetToItem.remove(itemWidget);
+ m_itemToItemWidget.remove(item);
+
+ recreateLayout();
+
+ delete itemWidget;
+}
+
+void LinearProgressWidget::slotItemChanged(WizardProgressItem *item)
+{
+ ProgressItemWidget *itemWidget = m_itemToItemWidget.value(item);
+ if (!itemWidget)
+ return;
+
+ itemWidget->setTitle(item->title());
+ itemWidget->setWordWrap(item->titleWordWrap());
+}
+
+void LinearProgressWidget::slotNextItemsChanged(WizardProgressItem *item, const QList<WizardProgressItem *> &nextItems)
+{
+ Q_UNUSED(nextItems)
+ if (m_visibleItems.contains(item))
+ recreateLayout();
+}
+
+void LinearProgressWidget::slotStartItemChanged(WizardProgressItem *item)
+{
+ Q_UNUSED(item)
+ recreateLayout();
+}
+
+void LinearProgressWidget::slotCurrentItemChanged(WizardProgressItem *item)
+{
+ Q_UNUSED(item)
+ if (m_wizardProgress->directlyReachableItems() == m_visibleItems)
+ updateProgress();
+ else
+ recreateLayout();
+}
+
+void LinearProgressWidget::recreateLayout()
+{
+ disableUpdates();
+
+ QMap<WizardProgressItem *, ProgressItemWidget *>::ConstIterator it = m_itemToItemWidget.constBegin();
+ QMap<WizardProgressItem *, ProgressItemWidget *>::ConstIterator itEnd = m_itemToItemWidget.constEnd();
+ while (it != itEnd) {
+ it.value()->setVisible(false);
+ ++it;
+ }
+ m_dotsItemWidget->setVisible(false);
+
+ for (int i = m_itemWidgetLayout->count() - 1; i >= 0; --i) {
+ QLayoutItem *item = m_itemWidgetLayout->takeAt(i);
+ delete item;
+ }
+
+ m_visibleItems = m_wizardProgress->directlyReachableItems();
+ for (int i = 0; i < m_visibleItems.count(); i++) {
+ ProgressItemWidget *itemWidget = m_itemToItemWidget.value(m_visibleItems.at(i));
+ m_itemWidgetLayout->addWidget(itemWidget);
+ itemWidget->setVisible(true);
+ }
+
+ if (!m_wizardProgress->isFinalItemDirectlyReachable()) {
+ m_itemWidgetLayout->addWidget(m_dotsItemWidget);
+ m_dotsItemWidget->setVisible(true);
+ }
+
+ enableUpdates();
+ updateProgress();
+}
+
+void LinearProgressWidget::updateProgress()
+{
+ disableUpdates();
+
+ QList<WizardProgressItem *> visitedItems = m_wizardProgress->visitedItems();
+
+ QMap<WizardProgressItem *, ProgressItemWidget *>::ConstIterator it = m_itemToItemWidget.constBegin();
+ QMap<WizardProgressItem *, ProgressItemWidget *>::ConstIterator itEnd = m_itemToItemWidget.constEnd();
+ while (it != itEnd) {
+ WizardProgressItem *item = it.key();
+ ProgressItemWidget *itemWidget = it.value();
+ itemWidget->setEnabled(visitedItems.contains(item));
+ itemWidget->setIndicatorVisible(false);
+ ++it;
+ }
+
+ WizardProgressItem *currentItem = m_wizardProgress->currentItem();
+ ProgressItemWidget *currentItemWidget = m_itemToItemWidget.value(currentItem);
+ if (currentItemWidget)
+ currentItemWidget->setIndicatorVisible(true);
+
+ enableUpdates();
+}
+
+void LinearProgressWidget::disableUpdates()
+{
+ if (m_disableUpdatesCount++ == 0) {
+ setUpdatesEnabled(false);
+ hide();
+ }
+}
+
+void LinearProgressWidget::enableUpdates()
+{
+ if (--m_disableUpdatesCount == 0) {
+ show();
+ setUpdatesEnabled(true);
+ }
+}
+
+class WizardPrivate
+{
+ Wizard *q_ptr;
+ Q_DECLARE_PUBLIC(Wizard)
+
+public:
+ WizardPrivate()
+ :
+ m_automaticProgressCreation(true),
+ m_wizardProgress(0)
+ {
+ }
+ bool m_automaticProgressCreation;
+ WizardProgress *m_wizardProgress;
+};
+
+Wizard::Wizard(QWidget *parent) :
+ QWizard(parent), d_ptr(new WizardPrivate)
+{
+ d_ptr->q_ptr = this;
+ d_ptr->m_wizardProgress = new WizardProgress(this);
+ connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(_q_currentPageChanged(int)));
+ connect(this, SIGNAL(pageAdded(int)), this, SLOT(_q_pageAdded(int)));
+ connect(this, SIGNAL(pageRemoved(int)), this, SLOT(_q_pageRemoved(int)));
+ setSideWidget(new LinearProgressWidget(d_ptr->m_wizardProgress, this));
+}
+
+bool Wizard::isAutomaticProgressCreationEnabled() const
+{
+ Q_D(const Wizard);
+
+ return d->m_automaticProgressCreation;
+}
+
+void Wizard::setAutomaticProgressCreationEnabled(bool enabled)
+{
+ Q_D(Wizard);
+
+ d->m_automaticProgressCreation = enabled;
+}
+
+void Wizard::setStartId(int pageId)
+{
+ Q_D(Wizard);
+
+ QWizard::setStartId(pageId);
+ d->m_wizardProgress->setStartPage(startId());
+}
+
+WizardProgress *Wizard::wizardProgress() const
+{
+ Q_D(const Wizard);
+
+ return d->m_wizardProgress;
+}
+
+void Wizard::_q_currentPageChanged(int pageId)
+{
+ Q_D(Wizard);
+
+ d->m_wizardProgress->setCurrentPage(pageId);
+}
+
+void Wizard::_q_pageAdded(int pageId)
+{
+ Q_D(Wizard);
+
+ if (!d->m_automaticProgressCreation)
+ return;
+
+ WizardProgressItem *item = d->m_wizardProgress->addItem(page(pageId)->title());
+ item->addPage(pageId);
+ d->m_wizardProgress->setStartPage(startId());
+ if (!d->m_wizardProgress->startItem())
+ return;
+
+ QList<int> pages = pageIds();
+ int index = pages.indexOf(pageId);
+ int prevId = -1;
+ int nextId = -1;
+ if (index > 0)
+ prevId = pages.at(index - 1);
+ if (index < pages.count() - 1)
+ nextId = pages.at(index + 1);
+
+ WizardProgressItem *prevItem = 0;
+ WizardProgressItem *nextItem = 0;
+
+ if (prevId >= 0)
+ prevItem = d->m_wizardProgress->item(prevId);
+ if (nextId >= 0)
+ nextItem = d->m_wizardProgress->item(nextId);
+
+ if (prevItem)
+ prevItem->setNextItems(QList<WizardProgressItem *>() << item);
+ if (nextItem)
+ item->setNextItems(QList<WizardProgressItem *>() << nextItem);
+}
+
+void Wizard::_q_pageRemoved(int pageId)
+{
+ Q_D(Wizard);
+
+ if (!d->m_automaticProgressCreation)
+ return;
+
+ WizardProgressItem *item = d->m_wizardProgress->item(pageId);
+ d->m_wizardProgress->removePage(pageId);
+ d->m_wizardProgress->setStartPage(startId());
+
+ if (!item->pages().isEmpty())
+ return;
+
+ QList<int> pages = pageIds();
+ int index = pages.indexOf(pageId);
+ int prevId = -1;
+ int nextId = -1;
+ if (index > 0)
+ prevId = pages.at(index - 1);
+ if (index < pages.count() - 1)
+ nextId = pages.at(index + 1);
+
+ WizardProgressItem *prevItem = 0;
+ WizardProgressItem *nextItem = 0;
+
+ if (prevId >= 0)
+ prevItem = d->m_wizardProgress->item(prevId);
+ if (nextId >= 0)
+ nextItem = d->m_wizardProgress->item(nextId);
+
+ if (prevItem && nextItem) {
+ QList<WizardProgressItem *> nextItems = prevItem->nextItems();
+ nextItems.removeOne(item);
+ if (!nextItems.contains(nextItem))
+ nextItems.append(nextItem);
+ prevItem->setNextItems(nextItems);
+ }
+ d->m_wizardProgress->removeItem(item);
+}
+
+
+
+
+class WizardProgressPrivate
+{
+ WizardProgress *q_ptr;
+ Q_DECLARE_PUBLIC(WizardProgress)
+
+public:
+ WizardProgressPrivate()
+ :
+ m_currentItem(0),
+ m_startItem(0)
+ {
+ }
+
+ bool isNextItem(WizardProgressItem *item, WizardProgressItem *nextItem) const;
+ // if multiple paths are possible the empty list is returned
+ QList<WizardProgressItem *> singlePathBetween(WizardProgressItem *fromItem, WizardProgressItem *toItem) const;
+ void updateReachableItems();
+
+ QMap<int, WizardProgressItem *> m_pageToItem;
+ QMap<WizardProgressItem *, WizardProgressItem *> m_itemToItem;
+
+ QList<WizardProgressItem *> m_items;
+
+ QList<WizardProgressItem *> m_visitedItems;
+ QList<WizardProgressItem *> m_reachableItems;
+
+ WizardProgressItem *m_currentItem;
+ WizardProgressItem *m_startItem;
+};
+
+class WizardProgressItemPrivate
+{
+ WizardProgressItem *q_ptr;
+ Q_DECLARE_PUBLIC(WizardProgressItem)
+public:
+ QString m_title;
+ bool m_titleWordWrap;
+ WizardProgress *m_wizardProgress;
+ QList<int> m_pages;
+ QList<WizardProgressItem *> m_nextItems;
+ QList<WizardProgressItem *> m_prevItems;
+};
+
+bool WizardProgressPrivate::isNextItem(WizardProgressItem *item, WizardProgressItem *nextItem) const
+{
+ QHash<WizardProgressItem *, bool> visitedItems;
+ QList<WizardProgressItem *> workingItems = item->nextItems();
+ while (!workingItems.isEmpty()) {
+ WizardProgressItem *workingItem = workingItems.takeFirst();
+ if (workingItem == nextItem)
+ return true;
+ if (visitedItems.contains(workingItem))
+ continue;
+ visitedItems.insert(workingItem, true);
+ workingItems += workingItem->nextItems();
+ }
+ return false;
+}
+
+QList<WizardProgressItem *> WizardProgressPrivate::singlePathBetween(WizardProgressItem *fromItem, WizardProgressItem *toItem) const
+{
+ WizardProgressItem *item = fromItem;
+ if (!item)
+ item = m_startItem;
+ if (!item)
+ return QList<WizardProgressItem *>();
+
+ // Optimization. It is workaround for case A->B, B->C, A->C where "from" is A and "to" is C.
+ // When we had X->A in addition and "from" was X and "to" was C, this would not work
+ // (it should return the shortest path which would be X->A->C).
+ if (item->nextItems().contains(toItem))
+ return QList<WizardProgressItem *>() << toItem;
+
+ QHash<WizardProgressItem *, QHash<WizardProgressItem *, bool> > visitedItemsToParents;
+ QList<QPair<WizardProgressItem *, WizardProgressItem *> > workingItems; // next to prev item
+
+ QList<WizardProgressItem *> items = item->nextItems();
+ for (int i = 0; i < items.count(); i++)
+ workingItems.append(qMakePair(items.at(i), item));
+
+ while (!workingItems.isEmpty()) {
+ QPair<WizardProgressItem *, WizardProgressItem *> workingItem = workingItems.takeFirst();
+
+ QHash<WizardProgressItem *, bool> &parents = visitedItemsToParents[workingItem.first];
+ parents.insert(workingItem.second, true);
+ if (parents.count() > 1)
+ continue;
+
+ QList<WizardProgressItem *> items = workingItem.first->nextItems();
+ for (int i = 0; i < items.count(); i++)
+ workingItems.append(qMakePair(items.at(i), workingItem.first));
+ }
+
+ QList<WizardProgressItem *> path;
+
+ WizardProgressItem *it = toItem;
+ QHash<WizardProgressItem *, QHash<WizardProgressItem *, bool> >::ConstIterator itItem = visitedItemsToParents.constFind(it);
+ QHash<WizardProgressItem *, QHash<WizardProgressItem *, bool> >::ConstIterator itEnd = visitedItemsToParents.constEnd();
+ while (itItem != itEnd) {
+ path.prepend(itItem.key());
+ if (itItem.value().count() != 1)
+ return QList<WizardProgressItem *>();
+ it = itItem.value().constBegin().key();
+ if (it == item) {
+ return path;
+ }
+ itItem = visitedItemsToParents.constFind(it);
+ }
+ return QList<WizardProgressItem *>();
+}
+
+void WizardProgressPrivate::updateReachableItems()
+{
+ m_reachableItems = m_visitedItems;
+ WizardProgressItem *item = 0;
+ if (m_visitedItems.count() > 0)
+ item = m_visitedItems.last();
+ if (!item) {
+ item = m_startItem;
+ m_reachableItems.append(item);
+ }
+ if (!item)
+ return;
+ while (item->nextItems().count() == 1) {
+ item = item->nextItems().first();
+ m_reachableItems.append(item);
+ }
+}
+
+
+WizardProgress::WizardProgress(QObject *parent)
+ : QObject(parent), d_ptr(new WizardProgressPrivate)
+{
+ d_ptr->q_ptr = this;
+}
+
+WizardProgress::~WizardProgress()
+{
+ Q_D(WizardProgress);
+
+ QMap<WizardProgressItem *, WizardProgressItem *>::ConstIterator it = d->m_itemToItem.constBegin();
+ QMap<WizardProgressItem *, WizardProgressItem *>::ConstIterator itEnd = d->m_itemToItem.constEnd();
+ while (it != itEnd) {
+ delete it.key();
+ ++it;
+ }
+}
+
+WizardProgressItem *WizardProgress::addItem(const QString &title)
+{
+ Q_D(WizardProgress);
+
+ WizardProgressItem *item = new WizardProgressItem(this, title);
+ d->m_itemToItem.insert(item, item);
+ emit itemAdded(item);
+ return item;
+}
+
+void WizardProgress::removeItem(WizardProgressItem *item)
+{
+ Q_D(WizardProgress);
+
+ QMap<WizardProgressItem *, WizardProgressItem *>::iterator it = d->m_itemToItem.find(item);
+ if (it == d->m_itemToItem.end()) {
+ qWarning("WizardProgress::removePage: Item is not a part of the wizard");
+ return;
+ }
+
+ // remove item from prev items
+ QList<WizardProgressItem *> prevItems = item->d_ptr->m_prevItems;
+ for (int i = 0; i < prevItems.count(); i++) {
+ WizardProgressItem *prevItem = prevItems.at(i);
+ prevItem->d_ptr->m_nextItems.removeOne(item);
+ }
+
+ // remove item from next items
+ QList<WizardProgressItem *> nextItems = item->d_ptr->m_nextItems;
+ for (int i = 0; i < nextItems.count(); i++) {
+ WizardProgressItem *nextItem = nextItems.at(i);
+ nextItem->d_ptr->m_prevItems.removeOne(item);
+ }
+
+ // update history
+ int idx = d->m_visitedItems.indexOf(item);
+ if (idx >= 0)
+ d->m_visitedItems.removeAt(idx);
+
+ // update reachable items
+ d->updateReachableItems();
+
+ emit itemRemoved(item);
+
+ QList<int> pages = item->pages();
+ for (int i = 0; i < pages.count(); i++)
+ d->m_pageToItem.remove(pages.at(i));
+ d->m_itemToItem.erase(it);
+ delete item;
+}
+
+void WizardProgress::removePage(int pageId)
+{
+ Q_D(WizardProgress);
+
+ QMap<int, WizardProgressItem *>::iterator it = d->m_pageToItem.find(pageId);
+ if (it == d->m_pageToItem.end()) {
+ qWarning("WizardProgress::removePage: page is not a part of the wizard");
+ return;
+ }
+ WizardProgressItem *item = it.value();
+ d->m_pageToItem.erase(it);
+ item->d_ptr->m_pages.removeOne(pageId);
+}
+
+QList<int> WizardProgress::pages(WizardProgressItem *item) const
+{
+ return item->pages();
+}
+
+WizardProgressItem *WizardProgress::item(int pageId) const
+{
+ Q_D(const WizardProgress);
+
+ return d->m_pageToItem.value(pageId);
+}
+
+WizardProgressItem *WizardProgress::currentItem() const
+{
+ Q_D(const WizardProgress);
+
+ return d->m_currentItem;
+}
+
+QList<WizardProgressItem *> WizardProgress::items() const
+{
+ Q_D(const WizardProgress);
+
+ return d->m_itemToItem.keys();
+}
+
+WizardProgressItem *WizardProgress::startItem() const
+{
+ Q_D(const WizardProgress);
+
+ return d->m_startItem;
+}
+
+QList<WizardProgressItem *> WizardProgress::visitedItems() const
+{
+ Q_D(const WizardProgress);
+
+ return d->m_visitedItems;
+}
+
+QList<WizardProgressItem *> WizardProgress::directlyReachableItems() const
+{
+ Q_D(const WizardProgress);
+
+ return d->m_reachableItems;
+}
+
+bool WizardProgress::isFinalItemDirectlyReachable() const
+{
+ Q_D(const WizardProgress);
+
+ if (d->m_reachableItems.isEmpty())
+ return false;
+
+ return d->m_reachableItems.last()->isFinalItem();
+}
+
+void WizardProgress::setCurrentPage(int pageId)
+{
+ Q_D(WizardProgress);
+
+ if (pageId < 0) { // reset history
+ d->m_currentItem = 0;
+ d->m_visitedItems.clear();
+ d->m_reachableItems.clear();
+ d->updateReachableItems();
+ return;
+ }
+
+ WizardProgressItem *item = d->m_pageToItem.value(pageId);
+ if (!item) {
+ qWarning("WizardProgress::setCurrentPage: page is not mapped to any wizard progress item");
+ return;
+ }
+
+ if (d->m_currentItem == item) // nothing changes
+ return;
+
+ const bool currentStartItem = !d->m_currentItem && d->m_startItem && d->m_startItem == item;
+
+ // Check if item is reachable with the provided history or with the next items.
+ const QList<WizardProgressItem *> singleItemPath = d->singlePathBetween(d->m_currentItem, item);
+ const int prevItemIndex = d->m_visitedItems.indexOf(item);
+
+ if (singleItemPath.isEmpty() && prevItemIndex < 0 && !currentStartItem) {
+ qWarning("WizardProgress::setCurrentPage: new current item is not directly reachable from the old current item");
+ return;
+ }
+
+ // Update the history accordingly.
+ if (prevItemIndex >= 0) {
+ while (prevItemIndex + 1 < d->m_visitedItems.count())
+ d->m_visitedItems.removeLast();
+ } else {
+ if (!d->m_currentItem && d->m_startItem && !singleItemPath.isEmpty() || currentStartItem)
+ d->m_visitedItems += d->m_startItem;
+ d->m_visitedItems += singleItemPath;
+ }
+
+ d->m_currentItem = item;
+
+ // Update reachable items accordingly.
+ d->updateReachableItems();
+
+ emit currentItemChanged(item);
+}
+
+void WizardProgress::setStartPage(int pageId)
+{
+ Q_D(WizardProgress);
+
+ WizardProgressItem *item = d->m_pageToItem.value(pageId);
+ if (!item) {
+ qWarning("WizardProgress::setStartPage: page is not mapped to any wizard progress item");
+ return;
+ }
+
+ d->m_startItem = item;
+ d->updateReachableItems();
+
+ emit startItemChanged(item);
+}
+
+WizardProgressItem::WizardProgressItem(WizardProgress *progress, const QString &title)
+ : d_ptr(new WizardProgressItemPrivate)
+{
+ d_ptr->q_ptr = this;
+ d_ptr->m_title = title;
+ d_ptr->m_titleWordWrap = false;
+ d_ptr->m_wizardProgress = progress;
+}
+
+WizardProgressItem::~WizardProgressItem()
+{
+
+}
+
+void WizardProgressItem::addPage(int pageId)
+{
+ Q_D(WizardProgressItem);
+
+ if (d->m_wizardProgress->d_ptr->m_pageToItem.contains(pageId)) {
+ qWarning("WizardProgress::addPage: Page is already added to the item");
+ return;
+ }
+ d->m_pages.append(pageId);
+ d->m_wizardProgress->d_ptr->m_pageToItem.insert(pageId, this);
+}
+
+QList<int> WizardProgressItem::pages() const
+{
+ Q_D(const WizardProgressItem);
+
+ return d->m_pages;
+}
+
+void WizardProgressItem::setNextItems(const QList<WizardProgressItem *> &items)
+{
+ Q_D(WizardProgressItem);
+
+ // check if we create cycle
+ for (int i = 0; i < items.count(); i++) {
+ WizardProgressItem *nextItem = items.at(i);
+ if (nextItem == this || d->m_wizardProgress->d_ptr->isNextItem(nextItem, this)) {
+ qWarning("WizardProgress::setNextItems: Setting one of the next items would create a cycle");
+ return;
+ }
+ }
+
+ if (d->m_nextItems == items) // nothing changes
+ return;
+
+ // update prev items (remove this item from the old next items)
+ for (int i = 0; i < d->m_nextItems.count(); i++) {
+ WizardProgressItem *nextItem = d->m_nextItems.at(i);
+ nextItem->d_ptr->m_prevItems.removeOne(this);
+ }
+
+ d->m_nextItems = items;
+
+ // update prev items (add this item to the new next items)
+ for (int i = 0; i < d->m_nextItems.count(); i++) {
+ WizardProgressItem *nextItem = d->m_nextItems.at(i);
+ nextItem->d_ptr->m_prevItems.append(this);
+ }
+ d->m_wizardProgress->d_ptr->updateReachableItems();
+
+ emit d->m_wizardProgress->nextItemsChanged(this, items);
+}
+
+QList<WizardProgressItem *> WizardProgressItem::nextItems() const
+{
+ Q_D(const WizardProgressItem);
+
+ return d->m_nextItems;
+}
+
+bool WizardProgressItem::isFinalItem() const
+{
+ return nextItems().isEmpty();
+}
+
+void WizardProgressItem::setTitle(const QString &title)
+{
+ Q_D(WizardProgressItem);
+
+ d->m_title = title;
+ emit d->m_wizardProgress->itemChanged(this);
+}
+
+QString WizardProgressItem::title() const
+{
+ Q_D(const WizardProgressItem);
+
+ return d->m_title;
+}
+
+void WizardProgressItem::setTitleWordWrap(bool wrap)
+{
+ Q_D(WizardProgressItem);
+
+ d->m_titleWordWrap = wrap;
+ emit d->m_wizardProgress->itemChanged(this);
+}
+
+bool WizardProgressItem::titleWordWrap() const
+{
+ Q_D(const WizardProgressItem);
+
+ return d->m_titleWordWrap;
+}
+
+} // namespace Utils
+
+#include "wizard.moc"
+#include "moc_wizard.cpp"
+