source: branches/vendor/trolltech/qt/current/tools/assistant/lib/qhelpindexwidget.cpp @ 2

Last change on this file since 2 was 2, checked in by Dmitry A. Kuminov, 13 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 12.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** This file is part of the Qt Assistant of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file.  Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file.  Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at qt-sales@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qhelpindexwidget.h"
43#include "qhelpenginecore.h"
44#include "qhelpengine_p.h"
45#include "qhelpdbreader_p.h"
46
47#include <QtCore/QThread>
48#include <QtCore/QMutex>
49#include <QtGui/QListView>
50#include <QtGui/QHeaderView>
51
52QT_BEGIN_NAMESPACE
53
54class QHelpIndexProvider : public QThread
55{
56public:
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;
64
65private:
66    void run();
67
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;
74    bool m_abort;
75};
76
77class QHelpIndexModelPrivate
78{
79public:
80    QHelpIndexModelPrivate(QHelpEnginePrivate *hE)
81    {
82        helpEngine = hE;
83        indexProvider = new QHelpIndexProvider(helpEngine);
84        insertedRows = 0;
85    }
86
87    QHelpEnginePrivate *helpEngine;
88    QHelpIndexProvider *indexProvider;
89    QStringList indices;
90    int insertedRows;
91    QString currentFilter;
92    QList<QHelpDBReader*> activeReaders;
93};
94
95static bool caseInsensitiveLessThan(const QString &as, const QString &bs)
96{
97    return QString::compare(as, bs, Qt::CaseInsensitive) < 0;
98}
99
100QHelpIndexProvider::QHelpIndexProvider(QHelpEnginePrivate *helpEngine)
101    : QThread(helpEngine)
102{
103    m_helpEngine = helpEngine;
104    m_abort = false;
105}
106
107QHelpIndexProvider::~QHelpIndexProvider()
108{
109    stopCollecting();
110}
111
112void QHelpIndexProvider::collectIndices(const QString &customFilterName)
113{
114    m_mutex.lock();
115    m_filterAttributes = m_helpEngine->q->filterAttributes(customFilterName);
116    m_mutex.unlock();
117    if (!isRunning()) {
118        start(LowPriority);
119    } else {
120        stopCollecting();
121        start(LowPriority);
122    }
123}
124
125void QHelpIndexProvider::stopCollecting()
126{
127    if (!isRunning())
128        return;
129    m_mutex.lock();
130    m_abort = true;
131    m_mutex.unlock();
132    wait();
133}
134
135QStringList QHelpIndexProvider::indices() const
136{
137    QMutexLocker lck(&m_mutex);
138    return m_indices;
139}
140
141QList<QHelpDBReader*> QHelpIndexProvider::activeReaders() const
142{
143    QMutexLocker lck(&m_mutex);
144    return m_activeReaders;
145}
146
147QSet<int> QHelpIndexProvider::indexIds(QHelpDBReader *reader) const
148{
149    QMutexLocker lck(&m_mutex);
150    if (m_indexIds.contains(reader))
151        return m_indexIds.value(reader);
152    return QSet<int>();
153}
154
155void QHelpIndexProvider::run()
156{
157    m_mutex.lock();
158    QStringList atts = m_filterAttributes;
159    m_indices.clear();
160    m_activeReaders.clear();
161    QSet<QString> indicesSet;
162    m_mutex.unlock();
163
164    foreach (QString dbFileName, m_helpEngine->fileNameReaderMap.keys()) {
165        m_mutex.lock();
166        if (m_abort) {
167            m_abort = false;
168            m_mutex.unlock();
169            return;
170        }
171        m_mutex.unlock();
172        QHelpDBReader reader(dbFileName,
173            QHelpGlobal::uniquifyConnectionName(dbFileName +
174            QLatin1String("FromIndexProvider"),
175            QThread::currentThread()), 0);
176        if (!reader.init())
177            continue;
178        QStringList lst = reader.indicesForFilter(atts);
179        if (!lst.isEmpty()) {
180            m_mutex.lock();
181            foreach (QString s, lst)
182                indicesSet.insert(s);
183            if (m_abort) {
184                m_abort = false;
185                m_mutex.unlock();
186                return;
187            }
188            QHelpDBReader *orgReader = m_helpEngine->fileNameReaderMap.value(dbFileName);
189            m_indexIds.insert(orgReader, reader.indexIds(atts));
190            m_activeReaders.append(orgReader);
191            m_mutex.unlock();
192        }
193    }
194    m_mutex.lock();
195    m_indices = indicesSet.values();
196    qSort(m_indices.begin(), m_indices.end(), caseInsensitiveLessThan);
197    m_abort = false;
198    m_mutex.unlock();
199}
200
201
202
203/*!
204    \class QHelpIndexModel
205    \since 4.4
206    \inmodule QtHelp
207    \brief The QHelpIndexModel class provides a model that
208    supplies index keywords to views.
209
210
211*/
212
213/*!
214    \fn void QHelpIndexModel::indexCreationStarted()
215
216    This signal is emitted when the creation of a new index
217    has started. The current index is invalid from this
218    point on until the signal indexCreated() is emitted.
219
220    \sa isCreatingIndex()
221*/
222
223/*!
224    \fn void QHelpIndexModel::indexCreated()
225
226    This signal is emitted when the index has been created.
227*/
228
229QHelpIndexModel::QHelpIndexModel(QHelpEnginePrivate *helpEngine)
230    : QStringListModel(helpEngine)
231{
232    d = new QHelpIndexModelPrivate(helpEngine);
233
234    connect(d->indexProvider, SIGNAL(finished()), this, SLOT(insertIndices()));
235    connect(helpEngine->q, SIGNAL(setupStarted()), this, SLOT(invalidateIndex()));
236}
237
238QHelpIndexModel::~QHelpIndexModel()
239{
240    delete d;
241}
242
243void QHelpIndexModel::invalidateIndex(bool onShutDown)
244{
245    if (onShutDown)
246        disconnect(this, SLOT(insertIndices()));
247    d->indexProvider->stopCollecting();
248    d->indices.clear();
249    filter(QString());
250}
251
252/*!
253    Creates a new index by querying the help system for
254    keywords for the specified \a customFilterName.
255*/
256void QHelpIndexModel::createIndex(const QString &customFilterName)
257{
258    d->currentFilter = customFilterName;
259    d->indexProvider->collectIndices(customFilterName);
260    emit indexCreationStarted();
261}
262
263void QHelpIndexModel::insertIndices()
264{
265    d->indices = d->indexProvider->indices();
266    d->activeReaders = d->indexProvider->activeReaders();
267    QStringList attributes = d->helpEngine->q->filterAttributes(d->currentFilter);
268    if (attributes.count() > 1) {
269        foreach (QHelpDBReader *r, d->activeReaders)
270            r->createAttributesCache(attributes, d->indexProvider->indexIds(r));
271    }
272    filter(QString());
273    emit indexCreated();
274}
275
276/*!
277    Returns true if the index is currently built up, otherwise
278    false.
279*/
280bool QHelpIndexModel::isCreatingIndex() const
281{
282    return d->indexProvider->isRunning();
283}
284
285/*!
286    Returns all hits found for the \a keyword. A hit consists of
287    the URL and the document title.
288*/
289QMap<QString, QUrl> QHelpIndexModel::linksForKeyword(const QString &keyword) const
290{
291    QMap<QString, QUrl> linkMap;
292    QStringList filterAttributes = d->helpEngine->q->filterAttributes(d->currentFilter);
293    foreach (QHelpDBReader *reader, d->activeReaders)
294        reader->linksForKeyword(keyword, filterAttributes, linkMap);
295    return linkMap;
296}
297
298/*!
299    Filters the indices and returns the model index of the best
300    matching keyword. In a first step, only the keywords containing
301    \a filter are kept in the model's index list. Analogously, if
302    \a wildcard is not empty, only the keywords matched are left
303    in the index list. In a second step, the best match is
304    determined and its index model returned. When specifying a
305    wildcard expression, the \a filter string is used to
306    search for the best match.
307*/
308QModelIndex QHelpIndexModel::filter(const QString &filter, const QString &wildcard)
309{
310    if (filter.isEmpty()) {
311        setStringList(d->indices);
312        return index(-1, 0, QModelIndex());
313    }
314
315    QStringList lst;
316    int goodMatch = -1;
317    int perfectMatch = -1;
318
319    if (!wildcard.isEmpty()) {
320        QRegExp regExp(wildcard, Qt::CaseInsensitive);
321        regExp.setPatternSyntax(QRegExp::Wildcard);
322        foreach (QString index, d->indices) {
323            if (index.contains(regExp)) {
324                lst.append(index);
325                if (perfectMatch == -1 && index.startsWith(filter, Qt::CaseInsensitive)) {
326                    if (goodMatch == -1)
327                        goodMatch = lst.count()-1;
328                    if (filter.length() == index.length()){
329                        perfectMatch = lst.count()-1;
330                    }
331                } else if (perfectMatch > -1 && index == filter) {
332                    perfectMatch = lst.count()-1;
333                }
334            }
335        }
336    } else {
337        foreach (QString index, d->indices) {
338            if (index.contains(filter, Qt::CaseInsensitive)) {
339                lst.append(index);
340                if (perfectMatch == -1 && index.startsWith(filter, Qt::CaseInsensitive)) {
341                    if (goodMatch == -1)
342                        goodMatch = lst.count()-1;
343                    if (filter.length() == index.length()){
344                        perfectMatch = lst.count()-1;
345                    }
346                } else if (perfectMatch > -1 && index == filter) {
347                    perfectMatch = lst.count()-1;
348                }
349            }
350        }
351
352    }
353
354    if (perfectMatch == -1)
355        perfectMatch = qMax(0, goodMatch);
356
357    setStringList(lst);
358    return index(perfectMatch, 0, QModelIndex());
359}
360
361
362
363/*!
364    \class QHelpIndexWidget
365    \inmodule QtHelp
366    \since 4.4
367    \brief The QHelpIndexWidget class provides a list view
368    displaying the QHelpIndexModel.
369*/
370
371/*!
372    \fn void QHelpIndexWidget::linkActivated(const QUrl &link,
373        const QString &keyword)
374
375    This signal is emitted when an item is activated and its
376    associated \a link should be shown. To know where the link
377    belongs to, the \a keyword is given as a second paremeter.
378*/
379
380/*!
381    \fn void QHelpIndexWidget::linksActivated(const QMap<QString, QUrl> &links,
382        const QString &keyword)
383
384    This signal is emitted when the item representing the \a keyword
385    is activated and the item has more than one link associated.
386    The \a links consist of the document title and their URL.
387*/
388
389QHelpIndexWidget::QHelpIndexWidget()
390    : QListView(0)
391{
392    setEditTriggers(QAbstractItemView::NoEditTriggers);
393    setUniformItemSizes(true);
394    connect(this, SIGNAL(activated(const QModelIndex&)),
395        this, SLOT(showLink(const QModelIndex&)));
396}
397
398void QHelpIndexWidget::showLink(const QModelIndex &index)
399{
400    if (!index.isValid())
401        return;
402
403    QHelpIndexModel *indexModel = qobject_cast<QHelpIndexModel*>(model());
404    if (!indexModel)
405        return;
406    QVariant v = indexModel->data(index, Qt::DisplayRole);
407    QString name;
408    if (v.isValid())
409        name = v.toString();
410
411    QMap<QString, QUrl> links = indexModel->linksForKeyword(name);
412    if (links.count() == 1) {
413        emit linkActivated(links.constBegin().value(), name);
414    } else if (links.count() > 1) {
415        emit linksActivated(links, name);
416    }
417}
418
419/*!
420    Activates the current item which will result eventually in
421    the emitting of a linkActivated() or linksActivated()
422    signal.
423*/
424void QHelpIndexWidget::activateCurrentItem()
425{
426    showLink(currentIndex());
427}
428
429/*!
430    Filters the indices according to \a filter or \a wildcard.
431    The item with the best match is set as current item.
432
433    \sa QHelpIndexModel::filter()
434*/
435void QHelpIndexWidget::filterIndices(const QString &filter, const QString &wildcard)
436{
437    QHelpIndexModel *indexModel = qobject_cast<QHelpIndexModel*>(model());
438    if (!indexModel)
439        return;
440    QModelIndex idx = indexModel->filter(filter, wildcard);
441    if (idx.isValid())
442        setCurrentIndex(idx);
443}
444
445QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.