Index: kdevplatform/interfaces/idocumentation.h =================================================================== --- kdevplatform/interfaces/idocumentation.h +++ kdevplatform/interfaces/idocumentation.h @@ -61,6 +61,14 @@ virtual IDocumentationProvider* provider() const = 0; + /** Documentation can override this method if it supports using an external browser. + * The method should trigger the external browser and return true in that case, + * or otherwise return false. When using an external browser the documentation + * toolview is not sollicited at all (i.e. it will not open nor be added to a toolbar). + * TODO: remove implementation + */ + virtual bool viewInExternalBrowser() { return false; } + Q_SIGNALS: void descriptionChanged(); }; Index: kdevplatform/shell/documentationcontroller.cpp =================================================================== --- kdevplatform/shell/documentationcontroller.cpp +++ kdevplatform/shell/documentationcontroller.cpp @@ -222,18 +222,20 @@ void KDevelop::DocumentationController::showDocumentation(const IDocumentation::Ptr& doc) { - QWidget* w = ICore::self()->uiController()->findToolView(i18n("Documentation"), m_factory, KDevelop::IUiController::CreateAndRaise); - if(!w) { - qCWarning(SHELL) << "Could not add documentation toolview"; - return; - } + if (!doc->viewInExternalBrowser()) { + QWidget* w = ICore::self()->uiController()->findToolView(i18n("Documentation"), m_factory, KDevelop::IUiController::CreateAndRaise); + if(!w) { + qCWarning(SHELL) << "Could not add documentation toolview"; + return; + } - DocumentationView* view = dynamic_cast(w); - if( !view ) { - qCWarning(SHELL) << "Could not cast toolview" << w << "to DocumentationView class!"; - return; + DocumentationView* view = dynamic_cast(w); + if( !view ) { + qCWarning(SHELL) << "Could not cast toolview" << w << "to DocumentationView class!"; + return; + } + view->showDocumentation(doc); } - view->showDocumentation(doc); } void DocumentationController::changedDocumentationProviders() Index: plugins/qthelp/CMakeLists.txt =================================================================== --- plugins/qthelp/CMakeLists.txt +++ plugins/qthelp/CMakeLists.txt @@ -10,12 +10,18 @@ qthelpproviderabstract.cpp qthelpprovider.cpp qthelpdocumentation.cpp + qthelpexternalassistant.cpp qthelpqtdoc.cpp qthelp_config_shared.cpp qthelpconfig.cpp # Configuration module for QtHelp plugin qthelpnetwork.cpp ${kdevqthelp_LOG_SRCS} ) +if(APPLE) + set(kdevqthelp_SRCS ${kdevqthelp_SRCS} + qthelpexternalassistant_mac.mm + ) +endif() ki18n_wrap_ui(kdevqthelp_SRCS qthelpconfig.ui @@ -27,6 +33,9 @@ target_link_libraries(kdevqthelp KF5::KCMUtils KF5::I18n KF5::KIOWidgets KF5::TextEditor KF5::IconThemes Qt5::Help KF5::NewStuff KDev::Language KDev::Documentation KDev::Interfaces) +if(APPLE) + target_link_libraries(kdevqthelp "-framework AppKit") +endif() if(BUILD_TESTING) add_subdirectory(tests) Index: plugins/qthelp/qthelp_config_shared.h =================================================================== --- plugins/qthelp/qthelp_config_shared.h +++ plugins/qthelp/qthelp_config_shared.h @@ -23,18 +23,33 @@ #include +class ExternalViewerSettings +{ +public: + ExternalViewerSettings(bool use = false, const QString& exe = QString()) + : useExtViewer(use) + , extViewerExecutable(exe) + {} + bool useExtViewer = false; + QString extViewerExecutable; +}; + void qtHelpReadConfig(QStringList& iconList, QStringList& nameList, QStringList& pathList, QStringList& ghnsList, QString& searchDir, - bool& loadQtDoc); + bool& loadQtDoc, + ExternalViewerSettings& extViewer + ); void qtHelpWriteConfig(const QStringList& iconList, const QStringList& nameList, const QStringList& pathList, const QStringList& ghnsList, const QString& searchDir, - const bool loadQtDoc); + const bool loadQtDoc, + const ExternalViewerSettings& extViewer + ); #endif // QTHELP_CONFIG_SHARED_H Index: plugins/qthelp/qthelp_config_shared.cpp =================================================================== --- plugins/qthelp/qthelp_config_shared.cpp +++ plugins/qthelp/qthelp_config_shared.cpp @@ -1,6 +1,7 @@ /* This file is part of KDevelop Copyright 2010 Milian Wolff + Copyright 2017 René J.V. Bertin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -25,26 +26,33 @@ void qtHelpReadConfig(QStringList& iconList, QStringList& nameList, QStringList& pathList, QStringList& ghnsList, - QString& searchDir, bool& loadQtDoc) + QString& searchDir, bool& loadQtDoc, + ExternalViewerSettings& extViewer) { KConfigGroup cg(KSharedConfig::openConfig(), "QtHelp Documentation"); iconList = cg.readEntry("iconList", QStringList()); nameList = cg.readEntry("nameList", QStringList()); pathList = cg.readEntry("pathList", QStringList()); ghnsList = cg.readEntry("ghnsList", QStringList()); searchDir = cg.readEntry("searchDir", QString()); loadQtDoc = cg.readEntry("loadQtDocs", true); + // TODO: make global setting? + extViewer.useExtViewer = cg.readEntry("useExternalViewer", false); + extViewer.extViewerExecutable = cg.readEntry("externalViewerExecutable", QString()); } void qtHelpWriteConfig(const QStringList& iconList, const QStringList& nameList, const QStringList& pathList, const QStringList& ghnsList, - const QString& searchDir, const bool loadQtDoc) + const QString& searchDir, const bool loadQtDoc, + const ExternalViewerSettings& extViewer) { KConfigGroup cg(KSharedConfig::openConfig(), "QtHelp Documentation"); cg.writeEntry("iconList", iconList); cg.writeEntry("nameList", nameList); cg.writeEntry("pathList", pathList); cg.writeEntry("ghnsList", ghnsList); cg.writeEntry("searchDir", searchDir); cg.writeEntry("loadQtDocs", loadQtDoc); + cg.writeEntry("useExternalViewer", extViewer.useExtViewer); + cg.writeEntry("externalViewerExecutable", extViewer.extViewerExecutable); } Index: plugins/qthelp/qthelpconfig.cpp =================================================================== --- plugins/qthelp/qthelpconfig.cpp +++ plugins/qthelp/qthelpconfig.cpp @@ -36,6 +36,7 @@ #include "qthelp_config_shared.h" #include "debug.h" #include "qthelpplugin.h" +#include "qthelpexternalassistant.h" enum Column { @@ -119,6 +120,27 @@ m_configWidget->qchSearchDir->setMode(KFile::Directory); connect(m_configWidget->qchSearchDir, &KUrlRequester::textChanged, this, &QtHelpConfig::changed); + connect(m_configWidget->externalViewerCheckBox, &QCheckBox::toggled, + this, static_cast(&QtHelpConfig::changed)); + m_configWidget->externalViewerCheckBox->setToolTip(i18n("Use Qt's Assistant as an external viewer,\n" + "instead of the embedded viewer.")); + m_configWidget->externalViewerExecutable->setToolTip( + i18n("The path to the Assistant copy to use as an external viewer,\n" + "can also be a script calling the Assistant or any other\n" + "application that understands the same remote control protocol.\n" + "KDevelop will try to find the application when this is not set.\n" + "It is your responsibility to ensure that the viewer is configured\n" + "to use a qhc help collection matching KDevelop's collection!")); + QString currentExtViewer; + if (KDevelop::QtHelpExternalAssistant::self()->hasExternalViewer(¤tExtViewer)) { + m_configWidget->externalViewerExecutable->setPlaceholderText( + i18n("path to Qt Assistant (%1)", currentExtViewer)); + } else { + m_configWidget->externalViewerExecutable->setPlaceholderText( + i18n("path to Qt Assistant")); + } + connect(m_configWidget->externalViewerExecutable, &KUrlRequester::textChanged, + this, &QtHelpConfig::changed); // Set availability information for QtHelp m_configWidget->messageAvailabilityQtDocs->setCloseButtonVisible(false); @@ -128,6 +150,8 @@ m_configWidget->messageAvailabilityQtDocs->setText( i18n("The command \"qmake -query\" could not provide a path to a QtHelp file (QCH).")); m_configWidget->loadQtDocsCheckBox->setVisible(false); + m_configWidget->externalViewerCheckBox->setVisible(false); + m_configWidget->externalViewerExecutable->setVisible(false); } reset(); } @@ -154,8 +178,10 @@ } QString searchDir = m_configWidget->qchSearchDir->text(); bool loadQtDoc = m_configWidget->loadQtDocsCheckBox->isChecked(); + ExternalViewerSettings extViewer(m_configWidget->externalViewerCheckBox->isChecked(), + m_configWidget->externalViewerExecutable->text()); - qtHelpWriteConfig(iconList, nameList, pathList, ghnsList, searchDir, loadQtDoc); + qtHelpWriteConfig(iconList, nameList, pathList, ghnsList, searchDir, loadQtDoc, extViewer); static_cast(plugin())->readConfig(); } @@ -166,15 +192,18 @@ QStringList iconList, nameList, pathList, ghnsList; QString searchDir; bool loadQtDoc; - qtHelpReadConfig(iconList, nameList, pathList, ghnsList, searchDir, loadQtDoc); + ExternalViewerSettings extViewer; + qtHelpReadConfig(iconList, nameList, pathList, ghnsList, searchDir, loadQtDoc, extViewer); const int size = qMin(qMin(iconList.size(), nameList.size()), pathList.size()); for(int i = 0; i < size; ++i) { QString ghnsStatus = ghnsList.size()>i ? ghnsList.at(i) : QStringLiteral("0"); addTableItem(iconList.at(i), nameList.at(i), pathList.at(i), ghnsStatus); } m_configWidget->qchSearchDir->setText(searchDir); m_configWidget->loadQtDocsCheckBox->setChecked(loadQtDoc); + m_configWidget->externalViewerCheckBox->setChecked(extViewer.useExtViewer); + m_configWidget->externalViewerExecutable->setText(extViewer.extViewerExecutable); emit changed(); } Index: plugins/qthelp/qthelpconfig.ui =================================================================== --- plugins/qthelp/qthelpconfig.ui +++ plugins/qthelp/qthelpconfig.ui @@ -14,7 +14,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -48,14 +57,14 @@ - + - + true - + @@ -165,6 +174,48 @@ + + + + QFormLayout::ExpandingFieldsGrow + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 3 + + + 3 + + + + + KFile::ExistingOnly|KFile::File|KFile::LocalOnly + + + + + + + + + + + + + + Use &external viewer: + + + externalViewerCheckBox + + + + + + + Qt::Vertical @@ -181,16 +232,16 @@ - KMessageWidget - QFrame -
kmessagewidget.h
-
- KUrlRequester QWidget -
kurlrequester.h
+
kurlrequester.h
1
+ + KMessageWidget + QFrame +
kmessagewidget.h
+
Index: plugins/qthelp/qthelpdocumentation.h =================================================================== --- plugins/qthelp/qthelpdocumentation.h +++ plugins/qthelp/qthelpdocumentation.h @@ -54,6 +54,8 @@ static QtHelpProviderAbstract* s_provider; + bool viewInExternalBrowser() override; + public Q_SLOTS: void viewContextMenuRequested(const QPoint& pos); Index: plugins/qthelp/qthelpdocumentation.cpp =================================================================== --- plugins/qthelp/qthelpdocumentation.cpp +++ plugins/qthelp/qthelpdocumentation.cpp @@ -37,6 +37,7 @@ #include #include "qthelpnetwork.h" #include "qthelpproviderabstract.h" +#include "qthelpexternalassistant.h" using namespace KDevelop; @@ -301,6 +302,14 @@ return m_provider; } +bool QtHelpDocumentation::viewInExternalBrowser() +{ + if (QtHelpExternalAssistant::self()->useExternalViewer()) { + return QtHelpExternalAssistant::openUrl(m_current.value()); + } + return false; +} + QtHelpAlternativeLink::QtHelpAlternativeLink(const QString& name, const QtHelpDocumentation* doc, QObject* parent) : QAction(name, parent), mDoc(doc), mName(name) { Index: plugins/qthelp/qthelpexternalassistant.h =================================================================== --- /dev/null +++ plugins/qthelp/qthelpexternalassistant.h @@ -0,0 +1,95 @@ +/* This file is part of KDevelop + Copyright 2017 René J.V. Bertin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef QTHELPEXTERNALASSISTANT_H +#define QTHELPEXTERNALASSISTANT_H + +#include +#include + +#include "debug.h" + +class QUrl; + +namespace KDevelop{ + +/** + * @brief Provides support for using Qt's Assistant (or compatible applications) + * as an external viewer for QtHelp documentation. A single instance will be + * launched when required (one external browser per KDevelop session), and + * controlled via the Assistant's remote control protocol. The application will + * be terminated when KDevelop exits. + */ +class QtHelpExternalAssistant : public QObject +{ + Q_OBJECT +public: + QtHelpExternalAssistant(QObject* parent); + ~QtHelpExternalAssistant(); + + /** + * @brief returns the current external viewer instance, + * creating and initialising it if necessary. It does + * not start the viewer. + */ + static QtHelpExternalAssistant* self(); + + /** + * @brief open @param url in the external viewer, + * launching it if required. No attempt is made to raise + * or unhide the viewer, beyond what the viewer does itself. + * @returns true when the command is transmitted successfully. + */ + static bool openUrl(const QUrl& url); + + /** + * @brief enable or disable use of the external viewer + */ + void setUseExternalViewer(bool extViewer); + bool useExternalViewer() { return m_useExternalViewer; } + + /** + * @brief sets the @param path to the external viewer executable. + */ + void setExternalViewerExecutable(QString& path); + /** + * @brief checks if a valid external viewer executable has been + * configured; @returns true if this is the case and links can thus + * be opened in this viewer instead of in the embedded browser. + * + * @param path can be used to obtain the configured path or the + * path to the Assistant copy found automatically. + */ + bool hasExternalViewer(QString* path = nullptr); + +private Q_SLOTS: + void externalViewerExit(int exitCode, QProcess::ExitStatus exitStatus); + +private: + void start(); + + static QtHelpExternalAssistant* s_self; + QProcess* m_externalViewerProcess = nullptr; + bool m_useExternalViewer = false; + QString m_externalViewerExecutable; +}; + +} + +#endif //QTHELPEXTERNALASSISTANT_H Index: plugins/qthelp/qthelpexternalassistant.cpp =================================================================== --- /dev/null +++ plugins/qthelp/qthelpexternalassistant.cpp @@ -0,0 +1,177 @@ +/* This file is part of KDevelop + Copyright 2017 René J.V. Bertin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "qthelpexternalassistant.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "debug.h" + +using namespace KDevelop; + +QtHelpExternalAssistant* QtHelpExternalAssistant::s_self = nullptr; + +QtHelpExternalAssistant::QtHelpExternalAssistant(QObject *parent) + : QObject(parent) +{ +} + +QtHelpExternalAssistant::~QtHelpExternalAssistant() +{ + if (m_externalViewerProcess) { + // stop monitoring the process. Prevents potential warnings + // (QCoreApplication::postEvent: Unexpected null receiver) + m_externalViewerProcess->disconnect(); +#ifdef Q_OS_UNIX + qint64 pid = m_externalViewerProcess->processId(); + if (pid > 0) { + QProcess* hup = new QProcess(); + hup->startDetached(QString::fromLatin1("kill -1 %1").arg(pid)); + hup->waitForFinished(50); + hup->deleteLater(); + } else +#endif + m_externalViewerProcess->terminate(); + // give the process a bit of time to react, just to prevent + // "QProcess destroyed while process still running" warnings. + m_externalViewerProcess->waitForFinished(50); + m_externalViewerProcess->deleteLater(); + m_externalViewerProcess = nullptr; + } + if (this == s_self) { + s_self = nullptr; + } +} + +void QtHelpExternalAssistant::externalViewerExit(int exitCode, QProcess::ExitStatus exitStatus) +{ + if (this == s_self) { + qCDebug(QTHELP) << "externalViewer" << this << "has exited with code" + << exitCode << "and status" << exitStatus; + deleteLater(); + m_externalViewerProcess = nullptr; + } +} + +void QtHelpExternalAssistant::setUseExternalViewer(bool extViewer) +{ + if (this == s_self) { + m_useExternalViewer = extViewer; + } else { + s_self->setUseExternalViewer(extViewer); + } +} + +void QtHelpExternalAssistant::setExternalViewerExecutable(QString& path) +{ + if (this == s_self) { + m_externalViewerExecutable = path; + } else { + s_self->setExternalViewerExecutable(path); + } +} + +#ifndef Q_OS_MACOS +bool QtHelpExternalAssistant::hasExternalViewer(QString* path) +{ + bool ret = false; + if (this == s_self) { + if (!m_externalViewerExecutable.isEmpty()) { + QFileInfo info(m_externalViewerExecutable); + if (info.isExecutable()) { + ret = true; + } else { + qCWarning(QTHELP) << "External viewer" << m_externalViewerExecutable << "is not executable!"; + } + // always return the configured executable path + if (path) { + *path = m_externalViewerExecutable; + } + } else if (path) { + // look for the application on the current path. The result is not cached so the + // lookup is performed only when we can return the result. + const QString assistant = QStandardPaths::findExecutable(QLatin1String("assistant")); + if (!assistant.isEmpty()) { + ret = true; + *path = assistant; + qCDebug(QTHELP) << "External viewer found at" << assistant; + } else { + qCWarning(QTHELP) << "External viewer not found!"; + } + } + } else { + return s_self->hasExternalViewer(path); + } + return ret; +} +#endif + +QtHelpExternalAssistant* QtHelpExternalAssistant::self() +{ + if (ICore::self()->shuttingDown()) { + // don't create a new instance if we're shutting down. + return s_self; + } + + if (!s_self) { + if (!s_self) { + s_self = new QtHelpExternalAssistant(qApp); + } + } + return s_self; +} + +bool QtHelpExternalAssistant::openUrl(const QUrl& url) +{ + if (ICore::self()->shuttingDown()) { + return false; + } + if (s_self || self()) { + // start the viewer if required (moved here from self()) + QString path; + if (!s_self->m_externalViewerProcess && s_self->hasExternalViewer(&path)) { + s_self->m_externalViewerProcess = new QProcess(); + connect(s_self->m_externalViewerProcess, + static_cast(&QProcess::finished), + s_self, &QtHelpExternalAssistant::externalViewerExit); + QStringList args = {"-enableRemoteControl"}; + s_self->m_externalViewerProcess->start(path, args, QIODevice::WriteOnly|QIODevice::Append); + if (!s_self->m_externalViewerProcess->waitForStarted()) { + s_self->m_externalViewerProcess->deleteLater(); + s_self->m_externalViewerProcess = nullptr; + } + } + const QByteArray command = QByteArrayLiteral("setSource ") + + url.toString().toUtf8() + QByteArrayLiteral("\n"); + if (s_self->m_externalViewerProcess->write(command) < 0 + || s_self->m_externalViewerProcess->write("show contents\n") < 0) { + return false; + } + s_self->m_externalViewerProcess->write("syncContents\n"); + return true; + } + return false; +} Index: plugins/qthelp/qthelpexternalassistant_mac.mm =================================================================== --- /dev/null +++ plugins/qthelp/qthelpexternalassistant_mac.mm @@ -0,0 +1,96 @@ +/* This file is part of KDevelop + Copyright 2017 René J.V. Bertin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "qthelpexternalassistant.h" + +#include +#include +#include +#include + +#include + +#include "debug.h" + +using namespace KDevelop; + +bool QtHelpExternalAssistant::hasExternalViewer(QString* path) +{ + bool ret = false; + QString execPath; + QFileInfo info; + + if (this == s_self ){ + if (!m_externalViewerExecutable.isEmpty()) { + execPath = m_externalViewerExecutable; + info = QFileInfo(execPath); + if (info.isBundle()) { + NSString* bundleExec = [[NSBundle bundleWithPath:m_externalViewerExecutable.toNSString()] executablePath]; + if (bundleExec) { + execPath = QString::fromNSString(bundleExec); + info = QFileInfo(execPath); + qCDebug(QTHELP) << "Found bundle exec" << execPath << "for external viewer" << m_externalViewerExecutable; + } else { + qCWarning(QTHELP) << "Could not find the bundle exec for external viewer" << m_externalViewerExecutable; + } + } + if (info.isExecutable()) { + ret = true; + } else { + qCWarning(QTHELP) << "External viewer" << execPath << "is not executable!"; + } + // always return the configured executable path + if (path) { + *path = execPath; + } + } else if (path) { + // look for the application on the current path. The result is not cached so the + // lookup is performed only when we can return the result. + // First try to find a regular executable, which the user could have created + // to invoke the right Assistant.app + const QString assistant = QStandardPaths::findExecutable(QLatin1String("assistant")); + if (!assistant.isEmpty()) { + ret = true; + *path = assistant; + qCDebug(QTHELP) << "External viewer found at" << assistant; + } else { + // try to locate a Qt5 Assistant app bundle + // There is no way to predict which we'll get beyond "probably the same as last time". + NSString *assistantAppBundle = [[NSWorkspace sharedWorkspace] + absolutePathForAppBundleWithIdentifier:@"org.qt-project.assistant"]; + if (!assistantAppBundle) { + // fall back to finding whatever Assistant.app application might be available. + assistantAppBundle = [[NSWorkspace sharedWorkspace] fullPathForApplication:@"Assistant.app"]; + } + NSString* assistantExec = assistantAppBundle ? [[NSBundle bundleWithPath:assistantAppBundle] executablePath] : nullptr; + if (assistantExec) { + execPath = QString::fromNSString(assistantExec); + ret = true; + *path = execPath; + qCDebug(QTHELP) << "Found bundle exec" << execPath << "for Assistant.app" << QString::fromNSString(assistantAppBundle); + } else { + qCWarning(QTHELP) << "External viewer not found!"; + } + } + } + } else { + return s_self->hasExternalViewer(path); + } + return ret; +} Index: plugins/qthelp/qthelpplugin.h =================================================================== --- plugins/qthelp/qthelpplugin.h +++ plugins/qthelp/qthelpplugin.h @@ -42,6 +42,8 @@ QList qtHelpProviderLoaded(); bool isQtHelpQtDocLoaded() const; bool isQtHelpAvailable() const; + bool useExternalViewer() const; + QString externalViewerExecutable() const; int configPages() const override; KDevelop::ConfigPage* configPage(int number, QWidget* parent) override; @@ -59,6 +61,8 @@ QList m_qtHelpProviders; QtHelpQtDoc* m_qtDoc; bool m_loadSystemQtDoc; + bool m_useExternalViewer; + QString m_externalViewerExecutable; }; #endif // QTHELPPLUGIN_H Index: plugins/qthelp/qthelpplugin.cpp =================================================================== --- plugins/qthelp/qthelpplugin.cpp +++ plugins/qthelp/qthelpplugin.cpp @@ -26,6 +26,7 @@ #include #include "qthelpprovider.h" #include "qthelpqtdoc.h" +#include "qthelpexternalassistant.h" #include "qthelp_config_shared.h" #include "debug.h" #include "qthelpconfig.h" @@ -39,6 +40,7 @@ , m_qtHelpProviders() , m_qtDoc(new QtHelpQtDoc(this, QVariantList())) , m_loadSystemQtDoc(false) + , m_useExternalViewer(false) { Q_UNUSED(args); s_plugin = this; @@ -55,11 +57,18 @@ { QStringList iconList, nameList, pathList, ghnsList; QString searchDir; - qtHelpReadConfig(iconList, nameList, pathList, ghnsList, searchDir, m_loadSystemQtDoc); + ExternalViewerSettings extViewer; + qtHelpReadConfig(iconList, nameList, pathList, ghnsList, searchDir, m_loadSystemQtDoc, extViewer); searchHelpDirectory(pathList, nameList, iconList, searchDir); loadQtHelpProvider(pathList, nameList, iconList); loadQtDocumentation(m_loadSystemQtDoc); + m_useExternalViewer = extViewer.useExtViewer; + m_externalViewerExecutable = extViewer.extViewerExecutable; + if (m_useExternalViewer) { + KDevelop::QtHelpExternalAssistant::self()->setUseExternalViewer(m_useExternalViewer); + KDevelop::QtHelpExternalAssistant::self()->setExternalViewerExecutable(m_externalViewerExecutable); + } emit changedProvidersList(); } @@ -159,6 +168,11 @@ return m_loadSystemQtDoc; } +bool QtHelpPlugin::useExternalViewer() const +{ + return m_useExternalViewer; +} + bool QtHelpPlugin::isQtHelpAvailable() const { return !m_qtDoc->qchFiles().isEmpty(); Index: plugins/qthelp/tests/CMakeLists.txt =================================================================== --- plugins/qthelp/tests/CMakeLists.txt +++ plugins/qthelp/tests/CMakeLists.txt @@ -8,12 +8,18 @@ ../qthelpproviderabstract.cpp ../qthelpprovider.cpp ../qthelpdocumentation.cpp + ../qthelpexternalassistant.cpp ../qthelpqtdoc.cpp ../qthelp_config_shared.cpp ../qthelpconfig.cpp ../qthelpnetwork.cpp ${kdevqthelp_LOG_SRCS} ) +if(APPLE) + set(test_qthelpplugin_SRCS ${test_qthelpplugin_SRCS} + ../qthelpexternalassistant_mac.mm + ) +endif() ki18n_wrap_ui(test_qthelpplugin_SRCS ../qthelpconfig.ui ../qthelpconfigeditdialog.ui @@ -24,3 +30,6 @@ TEST_NAME test_qthelpplugin LINK_LIBRARIES Qt5::Test KF5::NewStuff KF5::KIOWidgets KF5::TextEditor KF5::IconThemes Qt5::Help KDev::Tests KDev::Documentation ) +if(APPLE) + target_link_libraries(test_qthelpplugin "-framework AppKit") +endif() Index: plugins/qthelp/tests/test_qthelpplugin.cpp =================================================================== --- plugins/qthelp/tests/test_qthelpplugin.cpp +++ plugins/qthelp/tests/test_qthelpplugin.cpp @@ -58,7 +58,8 @@ { m_plugin = new QtHelpPlugin(m_testCore, QVariantList()); // write default config and read it - qtHelpWriteConfig(QStringList(), QStringList(), QStringList(), QStringList(), QString(), true); + ExternalViewerSettings extView; + qtHelpWriteConfig(QStringList(), QStringList(), QStringList(), QStringList(), QString(), true, extView); m_plugin->readConfig(); } @@ -82,7 +83,8 @@ void TestQtHelpPlugin::testUnsetQtHelpDoc() { - qtHelpWriteConfig(QStringList(), QStringList(), QStringList(), QStringList(), QString(), false); + ExternalViewerSettings extView; + qtHelpWriteConfig(QStringList(), QStringList(), QStringList(), QStringList(), QString(), false, extView); m_plugin->readConfig(); QCOMPARE(m_plugin->providers().size(), 0); @@ -95,7 +97,8 @@ name << QStringLiteral("file1"); icon << QStringLiteral("myIcon"); ghns << QStringLiteral("0"); - qtHelpWriteConfig(icon, name, path, ghns, QString(), true); + ExternalViewerSettings extView; + qtHelpWriteConfig(icon, name, path, ghns, QString(), true, extView); m_plugin->readConfig(); QCOMPARE(m_plugin->qtHelpProviderLoaded().size(), 1); @@ -111,7 +114,8 @@ name << QStringLiteral("file1") << QStringLiteral("file2"); icon << QStringLiteral("myIcon") << QStringLiteral("myIcon"); ghns << QStringLiteral("0") << QStringLiteral("0"); - qtHelpWriteConfig(icon, name, path, ghns, QString(), true); + ExternalViewerSettings extView; + qtHelpWriteConfig(icon, name, path, ghns, QString(), true, extView); m_plugin->readConfig(); QCOMPARE(m_plugin->qtHelpProviderLoaded().size(), 2); @@ -132,7 +136,8 @@ name << QStringLiteral("file1"); icon << QStringLiteral("myIcon"); ghns << QStringLiteral("0"); - qtHelpWriteConfig(icon, name, path, ghns, QString(), true); + ExternalViewerSettings extView; + qtHelpWriteConfig(icon, name, path, ghns, QString(), true, extView); m_plugin->readConfig(); QCOMPARE(m_plugin->qtHelpProviderLoaded().size(), 0); @@ -145,7 +150,8 @@ name << QStringLiteral("file1") << QStringLiteral("file2"); icon << QStringLiteral("myIcon") << QStringLiteral("myIcon"); ghns << QStringLiteral("0") << QStringLiteral("0"); - qtHelpWriteConfig(icon, name, path, ghns, QString(), true); + ExternalViewerSettings extView; + qtHelpWriteConfig(icon, name, path, ghns, QString(), true, extView); m_plugin->readConfig(); QCOMPARE(m_plugin->qtHelpProviderLoaded().size(), 1); @@ -158,7 +164,8 @@ name << QStringLiteral("file1") << QStringLiteral("file2"); icon << QStringLiteral("myIcon") << QStringLiteral("myIcon"); ghns << QStringLiteral("0") << QStringLiteral("0"); - qtHelpWriteConfig(icon, name, path, ghns, QString(), true); + ExternalViewerSettings extView; + qtHelpWriteConfig(icon, name, path, ghns, QString(), true, extView); m_plugin->readConfig(); QCOMPARE(m_plugin->qtHelpProviderLoaded().size(), 2); @@ -168,7 +175,7 @@ name.removeAt(1); icon.removeAt(1); ghns.removeAt(1); - qtHelpWriteConfig(icon, name, path, ghns, QString(), true); + qtHelpWriteConfig(icon, name, path, ghns, QString(), true, extView); m_plugin->readConfig(); QCOMPARE(m_plugin->qtHelpProviderLoaded().size(), 1);