1/****************************************************************************
2**
3** Copyright (C) 2020 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
29#include "mainwindow.h"
30
31#include "aboutdialog.h"
32#include "bookmarkmanager.h"
33#include "centralwidget.h"
34#include "cmdlineparser.h"
35#include "contentwindow.h"
36#include "globalactions.h"
37#include "helpenginewrapper.h"
38#include "indexwindow.h"
39#include "openpagesmanager.h"
40#include "preferencesdialog.h"
41#include "qtdocinstaller.h"
42#include "remotecontrol.h"
43#include "searchwidget.h"
44#include "topicchooser.h"
45#include "tracer.h"
46
47#include <QtCore/QByteArray>
48#include <QtCore/QDateTime>
49#include <QtCore/QDebug>
50#include <QtCore/QDir>
51#include <QtCore/QPair>
52#include <QtCore/QResource>
53#include <QtCore/QStandardPaths>
54#include <QtCore/QTextStream>
55#include <QtCore/QTimer>
56#include <QtCore/QBuffer>
57#include <QtCore/QLibraryInfo>
58#include <QtCore/QFileInfo>
59#include <QtCore/QDir>
60
61#include <QtWidgets/QAction>
62#include <QtWidgets/QComboBox>
63#include <QtWidgets/QDockWidget>
64#include <QtGui/QFontDatabase>
65#include <QtGui/QImageReader>
66#include <QtGui/QScreen>
67#include <QtWidgets/QFileDialog>
68#include <QtWidgets/QLabel>
69#include <QtWidgets/QLayout>
70#include <QtWidgets/QLineEdit>
71#include <QtWidgets/QMenuBar>
72#include <QtWidgets/QMessageBox>
73#include <QtWidgets/QProgressBar>
74#include <QtWidgets/QShortcut>
75#include <QtWidgets/QStatusBar>
76#include <QtWidgets/QToolBar>
77#include <QtWidgets/QToolButton>
78
79#include <QtHelp/QHelpContentModel>
80#include <QtHelp/QHelpEngineCore>
81#include <QtHelp/QHelpIndexModel>
82#include <QtHelp/QHelpSearchEngine>
83#include <QtHelp/QHelpFilterEngine>
84
85#include <cstdlib>
86
87QT_BEGIN_NAMESPACE
88
89enum { warnAboutMissingModules = 0 };
90
91MainWindow::MainWindow(CmdLineParser *cmdLine, QWidget *parent)
92 : QMainWindow(parent)
93 , m_cmdLine(cmdLine)
94{
95 TRACE_OBJ
96
97 setToolButtonStyle(Qt::ToolButtonFollowStyle);
98 setDockOptions(dockOptions() | AllowNestedDocks);
99
100 QString collectionFile;
101 if (usesDefaultCollection()) {
102 MainWindow::collectionFileDirectory(createDir: true);
103 collectionFile = MainWindow::defaultHelpCollectionFileName();
104 } else {
105 collectionFile = cmdLine->collectionFile();
106 }
107 HelpEngineWrapper &helpEngineWrapper =
108 HelpEngineWrapper::instance(collectionFile);
109 BookmarkManager *bookMarkManager = BookmarkManager::instance();
110
111 if (!initHelpDB(registerInternalDoc: !cmdLine->collectionFileGiven())) {
112 qDebug(msg: "Fatal error: Help engine initialization failed. "
113 "Error message was: %s\nAssistant will now exit.",
114 qPrintable(HelpEngineWrapper::instance().error()));
115 std::exit(status: 1);
116 }
117
118 m_centralWidget = new CentralWidget(this);
119 setCentralWidget(m_centralWidget);
120
121 m_indexWindow = new IndexWindow(this);
122 QDockWidget *indexDock = new QDockWidget(tr(s: "Index"), this);
123 indexDock->setObjectName(QLatin1String("IndexWindow"));
124 indexDock->setWidget(m_indexWindow);
125 addDockWidget(area: Qt::LeftDockWidgetArea, dockwidget: indexDock);
126
127 m_contentWindow = new ContentWindow;
128 QDockWidget *contentDock = new QDockWidget(tr(s: "Contents"), this);
129 contentDock->setObjectName(QLatin1String("ContentWindow"));
130 contentDock->setWidget(m_contentWindow);
131 addDockWidget(area: Qt::LeftDockWidgetArea, dockwidget: contentDock);
132
133 m_searchWindow = new SearchWidget(helpEngineWrapper.searchEngine());
134 m_searchWindow->setFont(!helpEngineWrapper.usesBrowserFont() ? qApp->font()
135 : helpEngineWrapper.browserFont());
136 QDockWidget *searchDock = new QDockWidget(tr(s: "Search"), this);
137 searchDock->setObjectName(QLatin1String("SearchWindow"));
138 searchDock->setWidget(m_searchWindow);
139 addDockWidget(area: Qt::LeftDockWidgetArea, dockwidget: searchDock);
140
141 QDockWidget *bookmarkDock = new QDockWidget(tr(s: "Bookmarks"), this);
142 bookmarkDock->setObjectName(QLatin1String("BookmarkWindow"));
143 bookmarkDock->setWidget(m_bookmarkWidget
144 = bookMarkManager->bookmarkDockWidget());
145 addDockWidget(area: Qt::LeftDockWidgetArea, dockwidget: bookmarkDock);
146
147 QDockWidget *openPagesDock = new QDockWidget(tr(s: "Open Pages"), this);
148 openPagesDock->setObjectName(QLatin1String("Open Pages"));
149 OpenPagesManager *openPagesManager
150 = OpenPagesManager::createInstance(parent: this, defaultCollection: usesDefaultCollection(), cmdLineUrl: m_cmdLine->url());
151 openPagesDock->setWidget(openPagesManager->openPagesWidget());
152 addDockWidget(area: Qt::LeftDockWidgetArea, dockwidget: openPagesDock);
153
154 connect(sender: m_centralWidget, signal: &CentralWidget::addBookmark,
155 receiver: bookMarkManager, slot: &BookmarkManager::addBookmark);
156 connect(sender: bookMarkManager, signal: &BookmarkManager::escapePressed,
157 receiver: this, slot: &MainWindow::activateCurrentCentralWidgetTab);
158 connect(sender: bookMarkManager, signal: &BookmarkManager::setSource,
159 receiver: m_centralWidget, slot: &CentralWidget::setSource);
160 connect(sender: bookMarkManager, signal: &BookmarkManager::setSourceInNewTab,
161 context: openPagesManager, slot: [openPagesManager](const QUrl &url){ openPagesManager->createPage(url); });
162
163 QHelpSearchEngine *searchEngine = helpEngineWrapper.searchEngine();
164 connect(sender: searchEngine, signal: &QHelpSearchEngine::indexingStarted,
165 receiver: this, slot: &MainWindow::indexingStarted);
166 connect(sender: searchEngine, signal: &QHelpSearchEngine::indexingFinished,
167 receiver: this, slot: &MainWindow::indexingFinished);
168
169 QString defWindowTitle = tr(s: "Qt Assistant");
170 setWindowTitle(defWindowTitle);
171
172 setupActions();
173 statusBar()->show();
174 m_centralWidget->connectTabBar();
175
176 setupFilterToolbar();
177 setupAddressToolbar();
178
179 const QString windowTitle = helpEngineWrapper.windowTitle();
180 setWindowTitle(windowTitle.isEmpty() ? defWindowTitle : windowTitle);
181 QByteArray iconArray = helpEngineWrapper.applicationIcon();
182 if (iconArray.size() > 0) {
183 QBuffer buffer(&iconArray);
184 QImageReader reader(&buffer);
185 QIcon appIcon;
186 do {
187 QPixmap pix;
188 pix.convertFromImage(img: reader.read());
189 appIcon.addPixmap(pixmap: pix);
190 } while (reader.jumpToNextImage());
191 qApp->setWindowIcon(appIcon);
192 }
193#if !defined(Q_OS_OSX) && !defined(Q_OS_WIN)
194 else {
195 QIcon appIcon(QLatin1String(":/qt-project.org/assistant/images/assistant-128.png"));
196 qApp->setWindowIcon(appIcon);
197 }
198#endif
199
200 QToolBar *toolBar = addToolBar(title: tr(s: "Bookmark Toolbar"));
201 toolBar->setObjectName(QLatin1String("Bookmark Toolbar"));
202 bookMarkManager->setBookmarksToolbar(toolBar);
203
204 toolBar->hide();
205 toolBarMenu()->addAction(action: toolBar->toggleViewAction());
206
207 QByteArray ba(helpEngineWrapper.mainWindow());
208 if (!ba.isEmpty())
209 restoreState(state: ba);
210
211 ba = helpEngineWrapper.mainWindowGeometry();
212 if (!ba.isEmpty()) {
213 restoreGeometry(geometry: ba);
214 } else {
215 tabifyDockWidget(first: contentDock, second: indexDock);
216 tabifyDockWidget(first: indexDock, second: bookmarkDock);
217 tabifyDockWidget(first: bookmarkDock, second: searchDock);
218 contentDock->raise();
219 const QRect screen = QGuiApplication::primaryScreen()->geometry();
220 resize(w: 4*screen.width()/5, h: 4*screen.height()/5);
221
222 adjustSize(); // make sure we won't start outside of the screen
223 move(screen.center() - rect().center());
224 }
225
226 if (!helpEngineWrapper.hasFontSettings()) {
227 helpEngineWrapper.setUseAppFont(false);
228 helpEngineWrapper.setUseBrowserFont(false);
229 helpEngineWrapper.setAppFont(qApp->font());
230 helpEngineWrapper.setAppWritingSystem(QFontDatabase::Latin);
231 helpEngineWrapper.setBrowserFont(qApp->font());
232 helpEngineWrapper.setBrowserWritingSystem(QFontDatabase::Latin);
233 } else {
234 updateApplicationFont();
235 }
236
237 updateAboutMenuText();
238
239 QTimer::singleShot(interval: 0, receiver: this, slot: &MainWindow::insertLastPages);
240 if (m_cmdLine->enableRemoteControl())
241 (void)new RemoteControl(this);
242
243 if (m_cmdLine->contents() == CmdLineParser::Show)
244 showContents();
245 else if (m_cmdLine->contents() == CmdLineParser::Hide)
246 hideContents();
247
248 if (m_cmdLine->index() == CmdLineParser::Show)
249 showIndex();
250 else if (m_cmdLine->index() == CmdLineParser::Hide)
251 hideIndex();
252
253 if (m_cmdLine->bookmarks() == CmdLineParser::Show)
254 showBookmarksDockWidget();
255 else if (m_cmdLine->bookmarks() == CmdLineParser::Hide)
256 hideBookmarksDockWidget();
257
258 if (m_cmdLine->search() == CmdLineParser::Show)
259 showSearch();
260 else if (m_cmdLine->search() == CmdLineParser::Hide)
261 hideSearch();
262
263 if (m_cmdLine->contents() == CmdLineParser::Activate)
264 showContents();
265 else if (m_cmdLine->index() == CmdLineParser::Activate)
266 showIndex();
267 else if (m_cmdLine->bookmarks() == CmdLineParser::Activate)
268 showBookmarksDockWidget();
269
270 if (!m_cmdLine->currentFilter().isEmpty()) {
271 const QString &curFilter = m_cmdLine->currentFilter();
272 if (helpEngineWrapper.filterEngine()->filters().contains(str: curFilter))
273 helpEngineWrapper.filterEngine()->setActiveFilter(curFilter);
274 }
275
276 if (usesDefaultCollection())
277 QTimer::singleShot(interval: 0, receiver: this, slot: &MainWindow::lookForNewQtDocumentation);
278 else
279 checkInitState();
280
281 connect(sender: &helpEngineWrapper, signal: &HelpEngineWrapper::documentationRemoved,
282 receiver: this, slot: &MainWindow::documentationRemoved);
283 connect(sender: &helpEngineWrapper, signal: &HelpEngineWrapper::documentationUpdated,
284 receiver: this, slot: &MainWindow::documentationUpdated);
285
286 setTabPosition(areas: Qt::AllDockWidgetAreas, tabPosition: QTabWidget::North);
287 GlobalActions::instance()->updateActions();
288 if (helpEngineWrapper.addressBarEnabled())
289 showNewAddress();
290}
291
292MainWindow::~MainWindow()
293{
294 TRACE_OBJ
295 delete m_qtDocInstaller;
296}
297
298bool MainWindow::usesDefaultCollection() const
299{
300 TRACE_OBJ
301 return m_cmdLine->collectionFile().isEmpty();
302}
303
304void MainWindow::closeEvent(QCloseEvent *e)
305{
306 TRACE_OBJ
307 BookmarkManager::destroy();
308 HelpEngineWrapper::instance().setMainWindow(saveState());
309 HelpEngineWrapper::instance().setMainWindowGeometry(saveGeometry());
310 QMainWindow::closeEvent(event: e);
311}
312
313bool MainWindow::initHelpDB(bool registerInternalDoc)
314{
315 TRACE_OBJ
316 HelpEngineWrapper &helpEngineWrapper = HelpEngineWrapper::instance();
317 if (!helpEngineWrapper.setupData())
318 return false;
319
320 if (!registerInternalDoc) {
321 if (helpEngineWrapper.defaultHomePage() == QLatin1String("help"))
322 helpEngineWrapper.setDefaultHomePage(QLatin1String("about:blank"));
323 return true;
324 }
325 bool assistantInternalDocRegistered = false;
326 QString intern(QLatin1String("org.qt-project.assistantinternal-"));
327 for (const QString &ns : helpEngineWrapper.registeredDocumentations()) {
328 if (ns.startsWith(s: intern)) {
329 intern = ns;
330 assistantInternalDocRegistered = true;
331 break;
332 }
333 }
334
335 const QString &collectionFile = helpEngineWrapper.collectionFile();
336 QFileInfo fi(collectionFile);
337 QString helpFile;
338 QTextStream(&helpFile) << fi.absolutePath() << QDir::separator()
339 << QLatin1String("assistant.qch.") << (QT_VERSION >> 16)
340 << QLatin1Char('.') << ((QT_VERSION >> 8) & 0xFF);
341
342 bool needsSetup = false;
343 if (!assistantInternalDocRegistered || !QFile::exists(fileName: helpFile)) {
344 QFile file(helpFile);
345 if (file.open(flags: QIODevice::WriteOnly)) {
346 QResource res(QLatin1String(":/qt-project.org/assistant/assistant.qch"));
347 if (file.write(data: (const char*)res.data(), len: res.size()) != res.size())
348 qDebug() << QLatin1String("could not write assistant.qch...");
349
350 file.close();
351 }
352 helpEngineWrapper.unregisterDocumentation(namespaceName: intern);
353 helpEngineWrapper.registerDocumentation(docFile: helpFile);
354 needsSetup = true;
355 }
356
357 if (needsSetup)
358 helpEngineWrapper.setupData();
359 return true;
360}
361
362static const char *docs[] = {
363 "assistant", "designer", "linguist", // Qt 4
364 "qmake",
365 "qt",
366 "qtqmake",
367 "activeqt",
368 "qtandroidextras",
369 "qtassistant",
370 "qtbluetooth",
371 "qtconcurrent",
372 "qtconnectivity",
373 "qtcore",
374 "qtdbus",
375 "qtdesigner",
376 "qtdoc",
377 "qtenginio",
378 "qtgraphicaleffects",
379 "qtgui",
380 "qthelp",
381 "qtimageformats",
382 "qtlinguist",
383 "qtlocation",
384 "qtmacextras",
385 "qtmultimedia",
386 "qtmultimediawidgets",
387 "qtnfc",
388 "qtnetwork",
389 "qtopengl",
390 "qtpositioning",
391 "qtprintsupport",
392 "qtqml",
393 "qtquick",
394 "qtscript",
395 "qtscripttools",
396 "qtsensors",
397 "qtsql",
398 "qtsvg",
399 "qttestlib",
400 "qtuitools",
401 "qtwebkit",
402 "qtwebkitexamples",
403 "qtwidgets",
404 "qtxml",
405 "qtxmlpatterns",
406 "qdoc",
407 "qtx11extras",
408 "qtserialport",
409 "qtquickcontrols",
410 "qtquickcontrolsstyles",
411 "qtquickdialogs",
412 "qtquicklayouts",
413 "qtwebsockets",
414 "qtwinextras"
415};
416
417static QStringList newQtDocumentation()
418{
419 QStringList result;
420 const QDir docDirectory(QLibraryInfo::location(QLibraryInfo::DocumentationPath));
421 const QFileInfoList entries = docDirectory.entryInfoList(nameFilters: QStringList(QStringLiteral("*.qch")),
422 filters: QDir::Files, sort: QDir::Name);
423 if (!entries.isEmpty()) {
424 result.reserve(alloc: entries.size());
425 for (const QFileInfo &fi : entries)
426 result.append(t: fi.baseName());
427 return result;
428 }
429 if (warnAboutMissingModules)
430 qWarning() << "No documentation found in " << QDir::toNativeSeparators(pathName: docDirectory.absolutePath());
431 const int docCount = int(sizeof(docs) / sizeof(docs[0]));
432 result.reserve(alloc: docCount);
433 for (int d = 0; d < docCount; ++d)
434 result.append(t: QLatin1String(docs[d]));
435 return result;
436}
437
438void MainWindow::lookForNewQtDocumentation()
439{
440 TRACE_OBJ
441 HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance();
442
443 const QStringList &docs = newQtDocumentation();
444 const int docCount = docs.size();
445 QList<QtDocInstaller::DocInfo> qtDocInfos;
446 qtDocInfos.reserve(alloc: docCount);
447 for (const QString &doc : docs) {
448 const QtDocInstaller::DocInfo docInfo(doc, helpEngine.qtDocInfo(component: doc));
449 qtDocInfos.append(t: docInfo);
450 if (warnAboutMissingModules && (docInfo.second.isEmpty() || docInfo.second.first().isEmpty()))
451 qWarning() << "No documentation found for " << doc;
452 }
453
454 m_qtDocInstaller = new QtDocInstaller(qtDocInfos);
455 connect(sender: m_qtDocInstaller, signal: &QtDocInstaller::docsInstalled,
456 receiver: this, slot: &MainWindow::qtDocumentationInstalled);
457 connect(sender: m_qtDocInstaller, signal: &QtDocInstaller::qchFileNotFound,
458 receiver: this, slot: &MainWindow::resetQtDocInfo);
459 connect(sender: m_qtDocInstaller, signal: &QtDocInstaller::registerDocumentation,
460 receiver: this, slot: &MainWindow::registerDocumentation);
461 if (helpEngine.qtDocInfo(component: QLatin1String("qt")).count() != 2)
462 statusBar()->showMessage(text: tr(s: "Looking for Qt Documentation..."));
463 m_qtDocInstaller->installDocs();
464}
465
466void MainWindow::qtDocumentationInstalled()
467{
468 TRACE_OBJ
469 statusBar()->clearMessage();
470 checkInitState();
471}
472
473void MainWindow::checkInitState()
474{
475 TRACE_OBJ
476 if (!m_cmdLine->enableRemoteControl()) {
477 HelpEngineWrapper::instance().initialDocSetupDone();
478 return;
479 }
480
481 HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance();
482 if (helpEngine.contentModel()->isCreatingContents()
483 || helpEngine.indexModel()->isCreatingIndex()) {
484 if (!m_connectedInitSignals) {
485 connect(sender: helpEngine.contentModel(), signal: &QHelpContentModel::contentsCreated,
486 receiver: this, slot: &MainWindow::checkInitState);
487 connect(sender: helpEngine.indexModel(), signal: &QHelpIndexModel::indexCreated,
488 receiver: this, slot: &MainWindow::checkInitState);
489 m_connectedInitSignals = true;
490 }
491 } else {
492 if (m_connectedInitSignals) {
493 disconnect(sender: helpEngine.contentModel(), signal: nullptr, receiver: this, member: nullptr);
494 disconnect(sender: helpEngine.indexModel(), signal: nullptr, receiver: this, member: nullptr);
495 }
496 HelpEngineWrapper::instance().initialDocSetupDone();
497 emit initDone();
498 }
499}
500
501void MainWindow::insertLastPages()
502{
503 TRACE_OBJ
504 if (m_cmdLine->search() == CmdLineParser::Activate)
505 showSearch();
506}
507
508void MainWindow::setupActions()
509{
510 TRACE_OBJ
511 QString resourcePath = QLatin1String(":/qt-project.org/assistant/images/");
512#ifdef Q_OS_MAC
513 setUnifiedTitleAndToolBarOnMac(true);
514 resourcePath.append(QLatin1String("mac"));
515#else
516 resourcePath.append(s: QLatin1String("win"));
517#endif
518
519 QMenu *menu = menuBar()->addMenu(title: tr(s: "&File"));
520 OpenPagesManager *const openPages = OpenPagesManager::instance();
521 m_newTabAction = menu->addAction(text: tr(s: "New &Tab"),
522 object: openPages, slot: &OpenPagesManager::createBlankPage);
523 m_newTabAction->setShortcut(QKeySequence::AddTab);
524 m_closeTabAction = menu->addAction(text: tr(s: "&Close Tab"),
525 object: openPages, slot: &OpenPagesManager::closeCurrentPage);
526 m_closeTabAction->setShortcuts(QKeySequence::Close);
527 m_closeTabAction->setEnabled(openPages->pageCount() > 1);
528 connect(sender: openPages, signal: &OpenPagesManager::pageClosed,
529 receiver: this, slot: &MainWindow::handlePageCountChanged);
530 connect(sender: openPages, signal: &OpenPagesManager::pageAdded,
531 receiver: this, slot: &MainWindow::handlePageCountChanged);
532
533 menu->addSeparator();
534
535 m_pageSetupAction = menu->addAction(text: tr(s: "Page Set&up..."),
536 object: m_centralWidget, slot: &CentralWidget::pageSetup);
537 m_printPreviewAction = menu->addAction(text: tr(s: "Print Preview..."),
538 object: m_centralWidget, slot: &CentralWidget::printPreview);
539
540 GlobalActions *globalActions = GlobalActions::instance(parent: this);
541 menu->addAction(action: globalActions->printAction());
542 menu->addSeparator();
543
544 QIcon appExitIcon = QIcon::fromTheme(name: "application-exit");
545 QAction *tmp;
546#ifdef Q_OS_WIN
547 tmp = menu->addAction(appExitIcon, tr("E&xit"),
548 this, &QWidget::close);
549 tmp->setShortcut(QKeySequence(tr("CTRL+Q")));
550#else
551 tmp = menu->addAction(actionIcon: appExitIcon, text: tr(s: "&Quit"),
552 object: this, slot: &QWidget::close);
553 tmp->setShortcut(QKeySequence::Quit);
554#endif
555 tmp->setMenuRole(QAction::QuitRole);
556
557 menu = menuBar()->addMenu(title: tr(s: "&Edit"));
558#if QT_CONFIG(clipboard)
559 menu->addAction(action: globalActions->copyAction());
560#endif
561 menu->addAction(action: globalActions->findAction());
562
563 QAction *findNextAction = menu->addAction(text: tr(s: "Find &Next"),
564 object: m_centralWidget, slot: &CentralWidget::findNext);
565 findNextAction->setShortcuts(QKeySequence::FindNext);
566
567 QAction *findPreviousAction = menu->addAction(text: tr(s: "Find &Previous"),
568 object: m_centralWidget, slot: &CentralWidget::findPrevious);
569 findPreviousAction->setShortcuts(QKeySequence::FindPrevious);
570
571 menu->addSeparator();
572 tmp = menu->addAction(text: tr(s: "Preferences..."),
573 object: this, slot: &MainWindow::showPreferences);
574 tmp->setMenuRole(QAction::PreferencesRole);
575
576 m_viewMenu = menuBar()->addMenu(title: tr(s: "&View"));
577 m_viewMenu->addAction(action: globalActions->zoomInAction());
578 m_viewMenu->addAction(action: globalActions->zoomOutAction());
579
580 m_resetZoomAction = m_viewMenu->addAction(text: tr(s: "Normal &Size"),
581 object: m_centralWidget, slot: &CentralWidget::resetZoom);
582 m_resetZoomAction->setPriority(QAction::LowPriority);
583 m_resetZoomAction->setIcon(QIcon(resourcePath + QLatin1String("/resetzoom.png")));
584 m_resetZoomAction->setShortcut(tr(s: "Ctrl+0"));
585
586 m_viewMenu->addSeparator();
587
588 m_viewMenu->addAction(text: tr(s: "Contents"),
589 object: this, slot: &MainWindow::showContents, shortcut: QKeySequence(tr(s: "ALT+C")));
590 m_viewMenu->addAction(text: tr(s: "Index"),
591 object: this, slot: &MainWindow::showIndex, shortcut: QKeySequence(tr(s: "ALT+I")));
592 m_viewMenu->addAction(text: tr(s: "Bookmarks"),
593 object: this, slot: &MainWindow::showBookmarksDockWidget, shortcut: QKeySequence(tr(s: "ALT+O")));
594 m_viewMenu->addAction(text: tr(s: "Search"),
595 object: this, slot: &MainWindow::showSearch, shortcut: QKeySequence(tr(s: "ALT+S")));
596 m_viewMenu->addAction(text: tr(s: "Open Pages"),
597 object: this, slot: &MainWindow::showOpenPages, shortcut: QKeySequence(tr(s: "ALT+P")));
598
599 menu = menuBar()->addMenu(title: tr(s: "&Go"));
600 menu->addAction(action: globalActions->homeAction());
601 menu->addAction(action: globalActions->backAction());
602 menu->addAction(action: globalActions->nextAction());
603
604 m_syncAction = menu->addAction(text: tr(s: "Sync with Table of Contents"),
605 object: this, slot: &MainWindow::syncContents);
606 m_syncAction->setIconText(tr(s: "Sync"));
607 m_syncAction->setIcon(QIcon(resourcePath + QLatin1String("/synctoc.png")));
608
609 menu->addSeparator();
610
611 tmp = menu->addAction(text: tr(s: "Next Page"),
612 object: openPages, slot: &OpenPagesManager::nextPage);
613 tmp->setShortcuts(QList<QKeySequence>() << QKeySequence(tr(s: "Ctrl+Alt+Right"))
614 << QKeySequence(Qt::CTRL | Qt::Key_PageDown));
615
616 tmp = menu->addAction(text: tr(s: "Previous Page"),
617 object: openPages, slot: &OpenPagesManager::previousPage);
618 tmp->setShortcuts(QList<QKeySequence>() << QKeySequence(tr(s: "Ctrl+Alt+Left"))
619 << QKeySequence(Qt::CTRL | Qt::Key_PageUp));
620
621 const Qt::Modifier modifier =
622#ifdef Q_OS_MAC
623 Qt::ALT;
624#else
625 Qt::CTRL;
626#endif
627
628 QShortcut *sct = new QShortcut(QKeySequence(modifier | Qt::Key_Tab), this);
629 connect(sender: sct, signal: &QShortcut::activated,
630 receiver: openPages, slot: &OpenPagesManager::nextPageWithSwitcher);
631 sct = new QShortcut(QKeySequence(modifier | Qt::SHIFT | Qt::Key_Tab), this);
632 connect(sender: sct, signal: &QShortcut::activated,
633 receiver: openPages, slot: &OpenPagesManager::previousPageWithSwitcher);
634
635 BookmarkManager::instance()->setBookmarksMenu(menuBar()->addMenu(title: tr(s: "&Bookmarks")));
636
637 menu = menuBar()->addMenu(title: tr(s: "&Help"));
638 m_aboutAction = menu->addAction(text: tr(s: "About..."),
639 object: this, slot: &MainWindow::showAboutDialog);
640 m_aboutAction->setMenuRole(QAction::AboutRole);
641
642#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
643 m_resetZoomAction->setIcon(QIcon::fromTheme(name: "zoom-original" , fallback: m_resetZoomAction->icon()));
644 m_syncAction->setIcon(QIcon::fromTheme(name: "view-refresh" , fallback: m_syncAction->icon()));
645#endif
646
647 QToolBar *navigationBar = addToolBar(title: tr(s: "Navigation Toolbar"));
648 navigationBar->setObjectName(QLatin1String("NavigationToolBar"));
649 navigationBar->addAction(action: globalActions->backAction());
650 navigationBar->addAction(action: globalActions->nextAction());
651 navigationBar->addAction(action: globalActions->homeAction());
652 navigationBar->addAction(action: m_syncAction);
653 navigationBar->addSeparator();
654#if QT_CONFIG(clipboard)
655 navigationBar->addAction(action: globalActions->copyAction());
656#endif
657 navigationBar->addAction(action: globalActions->printAction());
658 navigationBar->addAction(action: globalActions->findAction());
659 navigationBar->addSeparator();
660 navigationBar->addAction(action: globalActions->zoomInAction());
661 navigationBar->addAction(action: globalActions->zoomOutAction());
662 navigationBar->addAction(action: m_resetZoomAction);
663
664#if defined(Q_OS_MAC)
665 QMenu *windowMenu = new QMenu(tr("&Window"), this);
666 menuBar()->insertMenu(menu->menuAction(), windowMenu);
667 windowMenu->addAction(tr("Zoom"),
668 this, &QWidget::showMaximized);
669 windowMenu->addAction(tr("Minimize"),
670 this, &QWidget::showMinimized, QKeySequence(tr("Ctrl+M")));
671#endif
672
673 // content viewer connections
674#if QT_CONFIG(clipboard)
675 connect(sender: m_centralWidget, signal: &CentralWidget::copyAvailable,
676 receiver: globalActions, slot: &GlobalActions::setCopyAvailable);
677#endif
678 connect(sender: m_centralWidget, signal: &CentralWidget::currentViewerChanged,
679 receiver: globalActions, slot: &GlobalActions::updateActions);
680 connect(sender: m_centralWidget, signal: &CentralWidget::forwardAvailable,
681 receiver: globalActions, slot: &GlobalActions::updateActions);
682 connect(sender: m_centralWidget, signal: &CentralWidget::backwardAvailable,
683 receiver: globalActions, slot: &GlobalActions::updateActions);
684 connect(sender: m_centralWidget, signal: &CentralWidget::highlighted,
685 context: this, slot: [this](const QUrl &link) { statusBar()->showMessage(text: link.toString());} );
686
687 // index window
688 connect(sender: m_indexWindow, signal: &IndexWindow::linkActivated,
689 receiver: m_centralWidget, slot: &CentralWidget::setSource);
690 connect(sender: m_indexWindow, signal: &IndexWindow::documentsActivated,
691 receiver: this, slot: &MainWindow::showTopicChooser);
692 connect(sender: m_indexWindow, signal: &IndexWindow::escapePressed,
693 receiver: this, slot: &MainWindow::activateCurrentCentralWidgetTab);
694
695 // content window
696 connect(sender: m_contentWindow, signal: &ContentWindow::linkActivated,
697 receiver: m_centralWidget, slot: &CentralWidget::setSource);
698 connect(sender: m_contentWindow, signal: &ContentWindow::escapePressed,
699 receiver: this, slot: &MainWindow::activateCurrentCentralWidgetTab);
700
701 // search window
702 connect(sender: m_searchWindow, signal: &SearchWidget::requestShowLink,
703 receiver: CentralWidget::instance(), slot: &CentralWidget::setSourceFromSearch);
704 connect(sender: m_searchWindow, signal: &SearchWidget::requestShowLinkInNewTab,
705 receiver: OpenPagesManager::instance(), slot: &OpenPagesManager::createNewPageFromSearch);
706
707#if defined(QT_NO_PRINTER)
708 m_pageSetupAction->setVisible(false);
709 m_printPreviewAction->setVisible(false);
710 globalActions->printAction()->setVisible(false);
711#endif
712}
713
714QMenu *MainWindow::toolBarMenu()
715{
716 TRACE_OBJ
717 if (!m_toolBarMenu) {
718 m_viewMenu->addSeparator();
719 m_toolBarMenu = m_viewMenu->addMenu(title: tr(s: "Toolbars"));
720 }
721 return m_toolBarMenu;
722}
723
724void MainWindow::setupFilterToolbar()
725{
726 TRACE_OBJ
727 HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance();
728 if (!helpEngine.filterFunctionalityEnabled())
729 return;
730
731 m_filterCombo = new QComboBox(this);
732 m_filterCombo->setMinimumWidth(QFontMetrics(QFont()).
733 horizontalAdvance(QLatin1String("MakeTheComboBoxWidthEnough")));
734
735 QToolBar *filterToolBar = addToolBar(title: tr(s: "Filter Toolbar"));
736 filterToolBar->setObjectName(QLatin1String("FilterToolBar"));
737 filterToolBar->addWidget(widget: new QLabel(tr(s: "Filtered by:").append(c: QLatin1Char(' ')),
738 this));
739 filterToolBar->addWidget(widget: m_filterCombo);
740
741 if (!helpEngine.filterToolbarVisible())
742 filterToolBar->hide();
743 toolBarMenu()->addAction(action: filterToolBar->toggleViewAction());
744
745 connect(sender: &helpEngine, signal: &HelpEngineWrapper::setupFinished,
746 receiver: this, slot: &MainWindow::setupFilterCombo, type: Qt::QueuedConnection);
747 connect(sender: m_filterCombo, signal: QOverload<int>::of(ptr: &QComboBox::activated),
748 receiver: this, slot: &MainWindow::filterDocumentation);
749 connect(sender: helpEngine.filterEngine(), signal: &QHelpFilterEngine::filterActivated,
750 receiver: this, slot: &MainWindow::currentFilterChanged);
751
752 setupFilterCombo();
753}
754
755void MainWindow::setupAddressToolbar()
756{
757 TRACE_OBJ
758 HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance();
759 if (!helpEngine.addressBarEnabled())
760 return;
761
762 m_addressLineEdit = new QLineEdit(this);
763 QToolBar *addressToolBar = addToolBar(title: tr(s: "Address Toolbar"));
764 addressToolBar->setObjectName(QLatin1String("AddressToolBar"));
765 insertToolBarBreak(before: addressToolBar);
766
767 addressToolBar->addWidget(widget: new QLabel(tr(s: "Address:").append(c: QChar::Space),
768 this));
769 addressToolBar->addWidget(widget: m_addressLineEdit);
770
771 if (!helpEngine.addressBarVisible())
772 addressToolBar->hide();
773 toolBarMenu()->addAction(action: addressToolBar->toggleViewAction());
774
775 // address lineedit
776 connect(sender: m_addressLineEdit, signal: &QLineEdit::returnPressed,
777 receiver: this, slot: &MainWindow::gotoAddress);
778 connect(sender: m_centralWidget, signal: &CentralWidget::currentViewerChanged,
779 receiver: this, slot: QOverload<>::of(ptr: &MainWindow::showNewAddress));
780 connect(sender: m_centralWidget, signal: &CentralWidget::sourceChanged,
781 receiver: this, slot: QOverload<>::of(ptr: &MainWindow::showNewAddress));
782}
783
784void MainWindow::updateAboutMenuText()
785{
786 TRACE_OBJ
787 QByteArray ba = HelpEngineWrapper::instance().aboutMenuTexts();
788 if (ba.size() > 0) {
789 QString lang;
790 QString str;
791 QString trStr;
792 QString currentLang = QLocale::system().name();
793 int i = currentLang.indexOf(c: QLatin1Char('_'));
794 if (i > -1)
795 currentLang = currentLang.left(n: i);
796 QDataStream s(&ba, QIODevice::ReadOnly);
797 while (!s.atEnd()) {
798 s >> lang;
799 s >> str;
800 if (lang == QLatin1String("default") && trStr.isEmpty()) {
801 trStr = str;
802 } else if (lang == currentLang) {
803 trStr = str;
804 break;
805 }
806 }
807 if (!trStr.isEmpty())
808 m_aboutAction->setText(trStr);
809 }
810}
811
812void MainWindow::showNewAddress()
813{
814 TRACE_OBJ
815 showNewAddress(url: m_centralWidget->currentSource());
816}
817
818void MainWindow::showNewAddress(const QUrl &url)
819{
820 TRACE_OBJ
821 m_addressLineEdit->setText(url.toString());
822}
823
824void MainWindow::gotoAddress()
825{
826 TRACE_OBJ
827 m_centralWidget->setSource(m_addressLineEdit->text());
828}
829
830void MainWindow::showTopicChooser(const QList<QHelpLink> &documents,
831 const QString &keyword)
832{
833 TRACE_OBJ
834 TopicChooser tc(this, keyword, documents);
835 if (tc.exec() == QDialog::Accepted) {
836 m_centralWidget->setSource(tc.link());
837 }
838}
839
840void MainWindow::showPreferences()
841{
842 TRACE_OBJ
843 PreferencesDialog dia(this);
844 connect(sender: &dia, signal: &PreferencesDialog::updateApplicationFont,
845 receiver: this, slot: &MainWindow::updateApplicationFont);
846 connect(sender: &dia, signal: &PreferencesDialog::updateBrowserFont,
847 receiver: m_centralWidget, slot: &CentralWidget::updateBrowserFont);
848 connect(sender: &dia, signal: &PreferencesDialog::updateUserInterface,
849 receiver: m_centralWidget, slot: &CentralWidget::updateUserInterface);
850 dia.exec();
851}
852
853void MainWindow::syncContents()
854{
855 TRACE_OBJ
856 qApp->setOverrideCursor(QCursor(Qt::WaitCursor));
857 const QUrl url = m_centralWidget->currentSource();
858 showContents();
859 if (!m_contentWindow->syncToContent(url))
860 statusBar()->showMessage(
861 text: tr(s: "Could not find the associated content item."), timeout: 3000);
862 qApp->restoreOverrideCursor();
863}
864
865void MainWindow::showAboutDialog()
866{
867 TRACE_OBJ
868 HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance();
869 QByteArray contents;
870 QByteArray ba = helpEngine.aboutTexts();
871 if (!ba.isEmpty()) {
872 QString lang;
873 QByteArray cba;
874 QString currentLang = QLocale::system().name();
875 int i = currentLang.indexOf(c: QLatin1Char('_'));
876 if (i > -1)
877 currentLang = currentLang.left(n: i);
878 QDataStream s(&ba, QIODevice::ReadOnly);
879 while (!s.atEnd()) {
880 s >> lang;
881 s >> cba;
882 if (lang == QLatin1String("default") && contents.isEmpty()) {
883 contents = cba;
884 } else if (lang == currentLang) {
885 contents = cba;
886 break;
887 }
888 }
889 }
890
891 AboutDialog aboutDia(this);
892
893 QByteArray iconArray;
894 if (!contents.isEmpty()) {
895 iconArray = helpEngine.aboutIcon();
896 QByteArray resources = helpEngine.aboutImages();
897 QPixmap pix;
898 pix.loadFromData(buf: iconArray);
899 aboutDia.setText(text: QString::fromUtf8(str: contents), resources);
900 if (!pix.isNull())
901 aboutDia.setPixmap(pix);
902 aboutDia.setWindowTitle(aboutDia.documentTitle());
903 } else {
904 QByteArray resources;
905#if defined(BROWSER_QTWEBKIT)
906 const QString browser = QStringLiteral("Qt WebKit");
907#else
908 const QString browser = QStringLiteral("QTextBrowser");
909#endif
910 aboutDia.setText(text: tr(s: "<center>"
911 "<h3>%1</h3>"
912 "<p>Version %2</p>"
913 "<p>Browser: %3</p></center>"
914 "<p>Copyright (C) %4 The Qt Company Ltd.</p>")
915 .arg(args: tr(s: "Qt Assistant"), args: QLatin1String(QT_VERSION_STR), args: browser, QStringLiteral("2022")),
916 resources);
917 QLatin1String path(":/qt-project.org/assistant/images/assistant-128.png");
918 aboutDia.setPixmap(QString(path));
919 }
920 if (aboutDia.windowTitle().isEmpty())
921 aboutDia.setWindowTitle(tr(s: "About %1").arg(a: windowTitle()));
922 aboutDia.exec();
923}
924
925void MainWindow::setContentsVisible(bool visible)
926{
927 TRACE_OBJ
928 if (visible)
929 showContents();
930 else
931 hideContents();
932}
933
934void MainWindow::showContents()
935{
936 TRACE_OBJ
937 activateDockWidget(w: m_contentWindow);
938}
939
940void MainWindow::hideContents()
941{
942 TRACE_OBJ
943 m_contentWindow->parentWidget()->hide();
944}
945
946void MainWindow::setIndexVisible(bool visible)
947{
948 TRACE_OBJ
949 if (visible)
950 showIndex();
951 else
952 hideIndex();
953}
954
955void MainWindow::showIndex()
956{
957 TRACE_OBJ
958 activateDockWidget(w: m_indexWindow);
959}
960
961void MainWindow::hideIndex()
962{
963 TRACE_OBJ
964 m_indexWindow->parentWidget()->hide();
965}
966
967void MainWindow::setBookmarksVisible(bool visible)
968{
969 TRACE_OBJ
970 if (visible)
971 showBookmarksDockWidget();
972 else
973 hideBookmarksDockWidget();
974}
975
976void MainWindow::showBookmarksDockWidget()
977{
978 TRACE_OBJ
979 activateDockWidget(w: m_bookmarkWidget);
980}
981
982void MainWindow::hideBookmarksDockWidget()
983{
984 TRACE_OBJ
985 m_bookmarkWidget->parentWidget()->hide();
986}
987
988void MainWindow::setSearchVisible(bool visible)
989{
990 TRACE_OBJ
991 if (visible)
992 showSearch();
993 else
994 hideSearch();
995}
996
997void MainWindow::showSearch()
998{
999 TRACE_OBJ
1000 activateDockWidget(w: m_searchWindow);
1001}
1002
1003void MainWindow::showOpenPages()
1004{
1005 TRACE_OBJ
1006 activateDockWidget(w: OpenPagesManager::instance()->openPagesWidget());
1007}
1008
1009void MainWindow::hideSearch()
1010{
1011 TRACE_OBJ
1012 m_searchWindow->parentWidget()->hide();
1013}
1014
1015void MainWindow::activateDockWidget(QWidget *w)
1016{
1017 TRACE_OBJ
1018 w->parentWidget()->show();
1019 w->parentWidget()->raise();
1020 w->setFocus();
1021}
1022
1023void MainWindow::setIndexString(const QString &str)
1024{
1025 TRACE_OBJ
1026 m_indexWindow->setSearchLineEditText(str);
1027}
1028
1029void MainWindow::activateCurrentBrowser()
1030{
1031 TRACE_OBJ
1032 CentralWidget::instance()->activateTab();
1033}
1034
1035void MainWindow::activateCurrentCentralWidgetTab()
1036{
1037 TRACE_OBJ
1038 m_centralWidget->activateTab();
1039}
1040
1041void MainWindow::updateApplicationFont()
1042{
1043 TRACE_OBJ
1044 HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance();
1045 QFont font = qApp->font();
1046 if (helpEngine.usesAppFont())
1047 font = helpEngine.appFont();
1048
1049 const QWidgetList &widgets = QApplication::allWidgets();
1050 for (QWidget *widget : widgets)
1051 widget->setFont(font);
1052}
1053
1054void MainWindow::setupFilterCombo()
1055{
1056 TRACE_OBJ
1057 HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance();
1058 const QString currentFilter = helpEngine.filterEngine()->activeFilter();
1059 m_filterCombo->clear();
1060 m_filterCombo->addItem(atext: tr(s: "Unfiltered"));
1061 const QStringList allFilters = helpEngine.filterEngine()->filters();
1062 if (!allFilters.isEmpty())
1063 m_filterCombo->insertSeparator(index: 1);
1064 for (const QString &filter : allFilters)
1065 m_filterCombo->addItem(atext: filter, auserData: filter);
1066
1067 int idx = m_filterCombo->findData(data: currentFilter);
1068 if (idx < 0)
1069 idx = 0;
1070 m_filterCombo->setCurrentIndex(idx);
1071}
1072
1073void MainWindow::filterDocumentation(int filterIndex)
1074{
1075 TRACE_OBJ
1076
1077 const QString filter = m_filterCombo->itemData(index: filterIndex).toString();
1078 HelpEngineWrapper::instance().filterEngine()->setActiveFilter(filter);
1079}
1080
1081void MainWindow::expandTOC(int depth)
1082{
1083 TRACE_OBJ
1084 Q_ASSERT(depth >= -1);
1085 m_contentWindow->expandToDepth(depth);
1086}
1087
1088void MainWindow::indexingStarted()
1089{
1090 TRACE_OBJ
1091 if (!m_progressWidget) {
1092 m_progressWidget = new QWidget();
1093 QLayout* hlayout = new QHBoxLayout(m_progressWidget);
1094
1095 QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
1096
1097 QLabel *label = new QLabel(tr(s: "Updating search index"));
1098 label->setSizePolicy(sizePolicy);
1099 hlayout->addWidget(w: label);
1100
1101 QProgressBar *progressBar = new QProgressBar();
1102 progressBar->setRange(minimum: 0, maximum: 0);
1103 progressBar->setTextVisible(false);
1104 progressBar->setSizePolicy(sizePolicy);
1105
1106 hlayout->setSpacing(6);
1107 hlayout->setContentsMargins(QMargins());
1108 hlayout->addWidget(w: progressBar);
1109
1110 statusBar()->addPermanentWidget(widget: m_progressWidget);
1111 }
1112}
1113
1114void MainWindow::indexingFinished()
1115{
1116 TRACE_OBJ
1117 statusBar()->removeWidget(widget: m_progressWidget);
1118 delete m_progressWidget;
1119 m_progressWidget = nullptr;
1120}
1121
1122QString MainWindow::collectionFileDirectory(bool createDir, const QString &cacheDir)
1123{
1124 TRACE_OBJ
1125 QString collectionPath =
1126 QStandardPaths::writableLocation(type: QStandardPaths::GenericDataLocation);
1127 if (collectionPath.isEmpty()) {
1128 if (cacheDir.isEmpty())
1129 collectionPath = QDir::homePath() + QDir::separator()
1130 + QLatin1String(".assistant");
1131 else
1132 collectionPath = QDir::homePath() + QLatin1String("/.") + cacheDir;
1133 } else {
1134 if (cacheDir.isEmpty())
1135 collectionPath = collectionPath + QLatin1String("/QtProject/Assistant");
1136 else
1137 collectionPath = collectionPath + QDir::separator() + cacheDir;
1138 }
1139 if (createDir) {
1140 QDir dir;
1141 if (!dir.exists(name: collectionPath))
1142 dir.mkpath(dirPath: collectionPath);
1143 }
1144 return collectionPath;
1145}
1146
1147QString MainWindow::defaultHelpCollectionFileName()
1148{
1149 TRACE_OBJ
1150 // forces creation of the default collection file path
1151 return collectionFileDirectory(createDir: true) + QDir::separator() +
1152 QString(QLatin1String("qthelpcollection_%1.qhc")).
1153 arg(a: QLatin1String(QT_VERSION_STR));
1154}
1155
1156void MainWindow::currentFilterChanged(const QString &filter)
1157{
1158 TRACE_OBJ
1159 int index = m_filterCombo->findData(data: filter);
1160 if (index < 0)
1161 index = 0;
1162 m_filterCombo->setCurrentIndex(index);
1163}
1164
1165void MainWindow::documentationRemoved(const QString &namespaceName)
1166{
1167 TRACE_OBJ
1168 OpenPagesManager::instance()->closePages(nameSpace: namespaceName);
1169}
1170
1171void MainWindow::documentationUpdated(const QString &namespaceName)
1172{
1173 TRACE_OBJ
1174 OpenPagesManager::instance()->reloadPages(nameSpace: namespaceName);
1175}
1176
1177void MainWindow::resetQtDocInfo(const QString &component)
1178{
1179 TRACE_OBJ
1180 HelpEngineWrapper::instance().setQtDocInfo(component,
1181 doc: QStringList(QDateTime().toString(format: Qt::ISODate)));
1182}
1183
1184void MainWindow::registerDocumentation(const QString &component,
1185 const QString &absFileName)
1186{
1187 TRACE_OBJ
1188 QString ns = QHelpEngineCore::namespaceName(documentationFileName: absFileName);
1189 if (ns.isEmpty())
1190 return;
1191
1192 HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance();
1193 if (helpEngine.registeredDocumentations().contains(str: ns))
1194 helpEngine.unregisterDocumentation(namespaceName: ns);
1195 if (!helpEngine.registerDocumentation(docFile: absFileName)) {
1196 QMessageBox::warning(parent: this, title: tr(s: "Qt Assistant"),
1197 text: tr(s: "Could not register file '%1': %2").
1198 arg(a: absFileName).arg(a: helpEngine.error()));
1199 } else {
1200 QStringList docInfo;
1201 docInfo << QFileInfo(absFileName).lastModified().toString(format: Qt::ISODate)
1202 << absFileName;
1203 helpEngine.setQtDocInfo(component, doc: docInfo);
1204 }
1205}
1206
1207void MainWindow::handlePageCountChanged()
1208{
1209 m_closeTabAction->setEnabled(OpenPagesManager::instance()->pageCount() > 1);
1210}
1211
1212QT_END_NAMESPACE
1213

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