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