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

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

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

File size: 15.2 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 "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 (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 reset();
374}
375
376/*!
377 Creates new contents by querying the help system
378 for contents specified for the \a customFilterName.
379*/
380void QHelpContentModel::createContents(const QString &customFilterName)
381{
382 d->qhelpContentProvider->collectContents(customFilterName);
383 emit contentsCreationStarted();
384}
385
386void QHelpContentModel::insertContents()
387{
388 int count;
389 if (d->rootItem) {
390 count = d->rootItem->childCount() - 1;
391 beginRemoveRows(QModelIndex(), 0, count > 0 ? count : 0);
392 delete d->rootItem;
393 d->rootItem = 0;
394 endRemoveRows();
395 }
396
397 count = d->qhelpContentProvider->nextChildCount() - 1;
398 beginInsertRows(QModelIndex(), 0, count > 0 ? count : 0);
399 d->rootItem = d->qhelpContentProvider->rootItem();
400 endInsertRows();
401 reset();
402 emit contentsCreated();
403}
404
405/*!
406 Returns true if the contents are currently rebuilt, otherwise
407 false.
408*/
409bool QHelpContentModel::isCreatingContents() const
410{
411 return d->qhelpContentProvider->isRunning();
412}
413
414/*!
415 Returns the help content item at the model index position
416 \a index.
417*/
418QHelpContentItem *QHelpContentModel::contentItemAt(const QModelIndex &index) const
419{
420 if (index.isValid())
421 return static_cast<QHelpContentItem*>(index.internalPointer());
422 else
423 return d->rootItem;
424}
425
426/*!
427 Returns the index of the item in the model specified by
428 the given \a row, \a column and \a parent index.
429*/
430QModelIndex QHelpContentModel::index(int row, int column, const QModelIndex &parent) const
431{
432 if (!d->rootItem)
433 return QModelIndex();
434
435 QHelpContentItem *parentItem = contentItemAt(parent);
436 QHelpContentItem *item = parentItem->child(row);
437 if (!item)
438 return QModelIndex();
439 return createIndex(row, column, item);
440}
441
442/*!
443 Returns the parent of the model item with the given
444 \a index, or QModelIndex() if it has no parent.
445*/
446QModelIndex QHelpContentModel::parent(const QModelIndex &index) const
447{
448 QHelpContentItem *item = contentItemAt(index);
449 if (!item)
450 return QModelIndex();
451
452 QHelpContentItem *parentItem = static_cast<QHelpContentItem*>(item->parent());
453 if (!parentItem)
454 return QModelIndex();
455
456 QHelpContentItem *grandparentItem = static_cast<QHelpContentItem*>(parentItem->parent());
457 if (!grandparentItem)
458 return QModelIndex();
459
460 int row = grandparentItem->childPosition(parentItem);
461 return createIndex(row, index.column(), parentItem);
462}
463
464/*!
465 Returns the number of rows under the given \a parent.
466*/
467int QHelpContentModel::rowCount(const QModelIndex &parent) const
468{
469 QHelpContentItem *parentItem = contentItemAt(parent);
470 if (!parentItem)
471 return 0;
472 return parentItem->childCount();
473}
474
475/*!
476 Returns the number of columns under the given \a parent. Currently returns always 1.
477*/
478int QHelpContentModel::columnCount(const QModelIndex &parent) const
479{
480 Q_UNUSED(parent)
481
482 return 1;
483}
484
485/*!
486 Returns the data stored under the given \a role for
487 the item referred to by the \a index.
488*/
489QVariant QHelpContentModel::data(const QModelIndex &index, int role) const
490{
491 if (role != Qt::DisplayRole)
492 return QVariant();
493
494 QHelpContentItem *item = contentItemAt(index);
495 if (!item)
496 return QVariant();
497 return item->title();
498}
499
500
501
502/*!
503 \class QHelpContentWidget
504 \inmodule QtHelp
505 \brief The QHelpContentWidget class provides a tree view for displaying help content model items.
506 \since 4.4
507*/
508
509/*!
510 \fn void QHelpContentWidget::linkActivated(const QUrl &link)
511
512 This signal is emitted when a content item is activated and
513 its associated \a link should be shown.
514*/
515
516QHelpContentWidget::QHelpContentWidget()
517 : QTreeView(0)
518{
519 header()->hide();
520 setUniformRowHeights(true);
521 connect(this, SIGNAL(activated(const QModelIndex&)),
522 this, SLOT(showLink(const QModelIndex&)));
523}
524
525/*!
526 Returns the index of the content item with the \a link.
527 An invalid index is returned if no such an item exists.
528*/
529QModelIndex QHelpContentWidget::indexOf(const QUrl &link)
530{
531 QHelpContentModel *contentModel =
532 qobject_cast<QHelpContentModel*>(model());
533 if (!contentModel || link.scheme() != QLatin1String("qthelp"))
534 return QModelIndex();
535
536 m_syncIndex = QModelIndex();
537 for (int i=0; i<contentModel->rowCount(); ++i) {
538 QHelpContentItem *itm =
539 contentModel->contentItemAt(contentModel->index(i, 0));
540 if (itm && itm->url().host() == link.host()) {
541 QString path = link.path();
542 if (path.startsWith(QLatin1Char('/')))
543 path = path.mid(1);
544 if (searchContentItem(contentModel, contentModel->index(i, 0), path)) {
545 return m_syncIndex;
546 }
547 }
548 }
549 return QModelIndex();
550}
551
552bool QHelpContentWidget::searchContentItem(QHelpContentModel *model,
553 const QModelIndex &parent, const QString &path)
554{
555 QHelpContentItem *parentItem = model->contentItemAt(parent);
556 if (!parentItem)
557 return false;
558
559 if (QDir::cleanPath(parentItem->url().path()) == path) {
560 m_syncIndex = parent;
561 return true;
562 }
563
564 for (int i=0; i<parentItem->childCount(); ++i) {
565 if (searchContentItem(model, model->index(i, 0, parent), path))
566 return true;
567 }
568 return false;
569}
570
571void QHelpContentWidget::showLink(const QModelIndex &index)
572{
573 QHelpContentModel *contentModel = qobject_cast<QHelpContentModel*>(model());
574 if (!contentModel)
575 return;
576
577 QHelpContentItem *item = contentModel->contentItemAt(index);
578 if (!item)
579 return;
580 QUrl url = item->url();
581 if (url.isValid())
582 emit linkActivated(url);
583}
584
585QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.