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 | |
22 | QT_BEGIN_NAMESPACE |
23 | |
24 | namespace { |
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 | |
43 | class TimeoutForwarder : public QObject |
44 | { |
45 | Q_OBJECT |
46 | public: |
47 | TimeoutForwarder(const QString &fileName); |
48 | private slots: |
49 | void forward(); |
50 | private: |
51 | friend class HelpEngineWrapperPrivate; |
52 | |
53 | const QString m_fileName; |
54 | }; |
55 | |
56 | class HelpEngineWrapperPrivate : public QObject |
57 | { |
58 | Q_OBJECT |
59 | friend class HelpEngineWrapper; |
60 | friend class TimeoutForwarder; |
61 | private slots: |
62 | void qchFileChanged(const QString &fileName); |
63 | |
64 | signals: |
65 | void documentationRemoved(const QString &namespaceName); |
66 | void documentationUpdated(const QString &namespaceName); |
67 | |
68 | private: |
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 | |
86 | HelpEngineWrapper *HelpEngineWrapper::helpEngineWrapper = nullptr; |
87 | |
88 | HelpEngineWrapper &HelpEngineWrapper::instance() |
89 | { |
90 | return instance(collectionFile: {}); |
91 | } |
92 | |
93 | HelpEngineWrapper &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 | |
105 | void HelpEngineWrapper::removeInstance() |
106 | { |
107 | TRACE_OBJ |
108 | delete helpEngineWrapper; |
109 | helpEngineWrapper = nullptr; |
110 | } |
111 | |
112 | HelpEngineWrapper::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 | |
137 | HelpEngineWrapper::~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 | |
150 | void 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 | |
159 | QHelpSearchEngine *HelpEngineWrapper::searchEngine() const |
160 | { |
161 | TRACE_OBJ |
162 | return d->m_helpEngine->searchEngine(); |
163 | } |
164 | |
165 | QHelpContentModel *HelpEngineWrapper::contentModel() const |
166 | { |
167 | TRACE_OBJ |
168 | return d->m_helpEngine->contentModel(); |
169 | } |
170 | |
171 | QHelpIndexModel *HelpEngineWrapper::indexModel() const |
172 | { |
173 | TRACE_OBJ |
174 | return d->m_helpEngine->indexModel(); |
175 | } |
176 | |
177 | QHelpContentWidget *HelpEngineWrapper::contentWidget() |
178 | { |
179 | TRACE_OBJ |
180 | return d->m_helpEngine->contentWidget(); |
181 | } |
182 | |
183 | QHelpIndexWidget *HelpEngineWrapper::indexWidget() |
184 | { |
185 | TRACE_OBJ |
186 | return d->m_helpEngine->indexWidget(); |
187 | } |
188 | |
189 | const QStringList HelpEngineWrapper::registeredDocumentations() const |
190 | { |
191 | TRACE_OBJ |
192 | return d->m_helpEngine->registeredDocumentations(); |
193 | } |
194 | |
195 | QString HelpEngineWrapper::documentationFileName(const QString &namespaceName) const |
196 | { |
197 | TRACE_OBJ |
198 | return d->m_helpEngine->documentationFileName(namespaceName); |
199 | } |
200 | |
201 | const QString HelpEngineWrapper::collectionFile() const |
202 | { |
203 | TRACE_OBJ |
204 | return d->m_helpEngine->collectionFile(); |
205 | } |
206 | |
207 | bool 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 | |
218 | bool 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 | |
230 | bool HelpEngineWrapper::setupData() |
231 | { |
232 | TRACE_OBJ |
233 | return d->m_helpEngine->setupData(); |
234 | } |
235 | |
236 | QUrl HelpEngineWrapper::findFile(const QUrl &url) const |
237 | { |
238 | TRACE_OBJ |
239 | return d->m_helpEngine->findFile(url); |
240 | } |
241 | |
242 | QByteArray HelpEngineWrapper::fileData(const QUrl &url) const |
243 | { |
244 | TRACE_OBJ |
245 | return d->m_helpEngine->fileData(url); |
246 | } |
247 | |
248 | QList<QHelpLink> HelpEngineWrapper::documentsForIdentifier(const QString &id) const |
249 | { |
250 | TRACE_OBJ |
251 | return d->m_helpEngine->documentsForIdentifier(id); |
252 | } |
253 | |
254 | QString HelpEngineWrapper::error() const |
255 | { |
256 | TRACE_OBJ |
257 | return d->m_helpEngine->error(); |
258 | } |
259 | |
260 | QHelpFilterEngine *HelpEngineWrapper::filterEngine() const |
261 | { |
262 | return d->m_helpEngine->filterEngine(); |
263 | } |
264 | |
265 | const 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 | |
272 | void 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 | |
280 | const QStringList HelpEngineWrapper::lastShownPages() const |
281 | { |
282 | TRACE_OBJ |
283 | return CollectionConfiguration::lastShownPages(helpEngine: *d->m_helpEngine); |
284 | } |
285 | |
286 | void HelpEngineWrapper::setLastShownPages(const QStringList &lastShownPages) |
287 | { |
288 | TRACE_OBJ |
289 | CollectionConfiguration::setLastShownPages(helpEngine&: *d->m_helpEngine, lastShownPages); |
290 | } |
291 | |
292 | const QStringList HelpEngineWrapper::lastZoomFactors() const |
293 | { |
294 | TRACE_OBJ |
295 | return CollectionConfiguration::lastZoomFactors(helpEngine: *d->m_helpEngine); |
296 | } |
297 | |
298 | void HelpEngineWrapper::setLastZoomFactors(const QStringList &lastZoomFactors) |
299 | { |
300 | TRACE_OBJ |
301 | CollectionConfiguration::setLastZoomFactors(helPEngine&: *d->m_helpEngine, lastZoomFactors); |
302 | } |
303 | |
304 | const QString HelpEngineWrapper::cacheDir() const |
305 | { |
306 | TRACE_OBJ |
307 | return CollectionConfiguration::cacheDir(helpEngine: *d->m_helpEngine); |
308 | } |
309 | |
310 | bool HelpEngineWrapper::cacheDirIsRelativeToCollection() const |
311 | { |
312 | TRACE_OBJ |
313 | return CollectionConfiguration::cacheDirIsRelativeToCollection(helpEngine: *d->m_helpEngine); |
314 | } |
315 | |
316 | void HelpEngineWrapper::setCacheDir(const QString &cacheDir, |
317 | bool relativeToCollection) |
318 | { |
319 | TRACE_OBJ |
320 | CollectionConfiguration::setCacheDir(helpEngine&: *d->m_helpEngine, cacheDir, |
321 | relativeToCollection); |
322 | } |
323 | |
324 | bool HelpEngineWrapper::filterFunctionalityEnabled() const |
325 | { |
326 | TRACE_OBJ |
327 | return CollectionConfiguration::filterFunctionalityEnabled(helpEngine: *d->m_helpEngine); |
328 | } |
329 | |
330 | void HelpEngineWrapper::setFilterFunctionalityEnabled(bool enabled) |
331 | { |
332 | TRACE_OBJ |
333 | CollectionConfiguration::setFilterFunctionalityEnabled(helpEngine&: *d->m_helpEngine, |
334 | enabled); |
335 | } |
336 | |
337 | bool HelpEngineWrapper::filterToolbarVisible() const |
338 | { |
339 | TRACE_OBJ |
340 | return CollectionConfiguration::filterToolbarVisible(helpEngine: *d->m_helpEngine); |
341 | } |
342 | |
343 | void HelpEngineWrapper::setFilterToolbarVisible(bool visible) |
344 | { |
345 | TRACE_OBJ |
346 | CollectionConfiguration::setFilterToolbarVisible(helpEngine&: *d->m_helpEngine, visible); |
347 | } |
348 | |
349 | bool HelpEngineWrapper::addressBarEnabled() const |
350 | { |
351 | TRACE_OBJ |
352 | return CollectionConfiguration::addressBarEnabled(helpEngine: *d->m_helpEngine); |
353 | } |
354 | |
355 | void HelpEngineWrapper::setAddressBarEnabled(bool enabled) |
356 | { |
357 | TRACE_OBJ |
358 | CollectionConfiguration::setAddressBarEnabled(helpEngine&: *d->m_helpEngine, enabled); |
359 | } |
360 | |
361 | bool HelpEngineWrapper::addressBarVisible() const |
362 | { |
363 | TRACE_OBJ |
364 | return CollectionConfiguration::addressBarVisible(helpEngine: *d->m_helpEngine); |
365 | } |
366 | |
367 | void HelpEngineWrapper::setAddressBarVisible(bool visible) |
368 | { |
369 | TRACE_OBJ |
370 | CollectionConfiguration::setAddressBarVisible(helpEngine&: *d->m_helpEngine, visible); |
371 | } |
372 | |
373 | bool HelpEngineWrapper::documentationManagerEnabled() const |
374 | { |
375 | TRACE_OBJ |
376 | return CollectionConfiguration::documentationManagerEnabled(helpEngine: *d->m_helpEngine); |
377 | } |
378 | |
379 | void HelpEngineWrapper::setDocumentationManagerEnabled(bool enabled) |
380 | { |
381 | TRACE_OBJ |
382 | CollectionConfiguration::setDocumentationManagerEnabled(helpEngine&: *d->m_helpEngine, |
383 | enabled); |
384 | } |
385 | |
386 | const QByteArray HelpEngineWrapper::() const |
387 | { |
388 | TRACE_OBJ |
389 | return CollectionConfiguration::aboutMenuTexts(helpEngine: *d->m_helpEngine); |
390 | } |
391 | |
392 | void HelpEngineWrapper::(const QByteArray &texts) |
393 | { |
394 | TRACE_OBJ |
395 | CollectionConfiguration::setAboutMenuTexts(helpEngine&: *d->m_helpEngine, texts); |
396 | } |
397 | |
398 | const QByteArray HelpEngineWrapper::aboutIcon() const |
399 | { |
400 | TRACE_OBJ |
401 | return CollectionConfiguration::aboutIcon(helpEngine: *d->m_helpEngine); |
402 | } |
403 | |
404 | void HelpEngineWrapper::setAboutIcon(const QByteArray &icon) |
405 | { |
406 | TRACE_OBJ |
407 | CollectionConfiguration::setAboutIcon(helpEngine&: *d->m_helpEngine, icon); |
408 | } |
409 | |
410 | const QByteArray HelpEngineWrapper::aboutImages() const |
411 | { |
412 | TRACE_OBJ |
413 | return CollectionConfiguration::aboutImages(helpEngine: *d->m_helpEngine); |
414 | } |
415 | |
416 | void HelpEngineWrapper::setAboutImages(const QByteArray &images) |
417 | { |
418 | TRACE_OBJ |
419 | CollectionConfiguration::setAboutImages(helpEngine&: *d->m_helpEngine, images); |
420 | } |
421 | |
422 | const QByteArray HelpEngineWrapper::aboutTexts() const |
423 | { |
424 | TRACE_OBJ |
425 | return CollectionConfiguration::aboutTexts(helpEngine: *d->m_helpEngine); |
426 | } |
427 | |
428 | void HelpEngineWrapper::setAboutTexts(const QByteArray &texts) |
429 | { |
430 | TRACE_OBJ |
431 | CollectionConfiguration::setAboutTexts(helpEngine&: *d->m_helpEngine, texts); |
432 | } |
433 | |
434 | const QString HelpEngineWrapper::windowTitle() const |
435 | { |
436 | TRACE_OBJ |
437 | return CollectionConfiguration::windowTitle(helpEngine: *d->m_helpEngine); |
438 | } |
439 | |
440 | void HelpEngineWrapper::setWindowTitle(const QString &windowTitle) |
441 | { |
442 | TRACE_OBJ |
443 | CollectionConfiguration::setWindowTitle(helpEngine&: *d->m_helpEngine, windowTitle); |
444 | } |
445 | |
446 | const QByteArray HelpEngineWrapper::applicationIcon() const |
447 | { |
448 | TRACE_OBJ |
449 | return CollectionConfiguration::applicationIcon(helpEngine: *d->m_helpEngine); |
450 | } |
451 | |
452 | void HelpEngineWrapper::setApplicationIcon(const QByteArray &icon) |
453 | { |
454 | TRACE_OBJ |
455 | CollectionConfiguration::setApplicationIcon(helpEngine&: *d->m_helpEngine, icon); |
456 | } |
457 | |
458 | const QByteArray HelpEngineWrapper::mainWindow() const |
459 | { |
460 | TRACE_OBJ |
461 | return d->m_helpEngine->customValue(key: MainWindowKey).toByteArray(); |
462 | } |
463 | |
464 | void HelpEngineWrapper::setMainWindow(const QByteArray &mainWindow) |
465 | { |
466 | TRACE_OBJ |
467 | d->m_helpEngine->setCustomValue(key: MainWindowKey, value: mainWindow); |
468 | } |
469 | |
470 | const QByteArray HelpEngineWrapper::mainWindowGeometry() const |
471 | { |
472 | TRACE_OBJ |
473 | return d->m_helpEngine->customValue(key: MainWindowGeometryKey).toByteArray(); |
474 | } |
475 | |
476 | void HelpEngineWrapper::setMainWindowGeometry(const QByteArray &geometry) |
477 | { |
478 | TRACE_OBJ |
479 | d->m_helpEngine->setCustomValue(key: MainWindowGeometryKey, value: geometry); |
480 | } |
481 | |
482 | const QByteArray HelpEngineWrapper::bookmarks() const |
483 | { |
484 | TRACE_OBJ |
485 | return d->m_helpEngine->customValue(key: BookmarksKey).toByteArray(); |
486 | } |
487 | |
488 | void HelpEngineWrapper::setBookmarks(const QByteArray &bookmarks) |
489 | { |
490 | TRACE_OBJ |
491 | d->m_helpEngine->setCustomValue(key: BookmarksKey, value: bookmarks); |
492 | } |
493 | |
494 | int HelpEngineWrapper::lastTabPage() const |
495 | { |
496 | TRACE_OBJ |
497 | return CollectionConfiguration::lastTabPage(helpEngine: *d->m_helpEngine); |
498 | } |
499 | |
500 | void HelpEngineWrapper::setLastTabPage(int lastPage) |
501 | { |
502 | TRACE_OBJ |
503 | CollectionConfiguration::setLastTabPage(helpEngine&: *d->m_helpEngine, lastPage); |
504 | } |
505 | |
506 | int HelpEngineWrapper::startOption() const |
507 | { |
508 | TRACE_OBJ |
509 | return d->m_helpEngine->customValue(key: StartOptionKey, defaultValue: ShowLastPages).toInt(); |
510 | } |
511 | |
512 | void HelpEngineWrapper::setStartOption(int option) |
513 | { |
514 | TRACE_OBJ |
515 | d->m_helpEngine->setCustomValue(key: StartOptionKey, value: option); |
516 | } |
517 | |
518 | const 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 | |
528 | void HelpEngineWrapper::setHomePage(const QString &page) |
529 | { |
530 | TRACE_OBJ |
531 | d->m_helpEngine->setCustomValue(key: HomePageKey, value: page); |
532 | |
533 | } |
534 | |
535 | const QString HelpEngineWrapper::defaultHomePage() const |
536 | { |
537 | TRACE_OBJ |
538 | return CollectionConfiguration::defaultHomePage(helpEngine: *d->m_helpEngine); |
539 | } |
540 | |
541 | void HelpEngineWrapper::setDefaultHomePage(const QString &page) |
542 | { |
543 | TRACE_OBJ |
544 | CollectionConfiguration::setDefaultHomePage(helpEngine&: *d->m_helpEngine, page); |
545 | } |
546 | |
547 | bool HelpEngineWrapper::hasFontSettings() const |
548 | { |
549 | TRACE_OBJ |
550 | return d->m_helpEngine->customValue(key: UseAppFontKey).isValid(); |
551 | } |
552 | |
553 | bool HelpEngineWrapper::usesAppFont() const |
554 | { |
555 | TRACE_OBJ |
556 | return d->m_helpEngine->customValue(key: UseAppFontKey).toBool(); |
557 | } |
558 | |
559 | void HelpEngineWrapper::setUseAppFont(bool useAppFont) |
560 | { |
561 | TRACE_OBJ |
562 | d->m_helpEngine->setCustomValue(key: UseAppFontKey, value: useAppFont); |
563 | } |
564 | |
565 | bool HelpEngineWrapper::usesBrowserFont() const |
566 | { |
567 | TRACE_OBJ |
568 | return d->m_helpEngine->customValue(key: UseBrowserFontKey, defaultValue: false).toBool(); |
569 | } |
570 | |
571 | void HelpEngineWrapper::setUseBrowserFont(bool useBrowserFont) |
572 | { |
573 | TRACE_OBJ |
574 | d->m_helpEngine->setCustomValue(key: UseBrowserFontKey, value: useBrowserFont); |
575 | } |
576 | |
577 | const QFont HelpEngineWrapper::appFont() const |
578 | { |
579 | TRACE_OBJ |
580 | return qvariant_cast<QFont>(v: d->m_helpEngine->customValue(key: AppFontKey)); |
581 | } |
582 | |
583 | void HelpEngineWrapper::setAppFont(const QFont &font) |
584 | { |
585 | TRACE_OBJ |
586 | d->m_helpEngine->setCustomValue(key: AppFontKey, value: font); |
587 | } |
588 | |
589 | QFontDatabase::WritingSystem HelpEngineWrapper::appWritingSystem() const |
590 | { |
591 | TRACE_OBJ |
592 | return static_cast<QFontDatabase::WritingSystem>( |
593 | d->m_helpEngine->customValue(key: AppWritingSystemKey).toInt()); |
594 | } |
595 | |
596 | void HelpEngineWrapper::setAppWritingSystem(QFontDatabase::WritingSystem system) |
597 | { |
598 | TRACE_OBJ |
599 | d->m_helpEngine->setCustomValue(key: AppWritingSystemKey, value: system); |
600 | } |
601 | |
602 | const QFont HelpEngineWrapper::browserFont() const |
603 | { |
604 | TRACE_OBJ |
605 | return qvariant_cast<QFont>(v: d->m_helpEngine->customValue(key: BrowserFontKey)); |
606 | } |
607 | |
608 | void HelpEngineWrapper::setBrowserFont(const QFont &font) |
609 | { |
610 | TRACE_OBJ |
611 | d->m_helpEngine->setCustomValue(key: BrowserFontKey, value: font); |
612 | } |
613 | |
614 | QFontDatabase::WritingSystem HelpEngineWrapper::browserWritingSystem() const |
615 | { |
616 | TRACE_OBJ |
617 | return static_cast<QFontDatabase::WritingSystem>( |
618 | d->m_helpEngine->customValue(key: BrowserWritingSystemKey).toInt()); |
619 | } |
620 | |
621 | void HelpEngineWrapper::setBrowserWritingSystem(QFontDatabase::WritingSystem system) |
622 | { |
623 | TRACE_OBJ |
624 | d->m_helpEngine->setCustomValue(key: BrowserWritingSystemKey, value: system); |
625 | } |
626 | |
627 | bool HelpEngineWrapper::showTabs() const |
628 | { |
629 | TRACE_OBJ |
630 | return d->m_helpEngine->customValue(key: ShowTabsKey, defaultValue: false).toBool(); |
631 | } |
632 | |
633 | void HelpEngineWrapper::setShowTabs(bool show) |
634 | { |
635 | TRACE_OBJ |
636 | d->m_helpEngine->setCustomValue(key: ShowTabsKey, value: show); |
637 | } |
638 | |
639 | bool HelpEngineWrapper::fullTextSearchFallbackEnabled() const |
640 | { |
641 | TRACE_OBJ |
642 | return CollectionConfiguration::fullTextSearchFallbackEnabled(helpEngine: *d->m_helpEngine); |
643 | } |
644 | |
645 | const QByteArray HelpEngineWrapper::topicChooserGeometry() const |
646 | { |
647 | TRACE_OBJ |
648 | return d->m_helpEngine->customValue(key: TopicChooserGeometryKey).toByteArray(); |
649 | } |
650 | |
651 | void HelpEngineWrapper::setTopicChooserGeometry(const QByteArray &geometry) |
652 | { |
653 | TRACE_OBJ |
654 | d->m_helpEngine->setCustomValue(key: TopicChooserGeometryKey, value: geometry); |
655 | } |
656 | |
657 | QHelpEngineCore *HelpEngineWrapper::helpEngine() const |
658 | { |
659 | return d->m_helpEngine; |
660 | } |
661 | |
662 | |
663 | // -- TimeoutForwarder |
664 | |
665 | TimeoutForwarder::TimeoutForwarder(const QString &fileName) |
666 | : m_fileName(fileName) |
667 | { |
668 | TRACE_OBJ |
669 | } |
670 | |
671 | void TimeoutForwarder::forward() |
672 | { |
673 | TRACE_OBJ |
674 | HelpEngineWrapper::instance().d->qchFileChanged(fileName: m_fileName, fromTimeout: true); |
675 | } |
676 | |
677 | // -- HelpEngineWrapperPrivate |
678 | |
679 | HelpEngineWrapperPrivate::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 | |
689 | void 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 | |
700 | void HelpEngineWrapperPrivate::qchFileChanged(const QString &fileName) |
701 | { |
702 | TRACE_OBJ |
703 | qchFileChanged(fileName, fromTimeout: false); |
704 | } |
705 | |
706 | void 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 | |
717 | void 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 | |
788 | QT_END_NAMESPACE |
789 | |
790 | #include "helpenginewrapper.moc" |
791 | |