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

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