1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3#include "tracer.h"
4
5#include "helpenginewrapper.h"
6#include "../shared/collectionconfiguration.h"
7
8#include <QtCore/QDateTime>
9#include <QtCore/QFileInfo>
10#include <QtCore/QFileSystemWatcher>
11#include <QtCore/QTimer>
12#include <QtHelp/QHelpContentModel>
13#include <QtHelp/QHelpEngine>
14#include <QtHelp/QHelpFilterEngine>
15#include <QtHelp/QHelpIndexModel>
16#include <QtHelp/QHelpLink>
17#include <QtHelp/QHelpSearchEngine>
18
19#include <map>
20#include <memory>
21
22QT_BEGIN_NAMESPACE
23
24namespace {
25 const QString AppFontKey(QLatin1String("appFont"));
26 const QString AppWritingSystemKey(QLatin1String("appWritingSystem"));
27 const QString BookmarksKey(QLatin1String("Bookmarks"));
28 const QString BrowserFontKey(QLatin1String("browserFont"));
29 const QString BrowserWritingSystemKey(QLatin1String("browserWritingSystem"));
30 const QString HomePageKey(QLatin1String("homepage"));
31 const QString MainWindowKey(QLatin1String("MainWindow"));
32 const QString MainWindowGeometryKey(QLatin1String("MainWindowGeometry"));
33 const QString SearchWasAttachedKey(QLatin1String("SearchWasAttached"));
34 const QString StartOptionKey(QLatin1String("StartOption"));
35 const QString UseAppFontKey(QLatin1String("useAppFont"));
36 const QString UseBrowserFontKey(QLatin1String("useBrowserFont"));
37 const QString VersionKey(QString(QLatin1String("qtVersion%1$$$%2")).
38 arg(a: QLatin1String(QT_VERSION_STR)));
39 const QString ShowTabsKey(QLatin1String("showTabs"));
40 const QString TopicChooserGeometryKey(QLatin1String("TopicChooserGeometry"));
41} // anonymous namespace
42
43class TimeoutForwarder : public QObject
44{
45 Q_OBJECT
46public:
47 TimeoutForwarder(const QString &fileName);
48private slots:
49 void forward();
50private:
51 friend class HelpEngineWrapperPrivate;
52
53 const QString m_fileName;
54};
55
56class HelpEngineWrapperPrivate : public QObject
57{
58 Q_OBJECT
59 friend class HelpEngineWrapper;
60 friend class TimeoutForwarder;
61private slots:
62 void qchFileChanged(const QString &fileName);
63
64signals:
65 void documentationRemoved(const QString &namespaceName);
66 void documentationUpdated(const QString &namespaceName);
67
68private:
69 HelpEngineWrapperPrivate(const QString &collectionFile);
70
71 void initFileSystemWatchers();
72 void checkDocFilesWatched();
73 void qchFileChanged(const QString &fileName, bool fromTimeout);
74
75 static const int UpdateGracePeriod = 2000;
76
77 QHelpEngine * const m_helpEngine;
78 QFileSystemWatcher * const m_qchWatcher;
79 struct RecentSignal {
80 QDateTime timestamp;
81 std::unique_ptr<TimeoutForwarder> forwarder;
82 };
83 std::map<QString, RecentSignal> m_recentQchUpdates;
84};
85
86HelpEngineWrapper *HelpEngineWrapper::helpEngineWrapper = nullptr;
87
88HelpEngineWrapper &HelpEngineWrapper::instance()
89{
90 return instance(collectionFile: {});
91}
92
93HelpEngineWrapper &HelpEngineWrapper::instance(const QString &collectionFile)
94{
95 TRACE_OBJ
96 /*
97 * Note that this Singleton cannot be static, because it has to be
98 * deleted before the QApplication.
99 */
100 if (!helpEngineWrapper)
101 helpEngineWrapper = new HelpEngineWrapper(collectionFile);
102 return *helpEngineWrapper;
103}
104
105void HelpEngineWrapper::removeInstance()
106{
107 TRACE_OBJ
108 delete helpEngineWrapper;
109 helpEngineWrapper = nullptr;
110}
111
112HelpEngineWrapper::HelpEngineWrapper(const QString &collectionFile)
113 : d(new HelpEngineWrapperPrivate(collectionFile))
114{
115 TRACE_OBJ
116
117 /*
118 * Otherwise we will waste time if several new docs are found,
119 * because we will start to index them, only to be interrupted
120 * by the next request. Also, there is a nasty SQLITE bug that will
121 * cause the application to hang for minutes in that case.
122 * This call is reverted by initialDocSetupDone(), which must be
123 * called after the new docs have been installed.
124 */
125// TODO: probably remove it
126 disconnect(sender: d->m_helpEngine, signal: &QHelpEngineCore::setupFinished,
127 receiver: searchEngine(), slot: &QHelpSearchEngine::scheduleIndexDocumentation);
128
129 connect(sender: d, signal: &HelpEngineWrapperPrivate::documentationRemoved,
130 context: this, slot: &HelpEngineWrapper::documentationRemoved);
131 connect(sender: d, signal: &HelpEngineWrapperPrivate::documentationUpdated,
132 context: this, slot: &HelpEngineWrapper::documentationUpdated);
133 connect(sender: d->m_helpEngine, signal: &QHelpEngineCore::setupFinished,
134 context: this, slot: &HelpEngineWrapper::setupFinished);
135}
136
137HelpEngineWrapper::~HelpEngineWrapper()
138{
139 TRACE_OBJ
140 const QStringList &namespaces = d->m_helpEngine->registeredDocumentations();
141 for (const QString &nameSpace : namespaces) {
142 const QString &docFile
143 = d->m_helpEngine->documentationFileName(namespaceName: nameSpace);
144 d->m_qchWatcher->removePath(file: docFile);
145 }
146
147 delete d;
148}
149
150void HelpEngineWrapper::initialDocSetupDone()
151{
152 TRACE_OBJ
153// TODO: probably remove it
154 connect(sender: d->m_helpEngine, signal: &QHelpEngineCore::setupFinished,
155 context: searchEngine(), slot: &QHelpSearchEngine::scheduleIndexDocumentation);
156 setupData();
157}
158
159QHelpSearchEngine *HelpEngineWrapper::searchEngine() const
160{
161 TRACE_OBJ
162 return d->m_helpEngine->searchEngine();
163}
164
165QHelpContentModel *HelpEngineWrapper::contentModel() const
166{
167 TRACE_OBJ
168 return d->m_helpEngine->contentModel();
169}
170
171QHelpIndexModel *HelpEngineWrapper::indexModel() const
172{
173 TRACE_OBJ
174 return d->m_helpEngine->indexModel();
175}
176
177QHelpContentWidget *HelpEngineWrapper::contentWidget()
178{
179 TRACE_OBJ
180 return d->m_helpEngine->contentWidget();
181}
182
183QHelpIndexWidget *HelpEngineWrapper::indexWidget()
184{
185 TRACE_OBJ
186 return d->m_helpEngine->indexWidget();
187}
188
189const QStringList HelpEngineWrapper::registeredDocumentations() const
190{
191 TRACE_OBJ
192 return d->m_helpEngine->registeredDocumentations();
193}
194
195QString HelpEngineWrapper::documentationFileName(const QString &namespaceName) const
196{
197 TRACE_OBJ
198 return d->m_helpEngine->documentationFileName(namespaceName);
199}
200
201const QString HelpEngineWrapper::collectionFile() const
202{
203 TRACE_OBJ
204 return d->m_helpEngine->collectionFile();
205}
206
207bool HelpEngineWrapper::registerDocumentation(const QString &docFile)
208{
209 TRACE_OBJ
210 d->checkDocFilesWatched();
211 if (!d->m_helpEngine->registerDocumentation(documentationFileName: docFile))
212 return false;
213 d->m_qchWatcher->addPath(file: docFile);
214 d->checkDocFilesWatched();
215 return true;
216}
217
218bool HelpEngineWrapper::unregisterDocumentation(const QString &namespaceName)
219{
220 TRACE_OBJ
221 d->checkDocFilesWatched();
222 const QString &file = d->m_helpEngine->documentationFileName(namespaceName);
223 if (!d->m_helpEngine->unregisterDocumentation(namespaceName))
224 return false;
225 d->m_qchWatcher->removePath(file);
226 d->checkDocFilesWatched();
227 return true;
228}
229
230bool HelpEngineWrapper::setupData()
231{
232 TRACE_OBJ
233 return d->m_helpEngine->setupData();
234}
235
236QUrl HelpEngineWrapper::findFile(const QUrl &url) const
237{
238 TRACE_OBJ
239 return d->m_helpEngine->findFile(url);
240}
241
242QByteArray HelpEngineWrapper::fileData(const QUrl &url) const
243{
244 TRACE_OBJ
245 return d->m_helpEngine->fileData(url);
246}
247
248QList<QHelpLink> HelpEngineWrapper::documentsForIdentifier(const QString &id) const
249{
250 TRACE_OBJ
251 return d->m_helpEngine->documentsForIdentifier(id);
252}
253
254QString HelpEngineWrapper::error() const
255{
256 TRACE_OBJ
257 return d->m_helpEngine->error();
258}
259
260QHelpFilterEngine *HelpEngineWrapper::filterEngine() const
261{
262 return d->m_helpEngine->filterEngine();
263}
264
265const QStringList HelpEngineWrapper::qtDocInfo(const QString &component) const
266{
267 TRACE_OBJ
268 return d->m_helpEngine->customValue(key: VersionKey.arg(a: component)).toString().
269 split(sep: CollectionConfiguration::ListSeparator);
270}
271
272void HelpEngineWrapper::setQtDocInfo(const QString &component,
273 const QStringList &doc)
274{
275 TRACE_OBJ
276 d->m_helpEngine->setCustomValue(key: VersionKey.arg(a: component),
277 value: doc.join(sep: CollectionConfiguration::ListSeparator));
278}
279
280const QStringList HelpEngineWrapper::lastShownPages() const
281{
282 TRACE_OBJ
283 return CollectionConfiguration::lastShownPages(helpEngine: *d->m_helpEngine);
284}
285
286void HelpEngineWrapper::setLastShownPages(const QStringList &lastShownPages)
287{
288 TRACE_OBJ
289 CollectionConfiguration::setLastShownPages(helpEngine&: *d->m_helpEngine, lastShownPages);
290}
291
292const QStringList HelpEngineWrapper::lastZoomFactors() const
293{
294 TRACE_OBJ
295 return CollectionConfiguration::lastZoomFactors(helpEngine: *d->m_helpEngine);
296}
297
298void HelpEngineWrapper::setLastZoomFactors(const QStringList &lastZoomFactors)
299{
300 TRACE_OBJ
301 CollectionConfiguration::setLastZoomFactors(helPEngine&: *d->m_helpEngine, lastZoomFactors);
302}
303
304const QString HelpEngineWrapper::cacheDir() const
305{
306 TRACE_OBJ
307 return CollectionConfiguration::cacheDir(helpEngine: *d->m_helpEngine);
308}
309
310bool HelpEngineWrapper::cacheDirIsRelativeToCollection() const
311{
312 TRACE_OBJ
313 return CollectionConfiguration::cacheDirIsRelativeToCollection(helpEngine: *d->m_helpEngine);
314}
315
316void HelpEngineWrapper::setCacheDir(const QString &cacheDir,
317 bool relativeToCollection)
318{
319 TRACE_OBJ
320 CollectionConfiguration::setCacheDir(helpEngine&: *d->m_helpEngine, cacheDir,
321 relativeToCollection);
322}
323
324bool HelpEngineWrapper::filterFunctionalityEnabled() const
325{
326 TRACE_OBJ
327 return CollectionConfiguration::filterFunctionalityEnabled(helpEngine: *d->m_helpEngine);
328}
329
330void HelpEngineWrapper::setFilterFunctionalityEnabled(bool enabled)
331{
332 TRACE_OBJ
333 CollectionConfiguration::setFilterFunctionalityEnabled(helpEngine&: *d->m_helpEngine,
334 enabled);
335}
336
337bool HelpEngineWrapper::filterToolbarVisible() const
338{
339 TRACE_OBJ
340 return CollectionConfiguration::filterToolbarVisible(helpEngine: *d->m_helpEngine);
341}
342
343void HelpEngineWrapper::setFilterToolbarVisible(bool visible)
344{
345 TRACE_OBJ
346 CollectionConfiguration::setFilterToolbarVisible(helpEngine&: *d->m_helpEngine, visible);
347}
348
349bool HelpEngineWrapper::addressBarEnabled() const
350{
351 TRACE_OBJ
352 return CollectionConfiguration::addressBarEnabled(helpEngine: *d->m_helpEngine);
353}
354
355void HelpEngineWrapper::setAddressBarEnabled(bool enabled)
356{
357 TRACE_OBJ
358 CollectionConfiguration::setAddressBarEnabled(helpEngine&: *d->m_helpEngine, enabled);
359}
360
361bool HelpEngineWrapper::addressBarVisible() const
362{
363 TRACE_OBJ
364 return CollectionConfiguration::addressBarVisible(helpEngine: *d->m_helpEngine);
365}
366
367void HelpEngineWrapper::setAddressBarVisible(bool visible)
368{
369 TRACE_OBJ
370 CollectionConfiguration::setAddressBarVisible(helpEngine&: *d->m_helpEngine, visible);
371}
372
373bool HelpEngineWrapper::documentationManagerEnabled() const
374{
375 TRACE_OBJ
376 return CollectionConfiguration::documentationManagerEnabled(helpEngine: *d->m_helpEngine);
377}
378
379void HelpEngineWrapper::setDocumentationManagerEnabled(bool enabled)
380{
381 TRACE_OBJ
382 CollectionConfiguration::setDocumentationManagerEnabled(helpEngine&: *d->m_helpEngine,
383 enabled);
384}
385
386const QByteArray HelpEngineWrapper::aboutMenuTexts() const
387{
388 TRACE_OBJ
389 return CollectionConfiguration::aboutMenuTexts(helpEngine: *d->m_helpEngine);
390}
391
392void HelpEngineWrapper::setAboutMenuTexts(const QByteArray &texts)
393{
394 TRACE_OBJ
395 CollectionConfiguration::setAboutMenuTexts(helpEngine&: *d->m_helpEngine, texts);
396}
397
398const QByteArray HelpEngineWrapper::aboutIcon() const
399{
400 TRACE_OBJ
401 return CollectionConfiguration::aboutIcon(helpEngine: *d->m_helpEngine);
402}
403
404void HelpEngineWrapper::setAboutIcon(const QByteArray &icon)
405{
406 TRACE_OBJ
407 CollectionConfiguration::setAboutIcon(helpEngine&: *d->m_helpEngine, icon);
408}
409
410const QByteArray HelpEngineWrapper::aboutImages() const
411{
412 TRACE_OBJ
413 return CollectionConfiguration::aboutImages(helpEngine: *d->m_helpEngine);
414}
415
416void HelpEngineWrapper::setAboutImages(const QByteArray &images)
417{
418 TRACE_OBJ
419 CollectionConfiguration::setAboutImages(helpEngine&: *d->m_helpEngine, images);
420}
421
422const QByteArray HelpEngineWrapper::aboutTexts() const
423{
424 TRACE_OBJ
425 return CollectionConfiguration::aboutTexts(helpEngine: *d->m_helpEngine);
426}
427
428void HelpEngineWrapper::setAboutTexts(const QByteArray &texts)
429{
430 TRACE_OBJ
431 CollectionConfiguration::setAboutTexts(helpEngine&: *d->m_helpEngine, texts);
432}
433
434const QString HelpEngineWrapper::windowTitle() const
435{
436 TRACE_OBJ
437 return CollectionConfiguration::windowTitle(helpEngine: *d->m_helpEngine);
438}
439
440void HelpEngineWrapper::setWindowTitle(const QString &windowTitle)
441{
442 TRACE_OBJ
443 CollectionConfiguration::setWindowTitle(helpEngine&: *d->m_helpEngine, windowTitle);
444}
445
446const QByteArray HelpEngineWrapper::applicationIcon() const
447{
448 TRACE_OBJ
449 return CollectionConfiguration::applicationIcon(helpEngine: *d->m_helpEngine);
450}
451
452void HelpEngineWrapper::setApplicationIcon(const QByteArray &icon)
453{
454 TRACE_OBJ
455 CollectionConfiguration::setApplicationIcon(helpEngine&: *d->m_helpEngine, icon);
456}
457
458const QByteArray HelpEngineWrapper::mainWindow() const
459{
460 TRACE_OBJ
461 return d->m_helpEngine->customValue(key: MainWindowKey).toByteArray();
462}
463
464void HelpEngineWrapper::setMainWindow(const QByteArray &mainWindow)
465{
466 TRACE_OBJ
467 d->m_helpEngine->setCustomValue(key: MainWindowKey, value: mainWindow);
468}
469
470const QByteArray HelpEngineWrapper::mainWindowGeometry() const
471{
472 TRACE_OBJ
473 return d->m_helpEngine->customValue(key: MainWindowGeometryKey).toByteArray();
474}
475
476void HelpEngineWrapper::setMainWindowGeometry(const QByteArray &geometry)
477{
478 TRACE_OBJ
479 d->m_helpEngine->setCustomValue(key: MainWindowGeometryKey, value: geometry);
480}
481
482const QByteArray HelpEngineWrapper::bookmarks() const
483{
484 TRACE_OBJ
485 return d->m_helpEngine->customValue(key: BookmarksKey).toByteArray();
486}
487
488void HelpEngineWrapper::setBookmarks(const QByteArray &bookmarks)
489{
490 TRACE_OBJ
491 d->m_helpEngine->setCustomValue(key: BookmarksKey, value: bookmarks);
492}
493
494int HelpEngineWrapper::lastTabPage() const
495{
496 TRACE_OBJ
497 return CollectionConfiguration::lastTabPage(helpEngine: *d->m_helpEngine);
498}
499
500void HelpEngineWrapper::setLastTabPage(int lastPage)
501{
502 TRACE_OBJ
503 CollectionConfiguration::setLastTabPage(helpEngine&: *d->m_helpEngine, lastPage);
504}
505
506int HelpEngineWrapper::startOption() const
507{
508 TRACE_OBJ
509 return d->m_helpEngine->customValue(key: StartOptionKey, defaultValue: ShowLastPages).toInt();
510}
511
512void HelpEngineWrapper::setStartOption(int option)
513{
514 TRACE_OBJ
515 d->m_helpEngine->setCustomValue(key: StartOptionKey, value: option);
516}
517
518const QString HelpEngineWrapper::homePage() const
519{
520 TRACE_OBJ
521 const QString &homePage
522 = d->m_helpEngine->customValue(key: HomePageKey).toString();
523 if (!homePage.isEmpty())
524 return homePage;
525 return defaultHomePage();
526}
527
528void HelpEngineWrapper::setHomePage(const QString &page)
529{
530 TRACE_OBJ
531 d->m_helpEngine->setCustomValue(key: HomePageKey, value: page);
532
533}
534
535const QString HelpEngineWrapper::defaultHomePage() const
536{
537 TRACE_OBJ
538 return CollectionConfiguration::defaultHomePage(helpEngine: *d->m_helpEngine);
539}
540
541void HelpEngineWrapper::setDefaultHomePage(const QString &page)
542{
543 TRACE_OBJ
544 CollectionConfiguration::setDefaultHomePage(helpEngine&: *d->m_helpEngine, page);
545}
546
547bool HelpEngineWrapper::hasFontSettings() const
548{
549 TRACE_OBJ
550 return d->m_helpEngine->customValue(key: UseAppFontKey).isValid();
551}
552
553bool HelpEngineWrapper::usesAppFont() const
554{
555 TRACE_OBJ
556 return d->m_helpEngine->customValue(key: UseAppFontKey).toBool();
557}
558
559void HelpEngineWrapper::setUseAppFont(bool useAppFont)
560{
561 TRACE_OBJ
562 d->m_helpEngine->setCustomValue(key: UseAppFontKey, value: useAppFont);
563}
564
565bool HelpEngineWrapper::usesBrowserFont() const
566{
567 TRACE_OBJ
568 return d->m_helpEngine->customValue(key: UseBrowserFontKey, defaultValue: false).toBool();
569}
570
571void HelpEngineWrapper::setUseBrowserFont(bool useBrowserFont)
572{
573 TRACE_OBJ
574 d->m_helpEngine->setCustomValue(key: UseBrowserFontKey, value: useBrowserFont);
575}
576
577const QFont HelpEngineWrapper::appFont() const
578{
579 TRACE_OBJ
580 return qvariant_cast<QFont>(v: d->m_helpEngine->customValue(key: AppFontKey));
581}
582
583void HelpEngineWrapper::setAppFont(const QFont &font)
584{
585 TRACE_OBJ
586 d->m_helpEngine->setCustomValue(key: AppFontKey, value: font);
587}
588
589QFontDatabase::WritingSystem HelpEngineWrapper::appWritingSystem() const
590{
591 TRACE_OBJ
592 return static_cast<QFontDatabase::WritingSystem>(
593 d->m_helpEngine->customValue(key: AppWritingSystemKey).toInt());
594}
595
596void HelpEngineWrapper::setAppWritingSystem(QFontDatabase::WritingSystem system)
597{
598 TRACE_OBJ
599 d->m_helpEngine->setCustomValue(key: AppWritingSystemKey, value: system);
600}
601
602const QFont HelpEngineWrapper::browserFont() const
603{
604 TRACE_OBJ
605 return qvariant_cast<QFont>(v: d->m_helpEngine->customValue(key: BrowserFontKey));
606}
607
608void HelpEngineWrapper::setBrowserFont(const QFont &font)
609{
610 TRACE_OBJ
611 d->m_helpEngine->setCustomValue(key: BrowserFontKey, value: font);
612}
613
614QFontDatabase::WritingSystem HelpEngineWrapper::browserWritingSystem() const
615{
616 TRACE_OBJ
617 return static_cast<QFontDatabase::WritingSystem>(
618 d->m_helpEngine->customValue(key: BrowserWritingSystemKey).toInt());
619}
620
621void HelpEngineWrapper::setBrowserWritingSystem(QFontDatabase::WritingSystem system)
622{
623 TRACE_OBJ
624 d->m_helpEngine->setCustomValue(key: BrowserWritingSystemKey, value: system);
625}
626
627bool HelpEngineWrapper::showTabs() const
628{
629 TRACE_OBJ
630 return d->m_helpEngine->customValue(key: ShowTabsKey, defaultValue: false).toBool();
631}
632
633void HelpEngineWrapper::setShowTabs(bool show)
634{
635 TRACE_OBJ
636 d->m_helpEngine->setCustomValue(key: ShowTabsKey, value: show);
637}
638
639bool HelpEngineWrapper::fullTextSearchFallbackEnabled() const
640{
641 TRACE_OBJ
642 return CollectionConfiguration::fullTextSearchFallbackEnabled(helpEngine: *d->m_helpEngine);
643}
644
645const QByteArray HelpEngineWrapper::topicChooserGeometry() const
646{
647 TRACE_OBJ
648 return d->m_helpEngine->customValue(key: TopicChooserGeometryKey).toByteArray();
649}
650
651void HelpEngineWrapper::setTopicChooserGeometry(const QByteArray &geometry)
652{
653 TRACE_OBJ
654 d->m_helpEngine->setCustomValue(key: TopicChooserGeometryKey, value: geometry);
655}
656
657QHelpEngineCore *HelpEngineWrapper::helpEngine() const
658{
659 return d->m_helpEngine;
660}
661
662
663// -- TimeoutForwarder
664
665TimeoutForwarder::TimeoutForwarder(const QString &fileName)
666 : m_fileName(fileName)
667{
668 TRACE_OBJ
669}
670
671void TimeoutForwarder::forward()
672{
673 TRACE_OBJ
674 HelpEngineWrapper::instance().d->qchFileChanged(fileName: m_fileName, fromTimeout: true);
675}
676
677// -- HelpEngineWrapperPrivate
678
679HelpEngineWrapperPrivate::HelpEngineWrapperPrivate(const QString &collectionFile)
680 : m_helpEngine(new QHelpEngine(collectionFile, this)),
681 m_qchWatcher(new QFileSystemWatcher(this))
682{
683 TRACE_OBJ
684 m_helpEngine->setReadOnly(false);
685 m_helpEngine->setUsesFilterEngine(true);
686 initFileSystemWatchers();
687}
688
689void HelpEngineWrapperPrivate::initFileSystemWatchers()
690{
691 TRACE_OBJ
692 for (const QString &ns : m_helpEngine->registeredDocumentations())
693 m_qchWatcher->addPath(file: m_helpEngine->documentationFileName(namespaceName: ns));
694
695 connect(sender: m_qchWatcher, signal: &QFileSystemWatcher::fileChanged, context: this,
696 slot: QOverload<const QString &>::of(ptr: &HelpEngineWrapperPrivate::qchFileChanged));
697 checkDocFilesWatched();
698}
699
700void HelpEngineWrapperPrivate::qchFileChanged(const QString &fileName)
701{
702 TRACE_OBJ
703 qchFileChanged(fileName, fromTimeout: false);
704}
705
706void HelpEngineWrapperPrivate::checkDocFilesWatched()
707{
708 TRACE_OBJ
709 const int watchedFilesCount = m_qchWatcher->files().size();
710 const int docFilesCount = m_helpEngine->registeredDocumentations().size();
711 if (watchedFilesCount != docFilesCount) {
712 qWarning(msg: "Strange: Have %d docs, but %d are being watched",
713 watchedFilesCount, docFilesCount);
714 }
715}
716
717void HelpEngineWrapperPrivate::qchFileChanged(const QString &fileName,
718 bool fromTimeout)
719{
720 TRACE_OBJ
721
722 /*
723 * We don't use QHelpEngineCore::namespaceName(fileName), because the file
724 * may not exist anymore or contain a different namespace.
725 */
726 QString ns;
727 for (const QString &curNs : m_helpEngine->registeredDocumentations()) {
728 if (m_helpEngine->documentationFileName(namespaceName: curNs) == fileName) {
729 ns = curNs;
730 break;
731 }
732 }
733
734 /*
735 * We can't do an assertion here, because QFileSystemWatcher may send the
736 * signal more than once.
737 */
738 if (ns.isEmpty()) {
739 m_recentQchUpdates.erase(x: fileName);
740 return;
741 }
742
743 /*
744 * Since the QFileSystemWatcher typically sends the signal more than once,
745 * we repeatedly delay our reaction a bit until we think the last signal
746 * was sent.
747 */
748
749 const auto &it = m_recentQchUpdates.find(x: fileName);
750 const QDateTime now = QDateTime::currentDateTimeUtc();
751
752 // Case 1: This is the first recent signal for the file.
753 if (it == m_recentQchUpdates.end()) {
754 auto forwarder = std::make_unique<TimeoutForwarder>(args: fileName);
755 QTimer::singleShot(interval: UpdateGracePeriod, receiver: forwarder.get(),
756 slot: &TimeoutForwarder::forward);
757 m_recentQchUpdates.emplace(args: fileName,
758 args: RecentSignal{.timestamp: std::move(now), .forwarder: std::move(forwarder)});
759 return;
760 }
761
762 auto &[key, entry] = *it;
763
764 // Case 2: The last signal for this file has not expired yet.
765 if (entry.timestamp > now.addMSecs(msecs: -UpdateGracePeriod)) {
766 if (!fromTimeout)
767 entry.timestamp = now;
768 else
769 QTimer::singleShot(interval: UpdateGracePeriod, receiver: entry.forwarder.get(),
770 slot: &TimeoutForwarder::forward);
771 return;
772 }
773
774 // Case 3: The last signal for this file has expired.
775 if (m_helpEngine->unregisterDocumentation(namespaceName: ns)) {
776 if (!QFileInfo(fileName).exists()
777 || !m_helpEngine->registerDocumentation(documentationFileName: fileName)) {
778 m_qchWatcher->removePath(file: fileName);
779 emit documentationRemoved(namespaceName: ns);
780 } else {
781 emit documentationUpdated(namespaceName: ns);
782 }
783 m_helpEngine->setupData();
784 }
785 m_recentQchUpdates.erase(position: it);
786}
787
788QT_END_NAMESPACE
789
790#include "helpenginewrapper.moc"
791

source code of qttools/src/assistant/assistant/helpenginewrapper.cpp