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 
87 QT_BEGIN_NAMESPACE
88 
89 enum { warnAboutMissingModules = 0 };
90 
MainWindow(CmdLineParser * cmdLine,QWidget * parent)91 MainWindow::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(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(!cmdLine->collectionFileGiven())) {
112         qDebug("Fatal error: Help engine initialization failed. "
113             "Error message was: %s\nAssistant will now exit.",
114             qPrintable(HelpEngineWrapper::instance().error()));
115         std::exit(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("Index"), this);
123     indexDock->setObjectName(QLatin1String("IndexWindow"));
124     indexDock->setWidget(m_indexWindow);
125     addDockWidget(Qt::LeftDockWidgetArea, indexDock);
126 
127     m_contentWindow = new ContentWindow;
128     QDockWidget *contentDock = new QDockWidget(tr("Contents"), this);
129     contentDock->setObjectName(QLatin1String("ContentWindow"));
130     contentDock->setWidget(m_contentWindow);
131     addDockWidget(Qt::LeftDockWidgetArea, 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("Search"), this);
137     searchDock->setObjectName(QLatin1String("SearchWindow"));
138     searchDock->setWidget(m_searchWindow);
139     addDockWidget(Qt::LeftDockWidgetArea, searchDock);
140 
141     QDockWidget *bookmarkDock = new QDockWidget(tr("Bookmarks"), this);
142     bookmarkDock->setObjectName(QLatin1String("BookmarkWindow"));
143     bookmarkDock->setWidget(m_bookmarkWidget
144         = bookMarkManager->bookmarkDockWidget());
145     addDockWidget(Qt::LeftDockWidgetArea, bookmarkDock);
146 
147     QDockWidget *openPagesDock = new QDockWidget(tr("Open Pages"), this);
148     openPagesDock->setObjectName(QLatin1String("Open Pages"));
149     OpenPagesManager *openPagesManager
150         = OpenPagesManager::createInstance(this, usesDefaultCollection(), m_cmdLine->url());
151     openPagesDock->setWidget(openPagesManager->openPagesWidget());
152     addDockWidget(Qt::LeftDockWidgetArea, openPagesDock);
153 
154     connect(m_centralWidget, &CentralWidget::addBookmark,
155             bookMarkManager, &BookmarkManager::addBookmark);
156     connect(bookMarkManager, &BookmarkManager::escapePressed,
157             this, &MainWindow::activateCurrentCentralWidgetTab);
158     connect(bookMarkManager, &BookmarkManager::setSource,
159             m_centralWidget, &CentralWidget::setSource);
160     connect(bookMarkManager, &BookmarkManager::setSourceInNewTab,
161             openPagesManager, [openPagesManager](const QUrl &url){ openPagesManager->createPage(url); });
162 
163     QHelpSearchEngine *searchEngine = helpEngineWrapper.searchEngine();
164     connect(searchEngine, &QHelpSearchEngine::indexingStarted,
165             this, &MainWindow::indexingStarted);
166     connect(searchEngine, &QHelpSearchEngine::indexingFinished,
167             this, &MainWindow::indexingFinished);
168 
169     QString defWindowTitle = tr("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(reader.read());
189             appIcon.addPixmap(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(tr("Bookmark Toolbar"));
201     toolBar->setObjectName(QLatin1String("Bookmark Toolbar"));
202     bookMarkManager->setBookmarksToolbar(toolBar);
203 
204     toolBar->hide();
205     toolBarMenu()->addAction(toolBar->toggleViewAction());
206 
207     QByteArray ba(helpEngineWrapper.mainWindow());
208     if (!ba.isEmpty())
209         restoreState(ba);
210 
211     ba = helpEngineWrapper.mainWindowGeometry();
212     if (!ba.isEmpty()) {
213         restoreGeometry(ba);
214     } else {
215         tabifyDockWidget(contentDock, indexDock);
216         tabifyDockWidget(indexDock, bookmarkDock);
217         tabifyDockWidget(bookmarkDock, searchDock);
218         contentDock->raise();
219         const QRect screen = QGuiApplication::primaryScreen()->geometry();
220         resize(4*screen.width()/5, 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(0, this, &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(curFilter))
273             helpEngineWrapper.filterEngine()->setActiveFilter(curFilter);
274     }
275 
276     if (usesDefaultCollection())
277         QTimer::singleShot(0, this, &MainWindow::lookForNewQtDocumentation);
278     else
279         checkInitState();
280 
281     connect(&helpEngineWrapper, &HelpEngineWrapper::documentationRemoved,
282             this, &MainWindow::documentationRemoved);
283     connect(&helpEngineWrapper, &HelpEngineWrapper::documentationUpdated,
284             this, &MainWindow::documentationUpdated);
285 
286     setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
287     GlobalActions::instance()->updateActions();
288     if (helpEngineWrapper.addressBarEnabled())
289         showNewAddress();
290 }
291 
~MainWindow()292 MainWindow::~MainWindow()
293 {
294     TRACE_OBJ
295     delete m_qtDocInstaller;
296 }
297 
usesDefaultCollection() const298 bool MainWindow::usesDefaultCollection() const
299 {
300     TRACE_OBJ
301     return m_cmdLine->collectionFile().isEmpty();
302 }
303 
closeEvent(QCloseEvent * e)304 void MainWindow::closeEvent(QCloseEvent *e)
305 {
306     TRACE_OBJ
307     BookmarkManager::destroy();
308     HelpEngineWrapper::instance().setMainWindow(saveState());
309     HelpEngineWrapper::instance().setMainWindowGeometry(saveGeometry());
310     QMainWindow::closeEvent(e);
311 }
312 
initHelpDB(bool registerInternalDoc)313 bool 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(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(helpFile)) {
344         QFile file(helpFile);
345         if (file.open(QIODevice::WriteOnly)) {
346             QResource res(QLatin1String(":/qt-project.org/assistant/assistant.qch"));
347             if (file.write((const char*)res.data(), res.size()) != res.size())
348                 qDebug() << QLatin1String("could not write assistant.qch...");
349 
350             file.close();
351         }
352         helpEngineWrapper.unregisterDocumentation(intern);
353         helpEngineWrapper.registerDocumentation(helpFile);
354         needsSetup = true;
355     }
356 
357     if (needsSetup)
358         helpEngineWrapper.setupData();
359     return true;
360 }
361 
362 static 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 
newQtDocumentation()417 static QStringList newQtDocumentation()
418 {
419     QStringList result;
420     const QDir docDirectory(QLibraryInfo::location(QLibraryInfo::DocumentationPath));
421     const QFileInfoList entries = docDirectory.entryInfoList(QStringList(QStringLiteral("*.qch")),
422                                                              QDir::Files, QDir::Name);
423     if (!entries.isEmpty()) {
424         result.reserve(entries.size());
425         for (const QFileInfo &fi : entries)
426             result.append(fi.baseName());
427         return result;
428     }
429     if (warnAboutMissingModules)
430         qWarning() << "No documentation found in " << QDir::toNativeSeparators(docDirectory.absolutePath());
431     const int docCount = int(sizeof(docs) / sizeof(docs[0]));
432     result.reserve(docCount);
433     for (int d = 0; d < docCount; ++d)
434         result.append(QLatin1String(docs[d]));
435     return result;
436 }
437 
lookForNewQtDocumentation()438 void 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(docCount);
447     for (const QString &doc : docs) {
448         const QtDocInstaller::DocInfo docInfo(doc, helpEngine.qtDocInfo(doc));
449         qtDocInfos.append(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(m_qtDocInstaller, &QtDocInstaller::docsInstalled,
456             this, &MainWindow::qtDocumentationInstalled);
457     connect(m_qtDocInstaller, &QtDocInstaller::qchFileNotFound,
458             this, &MainWindow::resetQtDocInfo);
459     connect(m_qtDocInstaller, &QtDocInstaller::registerDocumentation,
460             this, &MainWindow::registerDocumentation);
461     if (helpEngine.qtDocInfo(QLatin1String("qt")).count() != 2)
462         statusBar()->showMessage(tr("Looking for Qt Documentation..."));
463     m_qtDocInstaller->installDocs();
464 }
465 
qtDocumentationInstalled()466 void MainWindow::qtDocumentationInstalled()
467 {
468     TRACE_OBJ
469     statusBar()->clearMessage();
470     checkInitState();
471 }
472 
checkInitState()473 void 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(helpEngine.contentModel(), &QHelpContentModel::contentsCreated,
486                     this, &MainWindow::checkInitState);
487             connect(helpEngine.indexModel(), &QHelpIndexModel::indexCreated,
488                     this, &MainWindow::checkInitState);
489             m_connectedInitSignals = true;
490         }
491     } else {
492         if (m_connectedInitSignals) {
493             disconnect(helpEngine.contentModel(), nullptr, this, nullptr);
494             disconnect(helpEngine.indexModel(), nullptr, this, nullptr);
495         }
496         HelpEngineWrapper::instance().initialDocSetupDone();
497         emit initDone();
498     }
499 }
500 
insertLastPages()501 void MainWindow::insertLastPages()
502 {
503     TRACE_OBJ
504     if (m_cmdLine->search() == CmdLineParser::Activate)
505         showSearch();
506 }
507 
setupActions()508 void 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(QLatin1String("win"));
517 #endif
518 
519     QMenu *menu = menuBar()->addMenu(tr("&File"));
520     OpenPagesManager *const openPages = OpenPagesManager::instance();
521     m_newTabAction = menu->addAction(tr("New &Tab"),
522             openPages, &OpenPagesManager::createBlankPage);
523     m_newTabAction->setShortcut(QKeySequence::AddTab);
524     m_closeTabAction = menu->addAction(tr("&Close Tab"),
525             openPages, &OpenPagesManager::closeCurrentPage);
526     m_closeTabAction->setShortcuts(QKeySequence::Close);
527     m_closeTabAction->setEnabled(openPages->pageCount() > 1);
528     connect(openPages, &OpenPagesManager::pageClosed,
529             this, &MainWindow::handlePageCountChanged);
530     connect(openPages, &OpenPagesManager::pageAdded,
531             this, &MainWindow::handlePageCountChanged);
532 
533     menu->addSeparator();
534 
535     m_pageSetupAction = menu->addAction(tr("Page Set&up..."),
536             m_centralWidget, &CentralWidget::pageSetup);
537     m_printPreviewAction = menu->addAction(tr("Print Preview..."),
538             m_centralWidget, &CentralWidget::printPreview);
539 
540     GlobalActions *globalActions = GlobalActions::instance(this);
541     menu->addAction(globalActions->printAction());
542     menu->addSeparator();
543 
544     QIcon appExitIcon = QIcon::fromTheme("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(appExitIcon, tr("&Quit"),
552             this, &QWidget::close);
553     tmp->setShortcut(QKeySequence::Quit);
554 #endif
555     tmp->setMenuRole(QAction::QuitRole);
556 
557     menu = menuBar()->addMenu(tr("&Edit"));
558 #if QT_CONFIG(clipboard)
559     menu->addAction(globalActions->copyAction());
560 #endif
561     menu->addAction(globalActions->findAction());
562 
563     QAction *findNextAction = menu->addAction(tr("Find &Next"),
564             m_centralWidget, &CentralWidget::findNext);
565     findNextAction->setShortcuts(QKeySequence::FindNext);
566 
567     QAction *findPreviousAction = menu->addAction(tr("Find &Previous"),
568             m_centralWidget, &CentralWidget::findPrevious);
569     findPreviousAction->setShortcuts(QKeySequence::FindPrevious);
570 
571     menu->addSeparator();
572     tmp = menu->addAction(tr("Preferences..."),
573             this, &MainWindow::showPreferences);
574     tmp->setMenuRole(QAction::PreferencesRole);
575 
576     m_viewMenu = menuBar()->addMenu(tr("&View"));
577     m_viewMenu->addAction(globalActions->zoomInAction());
578     m_viewMenu->addAction(globalActions->zoomOutAction());
579 
580     m_resetZoomAction = m_viewMenu->addAction(tr("Normal &Size"),
581             m_centralWidget, &CentralWidget::resetZoom);
582     m_resetZoomAction->setPriority(QAction::LowPriority);
583     m_resetZoomAction->setIcon(QIcon(resourcePath + QLatin1String("/resetzoom.png")));
584     m_resetZoomAction->setShortcut(tr("Ctrl+0"));
585 
586     m_viewMenu->addSeparator();
587 
588     m_viewMenu->addAction(tr("Contents"),
589             this, &MainWindow::showContents, QKeySequence(tr("ALT+C")));
590     m_viewMenu->addAction(tr("Index"),
591             this, &MainWindow::showIndex, QKeySequence(tr("ALT+I")));
592     m_viewMenu->addAction(tr("Bookmarks"),
593             this, &MainWindow::showBookmarksDockWidget, QKeySequence(tr("ALT+O")));
594     m_viewMenu->addAction(tr("Search"),
595             this, &MainWindow::showSearch, QKeySequence(tr("ALT+S")));
596     m_viewMenu->addAction(tr("Open Pages"),
597             this, &MainWindow::showOpenPages, QKeySequence(tr("ALT+P")));
598 
599     menu = menuBar()->addMenu(tr("&Go"));
600     menu->addAction(globalActions->homeAction());
601     menu->addAction(globalActions->backAction());
602     menu->addAction(globalActions->nextAction());
603 
604     m_syncAction = menu->addAction(tr("Sync with Table of Contents"),
605             this, &MainWindow::syncContents);
606     m_syncAction->setIconText(tr("Sync"));
607     m_syncAction->setIcon(QIcon(resourcePath + QLatin1String("/synctoc.png")));
608 
609     menu->addSeparator();
610 
611     tmp = menu->addAction(tr("Next Page"),
612             openPages, &OpenPagesManager::nextPage);
613     tmp->setShortcuts(QList<QKeySequence>() << QKeySequence(tr("Ctrl+Alt+Right"))
614         << QKeySequence(Qt::CTRL | Qt::Key_PageDown));
615 
616     tmp = menu->addAction(tr("Previous Page"),
617             openPages, &OpenPagesManager::previousPage);
618     tmp->setShortcuts(QList<QKeySequence>() << QKeySequence(tr("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(sct, &QShortcut::activated,
630             openPages, &OpenPagesManager::nextPageWithSwitcher);
631     sct = new QShortcut(QKeySequence(modifier | Qt::SHIFT | Qt::Key_Tab), this);
632     connect(sct, &QShortcut::activated,
633             openPages, &OpenPagesManager::previousPageWithSwitcher);
634 
635     BookmarkManager::instance()->setBookmarksMenu(menuBar()->addMenu(tr("&Bookmarks")));
636 
637     menu = menuBar()->addMenu(tr("&Help"));
638     m_aboutAction = menu->addAction(tr("About..."),
639             this, &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("zoom-original" , m_resetZoomAction->icon()));
644     m_syncAction->setIcon(QIcon::fromTheme("view-refresh" , m_syncAction->icon()));
645 #endif
646 
647     QToolBar *navigationBar = addToolBar(tr("Navigation Toolbar"));
648     navigationBar->setObjectName(QLatin1String("NavigationToolBar"));
649     navigationBar->addAction(globalActions->backAction());
650     navigationBar->addAction(globalActions->nextAction());
651     navigationBar->addAction(globalActions->homeAction());
652     navigationBar->addAction(m_syncAction);
653     navigationBar->addSeparator();
654 #if QT_CONFIG(clipboard)
655     navigationBar->addAction(globalActions->copyAction());
656 #endif
657     navigationBar->addAction(globalActions->printAction());
658     navigationBar->addAction(globalActions->findAction());
659     navigationBar->addSeparator();
660     navigationBar->addAction(globalActions->zoomInAction());
661     navigationBar->addAction(globalActions->zoomOutAction());
662     navigationBar->addAction(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(m_centralWidget, &CentralWidget::copyAvailable,
676             globalActions, &GlobalActions::setCopyAvailable);
677 #endif
678     connect(m_centralWidget, &CentralWidget::currentViewerChanged,
679             globalActions, &GlobalActions::updateActions);
680     connect(m_centralWidget, &CentralWidget::forwardAvailable,
681             globalActions, &GlobalActions::updateActions);
682     connect(m_centralWidget, &CentralWidget::backwardAvailable,
683             globalActions, &GlobalActions::updateActions);
684     connect(m_centralWidget, &CentralWidget::highlighted,
685             this, [this](const QUrl &link) { statusBar()->showMessage(link.toString());} );
686 
687     // index window
688     connect(m_indexWindow, &IndexWindow::linkActivated,
689             m_centralWidget, &CentralWidget::setSource);
690     connect(m_indexWindow, &IndexWindow::documentsActivated,
691             this, &MainWindow::showTopicChooser);
692     connect(m_indexWindow, &IndexWindow::escapePressed,
693             this, &MainWindow::activateCurrentCentralWidgetTab);
694 
695     // content window
696     connect(m_contentWindow, &ContentWindow::linkActivated,
697             m_centralWidget, &CentralWidget::setSource);
698     connect(m_contentWindow, &ContentWindow::escapePressed,
699             this, &MainWindow::activateCurrentCentralWidgetTab);
700 
701     // search window
702     connect(m_searchWindow, &SearchWidget::requestShowLink,
703             CentralWidget::instance(), &CentralWidget::setSourceFromSearch);
704     connect(m_searchWindow, &SearchWidget::requestShowLinkInNewTab,
705             OpenPagesManager::instance(), &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 
toolBarMenu()714 QMenu *MainWindow::toolBarMenu()
715 {
716     TRACE_OBJ
717     if (!m_toolBarMenu) {
718         m_viewMenu->addSeparator();
719         m_toolBarMenu = m_viewMenu->addMenu(tr("Toolbars"));
720     }
721     return m_toolBarMenu;
722 }
723 
setupFilterToolbar()724 void 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(tr("Filter Toolbar"));
736     filterToolBar->setObjectName(QLatin1String("FilterToolBar"));
737     filterToolBar->addWidget(new QLabel(tr("Filtered by:").append(QLatin1Char(' ')),
738         this));
739     filterToolBar->addWidget(m_filterCombo);
740 
741     if (!helpEngine.filterToolbarVisible())
742         filterToolBar->hide();
743     toolBarMenu()->addAction(filterToolBar->toggleViewAction());
744 
745     connect(&helpEngine, &HelpEngineWrapper::setupFinished,
746             this, &MainWindow::setupFilterCombo, Qt::QueuedConnection);
747     connect(m_filterCombo, QOverload<int>::of(&QComboBox::activated),
748             this, &MainWindow::filterDocumentation);
749     connect(helpEngine.filterEngine(), &QHelpFilterEngine::filterActivated,
750             this, &MainWindow::currentFilterChanged);
751 
752     setupFilterCombo();
753 }
754 
setupAddressToolbar()755 void 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(tr("Address Toolbar"));
764     addressToolBar->setObjectName(QLatin1String("AddressToolBar"));
765     insertToolBarBreak(addressToolBar);
766 
767     addressToolBar->addWidget(new QLabel(tr("Address:").append(QChar::Space),
768         this));
769     addressToolBar->addWidget(m_addressLineEdit);
770 
771     if (!helpEngine.addressBarVisible())
772         addressToolBar->hide();
773     toolBarMenu()->addAction(addressToolBar->toggleViewAction());
774 
775     // address lineedit
776     connect(m_addressLineEdit, &QLineEdit::returnPressed,
777             this, &MainWindow::gotoAddress);
778     connect(m_centralWidget, &CentralWidget::currentViewerChanged,
779             this, QOverload<>::of(&MainWindow::showNewAddress));
780     connect(m_centralWidget, &CentralWidget::sourceChanged,
781             this, QOverload<>::of(&MainWindow::showNewAddress));
782 }
783 
updateAboutMenuText()784 void 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(QLatin1Char('_'));
794         if (i > -1)
795             currentLang = currentLang.left(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 
showNewAddress()812 void MainWindow::showNewAddress()
813 {
814     TRACE_OBJ
815     showNewAddress(m_centralWidget->currentSource());
816 }
817 
showNewAddress(const QUrl & url)818 void MainWindow::showNewAddress(const QUrl &url)
819 {
820     TRACE_OBJ
821     m_addressLineEdit->setText(url.toString());
822 }
823 
gotoAddress()824 void MainWindow::gotoAddress()
825 {
826     TRACE_OBJ
827     m_centralWidget->setSource(m_addressLineEdit->text());
828 }
829 
showTopicChooser(const QList<QHelpLink> & documents,const QString & keyword)830 void 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 
showPreferences()840 void MainWindow::showPreferences()
841 {
842     TRACE_OBJ
843     PreferencesDialog dia(this);
844     connect(&dia, &PreferencesDialog::updateApplicationFont,
845             this, &MainWindow::updateApplicationFont);
846     connect(&dia, &PreferencesDialog::updateBrowserFont,
847             m_centralWidget, &CentralWidget::updateBrowserFont);
848     connect(&dia, &PreferencesDialog::updateUserInterface,
849             m_centralWidget, &CentralWidget::updateUserInterface);
850     dia.exec();
851 }
852 
syncContents()853 void 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             tr("Could not find the associated content item."), 3000);
862     qApp->restoreOverrideCursor();
863 }
864 
showAboutDialog()865 void 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(QLatin1Char('_'));
876         if (i > -1)
877             currentLang = currentLang.left(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(iconArray);
899         aboutDia.setText(QString::fromUtf8(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(tr("<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(tr("Qt Assistant"), QLatin1String(QT_VERSION_STR), browser, QStringLiteral("2020")),
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("About %1").arg(windowTitle()));
922     aboutDia.exec();
923 }
924 
setContentsVisible(bool visible)925 void MainWindow::setContentsVisible(bool visible)
926 {
927     TRACE_OBJ
928     if (visible)
929         showContents();
930     else
931         hideContents();
932 }
933 
showContents()934 void MainWindow::showContents()
935 {
936     TRACE_OBJ
937     activateDockWidget(m_contentWindow);
938 }
939 
hideContents()940 void MainWindow::hideContents()
941 {
942     TRACE_OBJ
943     m_contentWindow->parentWidget()->hide();
944 }
945 
setIndexVisible(bool visible)946 void MainWindow::setIndexVisible(bool visible)
947 {
948     TRACE_OBJ
949     if (visible)
950         showIndex();
951     else
952         hideIndex();
953 }
954 
showIndex()955 void MainWindow::showIndex()
956 {
957     TRACE_OBJ
958     activateDockWidget(m_indexWindow);
959 }
960 
hideIndex()961 void MainWindow::hideIndex()
962 {
963     TRACE_OBJ
964     m_indexWindow->parentWidget()->hide();
965 }
966 
setBookmarksVisible(bool visible)967 void MainWindow::setBookmarksVisible(bool visible)
968 {
969     TRACE_OBJ
970     if (visible)
971         showBookmarksDockWidget();
972     else
973         hideBookmarksDockWidget();
974 }
975 
showBookmarksDockWidget()976 void MainWindow::showBookmarksDockWidget()
977 {
978     TRACE_OBJ
979     activateDockWidget(m_bookmarkWidget);
980 }
981 
hideBookmarksDockWidget()982 void MainWindow::hideBookmarksDockWidget()
983 {
984     TRACE_OBJ
985     m_bookmarkWidget->parentWidget()->hide();
986 }
987 
setSearchVisible(bool visible)988 void MainWindow::setSearchVisible(bool visible)
989 {
990     TRACE_OBJ
991     if (visible)
992         showSearch();
993     else
994         hideSearch();
995 }
996 
showSearch()997 void MainWindow::showSearch()
998 {
999     TRACE_OBJ
1000     activateDockWidget(m_searchWindow);
1001 }
1002 
showOpenPages()1003 void MainWindow::showOpenPages()
1004 {
1005     TRACE_OBJ
1006     activateDockWidget(OpenPagesManager::instance()->openPagesWidget());
1007 }
1008 
hideSearch()1009 void MainWindow::hideSearch()
1010 {
1011     TRACE_OBJ
1012     m_searchWindow->parentWidget()->hide();
1013 }
1014 
activateDockWidget(QWidget * w)1015 void MainWindow::activateDockWidget(QWidget *w)
1016 {
1017     TRACE_OBJ
1018     w->parentWidget()->show();
1019     w->parentWidget()->raise();
1020     w->setFocus();
1021 }
1022 
setIndexString(const QString & str)1023 void MainWindow::setIndexString(const QString &str)
1024 {
1025     TRACE_OBJ
1026     m_indexWindow->setSearchLineEditText(str);
1027 }
1028 
activateCurrentBrowser()1029 void MainWindow::activateCurrentBrowser()
1030 {
1031     TRACE_OBJ
1032     CentralWidget::instance()->activateTab();
1033 }
1034 
activateCurrentCentralWidgetTab()1035 void MainWindow::activateCurrentCentralWidgetTab()
1036 {
1037     TRACE_OBJ
1038     m_centralWidget->activateTab();
1039 }
1040 
updateApplicationFont()1041 void 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 
setupFilterCombo()1054 void 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(tr("Unfiltered"));
1061     const QStringList allFilters = helpEngine.filterEngine()->filters();
1062     if (!allFilters.isEmpty())
1063         m_filterCombo->insertSeparator(1);
1064     for (const QString &filter : allFilters)
1065         m_filterCombo->addItem(filter, filter);
1066 
1067     int idx = m_filterCombo->findData(currentFilter);
1068     if (idx < 0)
1069         idx = 0;
1070     m_filterCombo->setCurrentIndex(idx);
1071 }
1072 
filterDocumentation(int filterIndex)1073 void MainWindow::filterDocumentation(int filterIndex)
1074 {
1075     TRACE_OBJ
1076 
1077     const QString filter = m_filterCombo->itemData(filterIndex).toString();
1078     HelpEngineWrapper::instance().filterEngine()->setActiveFilter(filter);
1079 }
1080 
expandTOC(int depth)1081 void MainWindow::expandTOC(int depth)
1082 {
1083     TRACE_OBJ
1084     Q_ASSERT(depth >= -1);
1085     m_contentWindow->expandToDepth(depth);
1086 }
1087 
indexingStarted()1088 void 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("Updating search index"));
1098         label->setSizePolicy(sizePolicy);
1099         hlayout->addWidget(label);
1100 
1101         QProgressBar *progressBar = new QProgressBar();
1102         progressBar->setRange(0, 0);
1103         progressBar->setTextVisible(false);
1104         progressBar->setSizePolicy(sizePolicy);
1105 
1106         hlayout->setSpacing(6);
1107         hlayout->setContentsMargins(QMargins());
1108         hlayout->addWidget(progressBar);
1109 
1110         statusBar()->addPermanentWidget(m_progressWidget);
1111     }
1112 }
1113 
indexingFinished()1114 void MainWindow::indexingFinished()
1115 {
1116     TRACE_OBJ
1117     statusBar()->removeWidget(m_progressWidget);
1118     delete m_progressWidget;
1119     m_progressWidget = nullptr;
1120 }
1121 
collectionFileDirectory(bool createDir,const QString & cacheDir)1122 QString MainWindow::collectionFileDirectory(bool createDir, const QString &cacheDir)
1123 {
1124     TRACE_OBJ
1125     QString collectionPath =
1126         QStandardPaths::writableLocation(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(collectionPath))
1142             dir.mkpath(collectionPath);
1143     }
1144     return collectionPath;
1145 }
1146 
defaultHelpCollectionFileName()1147 QString MainWindow::defaultHelpCollectionFileName()
1148 {
1149     TRACE_OBJ
1150     // forces creation of the default collection file path
1151     return collectionFileDirectory(true) + QDir::separator() +
1152         QString(QLatin1String("qthelpcollection_%1.qhc")).
1153         arg(QLatin1String(QT_VERSION_STR));
1154 }
1155 
currentFilterChanged(const QString & filter)1156 void MainWindow::currentFilterChanged(const QString &filter)
1157 {
1158     TRACE_OBJ
1159     int index = m_filterCombo->findData(filter);
1160     if (index < 0)
1161         index = 0;
1162     m_filterCombo->setCurrentIndex(index);
1163 }
1164 
documentationRemoved(const QString & namespaceName)1165 void MainWindow::documentationRemoved(const QString &namespaceName)
1166 {
1167     TRACE_OBJ
1168     OpenPagesManager::instance()->closePages(namespaceName);
1169 }
1170 
documentationUpdated(const QString & namespaceName)1171 void MainWindow::documentationUpdated(const QString &namespaceName)
1172 {
1173     TRACE_OBJ
1174     OpenPagesManager::instance()->reloadPages(namespaceName);
1175 }
1176 
resetQtDocInfo(const QString & component)1177 void MainWindow::resetQtDocInfo(const QString &component)
1178 {
1179     TRACE_OBJ
1180     HelpEngineWrapper::instance().setQtDocInfo(component,
1181         QStringList(QDateTime().toString(Qt::ISODate)));
1182 }
1183 
registerDocumentation(const QString & component,const QString & absFileName)1184 void MainWindow::registerDocumentation(const QString &component,
1185                                        const QString &absFileName)
1186 {
1187     TRACE_OBJ
1188     QString ns = QHelpEngineCore::namespaceName(absFileName);
1189     if (ns.isEmpty())
1190         return;
1191 
1192     HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance();
1193     if (helpEngine.registeredDocumentations().contains(ns))
1194         helpEngine.unregisterDocumentation(ns);
1195     if (!helpEngine.registerDocumentation(absFileName)) {
1196         QMessageBox::warning(this, tr("Qt Assistant"),
1197             tr("Could not register file '%1': %2").
1198             arg(absFileName).arg(helpEngine.error()));
1199     } else {
1200         QStringList docInfo;
1201         docInfo << QFileInfo(absFileName).lastModified().toString(Qt::ISODate)
1202                 << absFileName;
1203         helpEngine.setQtDocInfo(component, docInfo);
1204     }
1205 }
1206 
handlePageCountChanged()1207 void MainWindow::handlePageCountChanged()
1208 {
1209     m_closeTabAction->setEnabled(OpenPagesManager::instance()->pageCount() > 1);
1210 }
1211 
1212 QT_END_NAMESPACE
1213