Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3#include "tracer.h"
4
5#include <QtCore/QDir>
6#include <QtCore/QFileInfo>
7#include <QtCore/QLibraryInfo>
8#include <QtCore/QLocale>
9#include <QtCore/QStringList>
10#include <QtCore/QTranslator>
11#include <QtCore/QUrl>
12
13#include <QtWidgets/QApplication>
14#include <QtGui/QDesktopServices>
15
16#include <QtHelp/QHelpEngine>
17#include <QtHelp/QHelpSearchEngine>
18
19#include <QtSql/QSqlDatabase>
20
21#if defined(BROWSER_QTWEBKIT)
22#include <QtGui/QFont>
23#include <QWebSettings>
24#endif
25
26#include "../shared/collectionconfiguration.h"
28#include "mainwindow.h"
29#include "cmdlineparser.h"
30
31#include <memory>
32
33// #define TRACING_REQUESTED
34
35QT_USE_NAMESPACE
36
37using namespace Qt::StringLiterals;
38
39namespace {
40
41void
42updateLastPagesOnUnregister(QHelpEngineCore& helpEngine, const QString& nsName)
43{
45 int lastPage = CollectionConfiguration::lastTabPage(helpEngine);
46 QStringList currentPages = CollectionConfiguration::lastShownPages(helpEngine);
47 if (!currentPages.isEmpty()) {
48 QStringList zoomList = CollectionConfiguration::lastZoomFactors(helpEngine);
49 while (zoomList.size() < currentPages.size())
50 zoomList.append(CollectionConfiguration::DefaultZoomFactor);
51
52 for (int i = currentPages.size(); --i >= 0;) {
53 if (QUrl(currentPages.at(i)).host() == nsName) {
54 zoomList.removeAt(i);
55 currentPages.removeAt(i);
56 lastPage = (lastPage == (i + 1)) ? 1 : lastPage;
57 }
58 }
59
60 CollectionConfiguration::setLastShownPages(helpEngine, currentPages);
62 CollectionConfiguration::setLastZoomFactors(helpEngine, zoomList);
63 }
64}
65
66void stripNonexistingDocs(QHelpEngineCore& collection)
67{
69 const QStringList &namespaces = collection.registeredDocumentations();
70 for (const QString &ns : namespaces) {
71 QFileInfo fi(collection.documentationFileName(ns));
72 if (!fi.exists() || !fi.isFile())
73 collection.unregisterDocumentation(ns);
74 }
75}
76
77QString indexFilesFolder(const QString &collectionFile)
78{
80 QString indexFilesFolder = ".fulltextsearch"_L1;
81 if (!collectionFile.isEmpty()) {
82 QFileInfo fi(collectionFile);
83 indexFilesFolder = u'.' + fi.fileName().left(fi.fileName().lastIndexOf(".qhc"_L1));
84 }
85 return indexFilesFolder;
86}
87
88/*
89 * Returns the expected absolute file path of the cached collection file
90 * correspondinging to the given collection's file.
91 * It may or may not exist yet.
92 */
93QString constructCachedCollectionFilePath(const QHelpEngineCore &collection)
94{
96 const QString &filePath = collection.collectionFile();
97 const QString &fileName = QFileInfo(filePath).fileName();
98 const QString &cacheDir = CollectionConfiguration::cacheDir(collection);
99 const QString &dir = !cacheDir.isEmpty()
100 && CollectionConfiguration::cacheDirIsRelativeToCollection(collection)
101 ? QFileInfo(filePath).dir().absolutePath()
102 + QDir::separator() + cacheDir
103 : MainWindow::collectionFileDirectory(false, cacheDir);
104 return dir + QDir::separator() + fileName;
105}
106
107bool synchronizeDocs(QHelpEngineCore &collection,
108 QHelpEngineCore &cachedCollection,
109 CmdLineParser &cmd)
110{
112 const QDateTime &lastCollectionRegisterTime =
113 CollectionConfiguration::lastRegisterTime(collection);
114 if (!lastCollectionRegisterTime.isValid() || lastCollectionRegisterTime
115 < CollectionConfiguration::lastRegisterTime(cachedCollection))
116 return true;
117
118 const QStringList &docs = collection.registeredDocumentations();
119 const QStringList &cachedDocs = cachedCollection.registeredDocumentations();
120
121 /*
122 * Ensure that the cached collection contains all docs that
123 * the collection contains.
124 */
125 for (const QString &doc : docs) {
126 if (!cachedDocs.contains(doc)) {
127 const QString &docFile = collection.documentationFileName(doc);
128 if (!cachedCollection.registerDocumentation(docFile)) {
129 cmd.showMessage(QCoreApplication::translate("Assistant",
130 "Error registering documentation file '%1': %2").
131 arg(docFile, cachedCollection.error()), true);
132 return false;
133 }
134 }
135 }
136
138
139 return true;
140}
141
142bool removeSearchIndex(const QString &collectionFile)
143{
145 const QString path =
146 QFileInfo(collectionFile).path() + u'/' + indexFilesFolder(collectionFile);
147
148 QDir dir(path);
149 if (!dir.exists())
150 return false;
151
152 const QStringList &list = dir.entryList(QDir::Files | QDir::Hidden);
153 for (const QString &item : list)
154 dir.remove(item);
155 return true;
156}
157
158QCoreApplication* createApplication(int &argc, char *argv[])
159{
161#ifndef Q_OS_WIN
162 // Look for arguments that imply command-line mode.
163 const char * cmdModeArgs[] = {
164 "-help", "-register", "-unregister", "-remove-search-index",
165 "-rebuild-search-index"
166 };
167 for (int i = 1; i < argc; ++i) {
168 for (size_t j = 0; j < sizeof cmdModeArgs/sizeof *cmdModeArgs; ++j) {
169 if (strcmp(argv[i], cmdModeArgs[j]) == 0)
170 return new QCoreApplication(argc, argv);
171 }
172 }
173#endif
174 QApplication *app = new QApplication(argc, argv);
175 app->connect(app, &QGuiApplication::lastWindowClosed, app,
176 &QCoreApplication::quit);
177 return app;
178}
179
180bool registerDocumentation(QHelpEngineCore &collection, CmdLineParser &cmd,
181 bool printSuccess)
182{
184 if (!collection.registerDocumentation(cmd.helpFile())) {
185 cmd.showMessage(QCoreApplication::translate("Assistant",
186 "Could not register documentation file\n%1\n\nReason:\n%2")
187 .arg(cmd.helpFile(), collection.error()), true);
188 return false;
189 }
190 if (printSuccess)
191 cmd.showMessage(QCoreApplication::translate("Assistant",
192 "Documentation successfully registered."),
193 false);
195 return true;
196}
197
198bool unregisterDocumentation(QHelpEngineCore &collection,
199 const QString &namespaceName, CmdLineParser &cmd, bool printSuccess)
200{
202 if (!collection.unregisterDocumentation(namespaceName)) {
203 cmd.showMessage(QCoreApplication::translate("Assistant",
204 "Could not unregister documentation"
205 " file\n%1\n\nReason:\n%2").
206 arg(cmd.helpFile(), collection.error()), true);
207 return false;
208 }
209 updateLastPagesOnUnregister(collection, namespaceName);
210 if (printSuccess)
211 cmd.showMessage(QCoreApplication::translate("Assistant",
212 "Documentation successfully unregistered."),
213 false);
214 return true;
215}
216
217void setupTranslation(const QString &fileName, const QString &dir)
218{
219 QTranslator *translator = new QTranslator(QCoreApplication::instance());
220 if (translator->load(QLocale(), fileName, "_"_L1, dir))
221 QCoreApplication::installTranslator(translator);
222}
223
224void setupTranslations()
225{
227 const QString &resourceDir
228 = QLibraryInfo::path(QLibraryInfo::TranslationsPath);
229 setupTranslation("assistant"_L1, resourceDir);
230 setupTranslation("qt"_L1, resourceDir);
231 setupTranslation("qt_help"_L1, resourceDir);
232}
233
234} // Anonymous namespace.
235
241
243{
244 /*
245 * Create the collection objects that we need. We always have the
246 * cached collection file. Depending on whether the user specified
247 * one, we also may have an input collection file.
248 */
249 const QString collectionFile = cmd->collectionFile();
250 const bool collectionFileGiven = !collectionFile.isEmpty();
251 std::unique_ptr<QHelpEngineCore> collection;
252 if (collectionFileGiven) {
253 collection.reset(new QHelpEngineCore(collectionFile));
254 if (!collection->setupData()) {
255 cmd->showMessage(QCoreApplication::translate("Assistant",
256 "Error reading collection file '%1': %2.")
257 .arg(collectionFile, collection->error()), true);
258 return ExitFailure;
259 }
260 }
261 const QString &cachedCollectionFile = collectionFileGiven
262 ? constructCachedCollectionFilePath(*collection)
263 : MainWindow::defaultHelpCollectionFileName();
264 if (collectionFileGiven && !QFileInfo(cachedCollectionFile).exists()
265 && !collection->copyCollectionFile(cachedCollectionFile)) {
266 cmd->showMessage(QCoreApplication::translate("Assistant",
267 "Error creating collection file '%1': %2.")
268 .arg(cachedCollectionFile, collection->error()), true);
269 return ExitFailure;
270 }
271 QHelpEngineCore cachedCollection(cachedCollectionFile);
272 if (!cachedCollection.setupData()) {
273 cmd->showMessage(QCoreApplication::translate("Assistant",
274 "Error reading collection file '%1': %2.")
275 .arg(cachedCollectionFile, cachedCollection.error()), true);
276 return ExitFailure;
277 }
278
279 stripNonexistingDocs(cachedCollection);
280 if (collectionFileGiven) {
281 if (CollectionConfiguration::isNewer(*collection, cachedCollection))
283 cachedCollection);
284 if (!synchronizeDocs(*collection, cachedCollection, *cmd))
285 return ExitFailure;
286 }
287
289 const QStringList &cachedDocs =
290 cachedCollection.registeredDocumentations();
291 const QString &namespaceName =
292 QHelpEngineCore::namespaceName(cmd->helpFile());
294 if (collectionFileGiven
295 && !registerDocumentation(*collection, *cmd, true))
296 return ExitFailure;
297 if (!cachedDocs.contains(namespaceName)
298 && !registerDocumentation(cachedCollection, *cmd, !collectionFileGiven))
299 return ExitFailure;
300 return ExitSuccess;
301 }
303 if (collectionFileGiven
304 && !unregisterDocumentation(*collection, namespaceName, *cmd, true))
305 return ExitFailure;
306 if (cachedDocs.contains(namespaceName)
307 && !unregisterDocumentation(cachedCollection, namespaceName,
308 *cmd, !collectionFileGiven))
309 return ExitFailure;
310 return ExitSuccess;
311 }
312 }
313
314 if (cmd->removeSearchIndex()) {
315 return removeSearchIndex(cachedCollectionFile)
316 ? ExitSuccess : ExitFailure;
317 }
318
319 if (!QSqlDatabase::isDriverAvailable("QSQLITE"_L1)) {
320 cmd->showMessage(QCoreApplication::translate("Assistant",
321 "Cannot load sqlite database driver!"),
322 true);
323 return ExitFailure;
324 }
325
326 if (!cmd->currentFilter().isEmpty()) {
327 if (collectionFileGiven)
328 collection->setCurrentFilter(cmd->currentFilter());
329 cachedCollection.setCurrentFilter(cmd->currentFilter());
330 }
331
332 if (collectionFileGiven)
333 cmd->setCollectionFile(cachedCollectionFile);
334
335 return NoExit;
336}
337
338int main(int argc, char *argv[])
339{
341 std::unique_ptr<QCoreApplication> a(createApplication(argc, argv));
342#if QT_CONFIG(library)
343 a->addLibraryPath(a->applicationDirPath() + "/plugins"_L1);
344#endif
345 setupTranslations();
346
347#if defined(BROWSER_QTWEBKIT)
348 if (qobject_cast<QApplication *>(a.data())) {
349 QFont f;
350 f.setStyleHint(QFont::SansSerif);
351 QWebSettings::globalSettings()->setFontFamily(QWebSettings::StandardFont, f.defaultFamily());
352 }
353#endif // BROWSER_QTWEBKIT
354
355 // Parse arguments.
356 CmdLineParser cmd(a->arguments());
357 CmdLineParser::Result res = cmd.parse();
358 if (res == CmdLineParser::Help)
359 return 0;
360 else if (res == CmdLineParser::Error)
361 return -1;
362
363 const ExitStatus status = preliminarySetup(&cmd);
364 switch (status) {
365 case ExitFailure: return EXIT_FAILURE;
366 case ExitSuccess: return EXIT_SUCCESS;
367 default: break;
368 }
369
370 MainWindow *w = new MainWindow(&cmd);
371 w->show();
372
373 /*
374 * We need to be careful here: The main window has to be deleted before
375 * the help engine wrapper, which has to be deleted before the
376 * QApplication.
377 */
378 const int retval = a->exec();
379 delete w;
381 return retval;
382}
RegisterState registerRequest() const
bool removeSearchIndex() const
static bool isNewer(const QHelpEngineCore &newer, const QHelpEngineCore &older)
static void setLastTabPage(QHelpEngineCore &helpEngine, int lastPage)
static void copyConfiguration(const QHelpEngineCore &source, QHelpEngineCore &target)
static void updateLastRegisterTime(QHelpEngineCore &helpEngine)
static int lastTabPage(const QHelpEngineCore &helpEngine)
static void removeInstance()
static ExitStatus preliminarySetup(CmdLineParser *cmd)
Definition main.cpp:242
ExitStatus
Definition main.cpp:236
@ ExitSuccess
Definition main.cpp:237
@ ExitFailure
Definition main.cpp:238
@ NoExit
Definition main.cpp:239
int main(int argc, char *argv[])
[ctor_close]
#define TRACE_OBJ
Definition tracer.h:34