Index: qthelp/qthelp_config_shared.h =================================================================== --- qthelp/qthelp_config_shared.h +++ qthelp/qthelp_config_shared.h @@ -28,13 +28,17 @@ QStringList& pathList, QStringList& ghnsList, QString& searchDir, - bool& loadQtDoc); + bool& loadQtDoc, + bool& 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 bool extViewer + ); #endif // QTHELP_CONFIG_SHARED_H Index: qthelp/qthelp_config_shared.cpp =================================================================== --- qthelp/qthelp_config_shared.cpp +++ qthelp/qthelp_config_shared.cpp @@ -25,7 +25,7 @@ void qtHelpReadConfig(QStringList& iconList, QStringList& nameList, QStringList& pathList, QStringList& ghnsList, - QString& searchDir, bool& loadQtDoc) + QString& searchDir, bool& loadQtDoc, bool& extViewer) { KConfigGroup cg(KSharedConfig::openConfig(), "QtHelp Documentation"); iconList = cg.readEntry("iconList", QStringList()); @@ -34,11 +34,12 @@ ghnsList = cg.readEntry("ghnsList", QStringList()); searchDir = cg.readEntry("searchDir", QString()); loadQtDoc = cg.readEntry("loadQtDocs", true); + extViewer = cg.readEntry("useExternalViewer", false); } 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 bool extViewer) { KConfigGroup cg(KSharedConfig::openConfig(), "QtHelp Documentation"); cg.writeEntry("iconList", iconList); @@ -47,4 +48,5 @@ cg.writeEntry("ghnsList", ghnsList); cg.writeEntry("searchDir", searchDir); cg.writeEntry("loadQtDocs", loadQtDoc); + cg.writeEntry("useExternalViewer", extViewer); } Index: qthelp/qthelpconfig.cpp =================================================================== --- qthelp/qthelpconfig.cpp +++ qthelp/qthelpconfig.cpp @@ -121,6 +121,10 @@ 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" + "called through a script or symlink \"kdevelop-qthelp-viewer\"")); // Set availability information for QtHelp m_configWidget->messageAvailabilityQtDocs->setCloseButtonVisible(false); @@ -130,6 +134,7 @@ 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); } reset(); } @@ -156,8 +161,9 @@ } QString searchDir = m_configWidget->qchSearchDir->text(); bool loadQtDoc = m_configWidget->loadQtDocsCheckBox->isChecked(); + bool extViewer = m_configWidget->externalViewerCheckBox->isChecked(); - qtHelpWriteConfig(iconList, nameList, pathList, ghnsList, searchDir, loadQtDoc); + qtHelpWriteConfig(iconList, nameList, pathList, ghnsList, searchDir, loadQtDoc, extViewer); static_cast(plugin())->readConfig(); } @@ -167,8 +173,8 @@ QStringList iconList, nameList, pathList, ghnsList; QString searchDir; - bool loadQtDoc; - qtHelpReadConfig(iconList, nameList, pathList, ghnsList, searchDir, loadQtDoc); + bool loadQtDoc, 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) { @@ -177,6 +183,7 @@ } m_configWidget->qchSearchDir->setText(searchDir); m_configWidget->loadQtDocsCheckBox->setChecked(loadQtDoc); + m_configWidget->externalViewerCheckBox->setChecked(extViewer); emit changed(); } Index: qthelp/qthelpconfig.ui =================================================================== --- qthelp/qthelpconfig.ui +++ qthelp/qthelpconfig.ui @@ -14,7 +14,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -48,10 +57,10 @@ - + - + true @@ -61,7 +70,7 @@ 0 - + @@ -171,6 +180,29 @@ + + + + + + &Use external viewer: + + + externalViewerCheckBox + + + + + + + + + + + + + + Qt::Vertical @@ -187,16 +219,16 @@ - KMessageWidget - QFrame -
kmessagewidget.h
-
- KUrlRequester QWidget -
kurlrequester.h
+
kurlrequester.h
1
+ + KMessageWidget + QFrame +
kmessagewidget.h
+
Index: qthelp/qthelpplugin.h =================================================================== --- qthelp/qthelpplugin.h +++ qthelp/qthelpplugin.h @@ -42,6 +42,7 @@ QList qtHelpProviderLoaded(); bool isQtHelpQtDocLoaded() const; bool isQtHelpAvailable() const; + bool useExternalViewer() const; int configPages() const override; KDevelop::ConfigPage* configPage(int number, QWidget* parent) override; @@ -59,6 +60,7 @@ QList m_qtHelpProviders; QtHelpQtDoc* m_qtDoc; bool m_loadSystemQtDoc; + bool m_useExternalViewer; }; #endif // QTHELPPLUGIN_H Index: qthelp/qthelpplugin.cpp =================================================================== --- qthelp/qthelpplugin.cpp +++ qthelp/qthelpplugin.cpp @@ -40,6 +40,7 @@ , m_qtHelpProviders() , m_qtDoc(new QtHelpQtDoc(this, QVariantList())) , m_loadSystemQtDoc(false) + , m_useExternalViewer(false) { Q_UNUSED(args); s_plugin = this; @@ -56,11 +57,12 @@ { QStringList iconList, nameList, pathList, ghnsList; QString searchDir; - qtHelpReadConfig(iconList, nameList, pathList, ghnsList, searchDir, m_loadSystemQtDoc); + qtHelpReadConfig(iconList, nameList, pathList, ghnsList, searchDir, m_loadSystemQtDoc, m_useExternalViewer); searchHelpDirectory(pathList, nameList, iconList, searchDir); loadQtHelpProvider(pathList, nameList, iconList); loadQtDocumentation(m_loadSystemQtDoc); + m_qtDoc->setUseExternalViewer(m_useExternalViewer); emit changedProvidersList(); } @@ -160,6 +162,11 @@ return m_loadSystemQtDoc; } +bool QtHelpPlugin::useExternalViewer() const +{ + return m_useExternalViewer; +} + bool QtHelpPlugin::isQtHelpAvailable() const { return !m_qtDoc->qchFiles().isEmpty(); Index: qthelp/qthelpproviderabstract.h =================================================================== --- qthelp/qthelpproviderabstract.h +++ qthelp/qthelpproviderabstract.h @@ -29,6 +29,8 @@ #include #include +class ExternalViewerProcess; + class QtHelpProviderAbstract : public QObject, public KDevelop::IDocumentationProvider { Q_OBJECT @@ -46,6 +48,10 @@ /// @return False in case we failed to load any documentation files, else true bool isValid() const; + ExternalViewerProcess *externalViewer() const; + template bool externalViewerCommand(const T& command) const; + void setUseExternalViewer(const bool extViewer); + QHelpEngine* engine() { return &m_engine; } public slots: void jumpedTo(const QUrl& newUrl) const; @@ -53,6 +59,9 @@ void addHistory(const KDevelop::IDocumentation::Ptr& doc) const override; protected: QHelpEngine m_engine; + static bool m_useExternalViewer; + static ExternalViewerProcess* m_externalViewerProcess; + friend class ExternalViewerProcess; }; #endif // QTHELPPROVIDERABSTRACT_H Index: qthelp/qthelpproviderabstract.cpp =================================================================== --- qthelp/qthelpproviderabstract.cpp +++ qthelp/qthelpproviderabstract.cpp @@ -27,6 +27,10 @@ #include +#include +#include +#include + #include #include #include @@ -37,6 +41,46 @@ using namespace KDevelop; +// subclass QProcess so we can be our own exit watchdog +class ExternalViewerProcess : public QProcess +{ + Q_OBJECT +public: + ExternalViewerProcess(QObject* parent); + ~ExternalViewerProcess() + { + qint64 pid = processId(); + if (pid > 0) { + QProcess *hup = new QProcess(this); + hup->start(QString::fromLatin1("kill -1 %1").arg(pid)); + hup->waitForFinished(500); + } + QProcess::waitForFinished(500); + } +public slots: + void externalViewerExit(int exitCode, QProcess::ExitStatus exitStatus); +}; + +ExternalViewerProcess* QtHelpProviderAbstract::m_externalViewerProcess = Q_NULLPTR; +bool QtHelpProviderAbstract::m_useExternalViewer = false; + +ExternalViewerProcess::ExternalViewerProcess(QObject *parent) + : QProcess(parent) +{ + connect(this, static_cast(&ExternalViewerProcess::finished), + &ExternalViewerProcess::externalViewerExit); +} + +void ExternalViewerProcess::externalViewerExit(int exitCode, QProcess::ExitStatus exitStatus) +{ + if (this == QtHelpProviderAbstract::m_externalViewerProcess) { + qCDebug(QTHELP) << Q_FUNC_INFO << "externalViewer" << this << "has exited with code" + << exitCode << "and status" << exitStatus; + deleteLater(); + QtHelpProviderAbstract::m_externalViewerProcess = Q_NULLPTR; + } +} + QtHelpProviderAbstract::QtHelpProviderAbstract(QObject *parent, const QString &collectionFileName, const QVariantList &args) : QObject(parent) , m_engine(QStandardPaths::writableLocation(QStandardPaths::DataLocation)+'/'+collectionFileName) @@ -45,13 +89,47 @@ if( !m_engine.setupData() ) { qWarning() << "Couldn't setup QtHelp Collection file"; } + m_useExternalViewer = false; } - QtHelpProviderAbstract::~QtHelpProviderAbstract() { } +void QtHelpProviderAbstract::setUseExternalViewer(const bool extViewer) +{ + m_useExternalViewer = extViewer; +} + +ExternalViewerProcess* QtHelpProviderAbstract::externalViewer() const +{ + // turning off the use of an external help viewer shouldn't terminate + // an already running viewer process, we just stop using it. + if (!m_useExternalViewer) { + return Q_NULLPTR; + } + if (!m_externalViewerProcess) { + m_externalViewerProcess = new ExternalViewerProcess(qApp); + QStringList args = {"-enableRemoteControl"}; + m_externalViewerProcess->start(QStandardPaths::findExecutable(QLatin1String("kdevelop-qthelp-viewer")), + args, QIODevice::WriteOnly|QIODevice::Append); + if (!m_externalViewerProcess->waitForStarted()) { + m_externalViewerProcess->deleteLater(); + m_externalViewerProcess = NULL; + } + } + return m_externalViewerProcess; +} + +template bool QtHelpProviderAbstract::externalViewerCommand(const T& command) const +{ + if (externalViewer()) { + return externalViewer()->write(command) >= 0; + } else { + return false; + } +} + IDocumentation::Ptr QtHelpProviderAbstract::documentationForDeclaration(Declaration* dec) const { QtHelpDocumentation::s_provider = const_cast(this); @@ -69,8 +147,22 @@ if (!id.isEmpty()) { QMap links = m_engine.linksForIdentifier(id); - if(!links.isEmpty()) + if(!links.isEmpty()) { + if (externalViewer()) { + QByteArray ba; + QList urls = links.values(); + foreach (const auto url, urls) { + if (!url.isEmpty()) { + ba.append(QString::fromLatin1("setSource ") + url.toString() + QLatin1String("\n")); + } + } + qCDebug(QTHELP) << Q_FUNC_INFO << "Id=" << id << "links=" << links << "->" << ba; + externalViewerCommand(ba); + externalViewerCommand("show contents\n"); + externalViewerCommand("syncContents\n"); + } return IDocumentation::Ptr(new QtHelpDocumentation(id, links)); + } } } @@ -87,7 +179,21 @@ { QtHelpDocumentation::s_provider = const_cast(this); QString name=idx.data(Qt::DisplayRole).toString(); - return IDocumentation::Ptr(new QtHelpDocumentation(name, m_engine.indexModel()->linksForKeyword(name))); + QMap links = m_engine.indexModel()->linksForKeyword(name); + if(!links.isEmpty() && externalViewer()) { + QByteArray ba; + QList urls = links.values(); + foreach (const auto url, urls) { + if (!url.isEmpty()) { + ba.append(QString::fromLatin1("setSource ") + url.toString() + QLatin1String("\n")); + ba.append(QLatin1String("show contents\n")); + } + } + qCDebug(QTHELP) << Q_FUNC_INFO << "name=" << name << "->" << ba; + externalViewerCommand(ba); + externalViewerCommand("syncContents\n"); + } + return IDocumentation::Ptr(new QtHelpDocumentation(name, links)); } void QtHelpProviderAbstract::jumpedTo(const QUrl& newUrl) const @@ -109,3 +215,5 @@ { return !m_engine.registeredDocumentations().isEmpty(); } + +#include "qthelpproviderabstract.moc" Index: qthelp/tests/test_qthelpplugin.cpp =================================================================== --- qthelp/tests/test_qthelpplugin.cpp +++ qthelp/tests/test_qthelpplugin.cpp @@ -58,7 +58,7 @@ { m_plugin = new QtHelpPlugin(m_testCore, QVariantList()); // write default config and read it - qtHelpWriteConfig(QStringList(), QStringList(), QStringList(), QStringList(), QString(), true); + qtHelpWriteConfig(QStringList(), QStringList(), QStringList(), QStringList(), QString(), true, false); m_plugin->readConfig(); } @@ -82,7 +82,7 @@ void TestQtHelpPlugin::testUnsetQtHelpDoc() { - qtHelpWriteConfig(QStringList(), QStringList(), QStringList(), QStringList(), QString(), false); + qtHelpWriteConfig(QStringList(), QStringList(), QStringList(), QStringList(), QString(), false, false); m_plugin->readConfig(); QCOMPARE(m_plugin->providers().size(), 0); @@ -95,7 +95,7 @@ name << "file1"; icon << "myIcon"; ghns << "0"; - qtHelpWriteConfig(icon, name, path, ghns, QString(), true); + qtHelpWriteConfig(icon, name, path, ghns, QString(), true, false); m_plugin->readConfig(); QCOMPARE(m_plugin->qtHelpProviderLoaded().size(), 1); @@ -111,7 +111,7 @@ name << "file1" << "file2"; icon << "myIcon" << "myIcon"; ghns << "0" << "0"; - qtHelpWriteConfig(icon, name, path, ghns, QString(), true); + qtHelpWriteConfig(icon, name, path, ghns, QString(), true, false); m_plugin->readConfig(); QCOMPARE(m_plugin->qtHelpProviderLoaded().size(), 2); @@ -132,7 +132,7 @@ name << "file1"; icon << "myIcon"; ghns << "0"; - qtHelpWriteConfig(icon, name, path, ghns, QString(), true); + qtHelpWriteConfig(icon, name, path, ghns, QString(), true, false); m_plugin->readConfig(); QCOMPARE(m_plugin->qtHelpProviderLoaded().size(), 0); @@ -145,7 +145,7 @@ name << "file1" << "file2"; icon << "myIcon" << "myIcon"; ghns << "0" << "0"; - qtHelpWriteConfig(icon, name, path, ghns, QString(), true); + qtHelpWriteConfig(icon, name, path, ghns, QString(), true, false); m_plugin->readConfig(); QCOMPARE(m_plugin->qtHelpProviderLoaded().size(), 1); @@ -158,7 +158,7 @@ name << "file1" << "file2"; icon << "myIcon" << "myIcon"; ghns << "0" << "0"; - qtHelpWriteConfig(icon, name, path, ghns, QString(), true); + qtHelpWriteConfig(icon, name, path, ghns, QString(), true, false); m_plugin->readConfig(); QCOMPARE(m_plugin->qtHelpProviderLoaded().size(), 2); @@ -168,7 +168,7 @@ name.removeAt(1); icon.removeAt(1); ghns.removeAt(1); - qtHelpWriteConfig(icon, name, path, ghns, QString(), true); + qtHelpWriteConfig(icon, name, path, ghns, QString(), true, false); m_plugin->readConfig(); QCOMPARE(m_plugin->qtHelpProviderLoaded().size(), 1);