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 | |
47 | QT_BEGIN_NAMESPACE |
48 | |
49 | namespace { |
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 | |
68 | class TimeoutForwarder : public QObject |
69 | { |
70 | Q_OBJECT |
71 | public: |
72 | TimeoutForwarder(const QString &fileName); |
73 | private slots: |
74 | void forward(); |
75 | private: |
76 | friend class HelpEngineWrapperPrivate; |
77 | |
78 | const QString m_fileName; |
79 | }; |
80 | |
81 | class HelpEngineWrapperPrivate : public QObject |
82 | { |
83 | Q_OBJECT |
84 | friend class HelpEngineWrapper; |
85 | friend class TimeoutForwarder; |
86 | private slots: |
87 | void qchFileChanged(const QString &fileName); |
88 | |
89 | signals: |
90 | void documentationRemoved(const QString &namespaceName); |
91 | void documentationUpdated(const QString &namespaceName); |
92 | |
93 | private: |
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 | |
108 | HelpEngineWrapper *HelpEngineWrapper::helpEngineWrapper = nullptr; |
109 | |
110 | HelpEngineWrapper &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 | |
122 | void HelpEngineWrapper::removeInstance() |
123 | { |
124 | TRACE_OBJ |
125 | delete helpEngineWrapper; |
126 | helpEngineWrapper = nullptr; |
127 | } |
128 | |
129 | HelpEngineWrapper::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 | |
154 | HelpEngineWrapper::~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 | |
167 | void 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 | |
176 | QHelpSearchEngine *HelpEngineWrapper::searchEngine() const |
177 | { |
178 | TRACE_OBJ |
179 | return d->m_helpEngine->searchEngine(); |
180 | } |
181 | |
182 | QHelpContentModel *HelpEngineWrapper::contentModel() const |
183 | { |
184 | TRACE_OBJ |
185 | return d->m_helpEngine->contentModel(); |
186 | } |
187 | |
188 | QHelpIndexModel *HelpEngineWrapper::indexModel() const |
189 | { |
190 | TRACE_OBJ |
191 | return d->m_helpEngine->indexModel(); |
192 | } |
193 | |
194 | QHelpContentWidget *HelpEngineWrapper::contentWidget() |
195 | { |
196 | TRACE_OBJ |
197 | return d->m_helpEngine->contentWidget(); |
198 | } |
199 | |
200 | QHelpIndexWidget *HelpEngineWrapper::indexWidget() |
201 | { |
202 | TRACE_OBJ |
203 | return d->m_helpEngine->indexWidget(); |
204 | } |
205 | |
206 | const QStringList HelpEngineWrapper::registeredDocumentations() const |
207 | { |
208 | TRACE_OBJ |
209 | return d->m_helpEngine->registeredDocumentations(); |
210 | } |
211 | |
212 | QString HelpEngineWrapper::documentationFileName(const QString &namespaceName) const |
213 | { |
214 | TRACE_OBJ |
215 | return d->m_helpEngine->documentationFileName(namespaceName); |
216 | } |
217 | |
218 | const QString HelpEngineWrapper::collectionFile() const |
219 | { |
220 | TRACE_OBJ |
221 | return d->m_helpEngine->collectionFile(); |
222 | } |
223 | |
224 | bool 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 | |
235 | bool 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 | |
247 | bool HelpEngineWrapper::setupData() |
248 | { |
249 | TRACE_OBJ |
250 | return d->m_helpEngine->setupData(); |
251 | } |
252 | |
253 | QUrl HelpEngineWrapper::findFile(const QUrl &url) const |
254 | { |
255 | TRACE_OBJ |
256 | return d->m_helpEngine->findFile(url); |
257 | } |
258 | |
259 | QByteArray HelpEngineWrapper::fileData(const QUrl &url) const |
260 | { |
261 | TRACE_OBJ |
262 | return d->m_helpEngine->fileData(url); |
263 | } |
264 | |
265 | QList<QHelpLink> HelpEngineWrapper::documentsForIdentifier(const QString &id) const |
266 | { |
267 | TRACE_OBJ |
268 | return d->m_helpEngine->documentsForIdentifier(id); |
269 | } |
270 | |
271 | QString HelpEngineWrapper::error() const |
272 | { |
273 | TRACE_OBJ |
274 | return d->m_helpEngine->error(); |
275 | } |
276 | |
277 | QHelpFilterEngine *HelpEngineWrapper::filterEngine() const |
278 | { |
279 | return d->m_helpEngine->filterEngine(); |
280 | } |
281 | |
282 | const 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 | |
289 | void 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 | |
297 | const QStringList HelpEngineWrapper::lastShownPages() const |
298 | { |
299 | TRACE_OBJ |
300 | return CollectionConfiguration::lastShownPages(helpEngine: *d->m_helpEngine); |
301 | } |
302 | |
303 | void HelpEngineWrapper::setLastShownPages(const QStringList &lastShownPages) |
304 | { |
305 | TRACE_OBJ |
306 | CollectionConfiguration::setLastShownPages(helpEngine&: *d->m_helpEngine, lastShownPages); |
307 | } |
308 | |
309 | const QStringList HelpEngineWrapper::lastZoomFactors() const |
310 | { |
311 | TRACE_OBJ |
312 | return CollectionConfiguration::lastZoomFactors(helpEngine: *d->m_helpEngine); |
313 | } |
314 | |
315 | void HelpEngineWrapper::setLastZoomFactors(const QStringList &lastZoomFactors) |
316 | { |
317 | TRACE_OBJ |
318 | CollectionConfiguration::setLastZoomFactors(helPEngine&: *d->m_helpEngine, lastZoomFactors); |
319 | } |
320 | |
321 | const QString HelpEngineWrapper::cacheDir() const |
322 | { |
323 | TRACE_OBJ |
324 | return CollectionConfiguration::cacheDir(helpEngine: *d->m_helpEngine); |
325 | } |
326 | |
327 | bool HelpEngineWrapper::cacheDirIsRelativeToCollection() const |
328 | { |
329 | TRACE_OBJ |
330 | return CollectionConfiguration::cacheDirIsRelativeToCollection(helpEngine: *d->m_helpEngine); |
331 | } |
332 | |
333 | void HelpEngineWrapper::setCacheDir(const QString &cacheDir, |
334 | bool relativeToCollection) |
335 | { |
336 | TRACE_OBJ |
337 | CollectionConfiguration::setCacheDir(helpEngine&: *d->m_helpEngine, cacheDir, |
338 | relativeToCollection); |
339 | } |
340 | |
341 | bool HelpEngineWrapper::filterFunctionalityEnabled() const |
342 | { |
343 | TRACE_OBJ |
344 | return CollectionConfiguration::filterFunctionalityEnabled(helpEngine: *d->m_helpEngine); |
345 | } |
346 | |
347 | void HelpEngineWrapper::setFilterFunctionalityEnabled(bool enabled) |
348 | { |
349 | TRACE_OBJ |
350 | CollectionConfiguration::setFilterFunctionalityEnabled(helpEngine&: *d->m_helpEngine, |
351 | enabled); |
352 | } |
353 | |
354 | bool HelpEngineWrapper::filterToolbarVisible() const |
355 | { |
356 | TRACE_OBJ |
357 | return CollectionConfiguration::filterToolbarVisible(helpEngine: *d->m_helpEngine); |
358 | } |
359 | |
360 | void HelpEngineWrapper::setFilterToolbarVisible(bool visible) |
361 | { |
362 | TRACE_OBJ |
363 | CollectionConfiguration::setFilterToolbarVisible(helpEngine&: *d->m_helpEngine, visible); |
364 | } |
365 | |
366 | bool HelpEngineWrapper::addressBarEnabled() const |
367 | { |
368 | TRACE_OBJ |
369 | return CollectionConfiguration::addressBarEnabled(helpEngine: *d->m_helpEngine); |
370 | } |
371 | |
372 | void HelpEngineWrapper::setAddressBarEnabled(bool enabled) |
373 | { |
374 | TRACE_OBJ |
375 | CollectionConfiguration::setAddressBarEnabled(helpEngine&: *d->m_helpEngine, enabled); |
376 | } |
377 | |
378 | bool HelpEngineWrapper::addressBarVisible() const |
379 | { |
380 | TRACE_OBJ |
381 | return CollectionConfiguration::addressBarVisible(helpEngine: *d->m_helpEngine); |
382 | } |
383 | |
384 | void HelpEngineWrapper::setAddressBarVisible(bool visible) |
385 | { |
386 | TRACE_OBJ |
387 | CollectionConfiguration::setAddressBarVisible(helpEngine&: *d->m_helpEngine, visible); |
388 | } |
389 | |
390 | bool HelpEngineWrapper::documentationManagerEnabled() const |
391 | { |
392 | TRACE_OBJ |
393 | return CollectionConfiguration::documentationManagerEnabled(helpEngine: *d->m_helpEngine); |
394 | } |
395 | |
396 | void HelpEngineWrapper::setDocumentationManagerEnabled(bool enabled) |
397 | { |
398 | TRACE_OBJ |
399 | CollectionConfiguration::setDocumentationManagerEnabled(helpEngine&: *d->m_helpEngine, |
400 | enabled); |
401 | } |
402 | |
403 | const QByteArray HelpEngineWrapper::() const |
404 | { |
405 | TRACE_OBJ |
406 | return CollectionConfiguration::aboutMenuTexts(helpEngine: *d->m_helpEngine); |
407 | } |
408 | |
409 | void HelpEngineWrapper::(const QByteArray &texts) |
410 | { |
411 | TRACE_OBJ |
412 | CollectionConfiguration::setAboutMenuTexts(helpEngine&: *d->m_helpEngine, texts); |
413 | } |
414 | |
415 | const QByteArray HelpEngineWrapper::aboutIcon() const |
416 | { |
417 | TRACE_OBJ |
418 | return CollectionConfiguration::aboutIcon(helpEngine: *d->m_helpEngine); |
419 | } |
420 | |
421 | void HelpEngineWrapper::setAboutIcon(const QByteArray &icon) |
422 | { |
423 | TRACE_OBJ |
424 | CollectionConfiguration::setAboutIcon(helpEngine&: *d->m_helpEngine, icon); |
425 | } |
426 | |
427 | const QByteArray HelpEngineWrapper::aboutImages() const |
428 | { |
429 | TRACE_OBJ |
430 | return CollectionConfiguration::aboutImages(helpEngine: *d->m_helpEngine); |
431 | } |
432 | |
433 | void HelpEngineWrapper::setAboutImages(const QByteArray &images) |
434 | { |
435 | TRACE_OBJ |
436 | CollectionConfiguration::setAboutImages(helpEngine&: *d->m_helpEngine, images); |
437 | } |
438 | |
439 | const QByteArray HelpEngineWrapper::aboutTexts() const |
440 | { |
441 | TRACE_OBJ |
442 | return CollectionConfiguration::aboutTexts(helpEngine: *d->m_helpEngine); |
443 | } |
444 | |
445 | void HelpEngineWrapper::setAboutTexts(const QByteArray &texts) |
446 | { |
447 | TRACE_OBJ |
448 | CollectionConfiguration::setAboutTexts(helpEngine&: *d->m_helpEngine, texts); |
449 | } |
450 | |
451 | const QString HelpEngineWrapper::windowTitle() const |
452 | { |
453 | TRACE_OBJ |
454 | return CollectionConfiguration::windowTitle(helpEngine: *d->m_helpEngine); |
455 | } |
456 | |
457 | void HelpEngineWrapper::setWindowTitle(const QString &windowTitle) |
458 | { |
459 | TRACE_OBJ |
460 | CollectionConfiguration::setWindowTitle(helpEngine&: *d->m_helpEngine, windowTitle); |
461 | } |
462 | |
463 | const QByteArray HelpEngineWrapper::applicationIcon() const |
464 | { |
465 | TRACE_OBJ |
466 | return CollectionConfiguration::applicationIcon(helpEngine: *d->m_helpEngine); |
467 | } |
468 | |
469 | void HelpEngineWrapper::setApplicationIcon(const QByteArray &icon) |
470 | { |
471 | TRACE_OBJ |
472 | CollectionConfiguration::setApplicationIcon(helpEngine&: *d->m_helpEngine, icon); |
473 | } |
474 | |
475 | const QByteArray HelpEngineWrapper::mainWindow() const |
476 | { |
477 | TRACE_OBJ |
478 | return d->m_helpEngine->customValue(key: MainWindowKey).toByteArray(); |
479 | } |
480 | |
481 | void HelpEngineWrapper::setMainWindow(const QByteArray &mainWindow) |
482 | { |
483 | TRACE_OBJ |
484 | d->m_helpEngine->setCustomValue(key: MainWindowKey, value: mainWindow); |
485 | } |
486 | |
487 | const QByteArray HelpEngineWrapper::mainWindowGeometry() const |
488 | { |
489 | TRACE_OBJ |
490 | return d->m_helpEngine->customValue(key: MainWindowGeometryKey).toByteArray(); |
491 | } |
492 | |
493 | void HelpEngineWrapper::setMainWindowGeometry(const QByteArray &geometry) |
494 | { |
495 | TRACE_OBJ |
496 | d->m_helpEngine->setCustomValue(key: MainWindowGeometryKey, value: geometry); |
497 | } |
498 | |
499 | const QByteArray HelpEngineWrapper::bookmarks() const |
500 | { |
501 | TRACE_OBJ |
502 | return d->m_helpEngine->customValue(key: BookmarksKey).toByteArray(); |
503 | } |
504 | |
505 | void HelpEngineWrapper::setBookmarks(const QByteArray &bookmarks) |
506 | { |
507 | TRACE_OBJ |
508 | d->m_helpEngine->setCustomValue(key: BookmarksKey, value: bookmarks); |
509 | } |
510 | |
511 | int HelpEngineWrapper::lastTabPage() const |
512 | { |
513 | TRACE_OBJ |
514 | return CollectionConfiguration::lastTabPage(helpEngine: *d->m_helpEngine); |
515 | } |
516 | |
517 | void HelpEngineWrapper::setLastTabPage(int lastPage) |
518 | { |
519 | TRACE_OBJ |
520 | CollectionConfiguration::setLastTabPage(helpEngine&: *d->m_helpEngine, lastPage); |
521 | } |
522 | |
523 | int HelpEngineWrapper::startOption() const |
524 | { |
525 | TRACE_OBJ |
526 | return d->m_helpEngine->customValue(key: StartOptionKey, defaultValue: ShowLastPages).toInt(); |
527 | } |
528 | |
529 | void HelpEngineWrapper::setStartOption(int option) |
530 | { |
531 | TRACE_OBJ |
532 | d->m_helpEngine->setCustomValue(key: StartOptionKey, value: option); |
533 | } |
534 | |
535 | const 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 | |
545 | void HelpEngineWrapper::setHomePage(const QString &page) |
546 | { |
547 | TRACE_OBJ |
548 | d->m_helpEngine->setCustomValue(key: HomePageKey, value: page); |
549 | |
550 | } |
551 | |
552 | const QString HelpEngineWrapper::defaultHomePage() const |
553 | { |
554 | TRACE_OBJ |
555 | return CollectionConfiguration::defaultHomePage(helpEngine: *d->m_helpEngine); |
556 | } |
557 | |
558 | void HelpEngineWrapper::setDefaultHomePage(const QString &page) |
559 | { |
560 | TRACE_OBJ |
561 | CollectionConfiguration::setDefaultHomePage(helpEngine&: *d->m_helpEngine, page); |
562 | } |
563 | |
564 | bool HelpEngineWrapper::hasFontSettings() const |
565 | { |
566 | TRACE_OBJ |
567 | return d->m_helpEngine->customValue(key: UseAppFontKey).isValid(); |
568 | } |
569 | |
570 | bool HelpEngineWrapper::usesAppFont() const |
571 | { |
572 | TRACE_OBJ |
573 | return d->m_helpEngine->customValue(key: UseAppFontKey).toBool(); |
574 | } |
575 | |
576 | void HelpEngineWrapper::setUseAppFont(bool useAppFont) |
577 | { |
578 | TRACE_OBJ |
579 | d->m_helpEngine->setCustomValue(key: UseAppFontKey, value: useAppFont); |
580 | } |
581 | |
582 | bool HelpEngineWrapper::usesBrowserFont() const |
583 | { |
584 | TRACE_OBJ |
585 | return d->m_helpEngine->customValue(key: UseBrowserFontKey, defaultValue: false).toBool(); |
586 | } |
587 | |
588 | void HelpEngineWrapper::setUseBrowserFont(bool useBrowserFont) |
589 | { |
590 | TRACE_OBJ |
591 | d->m_helpEngine->setCustomValue(key: UseBrowserFontKey, value: useBrowserFont); |
592 | } |
593 | |
594 | const QFont HelpEngineWrapper::appFont() const |
595 | { |
596 | TRACE_OBJ |
597 | return qvariant_cast<QFont>(v: d->m_helpEngine->customValue(key: AppFontKey)); |
598 | } |
599 | |
600 | void HelpEngineWrapper::setAppFont(const QFont &font) |
601 | { |
602 | TRACE_OBJ |
603 | d->m_helpEngine->setCustomValue(key: AppFontKey, value: font); |
604 | } |
605 | |
606 | QFontDatabase::WritingSystem HelpEngineWrapper::appWritingSystem() const |
607 | { |
608 | TRACE_OBJ |
609 | return static_cast<QFontDatabase::WritingSystem>( |
610 | d->m_helpEngine->customValue(key: AppWritingSystemKey).toInt()); |
611 | } |
612 | |
613 | void HelpEngineWrapper::setAppWritingSystem(QFontDatabase::WritingSystem system) |
614 | { |
615 | TRACE_OBJ |
616 | d->m_helpEngine->setCustomValue(key: AppWritingSystemKey, value: system); |
617 | } |
618 | |
619 | const QFont HelpEngineWrapper::browserFont() const |
620 | { |
621 | TRACE_OBJ |
622 | return qvariant_cast<QFont>(v: d->m_helpEngine->customValue(key: BrowserFontKey)); |
623 | } |
624 | |
625 | void HelpEngineWrapper::setBrowserFont(const QFont &font) |
626 | { |
627 | TRACE_OBJ |
628 | d->m_helpEngine->setCustomValue(key: BrowserFontKey, value: font); |
629 | } |
630 | |
631 | QFontDatabase::WritingSystem HelpEngineWrapper::browserWritingSystem() const |
632 | { |
633 | TRACE_OBJ |
634 | return static_cast<QFontDatabase::WritingSystem>( |
635 | d->m_helpEngine->customValue(key: BrowserWritingSystemKey).toInt()); |
636 | } |
637 | |
638 | void HelpEngineWrapper::setBrowserWritingSystem(QFontDatabase::WritingSystem system) |
639 | { |
640 | TRACE_OBJ |
641 | d->m_helpEngine->setCustomValue(key: BrowserWritingSystemKey, value: system); |
642 | } |
643 | |
644 | bool HelpEngineWrapper::showTabs() const |
645 | { |
646 | TRACE_OBJ |
647 | return d->m_helpEngine->customValue(key: ShowTabsKey, defaultValue: false).toBool(); |
648 | } |
649 | |
650 | void HelpEngineWrapper::setShowTabs(bool show) |
651 | { |
652 | TRACE_OBJ |
653 | d->m_helpEngine->setCustomValue(key: ShowTabsKey, value: show); |
654 | } |
655 | |
656 | bool HelpEngineWrapper::fullTextSearchFallbackEnabled() const |
657 | { |
658 | TRACE_OBJ |
659 | return CollectionConfiguration::fullTextSearchFallbackEnabled(helpEngine: *d->m_helpEngine); |
660 | } |
661 | |
662 | const QByteArray HelpEngineWrapper::topicChooserGeometry() const |
663 | { |
664 | TRACE_OBJ |
665 | return d->m_helpEngine->customValue(key: TopicChooserGeometryKey).toByteArray(); |
666 | } |
667 | |
668 | void HelpEngineWrapper::setTopicChooserGeometry(const QByteArray &geometry) |
669 | { |
670 | TRACE_OBJ |
671 | d->m_helpEngine->setCustomValue(key: TopicChooserGeometryKey, value: geometry); |
672 | } |
673 | |
674 | QHelpEngineCore *HelpEngineWrapper::helpEngine() const |
675 | { |
676 | return d->m_helpEngine; |
677 | } |
678 | |
679 | |
680 | // -- TimeoutForwarder |
681 | |
682 | TimeoutForwarder::TimeoutForwarder(const QString &fileName) |
683 | : m_fileName(fileName) |
684 | { |
685 | TRACE_OBJ |
686 | } |
687 | |
688 | void TimeoutForwarder::forward() |
689 | { |
690 | TRACE_OBJ |
691 | HelpEngineWrapper::instance().d->qchFileChanged(fileName: m_fileName, fromTimeout: true); |
692 | } |
693 | |
694 | // -- HelpEngineWrapperPrivate |
695 | |
696 | HelpEngineWrapperPrivate::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 | |
705 | void 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 | |
716 | void HelpEngineWrapperPrivate::qchFileChanged(const QString &fileName) |
717 | { |
718 | TRACE_OBJ |
719 | qchFileChanged(fileName, fromTimeout: false); |
720 | } |
721 | |
722 | void 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 | |
733 | void 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 | |
801 | QT_END_NAMESPACE |
802 | |
803 | #include "helpenginewrapper.moc" |
804 | |