/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the Qt Assistant of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** 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. ** ** In addition, as a special exception, Nokia gives you certain ** additional rights. These rights are described in the Nokia Qt LGPL ** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qhelpcontentwidget.h" #include "qhelpenginecore.h" #include "qhelpengine_p.h" #include "qhelpdbreader_p.h" #include #include #include #include #include QT_BEGIN_NAMESPACE class QHelpContentItemPrivate { public: QHelpContentItemPrivate(const QString &t, const QString &l, QHelpDBReader *r, QHelpContentItem *p) { parent = p; title = t; link = l; helpDBReader = r; } QList childItems; QHelpContentItem *parent; QString title; QString link; QHelpDBReader *helpDBReader; }; class QHelpContentProvider : public QThread { public: QHelpContentProvider(QHelpEnginePrivate *helpEngine); ~QHelpContentProvider(); void collectContents(const QString &customFilterName); void stopCollecting(); QHelpContentItem *rootItem(); int nextChildCount() const; private: void run(); QHelpEnginePrivate *m_helpEngine; QHelpContentItem *m_rootItem; QStringList m_filterAttributes; QQueue m_rootItems; QMutex m_mutex; bool m_abort; }; class QHelpContentModelPrivate { public: QHelpContentItem *rootItem; QHelpContentProvider *qhelpContentProvider; }; /*! \class QHelpContentItem \inmodule QtHelp \brief The QHelpContentItem class provides an item for use with QHelpContentModel. \since 4.4 */ QHelpContentItem::QHelpContentItem(const QString &name, const QString &link, QHelpDBReader *reader, QHelpContentItem *parent) { d = new QHelpContentItemPrivate(name, link, reader, parent); } /*! Destroys the help content item. */ QHelpContentItem::~QHelpContentItem() { qDeleteAll(d->childItems); delete d; } void QHelpContentItem::appendChild(QHelpContentItem *item) { d->childItems.append(item); } /*! Returns the child of the content item in the give \a row. \sa parent() */ QHelpContentItem *QHelpContentItem::child(int row) const { if (row >= childCount()) return 0; return d->childItems.value(row); } /*! Returns the number of child items. */ int QHelpContentItem::childCount() const { return d->childItems.count(); } /*! Returns the row of this item from its parents view. */ int QHelpContentItem::row() const { if (d->parent) return d->parent->d->childItems.indexOf(const_cast(this)); return 0; } /*! Returns the title of the content item. */ QString QHelpContentItem::title() const { return d->title; } /*! Returns the URL of this content item. */ QUrl QHelpContentItem::url() const { return d->helpDBReader->urlOfPath(d->link); } /*! Returns the parent content item. */ QHelpContentItem *QHelpContentItem::parent() const { return d->parent; } /*! Returns the position of a given \a child. */ int QHelpContentItem::childPosition(QHelpContentItem *child) const { return d->childItems.indexOf(child); } QHelpContentProvider::QHelpContentProvider(QHelpEnginePrivate *helpEngine) : QThread(helpEngine) { m_helpEngine = helpEngine; m_rootItem = 0; m_abort = false; } QHelpContentProvider::~QHelpContentProvider() { stopCollecting(); } void QHelpContentProvider::collectContents(const QString &customFilterName) { m_mutex.lock(); m_filterAttributes = m_helpEngine->q->filterAttributes(customFilterName); m_mutex.unlock(); if (!isRunning()) { start(LowPriority); } else { stopCollecting(); start(LowPriority); } } void QHelpContentProvider::stopCollecting() { if (!isRunning()) return; m_mutex.lock(); m_abort = true; m_mutex.unlock(); wait(); } QHelpContentItem *QHelpContentProvider::rootItem() { QMutexLocker locker(&m_mutex); return m_rootItems.dequeue(); } int QHelpContentProvider::nextChildCount() const { return m_rootItems.head()->childCount(); } void QHelpContentProvider::run() { QString title; QString link; int depth = 0; QHelpContentItem *item = 0; m_mutex.lock(); m_rootItem = new QHelpContentItem(QString(), QString(), 0); m_rootItems.enqueue(m_rootItem); QStringList atts = m_filterAttributes; const QStringList fileNames = m_helpEngine->orderedFileNameList; m_mutex.unlock(); foreach (QString dbFileName, fileNames) { m_mutex.lock(); if (m_abort) { m_abort = false; m_mutex.unlock(); break; } m_mutex.unlock(); QHelpDBReader reader(dbFileName, QHelpGlobal::uniquifyConnectionName(dbFileName + QLatin1String("FromQHelpContentProvider"), QThread::currentThread()), 0); if (!reader.init()) continue; foreach (const QByteArray& ba, reader.contentsForFilter(atts)) { if (ba.size() < 1) continue; int _depth = 0; bool _root = false; QStack stack; QDataStream s(ba); for (;;) { s >> depth; s >> link; s >> title; if (title.isEmpty()) break; CHECK_DEPTH: if (depth == 0) { m_mutex.lock(); item = new QHelpContentItem(title, link, m_helpEngine->fileNameReaderMap.value(dbFileName), m_rootItem); m_rootItem->appendChild(item); m_mutex.unlock(); stack.push(item); _depth = 1; _root = true; } else { if (depth > _depth && _root) { _depth = depth; stack.push(item); } if (depth == _depth) { item = new QHelpContentItem(title, link, m_helpEngine->fileNameReaderMap.value(dbFileName), stack.top()); stack.top()->appendChild(item); } else if (depth < _depth) { stack.pop(); --_depth; goto CHECK_DEPTH; } } } } } m_mutex.lock(); m_abort = false; m_mutex.unlock(); } /*! \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(QHelpEnginePrivate *helpEngine) : QAbstractItemModel(helpEngine) { d = new QHelpContentModelPrivate(); d->rootItem = 0; d->qhelpContentProvider = new QHelpContentProvider(helpEngine); connect(d->qhelpContentProvider, SIGNAL(finished()), this, SLOT(insertContents()), Qt::QueuedConnection); connect(helpEngine->q, SIGNAL(setupStarted()), this, SLOT(invalidateContents())); } /*! Destroys the help content model. */ QHelpContentModel::~QHelpContentModel() { delete d->rootItem; delete d; } void QHelpContentModel::invalidateContents(bool onShutDown) { if (onShutDown) disconnect(this, SLOT(insertContents())); d->qhelpContentProvider->stopCollecting(); if (d->rootItem) { delete d->rootItem; d->rootItem = 0; } reset(); } /*! Creates new contents by querying the help system for contents specified for the \a customFilterName. */ void QHelpContentModel::createContents(const QString &customFilterName) { d->qhelpContentProvider->collectContents(customFilterName); emit contentsCreationStarted(); } void QHelpContentModel::insertContents() { int count; if (d->rootItem) { count = d->rootItem->childCount() - 1; beginRemoveRows(QModelIndex(), 0, count > 0 ? count : 0); delete d->rootItem; d->rootItem = 0; endRemoveRows(); } count = d->qhelpContentProvider->nextChildCount() - 1; beginInsertRows(QModelIndex(), 0, count > 0 ? count : 0); d->rootItem = d->qhelpContentProvider->rootItem(); endInsertRows(); reset(); emit contentsCreated(); } /*! Returns true if the contents are currently rebuilt, otherwise false. */ bool QHelpContentModel::isCreatingContents() const { return d->qhelpContentProvider->isRunning(); } /*! Returns the help content item at the model index position \a index. */ QHelpContentItem *QHelpContentModel::contentItemAt(const QModelIndex &index) const { if (index.isValid()) return static_cast(index.internalPointer()); else return d->rootItem; } /*! 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 QModelIndex(); QHelpContentItem *parentItem = contentItemAt(parent); QHelpContentItem *item = parentItem->child(row); if (!item) return QModelIndex(); 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 QModelIndex(); QHelpContentItem *parentItem = static_cast(item->parent()); if (!parentItem) return QModelIndex(); QHelpContentItem *grandparentItem = static_cast(parentItem->parent()); if (!grandparentItem) return QModelIndex(); 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 0; return parentItem->childCount(); } /*! 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) return QVariant(); QHelpContentItem *item = contentItemAt(index); if (!item) return QVariant(); return item->title(); } /*! \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() : QTreeView(0) { header()->hide(); setUniformRowHeights(true); connect(this, SIGNAL(activated(const QModelIndex&)), this, SLOT(showLink(const QModelIndex&))); } /*! 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(model()); if (!contentModel || link.scheme() != QLatin1String("qthelp")) return QModelIndex(); m_syncIndex = QModelIndex(); for (int i=0; irowCount(); ++i) { QHelpContentItem *itm = contentModel->contentItemAt(contentModel->index(i, 0)); if (itm && itm->url().host() == link.host()) { QString path = link.path(); if (path.startsWith(QLatin1Char('/'))) path = path.mid(1); if (searchContentItem(contentModel, contentModel->index(i, 0), path)) { return m_syncIndex; } } } return QModelIndex(); } bool QHelpContentWidget::searchContentItem(QHelpContentModel *model, const QModelIndex &parent, const QString &path) { QHelpContentItem *parentItem = model->contentItemAt(parent); if (!parentItem) return false; if (QDir::cleanPath(parentItem->url().path()) == path) { m_syncIndex = parent; return true; } for (int i=0; ichildCount(); ++i) { if (searchContentItem(model, model->index(i, 0, parent), path)) return true; } return false; } void QHelpContentWidget::showLink(const QModelIndex &index) { QHelpContentModel *contentModel = qobject_cast(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