Revision control

++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "qhelpcontentwidget.h"
++ #include "qhelpenginecore.h"
++
++ #if QT_CONFIG(future)
++ #include <QtCore/qfuturewatcher.h>
++ #endif
++
++ #include <QtCore/qdir.h>
++ #include <QtWidgets/qheaderview.h>
++
++ QT_BEGIN_NAMESPACE
++
++ using namespace Qt::StringLiterals;
++
++ class QHelpContentModelPrivate
++ {
++ #if QT_CONFIG(future)
++ using ItemFutureProvider = std::function<QFuture<std::shared_ptr<QHelpContentItem>>()>;
++
++ struct WatcherDeleter
++ {
++ void operator()(QFutureWatcherBase *watcher) {
++ watcher->disconnect();
++ watcher->cancel();
++ watcher->waitForFinished();
++ delete watcher;
++ }
++ };
++ #endif
++
++ public:
++ #if QT_CONFIG(future)
++ void createContents(const ItemFutureProvider &futureProvider);
++ #endif
++
++ QHelpContentModel *q = nullptr;
++ QHelpEngineCore *helpEngine = nullptr;
++ std::shared_ptr<QHelpContentItem> rootItem = {};
++ #if QT_CONFIG(future)
++ std::unique_ptr<QFutureWatcher<std::shared_ptr<QHelpContentItem>>, WatcherDeleter> watcher = {};
++ #endif
++ };
++
++ #if QT_CONFIG(future)
++ void QHelpContentModelPrivate::createContents(const ItemFutureProvider &futureProvider)
++ {
++ const bool wasRunning = bool(watcher);
++ watcher.reset(new QFutureWatcher<std::shared_ptr<QHelpContentItem>>);
++ QObject::connect(watcher.get(), &QFutureWatcherBase::finished, q, [this] {
++ if (!watcher->isCanceled()) {
++ const std::shared_ptr<QHelpContentItem> result = watcher->result();
++ if (result && result.get()) {
++ q->beginResetModel();
++ rootItem = result;
++ q->endResetModel();
++ }
++ }
++ watcher.release()->deleteLater();
++ emit q->contentsCreated();
++ });
++ watcher->setFuture(futureProvider());
++
++ if (wasRunning)
++ return;
++
++ if (rootItem) {
++ q->beginResetModel();
++ rootItem.reset();
++ q->endResetModel();
++ }
++ emit q->contentsCreationStarted();
++ }
++ #endif
++
++ /*!
++ \class QHelpContentModel
++ \inmodule QtHelp
++ \brief The QHelpContentModel class provides a model that supplies content to views.
++ \since 4.4
++ */
++
++ /*!
++ \fn void QHelpContentModel::contentsCreationStarted()
++
++ This signal is emitted when the creation of the contents has
++ started. The current contents are invalid from this point on
++ until the signal contentsCreated() is emitted.
++
++ \sa isCreatingContents()
++ */
++
++ /*!
++ \fn void QHelpContentModel::contentsCreated()
++
++ This signal is emitted when the contents have been created.
++ */
++
++ QHelpContentModel::QHelpContentModel(QHelpEngineCore *helpEngine)
++ : QAbstractItemModel(helpEngine)
++ , d(new QHelpContentModelPrivate{this, helpEngine})
++ {}
++
++ /*!
++ Destroys the help content model.
++ */
++ QHelpContentModel::~QHelpContentModel()
++ {
++ delete d;
++ }
++
++ /*!
++ \since 6.8
++
++ Creates new contents by querying the help system for contents specified for the current filter.
++ */
++ void QHelpContentModel::createContentsForCurrentFilter()
++ {
++ #if QT_CONFIG(future)
++ d->createContents([this] { return d->helpEngine->requestContentForCurrentFilter(); });
++ #endif
++ }
++
++ /*!
++ Creates new contents by querying the help system
++ for contents specified for the custom \a filter name.
++ */
++ void QHelpContentModel::createContents(const QString &filter)
++ {
++ #if QT_CONFIG(future)
++ d->createContents([this, filter] { return d->helpEngine->requestContent(filter); });
++ #endif
++ }
++
++ // TODO: Remove me
++ void QHelpContentModel::insertContents()
++ {}
++
++ /*!
++ Returns true if the contents are currently rebuilt, otherwise
++ false.
++ */
++ bool QHelpContentModel::isCreatingContents() const
++ {
++ #if QT_CONFIG(future)
++ return bool(d->watcher);
++ #else
++ return false;
++ #endif
++ }
++
++ /*!
++ Returns the help content item at the model index position
++ \a index.
++ */
++ QHelpContentItem *QHelpContentModel::contentItemAt(const QModelIndex &index) const
++ {
++ return index.isValid() ? static_cast<QHelpContentItem *>(index.internalPointer())
++ : d->rootItem.get();
++ }
++
++ /*!
++ Returns the index of the item in the model specified by
++ the given \a row, \a column and \a parent index.
++ */
++ QModelIndex QHelpContentModel::index(int row, int column, const QModelIndex &parent) const
++ {
++ if (!d->rootItem)
++ return {};
++
++ QHelpContentItem *parentItem = contentItemAt(parent);
++ QHelpContentItem *item = parentItem->child(row);
++ if (!item)
++ return {};
++ return createIndex(row, column, item);
++ }
++
++ /*!
++ Returns the parent of the model item with the given
++ \a index, or QModelIndex() if it has no parent.
++ */
++ QModelIndex QHelpContentModel::parent(const QModelIndex &index) const
++ {
++ QHelpContentItem *item = contentItemAt(index);
++ if (!item)
++ return {};
++
++ QHelpContentItem *parentItem = static_cast<QHelpContentItem*>(item->parent());
++ if (!parentItem)
++ return {};
++
++ QHelpContentItem *grandparentItem = static_cast<QHelpContentItem*>(parentItem->parent());
++ if (!grandparentItem)
++ return {};
++
++ const int row = grandparentItem->childPosition(parentItem);
++ return createIndex(row, index.column(), parentItem);
++ }
++
++ /*!
++ Returns the number of rows under the given \a parent.
++ */
++ int QHelpContentModel::rowCount(const QModelIndex &parent) const
++ {
++ QHelpContentItem *parentItem = contentItemAt(parent);
++ if (parentItem)
++ return parentItem->childCount();
++ return 0;
++ }
++
++ /*!
++ Returns the number of columns under the given \a parent. Currently returns always 1.
++ */
++ int QHelpContentModel::columnCount(const QModelIndex &parent) const
++ {
++ Q_UNUSED(parent);
++ return 1;
++ }
++
++ /*!
++ Returns the data stored under the given \a role for
++ the item referred to by the \a index.
++ */
++ QVariant QHelpContentModel::data(const QModelIndex &index, int role) const
++ {
++ if (role == Qt::DisplayRole) {
++ QHelpContentItem *item = contentItemAt(index);
++ if (item)
++ return item->title();
++ }
++ return {};
++ }
++
++ /*!
++ \class QHelpContentWidget
++ \inmodule QtHelp
++ \brief The QHelpContentWidget class provides a tree view for displaying help content model items.
++ \since 4.4
++ */
++
++ /*!
++ \fn void QHelpContentWidget::linkActivated(const QUrl &link)
++
++ This signal is emitted when a content item is activated and
++ its associated \a link should be shown.
++ */
++
++ QHelpContentWidget::QHelpContentWidget()
++ {
++ header()->hide();
++ setUniformRowHeights(true);
++ connect(this, &QAbstractItemView::activated, this, &QHelpContentWidget::showLink);
++ }
++
++ /*!
++ Returns the index of the content item with the \a link.
++ An invalid index is returned if no such an item exists.
++ */
++ QModelIndex QHelpContentWidget::indexOf(const QUrl &link)
++ {
++ QHelpContentModel *contentModel = qobject_cast<QHelpContentModel*>(model());
++ if (!contentModel || link.scheme() != "qthelp"_L1)
++ return {};
++
++ m_syncIndex = {};
++ for (int i = 0; i < contentModel->rowCount(); ++i) {
++ QHelpContentItem *itm = contentModel->contentItemAt(contentModel->index(i, 0));
++ if (itm && itm->url().host() == link.host()) {
++ if (searchContentItem(contentModel, contentModel->index(i, 0), QDir::cleanPath(link.path())))
++ return m_syncIndex;
++ }
++ }
++ return {};
++ }
++
++ bool QHelpContentWidget::searchContentItem(QHelpContentModel *model, const QModelIndex &parent,
++ const QString &cleanPath)
++ {
++ QHelpContentItem *parentItem = model->contentItemAt(parent);
++ if (!parentItem)
++ return false;
++
++ if (QDir::cleanPath(parentItem->url().path()) == cleanPath) {
++ m_syncIndex = parent;
++ return true;
++ }
++
++ for (int i = 0; i < parentItem->childCount(); ++i) {
++ if (searchContentItem(model, model->index(i, 0, parent), cleanPath))
++ return true;
++ }
++ return false;
++ }
++
++ void QHelpContentWidget::showLink(const QModelIndex &index)
++ {
++ QHelpContentModel *contentModel = qobject_cast<QHelpContentModel*>(model());
++ if (!contentModel)
++ return;
++
++ QHelpContentItem *item = contentModel->contentItemAt(index);
++ if (!item)
++ return;
++ QUrl url = item->url();
++ if (url.isValid())
++ emit linkActivated(url);
++ }
++
++ QT_END_NAMESPACE