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>
This commit is contained in:
Eike Ziller
2018-08-31 16:00:32 +02:00
parent 25400751a4
commit 2c17fbe8dd
21 changed files with 788 additions and 600 deletions

View File

@@ -69,10 +69,13 @@ using namespace Core;
using namespace Core::Internal;
using namespace Utils;
static CorePlugin *m_instance = nullptr;
CorePlugin::CorePlugin()
{
qRegisterMetaType<Id>();
qRegisterMetaType<Core::Search::TextPosition>();
m_instance = this;
}
CorePlugin::~CorePlugin()
@@ -89,6 +92,11 @@ CorePlugin::~CorePlugin()
setCreatorTheme(nullptr);
}
CorePlugin *CorePlugin::instance()
{
return m_instance;
}
struct CoreArguments {
QColor overrideColor;
Id themeId;
@@ -226,7 +234,6 @@ void CorePlugin::extensionsInitialized()
bool CorePlugin::delayedInitialize()
{
HelpManager::setupHelpManager();
m_locator->delayedInitialize();
IWizardFactory::allWizardFactories(); // scan for all wizard factories
return true;
@@ -290,6 +297,5 @@ ExtensionSystem::IPlugin::ShutdownFlag CorePlugin::aboutToShutdown()
{
Find::aboutToShutdown();
m_mainWindow->aboutToShutdown();
HelpManager::aboutToShutdown();
return SynchronousShutdown;
}

View File

@@ -53,6 +53,8 @@ public:
CorePlugin();
~CorePlugin() override;
static CorePlugin *instance();
bool initialize(const QStringList &arguments, QString *errorMessage = nullptr) override;
void extensionsInitialized() override;
bool delayedInitialize() override;

View File

@@ -5,8 +5,6 @@ QT += \
qml \
sql
qtHaveModule(help): QT += help
# embedding build time information prevents repeatedly binary exact versions from same source code
isEmpty(QTC_SHOW_BUILD_DATE): QTC_SHOW_BUILD_DATE = $$(QTC_SHOW_BUILD_DATE)
!isEmpty(QTC_SHOW_BUILD_DATE): DEFINES += QTC_SHOW_BUILD_DATE
@@ -195,6 +193,7 @@ HEADERS += corejsextensions.h \
designmode.h \
editortoolbar.h \
helpmanager.h \
helpmanager_implementation.h \
outputpanemanager.h \
navigationsubwidget.h \
sidebarwidget.h \

View File

@@ -9,7 +9,7 @@ Project {
QtcPlugin {
Depends {
name: "Qt"
submodules: ["widgets", "xml", "network", "qml", "sql", "help", "printsupport"]
submodules: ["widgets", "xml", "network", "qml", "sql", "printsupport"]
}
Depends {
@@ -86,6 +86,7 @@ Project {
"generatedfile.h",
"helpmanager.cpp",
"helpmanager.h",
"helpmanager_implementation.h",
"icontext.cpp",
"icontext.h",
"icore.cpp",

View File

@@ -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
// makes sure that plugins can connect to HelpManager signals even if the Help plugin is not loaded
Q_GLOBAL_STATIC(Signals, m_signals)
static Implementation *m_instance = nullptr;
static bool checkInstance()
{
HelpManagerPrivate() = default;
~HelpManagerPrivate();
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;
}
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;
QSet<QString> m_userRegisteredFiles;
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
Signals *Signals::instance()
{
DbCleaner(const QString &dbName) : name(dbName) {}
~DbCleaner() { QSqlDatabase::removeDatabase(name); }
QString name;
};
return m_signals;
}
// -- HelpManager
HelpManager::HelpManager(QObject *parent) :
QObject(parent)
Implementation::Implementation()
{
QTC_CHECK(!m_instance);
m_instance = this;
d = new HelpManagerPrivate;
}
HelpManager::~HelpManager()
Implementation::~Implementation()
{
delete d;
m_instance = nullptr;
}
HelpManager *HelpManager::instance()
void registerDocumentation(const QStringList &files)
{
Q_ASSERT(m_instance);
return m_instance;
if (checkInstance())
m_instance->registerDocumentation(files);
}
QString HelpManager::collectionFilePath()
void unregisterDocumentation(const QStringList &nameSpaces)
{
return QDir::cleanPath(ICore::userResourcePath()
+ QLatin1String("/helpcollection.qhc"));
if (checkInstance())
m_instance->unregisterDocumentation(nameSpaces);
}
void HelpManager::registerDocumentation(const QStringList &files)
QMap<QString, QUrl> linksForIdentifier(const QString &id)
{
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);
return checkInstance() ? m_instance->linksForIdentifier(id) : QMap<QString, QUrl>();
}
void HelpManager::registerDocumentationNow(QFutureInterface<bool> &futureInterface,
const QStringList &files)
QByteArray fileData(const QUrl &url)
{
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);
return checkInstance() ? m_instance->fileData(url) : QByteArray();
}
void HelpManager::unregisterDocumentation(const QStringList &nameSpaces)
void handleHelpRequest(const QUrl &url, HelpManager::HelpViewerLocation location)
{
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();
if (checkInstance())
m_instance->handleHelpRequest(url, location);
}
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;
}
static QUrl buildQUrl(const QString &ns, const QString &folder,
const QString &relFileName, const QString &anchor)
{
QUrl url;
url.setScheme(QLatin1String("qthelp"));
url.setAuthority(ns);
url.setPath(QLatin1Char('/') + folder + QLatin1Char('/') + relFileName);
url.setFragment(anchor);
return url;
}
// This should go into Qt 4.8 once we start using it for Qt Creator
QMap<QString, QUrl> HelpManager::linksForKeyword(const QString &key)
{
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 &registeredDocs = 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;
}
QMap<QString, QUrl> HelpManager::linksForIdentifier(const QString &id)
{
QMap<QString, QUrl> empty;
QTC_ASSERT(!d->m_needsSetup, return empty);
return d->m_helpEngine->linksForIdentifier(id);
}
QUrl HelpManager::findFile(const QUrl &url)
{
QTC_ASSERT(!d->m_needsSetup, return QUrl());
return d->m_helpEngine->findFile(url);
}
QByteArray HelpManager::fileData(const QUrl &url)
{
QTC_ASSERT(!d->m_needsSetup, return QByteArray());
return d->m_helpEngine->fileData(url);
}
void HelpManager::handleHelpRequest(const QUrl &url, HelpManager::HelpViewerLocation location)
{
emit m_instance->helpRequested(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 &registeredDocs = 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 &registeredDocs = 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

View File

@@ -28,85 +28,44 @@
#include "core_global.h"
#include <QObject>
#include <QStringList>
#include <QVariant>
#include <QMap>
#include <QHash>
#include <QFutureInterface>
QT_FORWARD_DECLARE_CLASS(QUrl)
QT_BEGIN_NAMESPACE
class QStringList;
class QUrl;
QT_END_NAMESPACE
namespace Core {
struct HelpManagerPrivate;
namespace Internal {
class CorePlugin;
class MainWindow;
}
namespace HelpManager {
class CORE_EXPORT HelpManager : public QObject
class CORE_EXPORT Signals : public QObject
{
Q_OBJECT
public:
enum HelpViewerLocation {
SideBySideIfPossible = 0,
SideBySideAlways = 1,
HelpModeAlways = 2,
ExternalHelpAlways = 3
};
using Filters = QHash<QString, QStringList>;
static HelpManager *instance();
static QString collectionFilePath();
static void registerDocumentation(const QStringList &fileNames);
static void unregisterDocumentation(const QStringList &nameSpaces);
static void registerUserDocumentation(const QStringList &filePaths);
static QSet<QString> userDocumentationPaths();
static QUrl findFile(const QUrl &url);
static QByteArray fileData(const QUrl &url);
static QMap<QString, QUrl> linksForKeyword(const QString &key);
static QMap<QString, QUrl> linksForIdentifier(const QString &id);
static QStringList registeredNamespaces();
static QString namespaceFromFile(const QString &file);
static QString fileFromNamespace(const QString &nameSpace);
static void setCustomValue(const QString &key, const QVariant &value);
static QVariant customValue(const QString &key, const QVariant &value = QVariant());
static Filters filters();
static Filters fixedFilters();
static Filters userDefinedFilters();
static void removeUserDefinedFilter(const QString &filter);
static void addUserDefinedFilter(const QString &filter, const QStringList &attr);
static void aboutToShutdown();
public slots:
static void handleHelpRequest(const QUrl &url,
Core::HelpManager::HelpViewerLocation location = HelpModeAlways);
static void handleHelpRequest(const QString &url,
Core::HelpManager::HelpViewerLocation location = HelpModeAlways);
static Signals *instance();
signals:
void setupFinished();
void documentationChanged();
void collectionFileChanged();
void helpRequested(const QUrl &url, Core::HelpManager::HelpViewerLocation location);
private:
explicit HelpManager(QObject *parent = nullptr);
~HelpManager() override;
static void setupHelpManager();
static void registerDocumentationNow(QFutureInterface<bool> &futureInterface,
const QStringList &fileNames);
friend class Core::Internal::CorePlugin; // setupHelpManager
friend class Core::Internal::MainWindow; // constructor/destructor
};
} // Core
enum HelpViewerLocation {
SideBySideIfPossible = 0,
SideBySideAlways = 1,
HelpModeAlways = 2,
ExternalHelpAlways = 3
};
CORE_EXPORT void registerDocumentation(const QStringList &fileNames);
CORE_EXPORT void unregisterDocumentation(const QStringList &nameSpaces);
CORE_EXPORT QMap<QString, QUrl> linksForIdentifier(const QString &id);
CORE_EXPORT QByteArray fileData(const QUrl &url);
CORE_EXPORT void handleHelpRequest(const QUrl &url, HelpViewerLocation location = HelpModeAlways);
CORE_EXPORT void handleHelpRequest(const QString &url, HelpViewerLocation location = HelpModeAlways);
} // HelpManager
} // Core

View File

@@ -0,0 +1,49 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "helpmanager.h"
namespace Core {
namespace HelpManager {
class CORE_EXPORT Implementation
{
protected:
Implementation();
virtual ~Implementation();
public:
virtual void registerDocumentation(const QStringList &fileNames) = 0;
virtual void unregisterDocumentation(const QStringList &nameSpaces) = 0;
virtual QMap<QString, QUrl> linksForIdentifier(const QString &id) = 0;
virtual QByteArray fileData(const QUrl &url) = 0;
virtual void handleHelpRequest(const QUrl &url, HelpViewerLocation location = HelpModeAlways) = 0;
};
} // HelpManager
} // Core

View File

@@ -31,7 +31,6 @@
#include "fancytabwidget.h"
#include "documentmanager.h"
#include "generalsettings.h"
#include "helpmanager.h"
#include "idocumentfactory.h"
#include "messagemanager.h"
#include "modemanager.h"
@@ -110,7 +109,6 @@ MainWindow::MainWindow() :
m_progressManager(new ProgressManagerPrivate),
m_jsExpander(new JsExpander),
m_vcsManager(new VcsManager),
m_helpManager(new HelpManager),
m_modeStack(new FancyTabWidget(this)),
m_generalSettings(new GeneralSettings),
m_systemSettings(new SystemSettings),
@@ -281,8 +279,6 @@ MainWindow::~MainWindow()
delete m_modeManager;
m_modeManager = nullptr;
delete m_helpManager;
m_helpManager = nullptr;
delete m_jsExpander;
m_jsExpander = nullptr;
}

View File

@@ -45,7 +45,6 @@ namespace Core {
class EditorManager;
class ExternalToolManager;
class HelpManager;
class IDocument;
class JsExpander;
class MessageManager;
@@ -154,7 +153,6 @@ private:
JsExpander *m_jsExpander = nullptr;
VcsManager *m_vcsManager = nullptr;
ModeManager *m_modeManager = nullptr;
HelpManager *m_helpManager = nullptr;
FancyTabWidget *m_modeStack = nullptr;
NavigationWidget *m_leftNavigationWidget = nullptr;
NavigationWidget *m_rightNavigationWidget = nullptr;

View File

@@ -365,8 +365,8 @@ void FormEditorData::fullInit()
m_formeditor->setIntegration(m_integration);
// Connect Qt Designer help request to HelpManager.
QObject::connect(m_integration, &QtCreatorIntegration::creatorHelpRequested,
HelpManager::instance(),
[](const QUrl &url) { HelpManager::instance()->handleHelpRequest(url, HelpManager::HelpModeAlways); });
HelpManager::Signals::instance(),
[](const QUrl &url) { HelpManager::handleHelpRequest(url, HelpManager::HelpModeAlways); });
/**
* This will initialize our TabOrder, Signals and slots and Buddy editors.

View File

@@ -25,6 +25,7 @@
#include "docsettingspage.h"
#include "helpconstants.h"
#include "helpmanager.h"
#include <coreplugin/helpmanager.h>
#include <utils/algorithm.h>
@@ -42,8 +43,6 @@
#include <algorithm>
using namespace Core;
namespace Help {
namespace Internal {
@@ -245,7 +244,7 @@ void DocSettingsPage::addDocumentation()
void DocSettingsPage::apply()
{
HelpManager::unregisterDocumentation(m_filesToUnregister.keys());
Core::HelpManager::unregisterDocumentation(m_filesToUnregister.keys());
QStringList files;
auto it = m_filesToRegisterUserManaged.constBegin();
while (it != m_filesToRegisterUserManaged.constEnd()) {

View File

@@ -26,6 +26,7 @@
#include "filtersettingspage.h"
#include "helpconstants.h"
#include "helpmanager.h"
#include <filternamedialog.h>
@@ -36,7 +37,6 @@
#include <QMessageBox>
using namespace Help::Internal;
using namespace Core;
FilterSettingsPage::FilterSettingsPage()
{
@@ -61,8 +61,10 @@ QWidget *FilterSettingsPage::widget()
this, &FilterSettingsPage::addFilter);
connect(m_ui.filterRemoveButton, &QPushButton::clicked,
this, &FilterSettingsPage::removeFilter);
connect(HelpManager::instance(), &HelpManager::documentationChanged,
this, &FilterSettingsPage::updateFilterPage);
connect(Core::HelpManager::Signals::instance(),
&Core::HelpManager::Signals::documentationChanged,
this,
&FilterSettingsPage::updateFilterPage);
}
return m_widget;
}
@@ -219,8 +221,10 @@ void FilterSettingsPage::apply()
void FilterSettingsPage::finish()
{
disconnect(HelpManager::instance(), &HelpManager::documentationChanged,
this, &FilterSettingsPage::updateFilterPage);
disconnect(Core::HelpManager::Signals::instance(),
&Core::HelpManager::Signals::documentationChanged,
this,
&FilterSettingsPage::updateFilterPage);
delete m_widget;
}

View File

@@ -17,6 +17,7 @@ HEADERS += \
helpfindsupport.h \
helpindexfilter.h \
localhelpmanager.h \
helpmanager.h \
helpmode.h \
helpplugin.h \
helpviewer.h \
@@ -39,6 +40,7 @@ SOURCES += \
helpfindsupport.cpp \
helpindexfilter.cpp \
localhelpmanager.cpp \
helpmanager.cpp \
helpmode.cpp \
helpplugin.cpp \
helpviewer.cpp \

View File

@@ -36,6 +36,7 @@ QtcPlugin {
"helpconstants.h",
"helpfindsupport.cpp", "helpfindsupport.h",
"helpindexfilter.cpp", "helpindexfilter.h",
"helpmanager.cpp", "helpmanager.h",
"helpmode.cpp", "helpmode.h",
"helpplugin.cpp", "helpplugin.h",
"helpviewer.cpp", "helpviewer.h",

View File

@@ -27,6 +27,7 @@
#include "centralwidget.h"
#include "helpicons.h"
#include "helpmanager.h"
#include "topicchooser.h"
#include <coreplugin/icore.h>
@@ -54,10 +55,12 @@ HelpIndexFilter::HelpIndexFilter()
setShortcutString("?");
m_icon = Utils::Icons::BOOKMARK.icon();
connect(HelpManager::instance(), &HelpManager::setupFinished,
this, &HelpIndexFilter::invalidateCache);
connect(HelpManager::instance(), &HelpManager::documentationChanged,
connect(Core::HelpManager::Signals::instance(), &Core::HelpManager::Signals::setupFinished,
this, &HelpIndexFilter::invalidateCache);
connect(Core::HelpManager::Signals::instance(),
&Core::HelpManager::Signals::documentationChanged,
this,
&HelpIndexFilter::invalidateCache);
connect(HelpManager::instance(), &HelpManager::collectionFileChanged,
this, &HelpIndexFilter::invalidateCache);
}

View File

@@ -0,0 +1,497 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "helpmanager.h"
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <utils/algorithm.h>
#include <utils/filesystemwatcher.h>
#include <utils/qtcassert.h>
#include <utils/runextensions.h>
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QFutureWatcher>
#include <QStringList>
#include <QUrl>
#include <QHelpEngineCore>
#include <QMutexLocker>
#include <QSqlDatabase>
#include <QSqlDriver>
#include <QSqlError>
#include <QSqlQuery>
using namespace Core;
static const char kUserDocumentationKey[] = "Help/UserDocumentation";
static const char kUpdateDocumentationTask[] = "UpdateDocumentationTask";
namespace Help {
namespace Internal {
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;
QSet<QString> m_userRegisteredFiles;
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
{
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;
}
QString HelpManager::collectionFilePath()
{
return QDir::cleanPath(ICore::userResourcePath()
+ QLatin1String("/helpcollection.qhc"));
}
void HelpManager::registerDocumentation(const QStringList &files)
{
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, this, [](bool docsChanged){
if (docsChanged) {
d->m_helpEngine->setupData();
emit Core::HelpManager::Signals::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 Core::HelpManager::Signals::instance()->documentationChanged();
}
void HelpManager::registerUserDocumentation(const QStringList &filePaths)
{
for (const QString &filePath : filePaths)
d->m_userRegisteredFiles.insert(filePath);
m_instance->registerDocumentation(filePaths);
}
QSet<QString> HelpManager::userDocumentationPaths()
{
return d->m_userRegisteredFiles;
}
static QUrl buildQUrl(const QString &ns, const QString &folder,
const QString &relFileName, const QString &anchor)
{
QUrl url;
url.setScheme(QLatin1String("qthelp"));
url.setAuthority(ns);
url.setPath(QLatin1Char('/') + folder + QLatin1Char('/') + relFileName);
url.setFragment(anchor);
return url;
}
// This should go into Qt 4.8 once we start using it for Qt Creator
QMap<QString, QUrl> HelpManager::linksForKeyword(const QString &key)
{
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 &registeredDocs = 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;
}
QMap<QString, QUrl> HelpManager::linksForIdentifier(const QString &id)
{
QMap<QString, QUrl> empty;
QTC_ASSERT(!d->m_needsSetup, return empty);
return d->m_helpEngine->linksForIdentifier(id);
}
QUrl HelpManager::findFile(const QUrl &url)
{
QTC_ASSERT(!d->m_needsSetup, return QUrl());
return d->m_helpEngine->findFile(url);
}
QByteArray HelpManager::fileData(const QUrl &url)
{
QTC_ASSERT(!d->m_needsSetup, return QByteArray());
return d->m_helpEngine->fileData(url);
}
void HelpManager::handleHelpRequest(const QUrl &url, Core::HelpManager::HelpViewerLocation location)
{
emit m_instance->helpRequested(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 &registeredDocs = 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()) {
m_instance->unregisterDocumentation(d->m_nameSpacesToUnregister.toList());
d->m_nameSpacesToUnregister.clear();
}
if (!d->m_filesToRegister.isEmpty()) {
m_instance->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 Core::HelpManager::Signals::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 &registeredDocs = 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);
}
} // Internal
} // Core

View File

@@ -0,0 +1,93 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <coreplugin/helpmanager_implementation.h>
QT_FORWARD_DECLARE_CLASS(QUrl)
#include <QFutureInterface>
#include <QVariant>
namespace Help {
namespace Internal {
class HelpManager : public QObject, public Core::HelpManager::Implementation
{
Q_OBJECT
public:
using Filters = QHash<QString, QStringList>;
explicit HelpManager(QObject *parent = nullptr);
~HelpManager() override;
static HelpManager *instance();
static QString collectionFilePath();
void registerDocumentation(const QStringList &fileNames) override;
void unregisterDocumentation(const QStringList &nameSpaces) override;
static void registerUserDocumentation(const QStringList &filePaths);
static QSet<QString> userDocumentationPaths();
static QMap<QString, QUrl> linksForKeyword(const QString &key);
QMap<QString, QUrl> linksForIdentifier(const QString &id) override;
static QUrl findFile(const QUrl &url);
QByteArray fileData(const QUrl &url) override;
static QStringList registeredNamespaces();
static QString namespaceFromFile(const QString &file);
static QString fileFromNamespace(const QString &nameSpace);
static void setCustomValue(const QString &key, const QVariant &value);
static QVariant customValue(const QString &key, const QVariant &value = QVariant());
static Filters filters();
static Filters fixedFilters();
static Filters userDefinedFilters();
static void removeUserDefinedFilter(const QString &filter);
static void addUserDefinedFilter(const QString &filter, const QStringList &attr);
static void aboutToShutdown();
void handleHelpRequest(
const QUrl &url,
Core::HelpManager::HelpViewerLocation location = Core::HelpManager::HelpModeAlways) override;
static void setupHelpManager();
static void registerDocumentationNow(QFutureInterface<bool> &futureInterface,
const QStringList &fileNames);
signals:
void collectionFileChanged();
void helpRequested(const QUrl &url, Core::HelpManager::HelpViewerLocation location);
};
} // Internal
} // Core

View File

@@ -33,6 +33,7 @@
#include "helpconstants.h"
#include "helpfindsupport.h"
#include "helpindexfilter.h"
#include "helpmanager.h"
#include "helpmode.h"
#include "helpviewer.h"
#include "localhelpmanager.h"
@@ -145,7 +146,7 @@ public:
HelpViewer *externalHelpViewer();
HelpViewer *helpModeHelpViewer();
HelpWidget *helpWidgetForWindow(QWidget *window);
HelpViewer *viewerForHelpViewerLocation(HelpManager::HelpViewerLocation location);
HelpViewer *viewerForHelpViewerLocation(Core::HelpManager::HelpViewerLocation location);
void showInHelpViewer(const QUrl &url, HelpViewer *viewer);
void doSetupIfNeeded();
@@ -160,7 +161,7 @@ public:
GeneralSettingsPage m_generalSettingsPage;
bool m_setupNeeded = true;
LocalHelpManager m_helpManager;
LocalHelpManager m_localHelpManager;
OpenPagesManager m_openPagesManager;
QString m_contextHelpHighlightId;
@@ -173,11 +174,19 @@ public:
};
static HelpPluginPrivate *dd = nullptr;
static HelpManager *m_helpManager = nullptr;
HelpPlugin::HelpPlugin()
{
m_helpManager = new HelpManager;
}
HelpPlugin::~HelpPlugin()
{
delete dd;
dd = nullptr;
delete m_helpManager;
m_helpManager = nullptr;
}
bool HelpPlugin::initialize(const QStringList &arguments, QString *error)
@@ -221,8 +230,10 @@ HelpPluginPrivate::HelpPluginPrivate()
connect(&m_filterSettingsPage, &FilterSettingsPage::filtersChanged,
this, &HelpPluginPrivate::setupHelpEngineIfNeeded);
connect(HelpManager::instance(), &HelpManager::documentationChanged,
this, &HelpPluginPrivate::setupHelpEngineIfNeeded);
connect(Core::HelpManager::Signals::instance(),
&Core::HelpManager::Signals::documentationChanged,
this,
&HelpPluginPrivate::setupHelpEngineIfNeeded);
connect(HelpManager::instance(), &HelpManager::collectionFileChanged,
this, &HelpPluginPrivate::setupHelpEngineIfNeeded);
@@ -319,7 +330,13 @@ void HelpPlugin::extensionsInitialized()
QStringList filesToRegister;
// we might need to register creators inbuild help
filesToRegister.append(ICore::documentationPath() + "/qtcreator.qch");
HelpManager::registerDocumentation(filesToRegister);
Core::HelpManager::registerDocumentation(filesToRegister);
}
bool HelpPlugin::delayedInitialize()
{
HelpManager::setupHelpManager();
return true;
}
ExtensionSystem::IPlugin::ShutdownFlag HelpPlugin::aboutToShutdown()
@@ -524,7 +541,7 @@ void HelpPluginPrivate::setupHelpEngineIfNeeded()
{
LocalHelpManager::setEngineNeedsUpdate();
if (ModeManager::currentModeId() == m_mode.id()
|| LocalHelpManager::contextHelpOption() == HelpManager::ExternalHelpAlways)
|| LocalHelpManager::contextHelpOption() == Core::HelpManager::ExternalHelpAlways)
LocalHelpManager::setupGuiHelpEngine();
}
@@ -564,29 +581,30 @@ HelpWidget *HelpPluginPrivate::helpWidgetForWindow(QWidget *window)
return m_centralWidget;
}
HelpViewer *HelpPlugin::viewerForHelpViewerLocation(HelpManager::HelpViewerLocation location)
HelpViewer *HelpPlugin::viewerForHelpViewerLocation(Core::HelpManager::HelpViewerLocation location)
{
return dd->viewerForHelpViewerLocation(location);
}
HelpViewer *HelpPluginPrivate::viewerForHelpViewerLocation(HelpManager::HelpViewerLocation location)
HelpViewer *HelpPluginPrivate::viewerForHelpViewerLocation(
Core::HelpManager::HelpViewerLocation location)
{
HelpManager::HelpViewerLocation actualLocation = location;
if (location == HelpManager::SideBySideIfPossible)
actualLocation = canShowHelpSideBySide() ? HelpManager::SideBySideAlways
: HelpManager::HelpModeAlways;
Core::HelpManager::HelpViewerLocation actualLocation = location;
if (location == Core::HelpManager::SideBySideIfPossible)
actualLocation = canShowHelpSideBySide() ? Core::HelpManager::SideBySideAlways
: Core::HelpManager::HelpModeAlways;
if (actualLocation == HelpManager::ExternalHelpAlways)
if (actualLocation == Core::HelpManager::ExternalHelpAlways)
return externalHelpViewer();
if (actualLocation == HelpManager::SideBySideAlways) {
if (actualLocation == Core::HelpManager::SideBySideAlways) {
createRightPaneContextViewer();
RightPaneWidget::instance()->setWidget(m_rightPaneSideBarWidget);
RightPaneWidget::instance()->setShown(true);
return m_rightPaneSideBarWidget->currentViewer();
}
QTC_CHECK(actualLocation == HelpManager::HelpModeAlways);
QTC_CHECK(actualLocation == Core::HelpManager::HelpModeAlways);
return helpModeHelpViewer();
}
@@ -658,7 +676,7 @@ void HelpPluginPrivate::showContextHelp(const QString &contextHelpId)
HelpViewer *viewer = viewerForContextHelp();
QTC_ASSERT(viewer, return);
QMap<QString, QUrl> links = HelpManager::linksForIdentifier(contextHelpId);
QMap<QString, QUrl> links = Core::HelpManager::linksForIdentifier(contextHelpId);
// Maybe the id is already an URL
if (links.isEmpty() && LocalHelpManager::isValidUrl(contextHelpId))
links.insert(contextHelpId, contextHelpId);
@@ -702,7 +720,8 @@ void HelpPluginPrivate::highlightSearchTermsInContextHelp()
m_contextHelpHighlightId.clear();
}
void HelpPluginPrivate::handleHelpRequest(const QUrl &url, HelpManager::HelpViewerLocation location)
void HelpPluginPrivate::handleHelpRequest(const QUrl &url,
Core::HelpManager::HelpViewerLocation location)
{
static const QString qtcreatorUnversionedID = "org.qt-project.qtcreator";
if (url.host() == qtcreatorUnversionedID) {

View File

@@ -43,7 +43,7 @@ class HelpPlugin : public ExtensionSystem::IPlugin
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Help.json")
public:
HelpPlugin() = default;
HelpPlugin();
~HelpPlugin() final;
static HelpViewer *viewerForHelpViewerLocation(Core::HelpManager::HelpViewerLocation location);
@@ -53,6 +53,7 @@ public:
private:
bool initialize(const QStringList &arguments, QString *errorMessage) final;
void extensionsInitialized() final;
bool delayedInitialize() final;
ShutdownFlag aboutToShutdown() final;
};

View File

@@ -27,6 +27,7 @@
#include "bookmarkmanager.h"
#include "helpconstants.h"
#include "helpmanager.h"
#include "helpviewer.h"
#include <app/app_version.h>
@@ -266,7 +267,7 @@ void LocalHelpManager::setupGuiHelpEngine()
{
if (m_needsCollectionFile) {
m_needsCollectionFile = false;
helpEngine().setCollectionFile(Core::HelpManager::collectionFilePath());
helpEngine().setCollectionFile(HelpManager::collectionFilePath());
m_guiNeedsSetup = true;
}

View File

@@ -115,10 +115,10 @@ ExampleSetModel::ExampleSetModel()
connect(QtVersionManager::instance(), &QtVersionManager::qtVersionsLoaded,
this, &ExampleSetModel::qtVersionManagerLoaded);
if (auto helpManager = Core::HelpManager::instance()) {
connect(helpManager, &Core::HelpManager::setupFinished,
this, &ExampleSetModel::helpManagerInitialized);
}
connect(Core::HelpManager::Signals::instance(),
&Core::HelpManager::Signals::setupFinished,
this,
&ExampleSetModel::helpManagerInitialized);
}
void ExampleSetModel::recreateModel(const QList<BaseQtVersion *> &qtVersions)
@@ -236,8 +236,10 @@ ExamplesListModel::ExamplesListModel(QObject *parent)
{
connect(&m_exampleSetModel, &ExampleSetModel::selectedExampleSetChanged,
this, &ExamplesListModel::updateExamples);
connect(Core::HelpManager::instance(), &Core::HelpManager::documentationChanged,
this, &ExamplesListModel::updateExamples);
connect(Core::HelpManager::Signals::instance(),
&Core::HelpManager::Signals::documentationChanged,
this,
&ExamplesListModel::updateExamples);
}
static QString fixStringForTags(const QString &string)
@@ -682,7 +684,7 @@ void ExampleSetModel::tryToInitialize()
return;
if (!m_qtVersionManagerInitialized)
return;
if (Core::HelpManager::instance() && !m_helpManagerInitialized)
if (!m_helpManagerInitialized)
return;
m_initalized = true;