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

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

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 12.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the Qt Assistant of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this 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 have questions regarding the use of this file, please contact
37** Nokia 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 m_abort = false;
134}
135
136QStringList QHelpIndexProvider::indices() const
137{
138 QMutexLocker lck(&m_mutex);
139 return m_indices;
140}
141
142QList<QHelpDBReader*> QHelpIndexProvider::activeReaders() const
143{
144 QMutexLocker lck(&m_mutex);
145 return m_activeReaders;
146}
147
148QSet<int> QHelpIndexProvider::indexIds(QHelpDBReader *reader) const
149{
150 QMutexLocker lck(&m_mutex);
151 if (m_indexIds.contains(reader))
152 return m_indexIds.value(reader);
153 return QSet<int>();
154}
155
156void QHelpIndexProvider::run()
157{
158 m_mutex.lock();
159 QStringList atts = m_filterAttributes;
160 m_indices.clear();
161 m_activeReaders.clear();
162 QSet<QString> indicesSet;
163 m_mutex.unlock();
164
165 foreach (const QString &dbFileName, m_helpEngine->fileNameReaderMap.keys()) {
166 m_mutex.lock();
167 if (m_abort) {
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 (const QString &s, lst)
182 indicesSet.insert(s);
183 if (m_abort) {
184 m_mutex.unlock();
185 return;
186 }
187 QHelpDBReader *orgReader = m_helpEngine->fileNameReaderMap.value(dbFileName);
188 m_indexIds.insert(orgReader, reader.indexIds(atts));
189 m_activeReaders.append(orgReader);
190 m_mutex.unlock();
191 }
192 }
193 m_mutex.lock();
194 m_indices = indicesSet.values();
195 qSort(m_indices.begin(), m_indices.end(), caseInsensitiveLessThan);
196 m_mutex.unlock();
197}
198
199
200
201/*!
202 \class QHelpIndexModel
203 \since 4.4
204 \inmodule QtHelp
205 \brief The QHelpIndexModel class provides a model that
206 supplies index keywords to views.
207
208
209*/
210
211/*!
212 \fn void QHelpIndexModel::indexCreationStarted()
213
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.
217
218 \sa isCreatingIndex()
219*/
220
221/*!
222 \fn void QHelpIndexModel::indexCreated()
223
224 This signal is emitted when the index has been created.
225*/
226
227QHelpIndexModel::QHelpIndexModel(QHelpEnginePrivate *helpEngine)
228 : QStringListModel(helpEngine)
229{
230 d = new QHelpIndexModelPrivate(helpEngine);
231
232 connect(d->indexProvider, SIGNAL(finished()), this, SLOT(insertIndices()));
233 connect(helpEngine->q, SIGNAL(setupStarted()), this, SLOT(invalidateIndex()));
234}
235
236QHelpIndexModel::~QHelpIndexModel()
237{
238 delete d;
239}
240
241void QHelpIndexModel::invalidateIndex(bool onShutDown)
242{
243 if (onShutDown)
244 disconnect(this, SLOT(insertIndices()));
245 d->indexProvider->stopCollecting();
246 d->indices.clear();
247 if (!onShutDown)
248 filter(QString());
249}
250
251/*!
252 Creates a new index by querying the help system for
253 keywords for the specified \a customFilterName.
254*/
255void QHelpIndexModel::createIndex(const QString &customFilterName)
256{
257 d->currentFilter = customFilterName;
258 d->indexProvider->collectIndices(customFilterName);
259 emit indexCreationStarted();
260}
261
262void QHelpIndexModel::insertIndices()
263{
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));
270 }
271 filter(QString());
272 emit indexCreated();
273}
274
275/*!
276 Returns true if the index is currently built up, otherwise
277 false.
278*/
279bool QHelpIndexModel::isCreatingIndex() const
280{
281 return d->indexProvider->isRunning();
282}
283
284/*!
285 Returns all hits found for the \a keyword. A hit consists of
286 the URL and the document title.
287*/
288QMap<QString, QUrl> QHelpIndexModel::linksForKeyword(const QString &keyword) const
289{
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);
294 return linkMap;
295}
296
297/*!
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.
306*/
307QModelIndex QHelpIndexModel::filter(const QString &filter, const QString &wildcard)
308{
309 if (filter.isEmpty()) {
310 setStringList(d->indices);
311 return index(-1, 0, QModelIndex());
312 }
313
314 QStringList lst;
315 int goodMatch = -1;
316 int perfectMatch = -1;
317
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)) {
323 lst.append(index);
324 if (perfectMatch == -1 && index.startsWith(filter, Qt::CaseInsensitive)) {
325 if (goodMatch == -1)
326 goodMatch = lst.count()-1;
327 if (filter.length() == index.length()){
328 perfectMatch = lst.count()-1;
329 }
330 } else if (perfectMatch > -1 && index == filter) {
331 perfectMatch = lst.count()-1;
332 }
333 }
334 }
335 } else {
336 foreach (const QString &index, d->indices) {
337 if (index.contains(filter, Qt::CaseInsensitive)) {
338 lst.append(index);
339 if (perfectMatch == -1 && index.startsWith(filter, Qt::CaseInsensitive)) {
340 if (goodMatch == -1)
341 goodMatch = lst.count()-1;
342 if (filter.length() == index.length()){
343 perfectMatch = lst.count()-1;
344 }
345 } else if (perfectMatch > -1 && index == filter) {
346 perfectMatch = lst.count()-1;
347 }
348 }
349 }
350
351 }
352
353 if (perfectMatch == -1)
354 perfectMatch = qMax(0, goodMatch);
355
356 setStringList(lst);
357 return index(perfectMatch, 0, QModelIndex());
358}
359
360
361
362/*!
363 \class QHelpIndexWidget
364 \inmodule QtHelp
365 \since 4.4
366 \brief The QHelpIndexWidget class provides a list view
367 displaying the QHelpIndexModel.
368*/
369
370/*!
371 \fn void QHelpIndexWidget::linkActivated(const QUrl &link,
372 const QString &keyword)
373
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.
377*/
378
379/*!
380 \fn void QHelpIndexWidget::linksActivated(const QMap<QString, QUrl> &links,
381 const QString &keyword)
382
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.
386*/
387
388QHelpIndexWidget::QHelpIndexWidget()
389 : QListView(0)
390{
391 setEditTriggers(QAbstractItemView::NoEditTriggers);
392 setUniformItemSizes(true);
393 connect(this, SIGNAL(activated(QModelIndex)),
394 this, SLOT(showLink(QModelIndex)));
395}
396
397void QHelpIndexWidget::showLink(const QModelIndex &index)
398{
399 if (!index.isValid())
400 return;
401
402 QHelpIndexModel *indexModel = qobject_cast<QHelpIndexModel*>(model());
403 if (!indexModel)
404 return;
405 QVariant v = indexModel->data(index, Qt::DisplayRole);
406 QString name;
407 if (v.isValid())
408 name = v.toString();
409
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);
415 }
416}
417
418/*!
419 Activates the current item which will result eventually in
420 the emitting of a linkActivated() or linksActivated()
421 signal.
422*/
423void QHelpIndexWidget::activateCurrentItem()
424{
425 showLink(currentIndex());
426}
427
428/*!
429 Filters the indices according to \a filter or \a wildcard.
430 The item with the best match is set as current item.
431
432 \sa QHelpIndexModel::filter()
433*/
434void QHelpIndexWidget::filterIndices(const QString &filter, const QString &wildcard)
435{
436 QHelpIndexModel *indexModel = qobject_cast<QHelpIndexModel*>(model());
437 if (!indexModel)
438 return;
439 QModelIndex idx = indexModel->filter(filter, wildcard);
440 if (idx.isValid())
441 setCurrentIndex(idx);
442}
443
444QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.