/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ****************************************************************************/ #include "wizard.h" #include "algorithm.h" #include "hostosinfo.h" #include "qtcassert.h" #include "wizardpage.h" #include #include #include #include #include #include #include #include #include #include #include /*! \class Utils::Wizard \brief The Wizard class implements a wizard with a progress bar on the left. Informs the user about the progress. */ namespace Utils { class ProgressItemWidget : public QWidget { Q_OBJECT public: ProgressItemWidget(const QPixmap &indicatorPixmap, const QString &title, QWidget *parent = nullptr) : 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); auto l = new QHBoxLayout(this); l->setContentsMargins(0, 0, 0, 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 = nullptr); private: void slotItemAdded(WizardProgressItem *item); void slotItemRemoved(WizardProgressItem *item); void slotItemChanged(WizardProgressItem *item); void slotNextItemsChanged(WizardProgressItem *item, const QList &nextItems); void slotNextShownItemChanged(WizardProgressItem *item, WizardProgressItem *nextItem); void slotStartItemChanged(WizardProgressItem *item); void slotCurrentItemChanged(WizardProgressItem *item); void recreateLayout(); void updateProgress(); void disableUpdates(); void enableUpdates(); QVBoxLayout *m_mainLayout; QVBoxLayout *m_itemWidgetLayout; WizardProgress *m_wizardProgress; QMap m_itemToItemWidget; QMap m_itemWidgetToItem; QList m_visibleItems; ProgressItemWidget *m_dotsItemWidget; int m_disableUpdatesCount; QPixmap m_indicatorPixmap; }; LinearProgressWidget::LinearProgressWidget(WizardProgress *progress, QWidget *parent) : QWidget(parent), m_dotsItemWidget(nullptr), m_disableUpdatesCount(0) { m_indicatorPixmap = QIcon::fromTheme(QLatin1String("go-next"), QIcon(QLatin1String(":/utils/images/arrow.png"))).pixmap(16); m_wizardProgress = progress; m_mainLayout = new QVBoxLayout(this); m_itemWidgetLayout = new QVBoxLayout(); auto 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, &WizardProgress::itemAdded, this, &LinearProgressWidget::slotItemAdded); connect(m_wizardProgress, &WizardProgress::itemRemoved, this, &LinearProgressWidget::slotItemRemoved); connect(m_wizardProgress, &WizardProgress::itemChanged, this, &LinearProgressWidget::slotItemChanged); connect(m_wizardProgress, &WizardProgress::nextItemsChanged, this, &LinearProgressWidget::slotNextItemsChanged); connect(m_wizardProgress, &WizardProgress::nextShownItemChanged, this, &LinearProgressWidget::slotNextShownItemChanged); connect(m_wizardProgress, &WizardProgress::startItemChanged, this, &LinearProgressWidget::slotStartItemChanged); connect(m_wizardProgress, &WizardProgress::currentItemChanged, this, &LinearProgressWidget::slotCurrentItemChanged); QList 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 &nextItems) { Q_UNUSED(nextItems) if (m_visibleItems.contains(item)) recreateLayout(); } void LinearProgressWidget::slotNextShownItemChanged(WizardProgressItem *item, WizardProgressItem *nextItem) { Q_UNUSED(nextItem) 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::ConstIterator it = m_itemToItemWidget.constBegin(); QMap::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 visitedItems = m_wizardProgress->visitedItems(); QMap::ConstIterator it = m_itemToItemWidget.constBegin(); QMap::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 { public: bool m_automaticProgressCreation = true; WizardProgress *m_wizardProgress = nullptr; QSet m_fieldNames; }; Wizard::Wizard(QWidget *parent, Qt::WindowFlags flags) : QWizard(parent, flags), d_ptr(new WizardPrivate) { d_ptr->m_wizardProgress = new WizardProgress(this); connect(this, &QWizard::currentIdChanged, this, &Wizard::_q_currentPageChanged); connect(this, &QWizard::pageAdded, this, &Wizard::_q_pageAdded); connect(this, &QWizard::pageRemoved, this, &Wizard::_q_pageRemoved); setSideWidget(new LinearProgressWidget(d_ptr->m_wizardProgress, this)); setOption(QWizard::NoCancelButton, false); setOption(QWizard::NoDefaultButton, false); setOption(QWizard::NoBackButtonOnStartPage, true); if (!Utils::creatorTheme()->preferredStyles().isEmpty()) setWizardStyle(QWizard::ModernStyle); if (HostOsInfo::isMacHost()) { setButtonLayout(QList() << QWizard::CancelButton << QWizard::Stretch << QWizard::BackButton << QWizard::NextButton << QWizard::CommitButton << QWizard::FinishButton); } } Wizard::~Wizard() { delete d_ptr; } 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; } bool Wizard::hasField(const QString &name) const { return d_ptr->m_fieldNames.contains(name); } void Wizard::registerFieldName(const QString &name) { QTC_ASSERT(!hasField(name), return); d_ptr->m_fieldNames.insert(name); } QSet Wizard::fieldNames() const { return d_ptr->m_fieldNames; } QHash Wizard::variables() const { QHash result; const QSet fields = fieldNames(); for (const QString &f : fields) result.insert(f, field(f)); return result; } QString typeOf(const QVariant &v) { QString result; switch (v.type()) { case QVariant::Map: result = QLatin1String("Object"); break; default: result = QLatin1String(v.typeName()); } return result; } void Wizard::showVariables() { QString result = QLatin1String("\n \n"); QHash vars = variables(); QList keys = vars.keys(); sort(keys); for (const QString &key : qAsConst(keys)) { const QVariant &v = vars.value(key); result += QLatin1String(" \n"); } result += QLatin1String("
KeyTypeValueEval
") + key + QLatin1String("") + typeOf(v) + QLatin1String("") + stringify(v) + QLatin1String("") + evaluate(v) + QLatin1String("
"); auto dialog = new QDialog(this); dialog->setMinimumSize(800, 600); auto layout = new QVBoxLayout(dialog); auto scrollArea = new QScrollArea; auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok, Qt::Horizontal); auto label = new QLabel(result); label->setWordWrap(true); label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); scrollArea->setWidget(label); scrollArea->setWidgetResizable(true); layout->addWidget(scrollArea); layout->addWidget(buttons); connect(buttons, &QDialogButtonBox::accepted, dialog, &QDialog::accept); connect(dialog, &QDialog::finished, dialog, &QObject::deleteLater); dialog->show(); } QString Wizard::stringify(const QVariant &v) const { return v.toString(); } QString Wizard::evaluate(const QVariant &v) const { return stringify(v); } bool Wizard::event(QEvent *event) { if (event->type() == QEvent::ShortcutOverride) { auto ke = static_cast(event); if (ke->key() == Qt::Key_Escape && !ke->modifiers()) { ke->accept(); return true; } } return QWizard::event(event); } void Wizard::_q_currentPageChanged(int pageId) { Q_D(Wizard); d->m_wizardProgress->setCurrentPage(pageId); } void Wizard::_q_pageAdded(int pageId) { Q_D(Wizard); QWizardPage *p = page(pageId); auto wp = qobject_cast(p); if (wp) wp->pageWasAdded(); if (!d->m_automaticProgressCreation) return; QVariant property = p->property(SHORT_TITLE_PROPERTY); const QString title = property.isNull() ? p->title() : property.toString(); WizardProgressItem *item = d->m_wizardProgress->addItem(title); item->addPage(pageId); d->m_wizardProgress->setStartPage(startId()); if (!d->m_wizardProgress->startItem()) return; QList 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 = nullptr; WizardProgressItem *nextItem = nullptr; if (prevId >= 0) prevItem = d->m_wizardProgress->item(prevId); if (nextId >= 0) nextItem = d->m_wizardProgress->item(nextId); if (prevItem) prevItem->setNextItems({item}); if (nextItem) item->setNextItems({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 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 = nullptr; WizardProgressItem *nextItem = nullptr; if (prevId >= 0) prevItem = d->m_wizardProgress->item(prevId); if (nextId >= 0) nextItem = d->m_wizardProgress->item(nextId); if (prevItem && nextItem) { QList 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() = default; bool isNextItem(WizardProgressItem *item, WizardProgressItem *nextItem) const; // if multiple paths are possible the empty list is returned QList singlePathBetween(WizardProgressItem *fromItem, WizardProgressItem *toItem) const; void updateReachableItems(); QMap m_pageToItem; QMap m_itemToItem; QList m_items; QList m_visitedItems; QList m_reachableItems; WizardProgressItem *m_currentItem = nullptr; WizardProgressItem *m_startItem = nullptr; }; class WizardProgressItemPrivate { WizardProgressItem *q_ptr; Q_DECLARE_PUBLIC(WizardProgressItem) public: QString m_title; bool m_titleWordWrap; WizardProgress *m_wizardProgress; QList m_pages; QList m_nextItems; QList m_prevItems; WizardProgressItem *m_nextShownItem; }; bool WizardProgressPrivate::isNextItem(WizardProgressItem *item, WizardProgressItem *nextItem) const { QHash visitedItems; QList 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 WizardProgressPrivate::singlePathBetween(WizardProgressItem *fromItem, WizardProgressItem *toItem) const { WizardProgressItem *item = fromItem; if (!item) item = m_startItem; if (!item) return QList(); // 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 {toItem}; QHash > visitedItemsToParents; QList > workingItems; // next to prev item QList items = item->nextItems(); for (int i = 0; i < items.count(); i++) workingItems.append(qMakePair(items.at(i), item)); while (!workingItems.isEmpty()) { QPair workingItem = workingItems.takeFirst(); QHash &parents = visitedItemsToParents[workingItem.first]; parents.insert(workingItem.second, true); if (parents.count() > 1) continue; QList items = workingItem.first->nextItems(); for (int i = 0; i < items.count(); i++) workingItems.append(qMakePair(items.at(i), workingItem.first)); } QList path; WizardProgressItem *it = toItem; QHash >::ConstIterator itItem = visitedItemsToParents.constFind(it); QHash >::ConstIterator itEnd = visitedItemsToParents.constEnd(); while (itItem != itEnd) { path.prepend(itItem.key()); if (itItem.value().count() != 1) return QList(); it = itItem.value().constBegin().key(); if (it == item) return path; itItem = visitedItemsToParents.constFind(it); } return {}; } void WizardProgressPrivate::updateReachableItems() { m_reachableItems = m_visitedItems; WizardProgressItem *item = nullptr; if (m_visitedItems.count() > 0) item = m_visitedItems.last(); if (!item) { item = m_startItem; m_reachableItems.append(item); } if (!item) return; while (item->nextShownItem()) { item = item->nextShownItem(); 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::ConstIterator it = d->m_itemToItem.constBegin(); QMap::ConstIterator itEnd = d->m_itemToItem.constEnd(); while (it != itEnd) { delete it.key(); ++it; } delete d_ptr; } WizardProgressItem *WizardProgress::addItem(const QString &title) { Q_D(WizardProgress); auto 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::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 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 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 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::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 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 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 WizardProgress::visitedItems() const { Q_D(const WizardProgress); return d->m_visitedItems; } QList 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 = nullptr; 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 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; d_ptr->m_nextShownItem = nullptr; } WizardProgressItem::~WizardProgressItem() { delete d_ptr; } 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 WizardProgressItem::pages() const { Q_D(const WizardProgressItem); return d->m_pages; } void WizardProgressItem::setNextItems(const QList &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; if (!items.contains(d->m_nextShownItem)) setNextShownItem(nullptr); // 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); if (items.count() == 1) setNextShownItem(items.first()); } QList WizardProgressItem::nextItems() const { Q_D(const WizardProgressItem); return d->m_nextItems; } void WizardProgressItem::setNextShownItem(WizardProgressItem *item) { Q_D(WizardProgressItem); if (d->m_nextShownItem == item) // nothing changes return; if (item && !d->m_nextItems.contains(item)) // the "item" is not a one of next items return; d->m_nextShownItem = item; d->m_wizardProgress->d_ptr->updateReachableItems(); emit d->m_wizardProgress->nextShownItemChanged(this, item); } WizardProgressItem *WizardProgressItem::nextShownItem() const { Q_D(const WizardProgressItem); return d->m_nextShownItem; } 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"