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

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

trunk: Merged in qt 4.6.1 sources.

File size: 12.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 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 (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 (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 filter(QString());
248}
249
250/*!
251 Creates a new index by querying the help system for
252 keywords for the specified \a customFilterName.
253*/
254void QHelpIndexModel::createIndex(const QString &customFilterName)
255{
256 d->currentFilter = customFilterName;
257 d->indexProvider->collectIndices(customFilterName);
258 emit indexCreationStarted();
259}
260
261void QHelpIndexModel::insertIndices()
262{
263 d->indices = d->indexProvider->indices();
264 d->activeReaders = d->indexProvider->activeReaders();
265 QStringList attributes = d->helpEngine->q->filterAttributes(d->currentFilter);
266 if (attributes.count() > 1) {
267 foreach (QHelpDBReader *r, d->activeReaders)
268 r->createAttributesCache(attributes, d->indexProvider->indexIds(r));
269 }
270 filter(QString());
271 emit indexCreated();
272}
273
274/*!
275 Returns true if the index is currently built up, otherwise
276 false.
277*/
278bool QHelpIndexModel::isCreatingIndex() const
279{
280 return d->indexProvider->isRunning();
281}
282
283/*!
284 Returns all hits found for the \a keyword. A hit consists of
285 the URL and the document title.
286*/
287QMap<QString, QUrl> QHelpIndexModel::linksForKeyword(const QString &keyword) const
288{
289 QMap<QString, QUrl> linkMap;
290 QStringList filterAttributes = d->helpEngine->q->filterAttributes(d->currentFilter);
291 foreach (QHelpDBReader *reader, d->activeReaders)
292 reader->linksForKeyword(keyword, filterAttributes, linkMap);
293 return linkMap;
294}
295
296/*!
297 Filters the indices and returns the model index of the best
298 matching keyword. In a first step, only the keywords containing
299 \a filter are kept in the model's index list. Analogously, if
300 \a wildcard is not empty, only the keywords matched are left
301 in the index list. In a second step, the best match is
302 determined and its index model returned. When specifying a
303 wildcard expression, the \a filter string is used to
304 search for the best match.
305*/
306QModelIndex QHelpIndexModel::filter(const QString &filter, const QString &wildcard)
307{
308 if (filter.isEmpty()) {
309 setStringList(d->indices);
310 return index(-1, 0, QModelIndex());
311 }
312
313 QStringList lst;
314 int goodMatch = -1;
315 int perfectMatch = -1;
316
317 if (!wildcard.isEmpty()) {
318 QRegExp regExp(wildcard, Qt::CaseInsensitive);
319 regExp.setPatternSyntax(QRegExp::Wildcard);
320 foreach (QString index, d->indices) {
321 if (index.contains(regExp)) {
322 lst.append(index);
323 if (perfectMatch == -1 && index.startsWith(filter, Qt::CaseInsensitive)) {
324 if (goodMatch == -1)
325 goodMatch = lst.count()-1;
326 if (filter.length() == index.length()){
327 perfectMatch = lst.count()-1;
328 }
329 } else if (perfectMatch > -1 && index == filter) {
330 perfectMatch = lst.count()-1;
331 }
332 }
333 }
334 } else {
335 foreach (QString index, d->indices) {
336 if (index.contains(filter, Qt::CaseInsensitive)) {
337 lst.append(index);
338 if (perfectMatch == -1 && index.startsWith(filter, Qt::CaseInsensitive)) {
339 if (goodMatch == -1)
340 goodMatch = lst.count()-1;
341 if (filter.length() == index.length()){
342 perfectMatch = lst.count()-1;
343 }
344 } else if (perfectMatch > -1 && index == filter) {
345 perfectMatch = lst.count()-1;
346 }
347 }
348 }
349
350 }
351
352 if (perfectMatch == -1)
353 perfectMatch = qMax(0, goodMatch);
354
355 setStringList(lst);
356 return index(perfectMatch, 0, QModelIndex());
357}
358
359
360
361/*!
362 \class QHelpIndexWidget
363 \inmodule QtHelp
364 \since 4.4
365 \brief The QHelpIndexWidget class provides a list view
366 displaying the QHelpIndexModel.
367*/
368
369/*!
370 \fn void QHelpIndexWidget::linkActivated(const QUrl &link,
371 const QString &keyword)
372
373 This signal is emitted when an item is activated and its
374 associated \a link should be shown. To know where the link
375 belongs to, the \a keyword is given as a second paremeter.
376*/
377
378/*!
379 \fn void QHelpIndexWidget::linksActivated(const QMap<QString, QUrl> &links,
380 const QString &keyword)
381
382 This signal is emitted when the item representing the \a keyword
383 is activated and the item has more than one link associated.
384 The \a links consist of the document title and their URL.
385*/
386
387QHelpIndexWidget::QHelpIndexWidget()
388 : QListView(0)
389{
390 setEditTriggers(QAbstractItemView::NoEditTriggers);
391 setUniformItemSizes(true);
392 connect(this, SIGNAL(activated(QModelIndex)),
393 this, SLOT(showLink(QModelIndex)));
394}
395
396void QHelpIndexWidget::showLink(const QModelIndex &index)
397{
398 if (!index.isValid())
399 return;
400
401 QHelpIndexModel *indexModel = qobject_cast<QHelpIndexModel*>(model());
402 if (!indexModel)
403 return;
404 QVariant v = indexModel->data(index, Qt::DisplayRole);
405 QString name;
406 if (v.isValid())
407 name = v.toString();
408
409 QMap<QString, QUrl> links = indexModel->linksForKeyword(name);
410 if (links.count() == 1) {
411 emit linkActivated(links.constBegin().value(), name);
412 } else if (links.count() > 1) {
413 emit linksActivated(links, name);
414 }
415}
416
417/*!
418 Activates the current item which will result eventually in
419 the emitting of a linkActivated() or linksActivated()
420 signal.
421*/
422void QHelpIndexWidget::activateCurrentItem()
423{
424 showLink(currentIndex());
425}
426
427/*!
428 Filters the indices according to \a filter or \a wildcard.
429 The item with the best match is set as current item.
430
431 \sa QHelpIndexModel::filter()
432*/
433void QHelpIndexWidget::filterIndices(const QString &filter, const QString &wildcard)
434{
435 QHelpIndexModel *indexModel = qobject_cast<QHelpIndexModel*>(model());
436 if (!indexModel)
437 return;
438 QModelIndex idx = indexModel->filter(filter, wildcard);
439 if (idx.isValid())
440 setCurrentIndex(idx);
441}
442
443QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.