source: trunk/tools/assistant/lib/qhelpenginecore.cpp@ 801

Last change on this file since 801 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

File size: 22.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 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(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. The function returns false if
410 the filter can not be added, e.g. when the filter already exists.
411
412 \sa customFilters(), removeCustomFilter()
413*/
414bool QHelpEngineCore::addCustomFilter(const QString &filterName,
415 const QStringList &attributes)
416{
417 d->error.clear();
418 d->needsSetup = true;
419 return d->collectionHandler->addCustomFilter(filterName,
420 attributes);
421}
422
423/*!
424 Returns true if the filter \a filterName was removed successfully,
425 otherwise false.
426
427 \sa addCustomFilter(), customFilters()
428*/
429bool QHelpEngineCore::removeCustomFilter(const QString &filterName)
430{
431 d->error.clear();
432 d->needsSetup = true;
433 return d->collectionHandler->removeCustomFilter(filterName);
434}
435
436/*!
437 Returns a list of all defined filter attributes.
438*/
439QStringList QHelpEngineCore::filterAttributes() const
440{
441 if (!d->setup())
442 return QStringList();
443 return d->collectionHandler->filterAttributes();
444}
445
446/*!
447 Returns a list of filter attributes used by the custom
448 filter \a filterName.
449*/
450QStringList QHelpEngineCore::filterAttributes(const QString &filterName) const
451{
452 if (!d->setup())
453 return QStringList();
454 return d->collectionHandler->filterAttributes(filterName);
455}
456
457/*!
458 \property QHelpEngineCore::currentFilter
459 \brief the name of the custom filter currently applied.
460 \since 4.5
461
462 Setting this property will save the new custom filter permanently in the
463 help collection file. To set a custom filter without saving it
464 permanently, disable the auto save filter mode.
465
466 \sa autoSaveFilter()
467*/
468QString QHelpEngineCore::currentFilter() const
469{
470 if (!d->setup())
471 return QString();
472
473 if (d->currentFilter.isEmpty()) {
474 QString filter =
475 d->collectionHandler->customValue(QLatin1String("CurrentFilter"),
476 QString()).toString();
477 if (!filter.isEmpty()
478 && d->collectionHandler->customFilters().contains(filter))
479 d->currentFilter = filter;
480 }
481 return d->currentFilter;
482}
483
484void QHelpEngineCore::setCurrentFilter(const QString &filterName)
485{
486 if (!d->setup() || filterName == d->currentFilter)
487 return;
488 d->currentFilter = filterName;
489 if (d->autoSaveFilter) {
490 d->collectionHandler->setCustomValue(QLatin1String("CurrentFilter"),
491 d->currentFilter);
492 }
493 emit currentFilterChanged(d->currentFilter);
494}
495
496/*!
497 Returns a list of filter attributes for the different filter sections
498 defined in the Qt compressed help file with the given namespace
499 \a namespaceName.
500*/
501QList<QStringList> QHelpEngineCore::filterAttributeSets(const QString &namespaceName) const
502{
503 if (d->setup()) {
504 QHelpDBReader *reader = d->readerMap.value(namespaceName);
505 if (reader)
506 return reader->filterAttributeSets();
507 }
508 return QList<QStringList>();
509}
510
511/*!
512 Returns a list of files contained in the Qt compressed help file \a
513 namespaceName. The files can be filtered by \a filterAttributes as
514 well as by their extension \a extensionFilter (e.g. 'html').
515*/
516QList<QUrl> QHelpEngineCore::files(const QString namespaceName,
517 const QStringList &filterAttributes,
518 const QString &extensionFilter)
519{
520 QList<QUrl> res;
521 if (!d->setup())
522 return res;
523 QHelpDBReader *reader = d->readerMap.value(namespaceName);
524 if (!reader) {
525 d->error = tr("The specified namespace does not exist!");
526 return res;
527 }
528
529 QUrl url;
530 url.setScheme(QLatin1String("qthelp"));
531 url.setAuthority(namespaceName);
532
533 const QStringList files = reader->files(filterAttributes, extensionFilter);
534 foreach (const QString &file, files) {
535 url.setPath(QLatin1String("/") + file);
536 res.append(url);
537 }
538 return res;
539}
540
541/*!
542 Returns an invalid URL if the file \a url cannot be found.
543 If the file exists, either the same url is returned or a
544 different url if the file is located in a different namespace
545 which is merged via a common virtual folder.
546*/
547QUrl QHelpEngineCore::findFile(const QUrl &url) const
548{
549 QUrl res;
550 if (!d->setup() || !url.isValid() || url.toString().count(QLatin1Char('/')) < 4
551 || url.scheme() != QLatin1String("qthelp"))
552 return res;
553
554 QString ns = url.authority();
555 QString filePath = QDir::cleanPath(url.path());
556 if (filePath.startsWith(QLatin1Char('/')))
557 filePath = filePath.mid(1);
558 QString virtualFolder = filePath.mid(0, filePath.indexOf(QLatin1Char('/'), 1));
559 filePath = filePath.mid(virtualFolder.length()+1);
560
561 QHelpDBReader *defaultReader = 0;
562 if (d->readerMap.contains(ns)) {
563 defaultReader = d->readerMap.value(ns);
564 if (defaultReader->fileExists(virtualFolder, filePath))
565 return url;
566 }
567
568 QStringList filterAtts = filterAttributes(currentFilter());
569 foreach (QHelpDBReader *reader, d->virtualFolderMap.values(virtualFolder)) {
570 if (reader == defaultReader)
571 continue;
572 if (reader->fileExists(virtualFolder, filePath, filterAtts)) {
573 res = url;
574 res.setAuthority(reader->namespaceName());
575 return res;
576 }
577 }
578
579 foreach (QHelpDBReader *reader, d->virtualFolderMap.values(virtualFolder)) {
580 if (reader == defaultReader)
581 continue;
582 if (reader->fileExists(virtualFolder, filePath)) {
583 res = url;
584 res.setAuthority(reader->namespaceName());
585 break;
586 }
587 }
588
589 return res;
590}
591
592/*!
593 Returns the data of the file specified by \a url. If the
594 file does not exist, an empty QByteArray is returned.
595
596 \sa findFile()
597*/
598QByteArray QHelpEngineCore::fileData(const QUrl &url) const
599{
600 if (!d->setup() || !url.isValid() || url.toString().count(QLatin1Char('/')) < 4
601 || url.scheme() != QLatin1String("qthelp"))
602 return QByteArray();
603
604 QString ns = url.authority();
605 QString filePath = QDir::cleanPath(url.path());
606 if (filePath.startsWith(QLatin1Char('/')))
607 filePath = filePath.mid(1);
608 QString virtualFolder = filePath.mid(0, filePath.indexOf(QLatin1Char('/'), 1));
609 filePath = filePath.mid(virtualFolder.length()+1);
610
611 QByteArray ba;
612 QHelpDBReader *defaultReader = 0;
613 if (d->readerMap.contains(ns)) {
614 defaultReader = d->readerMap.value(ns);
615 ba = defaultReader->fileData(virtualFolder, filePath);
616 }
617
618 if (ba.isEmpty()) {
619 foreach (QHelpDBReader *reader, d->virtualFolderMap.values(virtualFolder)) {
620 if (reader == defaultReader)
621 continue;
622 ba = reader->fileData(virtualFolder, filePath);
623 if (!ba.isEmpty())
624 return ba;
625 }
626 }
627 return ba;
628}
629
630/*!
631 Returns a map of hits found for the \a id. A hit contains the
632 title of the document and the url where the keyword is located.
633 The result depends on the current filter, meaning only the keywords
634 registered for the current filter will be returned.
635*/
636QMap<QString, QUrl> QHelpEngineCore::linksForIdentifier(const QString &id) const
637{
638 QMap<QString, QUrl> linkMap;
639 if (!d->setup())
640 return linkMap;
641
642 QStringList atts = filterAttributes(d->currentFilter);
643 foreach (QHelpDBReader *reader, d->readerMap)
644 reader->linksForIdentifier(id, atts, linkMap);
645
646 return linkMap;
647}
648
649/*!
650 Removes the \a key from the settings section in the
651 collection file. Returns true if the value was removed
652 successfully, otherwise false.
653
654 \sa customValue(), setCustomValue()
655*/
656bool QHelpEngineCore::removeCustomValue(const QString &key)
657{
658 d->error.clear();
659 return d->collectionHandler->removeCustomValue(key);
660}
661
662/*!
663 Returns the value assigned to the \a key. If the requested
664 key does not exist, the specified \a defaultValue is
665 returned.
666
667 \sa setCustomValue(), removeCustomValue()
668*/
669QVariant QHelpEngineCore::customValue(const QString &key, const QVariant &defaultValue) const
670{
671 if (!d->setup())
672 return QVariant();
673 return d->collectionHandler->customValue(key, defaultValue);
674}
675
676/*!
677 Save the \a value under the \a key. If the key already exist,
678 the value will be overwritten. Returns true if the value was
679 saved successfully, otherwise false.
680
681 \sa customValue(), removeCustomValue()
682*/
683bool QHelpEngineCore::setCustomValue(const QString &key, const QVariant &value)
684{
685 d->error.clear();
686 return d->collectionHandler->setCustomValue(key, value);
687}
688
689/*!
690 Returns the meta data for the Qt compressed help file \a
691 documentationFileName. If there is no data available for
692 \a name, an invalid QVariant() is returned. The meta
693 data is defined when creating the Qt compressed help file and
694 cannot be modified later. Common meta data includes e.g.
695 the author of the documentation.
696*/
697QVariant QHelpEngineCore::metaData(const QString &documentationFileName,
698 const QString &name)
699{
700 QHelpDBReader reader(documentationFileName, QLatin1String("GetMetaData"), 0);
701
702 if (reader.init())
703 return reader.metaData(name);
704 return QVariant();
705}
706
707/*!
708 Returns a description of the last error that occured.
709*/
710QString QHelpEngineCore::error() const
711{
712 return d->error;
713}
714
715/*!
716 \property QHelpEngineCore::autoSaveFilter
717 \brief whether QHelpEngineCore is in auto save filter mode or not.
718 \since 4.5
719
720 If QHelpEngineCore is in auto save filter mode, the current filter is
721 automatically saved when it is changed by the setCurrentFilter()
722 function. The filter is saved persistently in the help collection file.
723
724 By default, this mode is on.
725*/
726void QHelpEngineCore::setAutoSaveFilter(bool save)
727{
728 d->autoSaveFilter = save;
729}
730
731bool QHelpEngineCore::autoSaveFilter() const
732{
733 return d->autoSaveFilter;
734}
735
736QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.