tools/assistant/lib/qhelpcontentwidget.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Assistant of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    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 
       
    53 QT_BEGIN_NAMESPACE
       
    54 
       
    55 class QHelpContentItemPrivate
       
    56 {
       
    57 public:
       
    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 
       
    74 class QHelpContentProvider : public QThread
       
    75 {
       
    76 public:
       
    77     QHelpContentProvider(QHelpEnginePrivate *helpEngine);
       
    78     ~QHelpContentProvider();
       
    79     void collectContents(const QString &customFilterName);
       
    80     void stopCollecting();
       
    81     QHelpContentItem *rootItem();
       
    82     int nextChildCount() const;
       
    83 
       
    84 private:
       
    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 
       
    95 class QHelpContentModelPrivate
       
    96 {
       
    97 public:
       
    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 
       
   111 QHelpContentItem::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 */
       
   120 QHelpContentItem::~QHelpContentItem()
       
   121 {
       
   122     qDeleteAll(d->childItems);
       
   123     delete d;
       
   124 }
       
   125 
       
   126 void 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 */
       
   136 QHelpContentItem *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 */
       
   146 int QHelpContentItem::childCount() const
       
   147 {
       
   148     return d->childItems.count();
       
   149 }
       
   150 
       
   151 /*!
       
   152     Returns the row of this item from its parents view.
       
   153 */
       
   154 int 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 */
       
   164 QString QHelpContentItem::title() const
       
   165 {
       
   166     return d->title;
       
   167 }
       
   168 
       
   169 /*!
       
   170     Returns the URL of this content item.
       
   171 */
       
   172 QUrl QHelpContentItem::url() const
       
   173 {
       
   174     return d->helpDBReader->urlOfPath(d->link);
       
   175 }
       
   176 
       
   177 /*!
       
   178     Returns the parent content item.
       
   179 */
       
   180 QHelpContentItem *QHelpContentItem::parent() const
       
   181 {
       
   182     return d->parent;
       
   183 }
       
   184 
       
   185 /*!
       
   186     Returns the position of a given \a child.
       
   187 */
       
   188 int QHelpContentItem::childPosition(QHelpContentItem *child) const
       
   189 {
       
   190     return d->childItems.indexOf(child);
       
   191 }
       
   192 
       
   193 
       
   194 
       
   195 QHelpContentProvider::QHelpContentProvider(QHelpEnginePrivate *helpEngine)
       
   196     : QThread(helpEngine)
       
   197 {
       
   198     m_helpEngine = helpEngine;
       
   199     m_rootItem = 0;
       
   200     m_abort = false;
       
   201 }
       
   202 
       
   203 QHelpContentProvider::~QHelpContentProvider()
       
   204 {
       
   205     stopCollecting();
       
   206 }
       
   207 
       
   208 void 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 
       
   221 void QHelpContentProvider::stopCollecting()
       
   222 {
       
   223     if (!isRunning())
       
   224         return;
       
   225     m_mutex.lock();
       
   226     m_abort = true;
       
   227     m_mutex.unlock();
       
   228     wait();
       
   229 }
       
   230 
       
   231 QHelpContentItem *QHelpContentProvider::rootItem()
       
   232 {
       
   233     QMutexLocker locker(&m_mutex);
       
   234     return m_rootItems.dequeue();
       
   235 }
       
   236 
       
   237 int QHelpContentProvider::nextChildCount() const
       
   238 {
       
   239     return m_rootItems.head()->childCount();
       
   240 }
       
   241 
       
   242 void 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;
       
   285 CHECK_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 
       
   343 QHelpContentModel::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 */
       
   358 QHelpContentModel::~QHelpContentModel()
       
   359 {
       
   360     delete d->rootItem;
       
   361     delete d;
       
   362 }
       
   363 
       
   364 void 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 */
       
   380 void QHelpContentModel::createContents(const QString &customFilterName)
       
   381 {
       
   382     d->qhelpContentProvider->collectContents(customFilterName);
       
   383     emit contentsCreationStarted();
       
   384 }
       
   385 
       
   386 void 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 */
       
   409 bool 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 */
       
   418 QHelpContentItem *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 */
       
   430 QModelIndex 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 */
       
   446 QModelIndex 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 */
       
   467 int 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 */
       
   478 int 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 */
       
   489 QVariant 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 
       
   516 QHelpContentWidget::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 */
       
   529 QModelIndex 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 
       
   552 bool 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 
       
   571 void 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 
       
   585 QT_END_NAMESPACE