source: trunk/tools/assistant/lib/qhelpindexwidget.cpp@ 99

Last change on this file since 99 was 2, checked in by Dmitry A. Kuminov, 16 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 ([email protected])
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 [email protected].
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.