source: branches/vendor/trolltech/qt/current/tools/assistant/lib/qhelpenginecore.cpp @ 2

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

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

File size: 22.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
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 qt-sales@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qhelpenginecore.h"
43#include "qhelpengine_p.h"
44#include "qhelpdbreader_p.h"
45#include "qhelpcollectionhandler_p.h"
46
47#include <QtCore/QDir>
48#include <QtCore/QFile>
49#include <QtCore/QLibrary>
50#include <QtCore/QPluginLoader>
51#include <QtCore/QFileInfo>
52#include <QtCore/QThread>
53#include <QtGui/QApplication>
54#include <QtSql/QSqlQuery>
55
56QT_BEGIN_NAMESPACE
57
58QHelpEngineCorePrivate::QHelpEngineCorePrivate()
59{
60    QHelpGlobal::uniquifyConnectionName(QString(), this);
61    autoSaveFilter = true;
62}
63
64void QHelpEngineCorePrivate::init(const QString &collectionFile,
65                                  QHelpEngineCore *helpEngineCore)
66{
67    q = helpEngineCore;
68    collectionHandler = new QHelpCollectionHandler(collectionFile, helpEngineCore);
69    connect(collectionHandler, SIGNAL(error(const QString&)),
70        this, SLOT(errorReceived(const QString&)));
71    needsSetup = true;
72}
73
74QHelpEngineCorePrivate::~QHelpEngineCorePrivate()
75{
76    delete collectionHandler;
77    clearMaps();
78}
79
80void QHelpEngineCorePrivate::clearMaps()
81{
82    QMap<QString, QHelpDBReader*>::iterator it = readerMap.begin();
83    while (it != readerMap.end()) {
84        delete it.value();
85        ++it;
86    }
87    readerMap.clear();
88    fileNameReaderMap.clear();
89    virtualFolderMap.clear();
90    orderedFileNameList.clear();
91}
92
93bool QHelpEngineCorePrivate::setup()
94{
95    error.clear();
96    if (!needsSetup)
97        return true;
98
99    needsSetup = false;
100    emit q->setupStarted();
101    clearMaps();
102
103    if (!collectionHandler->openCollectionFile()) {
104        emit q->setupFinished();
105        return false;
106    }
107
108    const QHelpCollectionHandler::DocInfoList docList =
109        collectionHandler->registeredDocumentations();
110    QFileInfo fi(collectionHandler->collectionFile());
111    QString absFileName;
112    foreach(const QHelpCollectionHandler::DocInfo &info, docList) {
113        if (QDir::isAbsolutePath(info.fileName)) {
114            absFileName = info.fileName;
115        } else {
116            absFileName = QFileInfo(fi.absolutePath() + QDir::separator() + info.fileName)
117                .absoluteFilePath();
118        }
119        QHelpDBReader *reader = new QHelpDBReader(absFileName,
120            QHelpGlobal::uniquifyConnectionName(info.fileName, this), this);
121        if (!reader->init()) {
122            emit q->warning(tr("Cannot open documentation file %1: %2!")
123                .arg(absFileName, reader->errorMessage()));
124            continue;
125        }
126
127        readerMap.insert(info.namespaceName, reader);
128        fileNameReaderMap.insert(absFileName, reader);
129        virtualFolderMap.insert(info.folderName, reader);
130        orderedFileNameList.append(absFileName);
131    }
132    q->currentFilter();
133    emit q->setupFinished();
134    return true;
135}
136
137void QHelpEngineCorePrivate::errorReceived(const QString &msg)
138{
139    error = msg;
140}
141
142
143
144/*!
145    \class QHelpEngineCore
146    \since 4.4
147    \inmodule QtHelp
148    \brief The QHelpEngineCore class provides the core functionality
149    of the help system.
150
151    Before the help engine can be used, it must be initialized by
152    calling setupData(). At the beginning of the setup process the
153    signal setupStarted() is emitted. From this point on until
154    the signal setupFinished() is emitted, is the help data in an
155    undefined meaning unusable state.
156
157    The core help engine can be used to perform different tasks.
158    By calling linksForIdentifier() the engine returns
159    urls specifying the file locations inside the help system. The
160    actual file data can then be retrived by calling fileData(). In
161    contrast to all other functions in this class, linksForIdentifier()
162    depends on the currently set custom filter. Depending on the filter,
163    the function may return different hits.
164
165    Every help engine can contain any number of custom filters. A custom
166    filter is defined by a name and set of filter attributes and can be
167    added to the help engine by calling addCustomFilter(). Analogous,
168    it is removed by calling removeCustomFilter(). customFilters() returns
169    all defined filters.
170
171    The help engine also offers the possiblity to set and read values
172    in a persistant way comparable to ini files or Windows registry
173    entries. For more information see setValue() or value().
174
175    This class does not offer any GUI components or functionality for
176    indices or contents. If you need one of those use QHelpEngine
177    instead.
178*/
179
180/*!
181    \fn void QHelpEngineCore::setupStarted()
182
183    This signal is emitted when setup is started.
184*/
185
186/*!
187    \fn void QHelpEngineCore::setupFinished()
188
189    This signal is emitted when the setup is complete.
190*/
191
192/*!
193    \fn void QHelpEngineCore::currentFilterChanged(const QString &newFilter)
194
195    This signal is emitted when the current filter is changed to
196    \a newFilter.
197*/
198
199/*!
200    \fn void QHelpEngineCore::warning(const QString &msg)
201
202    This signal is emitted when a non critical error occurs.
203    The warning message is stored in \a msg.
204*/
205
206/*!
207    Constructs a new core help engine with a \a parent. The help engine
208    uses the information stored in the \a collectionFile to provide help.
209    If the collection file does not exist yet, it'll be created.
210*/
211QHelpEngineCore::QHelpEngineCore(const QString &collectionFile, QObject *parent)
212    : QObject(parent)
213{
214    d = new QHelpEngineCorePrivate();
215    d->init(collectionFile, this);
216}
217
218/*!
219    \internal
220*/
221QHelpEngineCore::QHelpEngineCore(QHelpEngineCorePrivate *helpEngineCorePrivate,
222                                 QObject *parent)
223    : QObject(parent)
224{
225    d = helpEngineCorePrivate;
226}
227
228/*!
229    Destructs the help engine.
230*/
231QHelpEngineCore::~QHelpEngineCore()
232{
233    delete d;
234}
235
236/*!
237    \property QHelpEngineCore::collectionFile
238    \brief the absolute file name of the collection file currently used.
239    \since 4.5
240
241    Setting this property leaves the help engine in an invalid state. It is
242    important to invoke setupData() or any getter function in order to setup
243    the help engine again.
244*/
245QString QHelpEngineCore::collectionFile() const
246{
247    return d->collectionHandler->collectionFile();
248}
249
250void QHelpEngineCore::setCollectionFile(const QString &fileName)
251{
252    if (fileName == collectionFile())
253        return;
254
255    if (d->collectionHandler) {
256        delete d->collectionHandler;
257        d->collectionHandler = 0;
258        d->clearMaps();
259    }
260    d->init(fileName, this);
261    d->needsSetup = true;
262}
263
264/*!
265    Sets up the help engine by processing the information found
266    in the collection file and returns true if successful; otherwise
267    returns false.
268
269    By calling the function, the help
270    engine is forced to initialize itself immediately. Most of
271    the times, this function does not have to be called
272    explicitly because getter functions which depend on a correctly
273    set up help engine do that themselves.
274
275    \note \c{qsqlite4.dll} needs to be deployed with the application as the
276    help system uses the sqlite driver when loading help collections.
277*/
278bool QHelpEngineCore::setupData()
279{
280    d->needsSetup = true;
281    return d->setup();
282}
283
284/*!
285    Creates the file \a fileName and copies all contents from
286    the current collection file into the newly created file,
287    and returns true if successful; otherwise returns false.
288
289    The copying process makes sure that file references to Qt
290    Collection files (\c{.qch}) files are updated accordingly.
291*/
292bool QHelpEngineCore::copyCollectionFile(const QString &fileName)
293{
294    if (!d->setup())
295        return false;
296    return d->collectionHandler->copyCollectionFile(fileName);
297}
298
299/*!
300    Returns the namespace name defined for the Qt compressed help file (.qch)
301    specified by its \a documentationFileName. If the file is not valid, an
302    empty string is returned.
303
304    \sa documentationFileName()
305*/
306QString QHelpEngineCore::namespaceName(const QString &documentationFileName)
307{
308    QHelpDBReader reader(documentationFileName,
309        QHelpGlobal::uniquifyConnectionName(QLatin1String("GetNamespaceName"),
310        QThread::currentThread()), 0);
311    if (reader.init())
312        return reader.namespaceName();
313    return QString();
314}
315
316/*!
317    Registers the Qt compressed help file (.qch) contained in the file
318    \a documentationFileName. One compressed help file, uniquely
319    identified by its namespace can only be registered once.
320    True is returned if the registration was successful, otherwise
321    false.
322
323    \sa unregisterDocumentation(), error()
324*/
325bool QHelpEngineCore::registerDocumentation(const QString &documentationFileName)
326{
327    d->error.clear();
328    d->needsSetup = true;
329    return d->collectionHandler->registerDocumentation(documentationFileName);
330}
331
332/*!
333    Unregisters the Qt compressed help file (.qch) identified by its
334    \a namespaceName from the help collection. Returns true
335    on success, otherwise false.
336
337    \sa registerDocumentation(), error()
338*/
339bool QHelpEngineCore::unregisterDocumentation(const QString &namespaceName)
340{
341    d->error.clear();
342    d->needsSetup = true;
343    return d->collectionHandler->unregisterDocumentation(namespaceName);
344}
345
346/*!
347    Returns the absolute file name of the Qt compressed help file (.qch)
348    identified by the \a namespaceName. If there is no Qt compressed help file
349    with the specified namespace registered, an empty string is returned.
350
351    \sa namespaceName()
352*/
353QString QHelpEngineCore::documentationFileName(const QString &namespaceName)
354{
355    QString res;
356    if (!d->setup())
357        return res;
358    const QHelpCollectionHandler::DocInfoList docList = d->collectionHandler->registeredDocumentations();
359    foreach(const QHelpCollectionHandler::DocInfo info, docList) {
360        if (info.namespaceName == namespaceName) {
361            QFileInfo fi(d->collectionHandler->collectionFile());
362            fi.setFile(fi.absolutePath() + QDir::separator() + info.fileName);
363            res = QDir::cleanPath(fi.absoluteFilePath());
364            break;
365        }
366    }
367    return res;
368}
369
370/*!
371    Returns a list of all registered Qt compressed help files of the current collection file.
372    The returned names are the namespaces of the registered Qt compressed help files (.qch).
373*/
374QStringList QHelpEngineCore::registeredDocumentations() const
375{
376    QStringList list;
377    if (!d->setup())
378        return list;
379    const QHelpCollectionHandler::DocInfoList docList = d->collectionHandler->registeredDocumentations();
380    foreach(const QHelpCollectionHandler::DocInfo info, docList) {
381        list.append(info.namespaceName);
382    }
383    return list;
384}
385
386/*!
387    Returns a list of custom filters.
388
389    \sa addCustomFilter(), removeCustomFilter()
390*/
391QStringList QHelpEngineCore::customFilters() const
392{
393    if (!d->setup())
394        return QStringList();
395    return d->collectionHandler->customFilters();
396}
397
398/*!
399    Adds the new custom filter \a filterName. The filter attributes
400    are specified by \a attributes. The function returns false if
401    the filter can not be added, e.g. when the filter already exists.
402
403    \sa customFilters(), removeCustomFilter()
404*/
405bool QHelpEngineCore::addCustomFilter(const QString &filterName,
406                                      const QStringList &attributes)
407{
408    d->error.clear();
409    d->needsSetup = true;
410    return d->collectionHandler->addCustomFilter(filterName,
411        attributes);
412}
413
414/*!
415    Returns true if the filter \a filterName was removed successfully,
416    otherwise false.
417
418    \sa addCustomFilter(), customFilters()
419*/
420bool QHelpEngineCore::removeCustomFilter(const QString &filterName)
421{
422    d->error.clear();
423    d->needsSetup = true;
424    return d->collectionHandler->removeCustomFilter(filterName);
425}
426
427/*!
428    Returns a list of all defined filter attributes.
429*/
430QStringList QHelpEngineCore::filterAttributes() const
431{
432    if (!d->setup())
433        return QStringList();
434    return d->collectionHandler->filterAttributes();
435}
436
437/*!
438    Returns a list of filter attributes used by the custom
439    filter \a filterName.
440*/
441QStringList QHelpEngineCore::filterAttributes(const QString &filterName) const
442{
443    if (!d->setup())
444        return QStringList();
445    return d->collectionHandler->filterAttributes(filterName);
446}
447
448/*!
449    \property QHelpEngineCore::currentFilter
450    \brief the name of the custom filter currently applied.
451    \since 4.5
452
453    Setting this property will save the new custom filter permanently in the
454    help collection file. To set a custom filter without saving it
455    permanently, disable the auto save filter mode.
456
457    \sa autoSaveFilter()
458*/
459QString QHelpEngineCore::currentFilter() const
460{
461    if (!d->setup())
462        return QString();
463
464    if (d->currentFilter.isEmpty()) {
465        QString filter =
466            d->collectionHandler->customValue(QLatin1String("CurrentFilter"),
467                QString()).toString();
468        if (!filter.isEmpty()
469            && d->collectionHandler->customFilters().contains(filter))
470            d->currentFilter = filter;
471    }
472    return d->currentFilter;
473}
474
475void QHelpEngineCore::setCurrentFilter(const QString &filterName)
476{
477    if (!d->setup() || filterName == d->currentFilter)
478        return;
479    d->currentFilter = filterName;
480    if (d->autoSaveFilter) {
481        d->collectionHandler->setCustomValue(QLatin1String("CurrentFilter"),
482            d->currentFilter);
483    }
484    emit currentFilterChanged(d->currentFilter);
485}
486
487/*!
488    Returns a list of filter attributes for the different filter sections
489    defined in the Qt compressed help file with the given namespace
490    \a namespaceName.
491*/
492QList<QStringList> QHelpEngineCore::filterAttributeSets(const QString &namespaceName) const
493{
494    if (d->setup()) {
495        QHelpDBReader *reader = d->readerMap.value(namespaceName);
496        if (reader)
497            return reader->filterAttributeSets();
498    }
499    return QList<QStringList>();
500}
501
502/*!
503    Returns a list of files contained in the Qt compressed help file \a
504    namespaceName. The files can be filtered by \a filterAttributes as
505    well as by their extension \a extensionFilter (e.g. 'html').
506*/
507QList<QUrl> QHelpEngineCore::files(const QString namespaceName,
508                                   const QStringList &filterAttributes,
509                                   const QString &extensionFilter)
510{
511    QList<QUrl> res;
512    if (!d->setup())
513        return res;
514    QHelpDBReader *reader = d->readerMap.value(namespaceName);
515    if (!reader) {
516        d->error = tr("The specified namespace does not exist!");
517        return res;
518    }
519
520    QUrl url;
521    url.setScheme(QLatin1String("qthelp"));
522    url.setAuthority(namespaceName);
523
524    const QStringList files = reader->files(filterAttributes, extensionFilter);
525    foreach (const QString file, files) {
526        url.setPath(QLatin1String("/") + file);
527        res.append(url);
528    }
529    return res;
530}
531
532/*!
533    Returns an invalid URL if the file \a url cannot be found.
534    If the file exists, either the same url is returned or a
535    different url if the file is located in a different namespace
536    which is merged via a common virtual folder.
537*/
538QUrl QHelpEngineCore::findFile(const QUrl &url) const
539{
540    QUrl res;
541    if (!d->setup() || !url.isValid() || url.toString().count(QLatin1Char('/')) < 4
542        || url.scheme() != QLatin1String("qthelp"))
543        return res;
544
545    QString ns = url.authority();
546    QString filePath = QDir::cleanPath(url.path());
547    if (filePath.startsWith(QLatin1Char('/')))
548        filePath = filePath.mid(1);
549    QString virtualFolder = filePath.mid(0, filePath.indexOf(QLatin1Char('/'), 1));
550    filePath = filePath.mid(virtualFolder.length()+1);
551
552    QHelpDBReader *defaultReader = 0;
553    if (d->readerMap.contains(ns)) {
554        defaultReader = d->readerMap.value(ns);
555        if (defaultReader->fileExists(virtualFolder, filePath))
556            return url;
557    }
558
559    QStringList filterAtts = filterAttributes(currentFilter());
560    foreach (QHelpDBReader *reader, d->virtualFolderMap.values(virtualFolder)) {
561        if (reader == defaultReader)
562            continue;
563        if (reader->fileExists(virtualFolder, filePath, filterAtts)) {
564            res = url;
565            res.setAuthority(reader->namespaceName());
566            return res;
567        }
568    }
569
570    foreach (QHelpDBReader *reader, d->virtualFolderMap.values(virtualFolder)) {
571        if (reader == defaultReader)
572            continue;
573        if (reader->fileExists(virtualFolder, filePath)) {
574            res = url;
575            res.setAuthority(reader->namespaceName());
576            break;
577        }
578    }
579
580    return res;
581}
582
583/*!
584    Returns the data of the file specified by \a url. If the
585    file does not exist, an empty QByteArray is returned.
586
587    \sa findFile()
588*/
589QByteArray QHelpEngineCore::fileData(const QUrl &url) const
590{
591    if (!d->setup() || !url.isValid() || url.toString().count(QLatin1Char('/')) < 4
592        || url.scheme() != QLatin1String("qthelp"))
593        return QByteArray();
594
595    QString ns = url.authority();
596    QString filePath = QDir::cleanPath(url.path());
597    if (filePath.startsWith(QLatin1Char('/')))
598        filePath = filePath.mid(1);
599    QString virtualFolder = filePath.mid(0, filePath.indexOf(QLatin1Char('/'), 1));
600    filePath = filePath.mid(virtualFolder.length()+1);
601
602    QByteArray ba;
603    QHelpDBReader *defaultReader = 0;
604    if (d->readerMap.contains(ns)) {
605        defaultReader = d->readerMap.value(ns);
606        ba = defaultReader->fileData(virtualFolder, filePath);
607    }
608
609    if (ba.isEmpty()) {
610        foreach (QHelpDBReader *reader, d->virtualFolderMap.values(virtualFolder)) {
611            if (reader == defaultReader)
612                continue;
613            ba = reader->fileData(virtualFolder, filePath);
614            if (!ba.isEmpty())
615                return ba;
616        }
617    }
618    return ba;
619}
620
621/*!
622    Returns a map of hits found for the \a id. A hit contains the
623    title of the document and the url where the keyword is located.
624    The result depends on the current filter, meaning only the keywords
625    registered for the current filter will be returned.
626*/
627QMap<QString, QUrl> QHelpEngineCore::linksForIdentifier(const QString &id) const
628{
629    QMap<QString, QUrl> linkMap;
630    if (!d->setup())
631        return linkMap;
632
633    QStringList atts = filterAttributes(d->currentFilter);
634    foreach (QHelpDBReader *reader, d->readerMap)
635        reader->linksForIdentifier(id, atts, linkMap);
636
637    return linkMap;
638}
639
640/*!
641    Removes the \a key from the settings section in the
642    collection file. Returns true if the value was removed
643    successfully, otherwise false.
644
645    \sa customValue(), setCustomValue()
646*/
647bool QHelpEngineCore::removeCustomValue(const QString &key)
648{
649    d->error.clear();
650    return d->collectionHandler->removeCustomValue(key);
651}
652
653/*!
654    Returns the value assigned to the \a key. If the requested
655    key does not exist, the specified \a defaultValue is
656    returned.
657
658    \sa setCustomValue(), removeCustomValue()
659*/
660QVariant QHelpEngineCore::customValue(const QString &key, const QVariant &defaultValue) const
661{
662    if (!d->setup())
663        return QVariant();
664    return d->collectionHandler->customValue(key, defaultValue);
665}
666
667/*!
668    Save the \a value under the \a key. If the key already exist,
669    the value will be overwritten. Returns true if the value was
670    saved successfully, otherwise false.
671
672    \sa customValue(), removeCustomValue()
673*/
674bool QHelpEngineCore::setCustomValue(const QString &key, const QVariant &value)
675{
676    d->error.clear();
677    return d->collectionHandler->setCustomValue(key, value);
678}
679
680/*!
681    Returns the meta data for the Qt compressed help file \a
682    documentationFileName. If there is no data available for
683    \a name, an invalid QVariant() is returned. The meta
684    data is defined when creating the Qt compressed help file and
685    cannot be modified later. Common meta data includes e.g.
686    the author of the documentation.
687*/
688QVariant QHelpEngineCore::metaData(const QString &documentationFileName,
689                                   const QString &name)
690{
691    QHelpDBReader reader(documentationFileName, QLatin1String("GetMetaData"), 0);
692
693    if (reader.init())
694        return reader.metaData(name);
695    return QVariant();
696}
697
698/*!
699    Returns a description of the last error that occured.
700*/
701QString QHelpEngineCore::error() const
702{
703    return d->error;
704}
705
706/*!
707    \property QHelpEngineCore::autoSaveFilter
708    \brief whether QHelpEngineCore is in auto save filter mode or not.
709    \since 4.5
710
711    If QHelpEngineCore is in auto save filter mode, the current filter is
712    automatically saved when it is changed by the setCurrentFilter()
713    function. The filter is saved persistently in the help collection file.
714
715    By default, this mode is on.
716*/
717void QHelpEngineCore::setAutoSaveFilter(bool save)
718{
719    d->autoSaveFilter = save;
720}
721
722bool QHelpEngineCore::autoSaveFilter() const
723{
724    return d->autoSaveFilter;
725}
726
727QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.