Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qhelpenginecore.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
9#include "qhelplink.h"
10
11#if QT_CONFIG(future)
12#include <QtConcurrent/qtconcurrentrun.h>
13#include <QtCore/qpromise.h>
14#endif
15
16#include <QtCore/qdir.h>
17#include <QtCore/qfileinfo.h>
18
20
21using namespace Qt::StringLiterals;
22
24{
25public:
26 QHelpEngineCorePrivate(const QString &collectionFile, QHelpEngineCore *helpEngineCore);
27
28 void init(const QString &collectionFile);
29 bool setup();
30
32 QHelpFilterEngine *filterEngine = nullptr;
35 bool needsSetup = true;
36 bool autoSaveFilter = true;
37 bool usesFilterEngine = false;
38 bool readOnly = true;
39
40 QHelpEngineCore *q;
41};
42
43QHelpEngineCorePrivate::QHelpEngineCorePrivate(const QString &collectionFile,
44 QHelpEngineCore *helpEngineCore)
45{
46 q = helpEngineCore;
47 filterEngine = new QHelpFilterEngine(q);
48 init(collectionFile);
49}
50
51void QHelpEngineCorePrivate::init(const QString &collectionFile)
52{
53 collectionHandler.reset(new QHelpCollectionHandler(collectionFile, q));
54 QObject::connect(collectionHandler.get(), &QHelpCollectionHandler::error, q,
55 [this](const QString &msg) { error = msg; });
56 filterEngine->setCollectionHandler(collectionHandler.get());
57 needsSetup = true;
58}
59
61{
62 error.clear();
63 if (!needsSetup)
64 return true;
65
66 needsSetup = false;
67 emit q->setupStarted();
68
69 collectionHandler->setReadOnly(q->isReadOnly());
70 const bool opened = collectionHandler->openCollectionFile();
71 if (opened)
72 q->currentFilter();
73
74 emit q->setupFinished();
75 return opened;
76}
77
78/*!
79 \class QHelpEngineCore
80 \since 4.4
81 \inmodule QtHelp
82 \brief The QHelpEngineCore class provides the core functionality
83 of the help system.
84
85 Before the help engine can be used, it must be initialized by
86 calling setupData(). At the beginning of the setup process the
87 signal setupStarted() is emitted. From this point on until
88 the signal setupFinished() is emitted, is the help data in an
89 undefined meaning unusable state.
90
91 The core help engine can be used to perform different tasks.
92 By calling documentsForIdentifier() the engine returns
93 URLs specifying the file locations inside the help system. The
94 actual file data can then be retrieved by calling fileData().
95
96 The help engine can contain any number of custom filters.
97 The management of the filters, including adding new filters,
98 changing filter definitions, or removing existing filters,
99 is done through the QHelpFilterEngine class, which can be accessed
100 by the filterEngine() method.
101
102 \note QHelpFilterEngine replaces the older filter API that is
103 deprecated since Qt 5.13. Call setUsesFilterEngine() with \c true to
104 enable the new functionality.
105
106 The core help engine has two modes:
107 \list
108 \li Read-only mode, where the help collection file is not changed
109 unless explicitly requested. This also works if the
110 collection file is in a read-only location,
111 and is the default.
112 \li Fully writable mode, which requires the help collection
113 file to be writable.
114 \endlist
115 The mode can be changed by calling setReadOnly() method, prior to
116 calling setupData().
117
118 The help engine also offers the possibility to set and read values
119 in a persistent way comparable to ini files or Windows registry
120 entries. For more information see \l setCustomValue() or \l customValue().
121
122 This class does not offer any GUI components or functionality for
123 indices or contents. If you need one of those use QHelpEngine
124 instead.
125*/
126
127/*!
128 \fn void QHelpEngineCore::setupStarted()
129
130 This signal is emitted when setup is started.
131*/
132
133/*!
134 \fn void QHelpEngineCore::setupFinished()
135
136 This signal is emitted when the setup is complete.
137*/
138
139/*!
140 \fn void QHelpEngineCore::readersAboutToBeInvalidated()
141 \deprecated
142*/
143
144/*!
145 \fn void QHelpEngineCore::currentFilterChanged(const QString &newFilter)
146 \deprecated
147
148 QHelpFilterEngine::filterActivated() should be used instead.
149
150 This signal is emitted when the current filter is changed to
151 \a newFilter.
152*/
153
154/*!
155 \fn void QHelpEngineCore::warning(const QString &msg)
156
157 This signal is emitted when a non critical error occurs.
158 The warning message is stored in \a msg.
159*/
160
161/*!
162 Constructs a new core help engine with a \a parent. The help engine
163 uses the information stored in the \a collectionFile to provide help.
164 If the collection file does not exist yet, it'll be created.
165*/
166QHelpEngineCore::QHelpEngineCore(const QString &collectionFile, QObject *parent)
167 : QObject(parent)
168 , d(new QHelpEngineCorePrivate(collectionFile, this))
169{}
170
171/*!
172 \internal
173*/
174#if QT_DEPRECATED_SINCE(6, 8)
175QHelpEngineCore::QHelpEngineCore(QHelpEngineCorePrivate *helpEngineCorePrivate, QObject *parent)
176 : QObject(parent)
177 , d(helpEngineCorePrivate)
178{}
179#endif
180
181/*!
182 Destructs the help engine.
183*/
184QHelpEngineCore::~QHelpEngineCore()
185{
186 delete d;
187}
188
189/*!
190 \property QHelpEngineCore::collectionFile
191 \brief the absolute file name of the collection file currently used.
192 \since 4.5
193
194 Setting this property leaves the help engine in an invalid state. It is
195 important to invoke setupData() or any getter function in order to setup
196 the help engine again.
197*/
198QString QHelpEngineCore::collectionFile() const
199{
200 return d->collectionHandler->collectionFile();
201}
202
203void QHelpEngineCore::setCollectionFile(const QString &fileName)
204{
205 if (fileName != collectionFile())
206 d->init(fileName);
207}
208
209/*!
210 \property QHelpEngineCore::readOnly
211 \brief whether the help engine is read-only.
212 \since 6.0
213
214 In read-only mode, the user can use the help engine
215 with a collection file installed in a read-only location.
216 In this case, some functionality won't be accessible,
217 like registering additional documentation, filter editing,
218 or any action that would require changes to the
219 collection file. Setting it to \c false enables the full
220 functionality of the help engine.
221
222 By default, this property is \c true.
223*/
224bool QHelpEngineCore::isReadOnly() const
225{
226 return d->readOnly;
227}
228
229void QHelpEngineCore::setReadOnly(bool enable)
230{
231 if (d->readOnly == enable)
232 return;
233
234 d->readOnly = enable;
235 d->init(collectionFile());
236}
237
238/*!
239 \since 5.13
240
241 Returns the filter engine associated with this help engine.
242 The filter engine allows for adding, changing, and removing existing
243 filters for this help engine. To use the engine you also have to call
244 \l setUsesFilterEngine() set to \c true.
245*/
246QHelpFilterEngine *QHelpEngineCore::filterEngine() const
247{
248 return d->filterEngine;
249}
250
251/*!
252 Sets up the help engine by processing the information found
253 in the collection file and returns true if successful; otherwise
254 returns false.
255
256 By calling the function, the help
257 engine is forced to initialize itself immediately. Most of
258 the times, this function does not have to be called
259 explicitly because getter functions which depend on a correctly
260 set up help engine do that themselves.
261
262 \note \c{qsqlite4.dll} needs to be deployed with the application as the
263 help system uses the sqlite driver when loading help collections.
264*/
265bool QHelpEngineCore::setupData()
266{
267 d->needsSetup = true;
268 return d->setup();
269}
270
271/*!
272 Creates the file \a fileName and copies all contents from
273 the current collection file into the newly created file,
274 and returns true if successful; otherwise returns false.
275
276 The copying process makes sure that file references to Qt
277 Collection files (\c{.qch}) files are updated accordingly.
278*/
279bool QHelpEngineCore::copyCollectionFile(const QString &fileName)
280{
281 if (!d->setup())
282 return false;
283 return d->collectionHandler->copyCollectionFile(fileName);
284}
285
286/*!
287 Returns the namespace name defined for the Qt compressed help file (.qch)
288 specified by its \a documentationFileName. If the file is not valid, an
289 empty string is returned.
290
291 \sa documentationFileName()
292*/
293QString QHelpEngineCore::namespaceName(const QString &documentationFileName)
294{
295 void *pointer = const_cast<QString *>(&documentationFileName);
296 QHelpDBReader reader(documentationFileName, QHelpGlobal::uniquifyConnectionName(
297 "GetNamespaceName"_L1, pointer), nullptr);
298 if (reader.init())
299 return reader.namespaceName();
300 return {};
301}
302
303/*!
304 Registers the Qt compressed help file (.qch) contained in the file
305 \a documentationFileName. One compressed help file, uniquely
306 identified by its namespace can only be registered once.
307 True is returned if the registration was successful, otherwise
308 false.
309
310 \sa unregisterDocumentation(), error()
311*/
312bool QHelpEngineCore::registerDocumentation(const QString &documentationFileName)
313{
314 d->error.clear();
315 d->needsSetup = true;
316 return d->collectionHandler->registerDocumentation(documentationFileName);
317}
318
319/*!
320 Unregisters the Qt compressed help file (.qch) identified by its
321 \a namespaceName from the help collection. Returns true
322 on success, otherwise false.
323
324 \sa registerDocumentation(), error()
325*/
326bool QHelpEngineCore::unregisterDocumentation(const QString &namespaceName)
327{
328 d->error.clear();
329 d->needsSetup = true;
330 return d->collectionHandler->unregisterDocumentation(namespaceName);
331}
332
333/*!
334 Returns the absolute file name of the Qt compressed help file (.qch)
335 identified by the \a namespaceName. If there is no Qt compressed help file
336 with the specified namespace registered, an empty string is returned.
337
338 \sa namespaceName()
339*/
340QString QHelpEngineCore::documentationFileName(const QString &namespaceName)
341{
342 if (!d->setup())
343 return {};
344
345 const QHelpCollectionHandler::FileInfo fileInfo =
346 d->collectionHandler->registeredDocumentation(namespaceName);
347
348 if (fileInfo.namespaceName.isEmpty())
349 return {};
350
351 if (QDir::isAbsolutePath(fileInfo.fileName))
352 return fileInfo.fileName;
353
354 return QFileInfo(QFileInfo(d->collectionHandler->collectionFile()).absolutePath()
355 + u'/' + fileInfo.fileName).absoluteFilePath();
356}
357
358/*!
359 Returns a list of all registered Qt compressed help files of the current collection file.
360 The returned names are the namespaces of the registered Qt compressed help files (.qch).
361*/
362QStringList QHelpEngineCore::registeredDocumentations() const
363{
364 if (!d->setup())
365 return {};
366 const auto &docList = d->collectionHandler->registeredDocumentations();
367 QStringList list;
368 for (const QHelpCollectionHandler::FileInfo &info : docList)
369 list.append(info.namespaceName);
370 return list;
371}
372
373/*!
374 \deprecated
375
376 QHelpFilterEngine::filters() should be used instead.
377
378 Returns a list of custom filters.
379
380 \sa addCustomFilter(), removeCustomFilter()
381*/
382QStringList QHelpEngineCore::customFilters() const
383{
384 if (!d->setup())
385 return {};
386 return d->collectionHandler->customFilters();
387}
388
389/*!
390 \deprecated
391
392 QHelpFilterEngine::setFilterData() should be used instead.
393
394 Adds the new custom filter \a filterName. The filter attributes
395 are specified by \a attributes. If the filter already exists,
396 its attribute set is replaced. The function returns true if
397 the operation succeeded, otherwise it returns false.
398
399 \sa customFilters(), removeCustomFilter()
400*/
401bool QHelpEngineCore::addCustomFilter(const QString &filterName,
402 const QStringList &attributes)
403{
404 d->error.clear();
405 d->needsSetup = true;
406 return d->collectionHandler->addCustomFilter(filterName, attributes);
407}
408
409/*!
410 \deprecated
411
412 QHelpFilterEngine::removeFilter() should be used instead.
413
414 Returns true if the filter \a filterName was removed successfully,
415 otherwise false.
416
417 \sa addCustomFilter(), customFilters()
418*/
419bool QHelpEngineCore::removeCustomFilter(const QString &filterName)
420{
421 d->error.clear();
422 d->needsSetup = true;
423 return d->collectionHandler->removeCustomFilter(filterName);
424}
425
426/*!
427 \deprecated
428
429 QHelpFilterEngine::availableComponents() should be used instead.
430
431 Returns a list of all defined filter attributes.
432*/
433QStringList QHelpEngineCore::filterAttributes() const
434{
435 if (!d->setup())
436 return {};
437 return d->collectionHandler->filterAttributes();
438}
439
440/*!
441 \deprecated
442
443 QHelpFilterEngine::filterData() should be used instead.
444
445 Returns a list of filter attributes used by the custom
446 filter \a filterName.
447*/
448QStringList QHelpEngineCore::filterAttributes(const QString &filterName) const
449{
450 if (!d->setup())
451 return {};
452 return d->collectionHandler->filterAttributes(filterName);
453}
454
455/*!
456 \deprecated
457 \property QHelpEngineCore::currentFilter
458 \brief the name of the custom filter currently applied.
459 \since 4.5
460
461 QHelpFilterEngine::activeFilter() should be used instead.
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 {};
473
474 if (d->currentFilter.isEmpty()) {
475 const QString &filter =
476 d->collectionHandler->customValue("CurrentFilter"_L1, QString()).toString();
477 if (!filter.isEmpty() && d->collectionHandler->customFilters().contains(filter))
478 d->currentFilter = filter;
479 }
480 return d->currentFilter;
481}
482
483void QHelpEngineCore::setCurrentFilter(const QString &filterName)
484{
485 if (!d->setup() || filterName == d->currentFilter)
486 return;
487 d->currentFilter = filterName;
488 if (d->autoSaveFilter)
489 d->collectionHandler->setCustomValue("CurrentFilter"_L1, d->currentFilter);
490 emit currentFilterChanged(d->currentFilter);
491}
492
493/*!
494 \deprecated
495
496 QHelpFilterEngine::filterData() should be used instead.
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 return {};
506 return d->collectionHandler->filterAttributeSets(namespaceName);
507}
508
509/*!
510 \deprecated
511
512 files() should be used instead.
513
514 Returns a list of files contained in the Qt compressed help file \a
515 namespaceName. The files can be filtered by \a filterAttributes as
516 well as by their extension \a extensionFilter (e.g. 'html').
517*/
518QList<QUrl> QHelpEngineCore::files(const QString namespaceName,
519 const QStringList &filterAttributes,
520 const QString &extensionFilter)
521{
522 QList<QUrl> res;
523 if (!d->setup())
524 return res;
525
526 QUrl url;
527 url.setScheme("qthelp"_L1);
528 url.setAuthority(namespaceName);
529
530 const QStringList &files = d->collectionHandler->files(
531 namespaceName, filterAttributes, extensionFilter);
532 for (const QString &file : files) {
533 url.setPath("/"_L1 + file);
534 res.append(url);
535 }
536 return res;
537}
538
539/*!
540 Returns a list of files contained in the Qt compressed help file
541 for \a namespaceName. The files can be filtered by \a filterName as
542 well as by their extension \a extensionFilter (for example, 'html').
543*/
544QList<QUrl> QHelpEngineCore::files(const QString namespaceName,
545 const QString &filterName,
546 const QString &extensionFilter)
547{
548 QList<QUrl> res;
549 if (!d->setup())
550 return res;
551
552 QUrl url;
553 url.setScheme("qthelp"_L1);
554 url.setAuthority(namespaceName);
555
556 const QStringList &files = d->collectionHandler->files(
557 namespaceName, filterName, extensionFilter);
558 for (const QString &file : files) {
559 url.setPath("/"_L1 + file);
560 res.append(url);
561 }
562 return res;
563}
564
565/*!
566 Returns the corrected URL for the \a url that may refer to
567 a different namespace defined by the virtual folder defined
568 as a part of the \a url. If the virtual folder matches the namespace
569 of the \a url, the method just checks if the file exists and returns
570 the same \a url. When the virtual folder doesn't match the namespace
571 of the \a url, it tries to find the best matching namespace according
572 to the active filter. When the namespace is found, it returns the
573 corrected URL if the file exists, otherwise it returns an invalid URL.
574*/
575QUrl QHelpEngineCore::findFile(const QUrl &url) const
576{
577 if (!d->setup())
578 return url;
579
580 QUrl result = d->usesFilterEngine
581 ? d->collectionHandler->findFile(url, d->filterEngine->activeFilter())
582 : d->collectionHandler->findFile(url, filterAttributes(currentFilter())); // obsolete
583 if (!result.isEmpty())
584 return result;
585
586 result = d->usesFilterEngine
587 ? d->collectionHandler->findFile(url, QString())
588 : d->collectionHandler->findFile(url, QStringList()); // obsolete
589 if (!result.isEmpty())
590 return result;
591
592 return url;
593}
594
595/*!
596 Returns the data of the file specified by \a url. If the
597 file does not exist, an empty QByteArray is returned.
598
599 \sa findFile()
600*/
601QByteArray QHelpEngineCore::fileData(const QUrl &url) const
602{
603 if (!d->setup())
604 return {};
605 return d->collectionHandler->fileData(url);
606}
607
608/*!
609 \since 5.15
610
611 Returns a list of all the document links found for the \a id.
612 The returned list contents depend on the current filter, and therefore only the keywords
613 registered for the current filter will be returned.
614*/
615QList<QHelpLink> QHelpEngineCore::documentsForIdentifier(const QString &id) const
616{
617 return documentsForIdentifier(
618 id, d->usesFilterEngine ? d->filterEngine->activeFilter() : d->currentFilter);
619}
620
621/*!
622 \since 5.15
623
624 Returns a list of the document links found for the \a id, filtered by \a filterName.
625 The returned list contents depend on the passed filter, and therefore only the keywords
626 registered for this filter will be returned. If you want to get all results unfiltered,
627 pass empty string as \a filterName.
628*/
629QList<QHelpLink> QHelpEngineCore::documentsForIdentifier(const QString &id, const QString &filterName) const
630{
631 if (!d->setup())
632 return {};
633
634 if (d->usesFilterEngine)
635 return d->collectionHandler->documentsForIdentifier(id, filterName);
636 return d->collectionHandler->documentsForIdentifier(id, filterAttributes(filterName));
637}
638
639/*!
640 \since 5.15
641
642 Returns a list of all the document links found for the \a keyword.
643 The returned list contents depend on the current filter, and therefore only the keywords
644 registered for the current filter will be returned.
645*/
646QList<QHelpLink> QHelpEngineCore::documentsForKeyword(const QString &keyword) const
647{
648 return documentsForKeyword(
649 keyword, d->usesFilterEngine ? d->filterEngine->activeFilter() : d->currentFilter);
650}
651
652/*!
653 \since 5.15
654
655 Returns a list of the document links found for the \a keyword, filtered by \a filterName.
656 The returned list contents depend on the passed filter, and therefore only the keywords
657 registered for this filter will be returned. If you want to get all results unfiltered,
658 pass empty string as \a filterName.
659*/
660QList<QHelpLink> QHelpEngineCore::documentsForKeyword(const QString &keyword, const QString &filterName) const
661{
662 if (!d->setup())
663 return {};
664
665 if (d->usesFilterEngine)
666 return d->collectionHandler->documentsForKeyword(keyword, filterName);
667 return d->collectionHandler->documentsForKeyword(keyword, filterAttributes(filterName));
668}
669
670/*!
671 Removes the \a key from the settings section in the
672 collection file. Returns true if the value was removed
673 successfully, otherwise false.
674
675 \sa customValue(), setCustomValue()
676*/
677bool QHelpEngineCore::removeCustomValue(const QString &key)
678{
679 d->error.clear();
680 return d->collectionHandler->removeCustomValue(key);
681}
682
683/*!
684 Returns the value assigned to the \a key. If the requested
685 key does not exist, the specified \a defaultValue is
686 returned.
687
688 \sa setCustomValue(), removeCustomValue()
689*/
690QVariant QHelpEngineCore::customValue(const QString &key, const QVariant &defaultValue) const
691{
692 if (!d->setup())
693 return {};
694 return d->collectionHandler->customValue(key, defaultValue);
695}
696
697/*!
698 Save the \a value under the \a key. If the key already exist,
699 the value will be overwritten. Returns true if the value was
700 saved successfully, otherwise false.
701
702 \sa customValue(), removeCustomValue()
703*/
704bool QHelpEngineCore::setCustomValue(const QString &key, const QVariant &value)
705{
706 d->error.clear();
707 return d->collectionHandler->setCustomValue(key, value);
708}
709
710/*!
711 Returns the meta data for the Qt compressed help file \a
712 documentationFileName. If there is no data available for
713 \a name, an invalid QVariant() is returned. The meta
714 data is defined when creating the Qt compressed help file and
715 cannot be modified later. Common meta data includes e.g.
716 the author of the documentation.
717*/
718QVariant QHelpEngineCore::metaData(const QString &documentationFileName,
719 const QString &name)
720{
721 QHelpDBReader reader(documentationFileName, "GetMetaData"_L1, nullptr);
722
723 if (reader.init())
724 return reader.metaData(name);
725 return {};
726}
727
728/*!
729 Returns a description of the last error that occurred.
730*/
731QString QHelpEngineCore::error() const
732{
733 return d->error;
734}
735
736/*!
737 \property QHelpEngineCore::autoSaveFilter
738 \brief whether QHelpEngineCore is in auto save filter mode or not.
739 \since 4.5
740
741 If QHelpEngineCore is in auto save filter mode, the current filter is
742 automatically saved when it is changed by the QHelpFilterEngine::setActiveFilter()
743 function. The filter is saved persistently in the help collection file.
744
745 By default, this mode is on.
746*/
747void QHelpEngineCore::setAutoSaveFilter(bool save)
748{
749 d->autoSaveFilter = save;
750}
751
752bool QHelpEngineCore::autoSaveFilter() const
753{
754 return d->autoSaveFilter;
755}
756
757/*!
758 \since 5.13
759
760 Enables or disables the new filter engine functionality
761 inside the help engine, according to the passed \a uses parameter.
762
763 \sa filterEngine()
764*/
765void QHelpEngineCore::setUsesFilterEngine(bool uses)
766{
767 d->usesFilterEngine = uses;
768}
769
770/*!
771 \since 5.13
772
773 Returns whether the help engine uses the new filter functionality.
774
775 \sa filterEngine()
776*/
777bool QHelpEngineCore::usesFilterEngine() const
778{
779 return d->usesFilterEngine;
780}
781
782#if QT_CONFIG(future)
783static QUrl constructUrl(const QString &namespaceName, const QString &folderName,
784 const QString &relativePath)
785{
786 const int idx = relativePath.indexOf(u'#');
787 const QString &rp = idx < 0 ? relativePath : relativePath.left(idx);
788 const QString anchor = idx < 0 ? QString() : relativePath.mid(idx + 1);
789 return QHelpCollectionHandler::buildQUrl(namespaceName, folderName, rp, anchor);
790}
791
792using ContentProviderResult = QList<QHelpCollectionHandler::ContentsData>;
793using ContentProvider = std::function<ContentProviderResult(const QString &)>;
794using ContentResult = std::shared_ptr<QHelpContentItem>;
795
796// This trick is needed because the c'tor of QHelpContentItem is private.
797QHelpContentItem *createContentItem(const QString &name = {}, const QUrl &link = {},
798 QHelpContentItem *parent = {})
799{
800 return new QHelpContentItem(name, link, parent);
801}
802
803static void requestContentHelper(QPromise<ContentResult> &promise, const ContentProvider &provider,
804 const QString &collectionFile)
805{
806 ContentResult rootItem(createContentItem());
807 const ContentProviderResult result = provider(collectionFile);
808 for (const auto &contentsData : result) {
809 const QString namespaceName = contentsData.namespaceName;
810 const QString folderName = contentsData.folderName;
811 for (const QByteArray &contents : contentsData.contentsList) {
812 if (promise.isCanceled())
813 return;
814
815 if (contents.isEmpty())
816 continue;
817
818 QList<QHelpContentItem *> stack;
819 QDataStream s(contents);
820 while (true) {
821 int depth = 0;
822 QString link, title;
823 s >> depth;
824 s >> link;
825 s >> title;
826 if (title.isEmpty())
827 break;
828
829// The example input (depth, link, title):
830//
831// 0 "graphicaleffects5.html" "Qt 5 Compatibility APIs: Qt Graphical Effects"
832// 1 "qtgraphicaleffects5-index.html" "QML Types"
833// 2 "qml-qt5compat-graphicaleffects-blend.html" "Blend Type Reference"
834// 3 "qml-qt5compat-graphicaleffects-blend-members.html" "List of all members"
835// 2 "qml-qt5compat-graphicaleffects-brightnesscontrast.html" "BrightnessContrast Type Reference"
836//
837// Thus, the valid order of depths is:
838// 1. Whenever the item's depth is < 0, we insert the item as its depth is 0.
839// 2. The first item's depth must be 0, otherwise we insert the item as its depth is 0.
840// 3. When the previous depth was N, the next depth must be in range [0, N+1] inclusively.
841// If next item's depth is M > N+1, we insert the item as its depth is N+1.
842
843 if (depth <= 0) {
844 stack.clear();
845 } else if (depth < stack.size()) {
846 stack = stack.sliced(0, depth);
847 } else if (depth > stack.size()) {
848 // Fill the gaps with the last item from the stack (or with the root).
849 // This branch handles the case when depths are broken, e.g. 0, 2, 2, 1.
850 // In this case, the 1st item is a root, and 2nd - 4th are all direct
851 // children of the 1st.
852 QHelpContentItem *substituteItem =
853 stack.isEmpty() ? rootItem.get() : stack.constLast();
854 while (depth > stack.size())
855 stack.append(substituteItem);
856 }
857
858 const QUrl url = constructUrl(namespaceName, folderName, link);
859 QHelpContentItem *parent = stack.isEmpty() ? rootItem.get() : stack.constLast();
860 stack.push_back(createContentItem(title, url, parent));
861 }
862 }
863 }
864 promise.addResult(rootItem);
865}
866
867static ContentProvider contentProviderFromFilterEngine(const QString &filter)
868{
869 return [filter](const QString &collectionFile) -> ContentProviderResult {
870 QHelpCollectionHandler collectionHandler(collectionFile);
871 if (!collectionHandler.openCollectionFile())
872 return {};
873 return collectionHandler.contentsForFilter(filter);
874 };
875}
876
877static ContentProvider contentProviderFromAttributes(const QStringList &attributes)
878{
879 return [attributes](const QString &collectionFile) -> ContentProviderResult {
880 QHelpCollectionHandler collectionHandler(collectionFile);
881 if (!collectionHandler.openCollectionFile())
882 return {};
883 return collectionHandler.contentsForFilter(attributes);
884 };
885}
886
887QFuture<ContentResult> QHelpEngineCore::requestContentForCurrentFilter() const
888{
889 const ContentProvider provider = usesFilterEngine()
890 ? contentProviderFromFilterEngine(filterEngine()->activeFilter())
891 : contentProviderFromAttributes(filterAttributes(d->currentFilter));
892 return QtConcurrent::run(requestContentHelper, provider, collectionFile());
893}
894
895QFuture<ContentResult> QHelpEngineCore::requestContent(const QString &filter) const
896{
897 const ContentProvider provider = usesFilterEngine()
898 ? contentProviderFromFilterEngine(filter)
899 : contentProviderFromAttributes(filterAttributes(filter));
900 return QtConcurrent::run(requestContentHelper, provider, collectionFile());
901}
902
903using IndexProvider = std::function<QStringList(const QString &)>;
904using IndexResult = QStringList;
905
906static IndexProvider indexProviderFromFilterEngine(const QString &filter)
907{
908 return [filter](const QString &collectionFile) -> IndexResult {
909 QHelpCollectionHandler collectionHandler(collectionFile);
910 if (!collectionHandler.openCollectionFile())
911 return {};
912 return collectionHandler.indicesForFilter(filter);
913 };
914}
915
916static IndexProvider indexProviderFromAttributes(const QStringList &attributes)
917{
918 return [attributes](const QString &collectionFile) -> IndexResult {
919 QHelpCollectionHandler collectionHandler(collectionFile);
920 if (!collectionHandler.openCollectionFile())
921 return {};
922 return collectionHandler.indicesForFilter(attributes);
923 };
924}
925
926QFuture<IndexResult> QHelpEngineCore::requestIndexForCurrentFilter() const
927{
928 const IndexProvider provider = usesFilterEngine()
929 ? indexProviderFromFilterEngine(filterEngine()->activeFilter())
930 : indexProviderFromAttributes(filterAttributes(d->currentFilter));
931 return QtConcurrent::run(std::move(provider), collectionFile());
932}
933
934QFuture<IndexResult> QHelpEngineCore::requestIndex(const QString &filter) const
935{
936 const IndexProvider provider = usesFilterEngine()
937 ? indexProviderFromFilterEngine(filter)
938 : indexProviderFromAttributes(filterAttributes(filter));
939 return QtConcurrent::run(std::move(provider), collectionFile());
940}
941#endif
942
943QT_END_NAMESPACE
std::unique_ptr< QHelpCollectionHandler > collectionHandler
QHelpFilterEngine * filterEngine
QHelpEngineCorePrivate(const QString &collectionFile, QHelpEngineCore *helpEngineCore)
void init(const QString &collectionFile)
Combined button and popup list for selecting options.