diff options
author | Eike Ziller <eike.ziller@qt.io> | 2018-08-31 16:00:32 +0200 |
---|---|---|
committer | Eike Ziller <eike.ziller@qt.io> | 2018-09-04 11:21:18 +0000 |
commit | 2c17fbe8dd9fdc619efdeceeddf68ad68f6dfcc1 (patch) | |
tree | 36439c95706d27a6c5f4ff9212bda25fe2125651 /src/plugins/coreplugin/helpmanager.cpp | |
parent | 25400751a4b4cd03758862b8365f57e8afed7985 (diff) | |
download | qt-creator-2c17fbe8dd9fdc619efdeceeddf68ad68f6dfcc1.tar.gz |
Make Core independent from QtHelp
We don't want various plugins to depend on the Help plugin,
but we also do not want Core to depend on QtHelp.
For example when turning the Help plugin off, documentation should
actually no longer be registered through QtHelp. So we need
parts of the interface in Core, which must then be delegated
to the actual implementation in Help.
As positive side-effects the interface in Core will be slimmer,
and the code in the Help plugin can later be simplified, too,
because then we don't have the "Core" and the "Gui" help engines
separated in different plugins anymore, which should remove the
need for some setup indirections.
Task-number: QTCREATORBUG-20381
Change-Id: I634c5811c45d6a3dfd6ddc682cae270e38384cbf
Reviewed-by: hjk <hjk@qt.io>
Diffstat (limited to 'src/plugins/coreplugin/helpmanager.cpp')
-rw-r--r-- | src/plugins/coreplugin/helpmanager.cpp | 522 |
1 files changed, 39 insertions, 483 deletions
diff --git a/src/plugins/coreplugin/helpmanager.cpp b/src/plugins/coreplugin/helpmanager.cpp index 51a2c426d7..a27b1499b3 100644 --- a/src/plugins/coreplugin/helpmanager.cpp +++ b/src/plugins/coreplugin/helpmanager.cpp @@ -23,526 +23,82 @@ ** ****************************************************************************/ -#include "helpmanager.h" +#include "helpmanager_implementation.h" -#include <coreplugin/icore.h> -#include <coreplugin/progressmanager/progressmanager.h> -#include <utils/algorithm.h> -#include <utils/filesystemwatcher.h> +#include "coreplugin.h" + +#include <extensionsystem/pluginspec.h> #include <utils/qtcassert.h> -#include <utils/runextensions.h> -#include <QDateTime> -#include <QDebug> -#include <QDir> -#include <QFileInfo> -#include <QFutureWatcher> -#include <QStringList> #include <QUrl> -#ifdef QT_HELP_LIB - -#include <QHelpEngineCore> - -#include <QMutexLocker> -#include <QSqlDatabase> -#include <QSqlDriver> -#include <QSqlError> -#include <QSqlQuery> - -static const char kUserDocumentationKey[] = "Help/UserDocumentation"; -static const char kUpdateDocumentationTask[] = "UpdateDocumentationTask"; - namespace Core { +namespace HelpManager { -struct HelpManagerPrivate -{ - HelpManagerPrivate() = default; - ~HelpManagerPrivate(); - - const QStringList documentationFromInstaller(); - void readSettings(); - void writeSettings(); - void cleanUpDocumentation(); - - bool m_needsSetup = true; - QHelpEngineCore *m_helpEngine = nullptr; - Utils::FileSystemWatcher *m_collectionWatcher = nullptr; - - // data for delayed initialization - QSet<QString> m_filesToRegister; - QSet<QString> m_nameSpacesToUnregister; - QHash<QString, QVariant> m_customValues; +// makes sure that plugins can connect to HelpManager signals even if the Help plugin is not loaded +Q_GLOBAL_STATIC(Signals, m_signals) - QSet<QString> m_userRegisteredFiles; +static Implementation *m_instance = nullptr; - QMutex m_helpengineMutex; - QFuture<bool> m_registerFuture; -}; - -static HelpManager *m_instance = nullptr; -static HelpManagerPrivate *d = nullptr; - -static const char linksForKeyQuery[] = "SELECT d.Title, f.Name, e.Name, " - "d.Name, a.Anchor FROM IndexTable a, FileNameTable d, FolderTable e, " - "NamespaceTable f WHERE a.FileId=d.FileId AND d.FolderId=e.Id AND " - "a.NamespaceId=f.Id AND a.Name='%1'"; - -// -- DbCleaner - -struct DbCleaner +static bool checkInstance() { - DbCleaner(const QString &dbName) : name(dbName) {} - ~DbCleaner() { QSqlDatabase::removeDatabase(name); } - QString name; -}; - -// -- HelpManager - -HelpManager::HelpManager(QObject *parent) : - QObject(parent) -{ - QTC_CHECK(!m_instance); - m_instance = this; - d = new HelpManagerPrivate; -} - -HelpManager::~HelpManager() -{ - delete d; - m_instance = nullptr; -} - -HelpManager *HelpManager::instance() -{ - Q_ASSERT(m_instance); - return m_instance; + auto plugin = Internal::CorePlugin::instance(); + // HelpManager API can only be used after the actual implementation has been created by the + // Help plugin, so check that the plugins have all been created. That is the case + // when the Core plugin is initialized. + QTC_CHECK(plugin && plugin->pluginSpec() + && plugin->pluginSpec()->state() >= ExtensionSystem::PluginSpec::Initialized); + return m_instance != nullptr; } -QString HelpManager::collectionFilePath() +Signals *Signals::instance() { - return QDir::cleanPath(ICore::userResourcePath() - + QLatin1String("/helpcollection.qhc")); + return m_signals; } -void HelpManager::registerDocumentation(const QStringList &files) +Implementation::Implementation() { - if (d->m_needsSetup) { - for (const QString &filePath : files) - d->m_filesToRegister.insert(filePath); - return; - } - - QFuture<bool> future = Utils::runAsync(&HelpManager::registerDocumentationNow, files); - Utils::onResultReady(future, m_instance, [](bool docsChanged){ - if (docsChanged) { - d->m_helpEngine->setupData(); - emit m_instance->documentationChanged(); - } - }); - ProgressManager::addTask(future, tr("Update Documentation"), - kUpdateDocumentationTask); -} - -void HelpManager::registerDocumentationNow(QFutureInterface<bool> &futureInterface, - const QStringList &files) -{ - QMutexLocker locker(&d->m_helpengineMutex); - - futureInterface.setProgressRange(0, files.count()); - futureInterface.setProgressValue(0); - - QHelpEngineCore helpEngine(collectionFilePath()); - helpEngine.setupData(); - bool docsChanged = false; - QStringList nameSpaces = helpEngine.registeredDocumentations(); - for (const QString &file : files) { - if (futureInterface.isCanceled()) - break; - futureInterface.setProgressValue(futureInterface.progressValue() + 1); - const QString &nameSpace = helpEngine.namespaceName(file); - if (nameSpace.isEmpty()) - continue; - if (!nameSpaces.contains(nameSpace)) { - if (helpEngine.registerDocumentation(file)) { - nameSpaces.append(nameSpace); - docsChanged = true; - } else { - qWarning() << "Error registering namespace '" << nameSpace - << "' from file '" << file << "':" << helpEngine.error(); - } - } else { - const QLatin1String key("CreationDate"); - const QString &newDate = helpEngine.metaData(file, key).toString(); - const QString &oldDate = helpEngine.metaData( - helpEngine.documentationFileName(nameSpace), key).toString(); - if (QDateTime::fromString(newDate, Qt::ISODate) - > QDateTime::fromString(oldDate, Qt::ISODate)) { - if (helpEngine.unregisterDocumentation(nameSpace)) { - docsChanged = true; - helpEngine.registerDocumentation(file); - } - } - } - } - futureInterface.reportResult(docsChanged); -} - -void HelpManager::unregisterDocumentation(const QStringList &nameSpaces) -{ - if (d->m_needsSetup) { - for (const QString &name : nameSpaces) - d->m_nameSpacesToUnregister.insert(name); - return; - } - - QMutexLocker locker(&d->m_helpengineMutex); - bool docsChanged = false; - for (const QString &nameSpace : nameSpaces) { - const QString filePath = d->m_helpEngine->documentationFileName(nameSpace); - if (d->m_helpEngine->unregisterDocumentation(nameSpace)) { - docsChanged = true; - d->m_userRegisteredFiles.remove(filePath); - } else { - qWarning() << "Error unregistering namespace '" << nameSpace - << "' from file '" << filePath - << "': " << d->m_helpEngine->error(); - } - } - locker.unlock(); - if (docsChanged) - emit m_instance->documentationChanged(); -} - -void HelpManager::registerUserDocumentation(const QStringList &filePaths) -{ - for (const QString &filePath : filePaths) - d->m_userRegisteredFiles.insert(filePath); - registerDocumentation(filePaths); -} - -QSet<QString> HelpManager::userDocumentationPaths() -{ - return d->m_userRegisteredFiles; + QTC_CHECK(!m_instance); + m_instance = this; } -static QUrl buildQUrl(const QString &ns, const QString &folder, - const QString &relFileName, const QString &anchor) +Implementation::~Implementation() { - QUrl url; - url.setScheme(QLatin1String("qthelp")); - url.setAuthority(ns); - url.setPath(QLatin1Char('/') + folder + QLatin1Char('/') + relFileName); - url.setFragment(anchor); - return url; + m_instance = nullptr; } -// This should go into Qt 4.8 once we start using it for Qt Creator -QMap<QString, QUrl> HelpManager::linksForKeyword(const QString &key) +void registerDocumentation(const QStringList &files) { - QMap<QString, QUrl> links; - QTC_ASSERT(!d->m_needsSetup, return links); - - const QLatin1String sqlite("QSQLITE"); - const QLatin1String name("HelpManager::linksForKeyword"); - - DbCleaner cleaner(name); - QSqlDatabase db = QSqlDatabase::addDatabase(sqlite, name); - if (db.driver() && db.driver()->lastError().type() == QSqlError::NoError) { - const QStringList ®isteredDocs = d->m_helpEngine->registeredDocumentations(); - for (const QString &nameSpace : registeredDocs) { - db.setDatabaseName(d->m_helpEngine->documentationFileName(nameSpace)); - if (db.open()) { - QSqlQuery query = QSqlQuery(db); - query.setForwardOnly(true); - query.exec(QString::fromLatin1(linksForKeyQuery).arg(key)); - while (query.next()) { - QString title = query.value(0).toString(); - if (title.isEmpty()) // generate a title + corresponding path - title = key + QLatin1String(" : ") + query.value(3).toString(); - links.insertMulti(title, buildQUrl(query.value(1).toString(), - query.value(2).toString(), query.value(3).toString(), - query.value(4).toString())); - } - } - } - } - return links; + if (checkInstance()) + m_instance->registerDocumentation(files); } -QMap<QString, QUrl> HelpManager::linksForIdentifier(const QString &id) +void unregisterDocumentation(const QStringList &nameSpaces) { - QMap<QString, QUrl> empty; - QTC_ASSERT(!d->m_needsSetup, return empty); - return d->m_helpEngine->linksForIdentifier(id); + if (checkInstance()) + m_instance->unregisterDocumentation(nameSpaces); } -QUrl HelpManager::findFile(const QUrl &url) +QMap<QString, QUrl> linksForIdentifier(const QString &id) { - QTC_ASSERT(!d->m_needsSetup, return QUrl()); - return d->m_helpEngine->findFile(url); + return checkInstance() ? m_instance->linksForIdentifier(id) : QMap<QString, QUrl>(); } -QByteArray HelpManager::fileData(const QUrl &url) +QByteArray fileData(const QUrl &url) { - QTC_ASSERT(!d->m_needsSetup, return QByteArray()); - return d->m_helpEngine->fileData(url); + return checkInstance() ? m_instance->fileData(url) : QByteArray(); } -void HelpManager::handleHelpRequest(const QUrl &url, HelpManager::HelpViewerLocation location) +void handleHelpRequest(const QUrl &url, HelpManager::HelpViewerLocation location) { - emit m_instance->helpRequested(url, location); + if (checkInstance()) + m_instance->handleHelpRequest(url, location); } -void HelpManager::handleHelpRequest(const QString &url, HelpViewerLocation location) +void handleHelpRequest(const QString &url, HelpViewerLocation location) { handleHelpRequest(QUrl(url), location); } -QStringList HelpManager::registeredNamespaces() -{ - QTC_ASSERT(!d->m_needsSetup, return QStringList()); - return d->m_helpEngine->registeredDocumentations(); -} - -QString HelpManager::namespaceFromFile(const QString &file) -{ - QTC_ASSERT(!d->m_needsSetup, return QString()); - return d->m_helpEngine->namespaceName(file); -} - -QString HelpManager::fileFromNamespace(const QString &nameSpace) -{ - QTC_ASSERT(!d->m_needsSetup, return QString()); - return d->m_helpEngine->documentationFileName(nameSpace); -} - -void HelpManager::setCustomValue(const QString &key, const QVariant &value) -{ - if (d->m_needsSetup) { - d->m_customValues.insert(key, value); - return; - } - if (d->m_helpEngine->setCustomValue(key, value)) - emit m_instance->collectionFileChanged(); -} - -QVariant HelpManager::customValue(const QString &key, const QVariant &value) -{ - QTC_ASSERT(!d->m_needsSetup, return QVariant()); - return d->m_helpEngine->customValue(key, value); -} - -HelpManager::Filters HelpManager::filters() -{ - QTC_ASSERT(!d->m_needsSetup, return Filters()); - - Filters filters; - const QStringList &customFilters = d->m_helpEngine->customFilters(); - for (const QString &filter : customFilters) - filters.insert(filter, d->m_helpEngine->filterAttributes(filter)); - return filters; -} - -HelpManager::Filters HelpManager::fixedFilters() -{ - Filters fixedFilters; - QTC_ASSERT(!d->m_needsSetup, return fixedFilters); - - const QLatin1String sqlite("QSQLITE"); - const QLatin1String name("HelpManager::fixedCustomFilters"); - - DbCleaner cleaner(name); - QSqlDatabase db = QSqlDatabase::addDatabase(sqlite, name); - if (db.driver() && db.driver()->lastError().type() == QSqlError::NoError) { - const QStringList ®isteredDocs = d->m_helpEngine->registeredDocumentations(); - for (const QString &nameSpace : registeredDocs) { - db.setDatabaseName(d->m_helpEngine->documentationFileName(nameSpace)); - if (db.open()) { - QSqlQuery query = QSqlQuery(db); - query.setForwardOnly(true); - query.exec(QLatin1String("SELECT Name FROM FilterNameTable")); - while (query.next()) { - const QString &filter = query.value(0).toString(); - fixedFilters.insert(filter, d->m_helpEngine->filterAttributes(filter)); - } - } - } - } - return fixedFilters; -} - -HelpManager::Filters HelpManager::userDefinedFilters() -{ - QTC_ASSERT(!d->m_needsSetup, return Filters()); - - Filters all = filters(); - const Filters &fixed = fixedFilters(); - for (Filters::const_iterator it = fixed.constBegin(); it != fixed.constEnd(); ++it) - all.remove(it.key()); - return all; -} - -void HelpManager::removeUserDefinedFilter(const QString &filter) -{ - QTC_ASSERT(!d->m_needsSetup, return); - - if (d->m_helpEngine->removeCustomFilter(filter)) - emit m_instance->collectionFileChanged(); -} - -void HelpManager::addUserDefinedFilter(const QString &filter, const QStringList &attr) -{ - QTC_ASSERT(!d->m_needsSetup, return); - - if (d->m_helpEngine->addCustomFilter(filter, attr)) - emit m_instance->collectionFileChanged(); -} - -void HelpManager::aboutToShutdown() -{ - if (d && d->m_registerFuture.isRunning()) { - d->m_registerFuture.cancel(); - d->m_registerFuture.waitForFinished(); - } -} - -// -- private - -void HelpManager::setupHelpManager() -{ - if (!d->m_needsSetup) - return; - d->m_needsSetup = false; - - d->readSettings(); - - // create the help engine - d->m_helpEngine = new QHelpEngineCore(collectionFilePath(), m_instance); - d->m_helpEngine->setupData(); - - for (const QString &filePath : d->documentationFromInstaller()) - d->m_filesToRegister.insert(filePath); - - d->cleanUpDocumentation(); - - if (!d->m_nameSpacesToUnregister.isEmpty()) { - unregisterDocumentation(d->m_nameSpacesToUnregister.toList()); - d->m_nameSpacesToUnregister.clear(); - } - - if (!d->m_filesToRegister.isEmpty()) { - registerDocumentation(d->m_filesToRegister.toList()); - d->m_filesToRegister.clear(); - } - - QHash<QString, QVariant>::const_iterator it; - for (it = d->m_customValues.constBegin(); it != d->m_customValues.constEnd(); ++it) - setCustomValue(it.key(), it.value()); - - emit m_instance->setupFinished(); -} - -void HelpManagerPrivate::cleanUpDocumentation() -{ - // mark documentation for removal for which there is no documentation file anymore - // mark documentation for removal that is neither user registered, nor marked for registration - const QStringList ®isteredDocs = m_helpEngine->registeredDocumentations(); - for (const QString &nameSpace : registeredDocs) { - const QString filePath = m_helpEngine->documentationFileName(nameSpace); - if (!QFileInfo::exists(filePath) - || (!m_filesToRegister.contains(filePath) - && !m_userRegisteredFiles.contains(filePath))) { - m_nameSpacesToUnregister.insert(nameSpace); - } - } -} - -HelpManagerPrivate::~HelpManagerPrivate() -{ - writeSettings(); - delete m_helpEngine; - m_helpEngine = nullptr; -} - -const QStringList HelpManagerPrivate::documentationFromInstaller() -{ - QSettings *installSettings = ICore::settings(); - const QStringList documentationPaths = installSettings->value(QLatin1String("Help/InstalledDocumentation")) - .toStringList(); - QStringList documentationFiles; - for (const QString &path : documentationPaths) { - QFileInfo pathInfo(path); - if (pathInfo.isFile() && pathInfo.isReadable()) { - documentationFiles << pathInfo.absoluteFilePath(); - } else if (pathInfo.isDir()) { - const QFileInfoList files(QDir(path).entryInfoList(QStringList(QLatin1String("*.qch")), - QDir::Files | QDir::Readable)); - for (const QFileInfo &fileInfo : files) - documentationFiles << fileInfo.absoluteFilePath(); - } - } - return documentationFiles; -} - -void HelpManagerPrivate::readSettings() -{ - m_userRegisteredFiles = ICore::settings()->value(QLatin1String(kUserDocumentationKey)) - .toStringList().toSet(); -} - -void HelpManagerPrivate::writeSettings() -{ - const QStringList list = m_userRegisteredFiles.toList(); - ICore::settings()->setValue(QLatin1String(kUserDocumentationKey), list); -} - -} // Core - -#else // QT_HELP_LIB - -namespace Core { - -HelpManager *HelpManager::instance() { return nullptr; } - -QString HelpManager::collectionFilePath() { return QString(); } - -void HelpManager::registerDocumentation(const QStringList &) {} -void HelpManager::registerDocumentationNow(QFutureInterface<bool> &, const QStringList &) {} -void HelpManager::unregisterDocumentation(const QStringList &) {} - -void HelpManager::registerUserDocumentation(const QStringList &) {} -QSet<QString> HelpManager::userDocumentationPaths() { return {}; } - -QMap<QString, QUrl> HelpManager::linksForKeyword(const QString &) { return {}; } -QMap<QString, QUrl> HelpManager::linksForIdentifier(const QString &) { return {}; } - -QUrl HelpManager::findFile(const QUrl &) { return QUrl();} -QByteArray HelpManager::fileData(const QUrl &) { return QByteArray();} - -QStringList HelpManager::registeredNamespaces() { return {}; } -QString HelpManager::namespaceFromFile(const QString &) { return QString(); } -QString HelpManager::fileFromNamespace(const QString &) { return QString(); } - -void HelpManager::setCustomValue(const QString &, const QVariant &) {} -QVariant HelpManager::customValue(const QString &, const QVariant &) { return QVariant(); } - -HelpManager::Filters filters() { return {}; } -HelpManager::Filters fixedFilters() { return {}; } - -HelpManager::Filters userDefinedFilters() { return {}; } - -void HelpManager::removeUserDefinedFilter(const QString &) {} -void HelpManager::addUserDefinedFilter(const QString &, const QStringList &) {} - -void HelpManager::handleHelpRequest(const QUrl &, HelpManager::HelpViewerLocation) {} -void HelpManager::handleHelpRequest(const QString &, HelpViewerLocation) {} - -HelpManager::HelpManager(QObject *) {} -HelpManager::~HelpManager() = default; -void HelpManager::aboutToShutdown() {} -void HelpManager::setupHelpManager() {} - -} // namespace Core - -#endif // QT_HELP_LIB +} // HelpManager +} // Core |