diff options
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 |