source: trunk/tools/assistant/lib/qhelpcontentwidget.cpp

Last change on this file 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: 15.2 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 "qhelpcontentwidget.h"
43#include "qhelpenginecore.h"
44#include "qhelpengine_p.h"
45#include "qhelpdbreader_p.h"
46
47#include <QtCore/QDir>
48#include <QtCore/QStack>
49#include <QtCore/QThread>
50#include <QtCore/QMutex>
51#include <QtGui/QHeaderView>
52
53QT_BEGIN_NAMESPACE
54
55class QHelpContentItemPrivate
56{
57public:
58 QHelpContentItemPrivate(const QString &t, const QString &l,
59 QHelpDBReader *r, QHelpContentItem *p)
60 {
61 parent = p;
62 title = t;
63 link = l;
64 helpDBReader = r;
65 }
66
67 QList<QHelpContentItem*> childItems;
68 QHelpContentItem *parent;
69 QString title;
70 QString link;
71 QHelpDBReader *helpDBReader;
72};
73
74class QHelpContentProvider : public QThread
75{
76public:
77 QHelpContentProvider(QHelpEnginePrivate *helpEngine);
78 ~QHelpContentProvider();
79 void collectContents(const QString &customFilterName);
80 void stopCollecting();
81 QHelpContentItem *rootItem();
82 int nextChildCount() const;
83
84private:
85 void run();
86
87 QHelpEnginePrivate *m_helpEngine;
88 QHelpContentItem *m_rootItem;
89 QStringList m_filterAttributes;
90 QQueue<QHelpContentItem*> m_rootItems;
91 QMutex m_mutex;
92 bool m_abort;
93};
94
95class QHelpContentModelPrivate
96{
97public:
98 QHelpContentItem *rootItem;
99 QHelpContentProvider *qhelpContentProvider;
100};
101
102
103
104/*!
105 \class QHelpContentItem
106 \inmodule QtHelp
107 \brief The QHelpContentItem class provides an item for use with QHelpContentModel.
108 \since 4.4
109*/
110
111QHelpContentItem::QHelpContentItem(const QString &name, const QString &link,
112 QHelpDBReader *reader, QHelpContentItem *parent)
113{
114 d = new QHelpContentItemPrivate(name, link, reader, parent);
115}
116
117/*!
118 Destroys the help content item.
119*/
120QHelpContentItem::~QHelpContentItem()
121{
122 qDeleteAll(d->childItems);
123 delete d;
124}
125
126void QHelpContentItem::appendChild(QHelpContentItem *item)
127{
128 d->childItems.append(item);
129}
130
131/*!
132 Returns the child of the content item in the give \a row.
133
134 \sa parent()
135*/
136QHelpContentItem *QHelpContentItem::child(int row) const
137{
138 if (row >= childCount())
139 return 0;
140 return d->childItems.value(row);
141}
142
143/*!
144 Returns the number of child items.
145*/
146int QHelpContentItem::childCount() const
147{
148 return d->childItems.count();
149}
150
151/*!
152 Returns the row of this item from its parents view.
153*/
154int QHelpContentItem::row() const
155{
156 if (d->parent)
157 return d->parent->d->childItems.indexOf(const_cast<QHelpContentItem*>(this));
158 return 0;
159}
160
161/*!
162 Returns the title of the content item.
163*/
164QString QHelpContentItem::title() const
165{
166 return d->title;
167}
168
169/*!
170 Returns the URL of this content item.
171*/
172QUrl QHelpContentItem::url() const
173{
174 return d->helpDBReader->urlOfPath(d->link);
175}
176
177/*!
178 Returns the parent content item.
179*/
180QHelpContentItem *QHelpContentItem::parent() const
181{
182 return d->parent;
183}
184
185/*!
186 Returns the position of a given \a child.
187*/
188int QHelpContentItem::childPosition(QHelpContentItem *child) const
189{
190 return d->childItems.indexOf(child);
191}
192
193
194
195QHelpContentProvider::QHelpContentProvider(QHelpEnginePrivate *helpEngine)
196 : QThread(helpEngine)
197{
198 m_helpEngine = helpEngine;
199 m_rootItem = 0;
200 m_abort = false;
201}
202
203QHelpContentProvider::~QHelpContentProvider()
204{
205 stopCollecting();
206}
207
208void QHelpContentProvider::collectContents(const QString &customFilterName)
209{
210 m_mutex.lock();
211 m_filterAttributes = m_helpEngine->q->filterAttributes(customFilterName);
212 m_mutex.unlock();
213 if (!isRunning()) {
214 start(LowPriority);
215 } else {
216 stopCollecting();
217 start(LowPriority);
218 }
219}
220
221void QHelpContentProvider::stopCollecting()
222{
223 if (!isRunning())
224 return;
225 m_mutex.lock();
226 m_abort = true;
227 m_mutex.unlock();
228 wait();
229}
230
231QHelpContentItem *QHelpContentProvider::rootItem()
232{
233 QMutexLocker locker(&m_mutex);
234 return m_rootItems.dequeue();
235}
236
237int QHelpContentProvider::nextChildCount() const
238{
239 return m_rootItems.head()->childCount();
240}
241
242void QHelpContentProvider::run()
243{
244 QString title;
245 QString link;
246 int depth = 0;
247 QHelpContentItem *item = 0;
248
249 m_mutex.lock();
250 m_rootItem = new QHelpContentItem(QString(), QString(), 0);
251 m_rootItems.enqueue(m_rootItem);
252 QStringList atts = m_filterAttributes;
253 const QStringList fileNames = m_helpEngine->orderedFileNameList;
254 m_mutex.unlock();
255
256 foreach (const QString &dbFileName, fileNames) {
257 m_mutex.lock();
258 if (m_abort) {
259 m_abort = false;
260 m_mutex.unlock();
261 break;
262 }
263 m_mutex.unlock();
264 QHelpDBReader reader(dbFileName,
265 QHelpGlobal::uniquifyConnectionName(dbFileName +
266 QLatin1String("FromQHelpContentProvider"),
267 QThread::currentThread()), 0);
268 if (!reader.init())
269 continue;
270 foreach (const QByteArray& ba, reader.contentsForFilter(atts)) {
271 if (ba.size() < 1)
272 continue;
273
274 int _depth = 0;
275 bool _root = false;
276 QStack<QHelpContentItem*> stack;
277
278 QDataStream s(ba);
279 for (;;) {
280 s >> depth;
281 s >> link;
282 s >> title;
283 if (title.isEmpty())
284 break;
285CHECK_DEPTH:
286 if (depth == 0) {
287 m_mutex.lock();
288 item = new QHelpContentItem(title, link,
289 m_helpEngine->fileNameReaderMap.value(dbFileName), m_rootItem);
290 m_rootItem->appendChild(item);
291 m_mutex.unlock();
292 stack.push(item);
293 _depth = 1;
294 _root = true;
295 } else {
296 if (depth > _depth && _root) {
297 _depth = depth;
298 stack.push(item);
299 }
300 if (depth == _depth) {
301 item = new QHelpContentItem(title, link,
302 m_helpEngine->fileNameReaderMap.value(dbFileName), stack.top());
303 stack.top()->appendChild(item);
304 } else if (depth < _depth) {
305 stack.pop();
306 --_depth;
307 goto CHECK_DEPTH;
308 }
309 }
310 }
311 }
312 }
313 m_mutex.lock();
314 m_abort = false;
315 m_mutex.unlock();
316}
317
318
319
320/*!
321 \class QHelpContentModel
322 \inmodule QtHelp
323 \brief The QHelpContentModel class provides a model that supplies content to views.
324 \since 4.4
325*/
326
327/*!
328 \fn void QHelpContentModel::contentsCreationStarted()
329
330 This signal is emitted when the creation of the contents has
331 started. The current contents are invalid from this point on
332 until the signal contentsCreated() is emitted.
333
334 \sa isCreatingContents()
335*/
336
337/*!
338 \fn void QHelpContentModel::contentsCreated()
339
340 This signal is emitted when the contents have been created.
341*/
342
343QHelpContentModel::QHelpContentModel(QHelpEnginePrivate *helpEngine)
344 : QAbstractItemModel(helpEngine)
345{
346 d = new QHelpContentModelPrivate();
347 d->rootItem = 0;
348 d->qhelpContentProvider = new QHelpContentProvider(helpEngine);
349
350 connect(d->qhelpContentProvider, SIGNAL(finished()),
351 this, SLOT(insertContents()), Qt::QueuedConnection);
352 connect(helpEngine->q, SIGNAL(setupStarted()), this, SLOT(invalidateContents()));
353}
354
355/*!
356 Destroys the help content model.
357*/
358QHelpContentModel::~QHelpContentModel()
359{
360 delete d->rootItem;
361 delete d;
362}
363
364void QHelpContentModel::invalidateContents(bool onShutDown)
365{
366 if (onShutDown)
367 disconnect(this, SLOT(insertContents()));
368 d->qhelpContentProvider->stopCollecting();
369 if (d->rootItem) {
370 delete d->rootItem;
371 d->rootItem = 0;
372 }
373 if (!onShutDown)
374 reset();
375}
376
377/*!
378 Creates new contents by querying the help system
379 for contents specified for the \a customFilterName.
380*/
381void QHelpContentModel::createContents(const QString &customFilterName)
382{
383 d->qhelpContentProvider->collectContents(customFilterName);
384 emit contentsCreationStarted();
385}
386
387void QHelpContentModel::insertContents()
388{
389 int count;
390 if (d->rootItem) {
391 count = d->rootItem->childCount() - 1;
392 beginRemoveRows(QModelIndex(), 0, count > 0 ? count : 0);
393 delete d->rootItem;
394 d->rootItem = 0;
395 endRemoveRows();
396 }
397
398 count = d->qhelpContentProvider->nextChildCount() - 1;
399 beginInsertRows(QModelIndex(), 0, count > 0 ? count : 0);
400 d->rootItem = d->qhelpContentProvider->rootItem();
401 endInsertRows();
402 reset();
403 emit contentsCreated();
404}
405
406/*!
407 Returns true if the contents are currently rebuilt, otherwise
408 false.
409*/
410bool QHelpContentModel::isCreatingContents() const
411{
412 return d->qhelpContentProvider->isRunning();
413}
414
415/*!
416 Returns the help content item at the model index position
417 \a index.
418*/
419QHelpContentItem *QHelpContentModel::contentItemAt(const QModelIndex &index) const
420{
421 if (index.isValid())
422 return static_cast<QHelpContentItem*>(index.internalPointer());
423 else
424 return d->rootItem;
425}
426
427/*!
428 Returns the index of the item in the model specified by
429 the given \a row, \a column and \a parent index.
430*/
431QModelIndex QHelpContentModel::index(int row, int column, const QModelIndex &parent) const
432{
433 if (!d->rootItem)
434 return QModelIndex();
435
436 QHelpContentItem *parentItem = contentItemAt(parent);
437 QHelpContentItem *item = parentItem->child(row);
438 if (!item)
439 return QModelIndex();
440 return createIndex(row, column, item);
441}
442
443/*!
444 Returns the parent of the model item with the given
445 \a index, or QModelIndex() if it has no parent.
446*/
447QModelIndex QHelpContentModel::parent(const QModelIndex &index) const
448{
449 QHelpContentItem *item = contentItemAt(index);
450 if (!item)
451 return QModelIndex();
452
453 QHelpContentItem *parentItem = static_cast<QHelpContentItem*>(item->parent());
454 if (!parentItem)
455 return QModelIndex();
456
457 QHelpContentItem *grandparentItem = static_cast<QHelpContentItem*>(parentItem->parent());
458 if (!grandparentItem)
459 return QModelIndex();
460
461 int row = grandparentItem->childPosition(parentItem);
462 return createIndex(row, index.column(), parentItem);
463}
464
465/*!
466 Returns the number of rows under the given \a parent.
467*/
468int QHelpContentModel::rowCount(const QModelIndex &parent) const
469{
470 QHelpContentItem *parentItem = contentItemAt(parent);
471 if (!parentItem)
472 return 0;
473 return parentItem->childCount();
474}
475
476/*!
477 Returns the number of columns under the given \a parent. Currently returns always 1.
478*/
479int QHelpContentModel::columnCount(const QModelIndex &parent) const
480{
481 Q_UNUSED(parent)
482
483 return 1;
484}
485
486/*!
487 Returns the data stored under the given \a role for
488 the item referred to by the \a index.
489*/
490QVariant QHelpContentModel::data(const QModelIndex &index, int role) const
491{
492 if (role != Qt::DisplayRole)
493 return QVariant();
494
495 QHelpContentItem *item = contentItemAt(index);
496 if (!item)
497 return QVariant();
498 return item->title();
499}
500
501
502
503/*!
504 \class QHelpContentWidget
505 \inmodule QtHelp
506 \brief The QHelpContentWidget class provides a tree view for displaying help content model items.
507 \since 4.4
508*/
509
510/*!
511 \fn void QHelpContentWidget::linkActivated(const QUrl &link)
512
513 This signal is emitted when a content item is activated and
514 its associated \a link should be shown.
515*/
516
517QHelpContentWidget::QHelpContentWidget()
518 : QTreeView(0)
519{
520 header()->hide();
521 setUniformRowHeights(true);
522 connect(this, SIGNAL(activated(QModelIndex)),
523 this, SLOT(showLink(QModelIndex)));
524}
525
526/*!
527 Returns the index of the content item with the \a link.
528 An invalid index is returned if no such an item exists.
529*/
530QModelIndex QHelpContentWidget::indexOf(const QUrl &link)
531{
532 QHelpContentModel *contentModel =
533 qobject_cast<QHelpContentModel*>(model());
534 if (!contentModel || link.scheme() != QLatin1String("qthelp"))
535 return QModelIndex();
536
537 m_syncIndex = QModelIndex();
538 for (int i=0; i<contentModel->rowCount(); ++i) {
539 QHelpContentItem *itm =
540 contentModel->contentItemAt(contentModel->index(i, 0));
541 if (itm && itm->url().host() == link.host()) {
542 QString path = link.path();
543 if (path.startsWith(QLatin1Char('/')))
544 path = path.mid(1);
545 if (searchContentItem(contentModel, contentModel->index(i, 0), path)) {
546 return m_syncIndex;
547 }
548 }
549 }
550 return QModelIndex();
551}
552
553bool QHelpContentWidget::searchContentItem(QHelpContentModel *model,
554 const QModelIndex &parent, const QString &path)
555{
556 QHelpContentItem *parentItem = model->contentItemAt(parent);
557 if (!parentItem)
558 return false;
559
560 if (QDir::cleanPath(parentItem->url().path()) == path) {
561 m_syncIndex = parent;
562 return true;
563 }
564
565 for (int i=0; i<parentItem->childCount(); ++i) {
566 if (searchContentItem(model, model->index(i, 0, parent), path))
567 return true;
568 }
569 return false;
570}
571
572void QHelpContentWidget::showLink(const QModelIndex &index)
573{
574 QHelpContentModel *contentModel = qobject_cast<QHelpContentModel*>(model());
575 if (!contentModel)
576 return;
577
578 QHelpContentItem *item = contentModel->contentItemAt(index);
579 if (!item)
580 return;
581 QUrl url = item->url();
582 if (url.isValid())
583 emit linkActivated(url);
584}
585
586QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.