Source code
Revision control
Copy as Markdown
// 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