1 /****************************************************************************
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the Qt Assistant of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qhelpindexwidget.h"
43 #include "qhelpenginecore.h"
44 #include "qhelpengine_p.h"
45 #include "qhelpdbreader_p.h"
47 #include <QtCore/QThread>
48 #include <QtCore/QMutex>
49 #include <QtGui/QListView>
50 #include <QtGui/QHeaderView>
54 class QHelpIndexProvider : public QThread
57 QHelpIndexProvider(QHelpEnginePrivate *helpEngine);
58 ~QHelpIndexProvider();
59 void collectIndices(const QString &customFilterName);
60 void stopCollecting();
61 QStringList indices() const;
62 QList<QHelpDBReader*> activeReaders() const;
63 QSet<int> indexIds(QHelpDBReader *reader) const;
68 QHelpEnginePrivate *m_helpEngine;
69 QStringList m_indices;
70 QList<QHelpDBReader*> m_activeReaders;
71 QMap<QHelpDBReader*, QSet<int> > m_indexIds;
72 QStringList m_filterAttributes;
73 mutable QMutex m_mutex;
77 class QHelpIndexModelPrivate
80 QHelpIndexModelPrivate(QHelpEnginePrivate *hE)
83 indexProvider = new QHelpIndexProvider(helpEngine);
87 QHelpEnginePrivate *helpEngine;
88 QHelpIndexProvider *indexProvider;
91 QString currentFilter;
92 QList<QHelpDBReader*> activeReaders;
95 static bool caseInsensitiveLessThan(const QString &as, const QString &bs)
97 return QString::compare(as, bs, Qt::CaseInsensitive) < 0;
100 QHelpIndexProvider::QHelpIndexProvider(QHelpEnginePrivate *helpEngine)
101 : QThread(helpEngine)
103 m_helpEngine = helpEngine;
107 QHelpIndexProvider::~QHelpIndexProvider()
112 void QHelpIndexProvider::collectIndices(const QString &customFilterName)
115 m_filterAttributes = m_helpEngine->q->filterAttributes(customFilterName);
125 void QHelpIndexProvider::stopCollecting()
136 QStringList QHelpIndexProvider::indices() const
138 QMutexLocker lck(&m_mutex);
142 QList<QHelpDBReader*> QHelpIndexProvider::activeReaders() const
144 QMutexLocker lck(&m_mutex);
145 return m_activeReaders;
148 QSet<int> QHelpIndexProvider::indexIds(QHelpDBReader *reader) const
150 QMutexLocker lck(&m_mutex);
151 if (m_indexIds.contains(reader))
152 return m_indexIds.value(reader);
156 void QHelpIndexProvider::run()
159 QStringList atts = m_filterAttributes;
161 m_activeReaders.clear();
162 QSet<QString> indicesSet;
165 foreach (const QString &dbFileName, m_helpEngine->fileNameReaderMap.keys()) {
172 QHelpDBReader reader(dbFileName,
173 QHelpGlobal::uniquifyConnectionName(dbFileName +
174 QLatin1String("FromIndexProvider"),
175 QThread::currentThread()), 0);
178 QStringList lst = reader.indicesForFilter(atts);
179 if (!lst.isEmpty()) {
181 foreach (const QString &s, lst)
182 indicesSet.insert(s);
187 QHelpDBReader *orgReader = m_helpEngine->fileNameReaderMap.value(dbFileName);
188 m_indexIds.insert(orgReader, reader.indexIds(atts));
189 m_activeReaders.append(orgReader);
194 m_indices = indicesSet.values();
195 qSort(m_indices.begin(), m_indices.end(), caseInsensitiveLessThan);
202 \class QHelpIndexModel
205 \brief The QHelpIndexModel class provides a model that
206 supplies index keywords to views.
212 \fn void QHelpIndexModel::indexCreationStarted()
214 This signal is emitted when the creation of a new index
215 has started. The current index is invalid from this
216 point on until the signal indexCreated() is emitted.
218 \sa isCreatingIndex()
222 \fn void QHelpIndexModel::indexCreated()
224 This signal is emitted when the index has been created.
227 QHelpIndexModel::QHelpIndexModel(QHelpEnginePrivate *helpEngine)
228 : QStringListModel(helpEngine)
230 d = new QHelpIndexModelPrivate(helpEngine);
232 connect(d->indexProvider, SIGNAL(finished()), this, SLOT(insertIndices()));
233 connect(helpEngine->q, SIGNAL(readersAboutToBeInvalidated()), this, SLOT(invalidateIndex()));
236 QHelpIndexModel::~QHelpIndexModel()
241 void QHelpIndexModel::invalidateIndex(bool onShutDown)
244 disconnect(this, SLOT(insertIndices()));
245 d->indexProvider->stopCollecting();
252 Creates a new index by querying the help system for
253 keywords for the specified \a customFilterName.
255 void QHelpIndexModel::createIndex(const QString &customFilterName)
257 d->currentFilter = customFilterName;
258 d->indexProvider->collectIndices(customFilterName);
259 emit indexCreationStarted();
262 void QHelpIndexModel::insertIndices()
264 d->indices = d->indexProvider->indices();
265 d->activeReaders = d->indexProvider->activeReaders();
266 QStringList attributes = d->helpEngine->q->filterAttributes(d->currentFilter);
267 if (attributes.count() > 1) {
268 foreach (QHelpDBReader *r, d->activeReaders)
269 r->createAttributesCache(attributes, d->indexProvider->indexIds(r));
276 Returns true if the index is currently built up, otherwise
279 bool QHelpIndexModel::isCreatingIndex() const
281 return d->indexProvider->isRunning();
285 Returns all hits found for the \a keyword. A hit consists of
286 the URL and the document title.
288 QMap<QString, QUrl> QHelpIndexModel::linksForKeyword(const QString &keyword) const
290 QMap<QString, QUrl> linkMap;
291 QStringList filterAttributes = d->helpEngine->q->filterAttributes(d->currentFilter);
292 foreach (QHelpDBReader *reader, d->activeReaders)
293 reader->linksForKeyword(keyword, filterAttributes, linkMap);
298 Filters the indices and returns the model index of the best
299 matching keyword. In a first step, only the keywords containing
300 \a filter are kept in the model's index list. Analogously, if
301 \a wildcard is not empty, only the keywords matched are left
302 in the index list. In a second step, the best match is
303 determined and its index model returned. When specifying a
304 wildcard expression, the \a filter string is used to
305 search for the best match.
307 QModelIndex QHelpIndexModel::filter(const QString &filter, const QString &wildcard)
309 if (filter.isEmpty()) {
310 setStringList(d->indices);
311 return index(-1, 0, QModelIndex());
316 int perfectMatch = -1;
318 if (!wildcard.isEmpty()) {
319 QRegExp regExp(wildcard, Qt::CaseInsensitive);
320 regExp.setPatternSyntax(QRegExp::Wildcard);
321 foreach (const QString &index, d->indices) {
322 if (index.contains(regExp)) {
324 if (perfectMatch == -1 && index.startsWith(filter, Qt::CaseInsensitive)) {
326 goodMatch = lst.count()-1;
327 if (filter.length() == index.length()){
328 perfectMatch = lst.count()-1;
330 } else if (perfectMatch > -1 && index == filter) {
331 perfectMatch = lst.count()-1;
336 foreach (const QString &index, d->indices) {
337 if (index.contains(filter, Qt::CaseInsensitive)) {
339 if (perfectMatch == -1 && index.startsWith(filter, Qt::CaseInsensitive)) {
341 goodMatch = lst.count()-1;
342 if (filter.length() == index.length()){
343 perfectMatch = lst.count()-1;
345 } else if (perfectMatch > -1 && index == filter) {
346 perfectMatch = lst.count()-1;
353 if (perfectMatch == -1)
354 perfectMatch = qMax(0, goodMatch);
357 return index(perfectMatch, 0, QModelIndex());
363 \class QHelpIndexWidget
366 \brief The QHelpIndexWidget class provides a list view
367 displaying the QHelpIndexModel.
371 \fn void QHelpIndexWidget::linkActivated(const QUrl &link,
372 const QString &keyword)
374 This signal is emitted when an item is activated and its
375 associated \a link should be shown. To know where the link
376 belongs to, the \a keyword is given as a second paremeter.
380 \fn void QHelpIndexWidget::linksActivated(const QMap<QString, QUrl> &links,
381 const QString &keyword)
383 This signal is emitted when the item representing the \a keyword
384 is activated and the item has more than one link associated.
385 The \a links consist of the document title and their URL.
388 QHelpIndexWidget::QHelpIndexWidget()
391 setEditTriggers(QAbstractItemView::NoEditTriggers);
392 setUniformItemSizes(true);
393 connect(this, SIGNAL(activated(QModelIndex)),
394 this, SLOT(showLink(QModelIndex)));
397 void QHelpIndexWidget::showLink(const QModelIndex &index)
399 if (!index.isValid())
402 QHelpIndexModel *indexModel = qobject_cast<QHelpIndexModel*>(model());
405 QVariant v = indexModel->data(index, Qt::DisplayRole);
410 QMap<QString, QUrl> links = indexModel->linksForKeyword(name);
411 if (links.count() == 1) {
412 emit linkActivated(links.constBegin().value(), name);
413 } else if (links.count() > 1) {
414 emit linksActivated(links, name);
419 Activates the current item which will result eventually in
420 the emitting of a linkActivated() or linksActivated()
423 void QHelpIndexWidget::activateCurrentItem()
425 showLink(currentIndex());
429 Filters the indices according to \a filter or \a wildcard.
430 The item with the best match is set as current item.
432 \sa QHelpIndexModel::filter()
434 void QHelpIndexWidget::filterIndices(const QString &filter, const QString &wildcard)
436 QHelpIndexModel *indexModel = qobject_cast<QHelpIndexModel*>(model());
439 QModelIndex idx = indexModel->filter(filter, wildcard);
441 setCurrentIndex(idx);