source: trunk/tools/assistant/lib/qhelpenginecore.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: 22.8 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 "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(QString)),
70 this, SLOT(errorReceived(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(QHelpEngineCore::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 possibility 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 When creating a custom help viewer the viewer can be
180 configured by writing a custom collection file which could contain various
181 keywords to be used to configure the help engine. These keywords and values
182 and their meaning can be found in the help information for
183 \l{assistant-custom-help-viewer.html#creating-a-custom-help-collection-file}
184 {creating a custom help collection file} for Assistant.
185*/
186
187/*!
188 \fn void QHelpEngineCore::setupStarted()
189
190 This signal is emitted when setup is started.
191*/
192
193/*!
194 \fn void QHelpEngineCore::setupFinished()
195
196 This signal is emitted when the setup is complete.
197*/
198
199/*!
200 \fn void QHelpEngineCore::currentFilterChanged(const QString &newFilter)
201
202 This signal is emitted when the current filter is changed to
203 \a newFilter.
204*/
205
206/*!
207 \fn void QHelpEngineCore::warning(const QString &msg)
208
209 This signal is emitted when a non critical error occurs.
210 The warning message is stored in \a msg.
211*/
212
213/*!
214 Constructs a new core help engine with a \a parent. The help engine
215 uses the information stored in the \a collectionFile to provide help.
216 If the collection file does not exist yet, it'll be created.
217*/
218QHelpEngineCore::QHelpEngineCore(const QString &collectionFile, QObject *parent)
219 : QObject(parent)
220{
221 d = new QHelpEngineCorePrivate();
222 d->init(collectionFile, this);
223}
224
225/*!
226 \internal
227*/
228QHelpEngineCore::QHelpEngineCore(QHelpEngineCorePrivate *helpEngineCorePrivate,
229 QObject *parent)
230 : QObject(parent)
231{
232 d = helpEngineCorePrivate;
233}
234
235/*!
236 Destructs the help engine.
237*/
238QHelpEngineCore::~QHelpEngineCore()
239{
240 delete d;
241}
242
243/*!
244 \property QHelpEngineCore::collectionFile
245 \brief the absolute file name of the collection file currently used.
246 \since 4.5
247
248 Setting this property leaves the help engine in an invalid state. It is
249 important to invoke setupData() or any getter function in order to setup
250 the help engine again.
251*/
252QString QHelpEngineCore::collectionFile() const
253{
254 return d->collectionHandler->collectionFile();
255}
256
257void QHelpEngineCore::setCollectionFile(const QString &fileName)
258{
259 if (fileName == collectionFile())
260 return;
261
262 if (d->collectionHandler) {
263 delete d->collectionHandler;
264 d->collectionHandler = 0;
265 d->clearMaps();
266 }
267 d->init(fileName, this);
268 d->needsSetup = true;
269}
270
271/*!
272 Sets up the help engine by processing the information found
273 in the collection file and returns true if successful; otherwise
274 returns false.
275
276 By calling the function, the help
277 engine is forced to initialize itself immediately. Most of
278 the times, this function does not have to be called
279 explicitly because getter functions which depend on a correctly
280 set up help engine do that themselves.
281
282 \note \c{qsqlite4.dll} needs to be deployed with the application as the
283 help system uses the sqlite driver when loading help collections.
284*/
285bool QHelpEngineCore::setupData()
286{
287 d->needsSetup = true;
288 return d->setup();
289}
290
291/*!
292 Creates the file \a fileName and copies all contents from
293 the current collection file into the newly created file,
294 and returns true if successful; otherwise returns false.
295
296 The copying process makes sure that file references to Qt
297 Collection files (\c{.qch}) files are updated accordingly.
298*/
299bool QHelpEngineCore::copyCollectionFile(const QString &fileName)
300{
301 if (!d->setup())
302 return false;
303 return d->collectionHandler->copyCollectionFile(fileName);
304}
305
306/*!
307 Returns the namespace name defined for the Qt compressed help file (.qch)
308 specified by its \a documentationFileName. If the file is not valid, an
309 empty string is returned.
310
311 \sa documentationFileName()
312*/
313QString QHelpEngineCore::namespaceName(const QString &documentationFileName)
314{
315 QHelpDBReader reader(documentationFileName,
316 QHelpGlobal::uniquifyConnectionName(QLatin1String("GetNamespaceName"),
317 QThread::currentThread()), 0);
318 if (reader.init())
319 return reader.namespaceName();
320 return QString();
321}
322
323/*!
324 Registers the Qt compressed help file (.qch) contained in the file
325 \a documentationFileName. One compressed help file, uniquely
326 identified by its namespace can only be registered once.
327 True is returned if the registration was successful, otherwise
328 false.
329
330 \sa unregisterDocumentation(), error()
331*/
332bool QHelpEngineCore::registerDocumentation(const QString &documentationFileName)
333{
334 d->error.clear();
335 d->needsSetup = true;
336 return d->collectionHandler->registerDocumentation(documentationFileName);
337}
338
339/*!
340 Unregisters the Qt compressed help file (.qch) identified by its
341 \a namespaceName from the help collection. Returns true
342 on success, otherwise false.
343
344 \sa registerDocumentation(), error()
345*/
346bool QHelpEngineCore::unregisterDocumentation(const QString &namespaceName)
347{
348 d->error.clear();
349 d->needsSetup = true;
350 return d->collectionHandler->unregisterDocumentation(namespaceName);
351}
352
353/*!
354 Returns the absolute file name of the Qt compressed help file (.qch)
355 identified by the \a namespaceName. If there is no Qt compressed help file
356 with the specified namespace registered, an empty string is returned.
357
358 \sa namespaceName()
359*/
360QString QHelpEngineCore::documentationFileName(const QString &namespaceName)
361{
362 if (d->setup()) {
363 const QHelpCollectionHandler::DocInfoList docList =
364 d->collectionHandler->registeredDocumentations();
365 foreach(const QHelpCollectionHandler::DocInfo &info, docList) {
366 if (info.namespaceName == namespaceName) {
367 if (QDir::isAbsolutePath(info.fileName))
368 return QDir::cleanPath(info.fileName);
369
370 QFileInfo fi(d->collectionHandler->collectionFile());
371 fi.setFile(fi.absolutePath() + QDir::separator() + info.fileName);
372 return QDir::cleanPath(fi.absoluteFilePath());
373 }
374 }
375 }
376 return QString();
377}
378
379/*!
380 Returns a list of all registered Qt compressed help files of the current collection file.
381 The returned names are the namespaces of the registered Qt compressed help files (.qch).
382*/
383QStringList QHelpEngineCore::registeredDocumentations() const
384{
385 QStringList list;
386 if (!d->setup())
387 return list;
388 const QHelpCollectionHandler::DocInfoList docList = d->collectionHandler->registeredDocumentations();
389 foreach(const QHelpCollectionHandler::DocInfo &info, docList) {
390 list.append(info.namespaceName);
391 }
392 return list;
393}
394
395/*!
396 Returns a list of custom filters.
397
398 \sa addCustomFilter(), removeCustomFilter()
399*/
400QStringList QHelpEngineCore::customFilters() const
401{
402 if (!d->setup())
403 return QStringList();
404 return d->collectionHandler->customFilters();
405}
406
407/*!
408 Adds the new custom filter \a filterName. The filter attributes
409 are specified by \a attributes. If the filter already exists,
410 its attribute set is replaced. The function returns true if
411 the operation succeeded, otherwise it returns false.
412
413 \sa customFilters(), removeCustomFilter()
414*/
415bool QHelpEngineCore::addCustomFilter(const QString &filterName,
416 const QStringList &attributes)
417{
418 d->error.clear();
419 d->needsSetup = true;
420 return d->collectionHandler->addCustomFilter(filterName,
421 attributes);
422}
423
424/*!
425 Returns true if the filter \a filterName was removed successfully,
426 otherwise false.
427
428 \sa addCustomFilter(), customFilters()
429*/
430bool QHelpEngineCore::removeCustomFilter(const QString &filterName)
431{
432 d->error.clear();
433 d->needsSetup = true;
434 return d->collectionHandler->removeCustomFilter(filterName);
435}
436
437/*!
438 Returns a list of all defined filter attributes.
439*/
440QStringList QHelpEngineCore::filterAttributes() const
441{
442 if (!d->setup())
443 return QStringList();
444 return d->collectionHandler->filterAttributes();
445}
446
447/*!
448 Returns a list of filter attributes used by the custom
449 filter \a filterName.
450*/
451QStringList QHelpEngineCore::filterAttributes(const QString &filterName) const
452{
453 if (!d->setup())
454 return QStringList();
455 return d->collectionHandler->filterAttributes(filterName);
456}
457
458/*!
459 \property QHelpEngineCore::currentFilter
460 \brief the name of the custom filter currently applied.
461 \since 4.5
462
463 Setting this property will save the new custom filter permanently in the
464 help collection file. To set a custom filter without saving it
465 permanently, disable the auto save filter mode.
466
467 \sa autoSaveFilter()
468*/
469QString QHelpEngineCore::currentFilter() const
470{
471 if (!d->setup())
472 return QString();
473
474 if (d->currentFilter.isEmpty()) {
475 QString filter =
476 d->collectionHandler->customValue(QLatin1String("CurrentFilter"),
477 QString()).toString();
478 if (!filter.isEmpty()
479 && d->collectionHandler->customFilters().contains(filter))
480 d->currentFilter = filter;
481 }
482 return d->currentFilter;
483}
484
485void QHelpEngineCore::setCurrentFilter(const QString &filterName)
486{
487 if (!d->setup() || filterName == d->currentFilter)
488 return;
489 d->currentFilter = filterName;
490 if (d->autoSaveFilter) {
491 d->collectionHandler->setCustomValue(QLatin1String("CurrentFilter"),
492 d->currentFilter);
493 }
494 emit currentFilterChanged(d->currentFilter);
495}
496
497/*!
498 Returns a list of filter attributes for the different filter sections
499 defined in the Qt compressed help file with the given namespace
500 \a namespaceName.
501*/
502QList<QStringList> QHelpEngineCore::filterAttributeSets(const QString &namespaceName) const
503{
504 if (d->setup()) {
505 QHelpDBReader *reader = d->readerMap.value(namespaceName);
506 if (reader)
507 return reader->filterAttributeSets();
508 }
509 return QList<QStringList>();
510}
511
512/*!
513 Returns a list of files contained in the Qt compressed help file \a
514 namespaceName. The files can be filtered by \a filterAttributes as
515 well as by their extension \a extensionFilter (e.g. 'html').
516*/
517QList<QUrl> QHelpEngineCore::files(const QString namespaceName,
518 const QStringList &filterAttributes,
519 const QString &extensionFilter)
520{
521 QList<QUrl> res;
522 if (!d->setup())
523 return res;
524 QHelpDBReader *reader = d->readerMap.value(namespaceName);
525 if (!reader) {
526 d->error = tr("The specified namespace does not exist!");
527 return res;
528 }
529
530 QUrl url;
531 url.setScheme(QLatin1String("qthelp"));
532 url.setAuthority(namespaceName);
533
534 const QStringList files = reader->files(filterAttributes, extensionFilter);
535 foreach (const QString &file, files) {
536 url.setPath(QLatin1String("/") + file);
537 res.append(url);
538 }
539 return res;
540}
541
542/*!
543 Returns an invalid URL if the file \a url cannot be found.
544 If the file exists, either the same url is returned or a
545 different url if the file is located in a different namespace
546 which is merged via a common virtual folder.
547*/
548QUrl QHelpEngineCore::findFile(const QUrl &url) const
549{
550 QUrl res;
551 if (!d->setup() || !url.isValid() || url.toString().count(QLatin1Char('/')) < 4
552 || url.scheme() != QLatin1String("qthelp"))
553 return res;
554
555 QString ns = url.authority();
556 QString filePath = QDir::cleanPath(url.path());
557 if (filePath.startsWith(QLatin1Char('/')))
558 filePath = filePath.mid(1);
559 QString virtualFolder = filePath.mid(0, filePath.indexOf(QLatin1Char('/'), 1));
560 filePath = filePath.mid(virtualFolder.length()+1);
561
562 QHelpDBReader *defaultReader = 0;
563 if (d->readerMap.contains(ns)) {
564 defaultReader = d->readerMap.value(ns);
565 if (defaultReader->fileExists(virtualFolder, filePath))
566 return url;
567 }
568
569 QStringList filterAtts = filterAttributes(currentFilter());
570 foreach (QHelpDBReader *reader, d->virtualFolderMap.values(virtualFolder)) {
571 if (reader == defaultReader)
572 continue;
573 if (reader->fileExists(virtualFolder, filePath, filterAtts)) {
574 res = url;
575 res.setAuthority(reader->namespaceName());
576 return res;
577 }
578 }
579
580 foreach (QHelpDBReader *reader, d->virtualFolderMap.values(virtualFolder)) {
581 if (reader == defaultReader)
582 continue;
583 if (reader->fileExists(virtualFolder, filePath)) {
584 res = url;
585 res.setAuthority(reader->namespaceName());
586 break;
587 }
588 }
589
590 return res;
591}
592
593/*!
594 Returns the data of the file specified by \a url. If the
595 file does not exist, an empty QByteArray is returned.
596
597 \sa findFile()
598*/
599QByteArray QHelpEngineCore::fileData(const QUrl &url) const
600{
601 if (!d->setup() || !url.isValid() || url.toString().count(QLatin1Char('/')) < 4
602 || url.scheme() != QLatin1String("qthelp"))
603 return QByteArray();
604
605 QString ns = url.authority();
606 QString filePath = QDir::cleanPath(url.path());
607 if (filePath.startsWith(QLatin1Char('/')))
608 filePath = filePath.mid(1);
609 QString virtualFolder = filePath.mid(0, filePath.indexOf(QLatin1Char('/'), 1));
610 filePath = filePath.mid(virtualFolder.length()+1);
611
612 QByteArray ba;
613 QHelpDBReader *defaultReader = 0;
614 if (d->readerMap.contains(ns)) {
615 defaultReader = d->readerMap.value(ns);
616 ba = defaultReader->fileData(virtualFolder, filePath);
617 }
618
619 if (ba.isEmpty()) {
620 foreach (QHelpDBReader *reader, d->virtualFolderMap.values(virtualFolder)) {
621 if (reader == defaultReader)
622 continue;
623 ba = reader->fileData(virtualFolder, filePath);
624 if (!ba.isEmpty())
625 return ba;
626 }
627 }
628 return ba;
629}
630
631/*!
632 Returns a map of hits found for the \a id. A hit contains the
633 title of the document and the url where the keyword is located.
634 The result depends on the current filter, meaning only the keywords
635 registered for the current filter will be returned.
636*/
637QMap<QString, QUrl> QHelpEngineCore::linksForIdentifier(const QString &id) const
638{
639 QMap<QString, QUrl> linkMap;
640 if (!d->setup())
641 return linkMap;
642
643 QStringList atts = filterAttributes(d->currentFilter);
644 foreach (QHelpDBReader *reader, d->readerMap)
645 reader->linksForIdentifier(id, atts, linkMap);
646
647 return linkMap;
648}
649
650/*!
651 Removes the \a key from the settings section in the
652 collection file. Returns true if the value was removed
653 successfully, otherwise false.
654
655 \sa customValue(), setCustomValue()
656*/
657bool QHelpEngineCore::removeCustomValue(const QString &key)
658{
659 d->error.clear();
660 return d->collectionHandler->removeCustomValue(key);
661}
662
663/*!
664 Returns the value assigned to the \a key. If the requested
665 key does not exist, the specified \a defaultValue is
666 returned.
667
668 \sa setCustomValue(), removeCustomValue()
669*/
670QVariant QHelpEngineCore::customValue(const QString &key, const QVariant &defaultValue) const
671{
672 if (!d->setup())
673 return QVariant();
674 return d->collectionHandler->customValue(key, defaultValue);
675}
676
677/*!
678 Save the \a value under the \a key. If the key already exist,
679 the value will be overwritten. Returns true if the value was
680 saved successfully, otherwise false.
681
682 \sa customValue(), removeCustomValue()
683*/
684bool QHelpEngineCore::setCustomValue(const QString &key, const QVariant &value)
685{
686 d->error.clear();
687 return d->collectionHandler->setCustomValue(key, value);
688}
689
690/*!
691 Returns the meta data for the Qt compressed help file \a
692 documentationFileName. If there is no data available for
693 \a name, an invalid QVariant() is returned. The meta
694 data is defined when creating the Qt compressed help file and
695 cannot be modified later. Common meta data includes e.g.
696 the author of the documentation.
697*/
698QVariant QHelpEngineCore::metaData(const QString &documentationFileName,
699 const QString &name)
700{
701 QHelpDBReader reader(documentationFileName, QLatin1String("GetMetaData"), 0);
702
703 if (reader.init())
704 return reader.metaData(name);
705 return QVariant();
706}
707
708/*!
709 Returns a description of the last error that occurred.
710*/
711QString QHelpEngineCore::error() const
712{
713 return d->error;
714}
715
716/*!
717 \property QHelpEngineCore::autoSaveFilter
718 \brief whether QHelpEngineCore is in auto save filter mode or not.
719 \since 4.5
720
721 If QHelpEngineCore is in auto save filter mode, the current filter is
722 automatically saved when it is changed by the setCurrentFilter()
723 function. The filter is saved persistently in the help collection file.
724
725 By default, this mode is on.
726*/
727void QHelpEngineCore::setAutoSaveFilter(bool save)
728{
729 d->autoSaveFilter = save;
730}
731
732bool QHelpEngineCore::autoSaveFilter() const
733{
734 return d->autoSaveFilter;
735}
736
737QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.