diff --git a/kdevplatform/documentation/documentationview.cpp b/kdevplatform/documentation/documentationview.cpp index 2c6cccf8bd..a0d389ffef 100644 --- a/kdevplatform/documentation/documentationview.cpp +++ b/kdevplatform/documentation/documentationview.cpp @@ -1,419 +1,410 @@ /* Copyright 2009 Aleix Pol Gonzalez Copyright 2010 Benjamin Port This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 "documentationview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "documentationfindwidget.h" #include "debug.h" using namespace KDevelop; DocumentationView::DocumentationView(QWidget* parent, ProvidersModel* model) : QWidget(parent), mProvidersModel(model) { setWindowIcon(QIcon::fromTheme(QStringLiteral("documentation"), windowIcon())); setWindowTitle(i18n("Documentation")); setLayout(new QVBoxLayout(this)); layout()->setMargin(0); layout()->setSpacing(0); mFindDoc = new DocumentationFindWidget; mFindDoc->hide(); // insert placeholder widget at location of doc view layout()->addWidget(new QWidget(this)); layout()->addWidget(mFindDoc); setupActions(); mCurrent = mHistory.end(); setFocusProxy(mIdentifiers); QMetaObject::invokeMethod(this, "initialize", Qt::QueuedConnection); } QList DocumentationView::contextMenuActions() const { // TODO: also show providers return {mBack, mForward, mHomeAction, mSeparatorBeforeFind, mFind}; } void DocumentationView::setupActions() { // use custom QAction's with createWidget for mProviders and mIdentifiers mBack = new QAction(QIcon::fromTheme(QStringLiteral("go-previous")), i18n("Back"), this); mBack->setEnabled(false); connect(mBack, &QAction::triggered, this, &DocumentationView::browseBack); addAction(mBack); mForward = new QAction(QIcon::fromTheme(QStringLiteral("go-next")), i18n("Forward"), this); mForward->setEnabled(false); connect(mForward, &QAction::triggered, this, &DocumentationView::browseForward); addAction(mForward); mHomeAction = new QAction(QIcon::fromTheme(QStringLiteral("go-home")), i18n("Home"), this); mHomeAction->setEnabled(false); connect(mHomeAction, &QAction::triggered, this, &DocumentationView::showHome); addAction(mHomeAction); mProviders = new QComboBox(this); auto providersAction = new QWidgetAction(this); providersAction->setDefaultWidget(mProviders); addAction(providersAction); mIdentifiers = new QLineEdit(this); mIdentifiers->setEnabled(false); mIdentifiers->setClearButtonEnabled(true); mIdentifiers->setPlaceholderText(i18n("Search...")); mIdentifiers->setCompleter(new QCompleter(mIdentifiers)); // mIdentifiers->completer()->setCompletionMode(QCompleter::UnfilteredPopupCompletion); mIdentifiers->completer()->setCaseSensitivity(Qt::CaseInsensitive); /* vertical size policy should be left to the style. */ mIdentifiers->setSizePolicy(QSizePolicy::Expanding, mIdentifiers->sizePolicy().verticalPolicy()); connect(mIdentifiers->completer(), static_cast(&QCompleter::activated), this, &DocumentationView::changedSelection); connect(mIdentifiers, &QLineEdit::returnPressed, this, &DocumentationView::returnPressed); auto identifiersAction = new QWidgetAction(this); identifiersAction->setDefaultWidget(mIdentifiers); addAction(identifiersAction); mSeparatorBeforeFind = new QAction(this); mSeparatorBeforeFind->setSeparator(true); addAction(mSeparatorBeforeFind); mFind = new QAction(QIcon::fromTheme(QStringLiteral("edit-find")), i18n("Find in Text..."), this); mFind->setToolTip(i18n("Find in text of current documentation page.")); mFind->setEnabled(false); connect(mFind, &QAction::triggered, mFindDoc, &DocumentationFindWidget::startSearch); addAction(mFind); auto closeFindBarShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); closeFindBarShortcut->setContext(Qt::WidgetWithChildrenShortcut); connect(closeFindBarShortcut, &QShortcut::activated, mFindDoc, &QWidget::hide); } void DocumentationView::initialize() { mProviders->setModel(mProvidersModel); connect(mProviders, static_cast(&QComboBox::activated), this, &DocumentationView::changedProvider); - foreach (IDocumentationProvider* p, mProvidersModel->providers()) { - // can't use new signal/slot syntax here, IDocumentation is not a QObject - connect(dynamic_cast(p), SIGNAL(addHistory(KDevelop::IDocumentation::Ptr)), - this, SLOT(addHistory(KDevelop::IDocumentation::Ptr))); - } connect(mProvidersModel, &ProvidersModel::providersChanged, this, &DocumentationView::emptyHistory); const bool hasProviders = (mProviders->count() > 0); mHomeAction->setEnabled(hasProviders); mIdentifiers->setEnabled(hasProviders); if (hasProviders) { changedProvider(0); } } void DocumentationView::browseBack() { --mCurrent; mBack->setEnabled(mCurrent != mHistory.begin()); mForward->setEnabled(true); updateView(); } void DocumentationView::browseForward() { ++mCurrent; mForward->setEnabled(mCurrent+1 != mHistory.end()); mBack->setEnabled(true); updateView(); } void DocumentationView::showHome() { auto prov = mProvidersModel->provider(mProviders->currentIndex()); showDocumentation(prov->homePage()); } void DocumentationView::returnPressed() { // Exit if search text is empty. It's necessary because of empty // line edit text not leads to "empty" completer indexes. if (mIdentifiers->text().isEmpty()) return; // Exit if completer popup has selected item - in this case 'Return' // key press emits QCompleter::activated signal which is already connected. if (mIdentifiers->completer()->popup()->currentIndex().isValid()) return; // If user doesn't select any item in popup we will try to use the first row. if (mIdentifiers->completer()->setCurrentRow(0)) changedSelection(mIdentifiers->completer()->currentIndex()); } void DocumentationView::changedSelection(const QModelIndex& idx) { if (idx.isValid()) { // Skip view update if user try to show already opened documentation mIdentifiers->setText(idx.data(Qt::DisplayRole).toString()); if (mIdentifiers->text() == (*mCurrent)->name()) { return; } IDocumentationProvider* prov = mProvidersModel->provider(mProviders->currentIndex()); auto doc = prov->documentationForIndex(idx); if (doc) { showDocumentation(doc); } } } void DocumentationView::showDocumentation(const IDocumentation::Ptr& doc) { qCDebug(DOCUMENTATION) << "showing" << doc->name(); - addHistory(doc); - updateView(); -} - -void DocumentationView::addHistory(const IDocumentation::Ptr& doc) -{ mBack->setEnabled(!mHistory.isEmpty()); mForward->setEnabled(false); // clear all history following the current item, unless we're already // at the end (otherwise this code crashes when history is empty, which // happens when addHistory is first called on startup to add the // homepage) if (mCurrent+1 < mHistory.end()) { mHistory.erase(mCurrent+1, mHistory.end()); } mHistory.append(doc); mCurrent = mHistory.end()-1; // NOTE: we assume an existing widget was used to navigate somewhere // but this history entry actually contains the new info for the // title... this is ugly and should be refactored somehow if (mIdentifiers->completer()->model() == (*mCurrent)->provider()->indexModel()) { mIdentifiers->setText((*mCurrent)->name()); } + + updateView(); } void DocumentationView::emptyHistory() { mHistory.clear(); mCurrent = mHistory.end(); mBack->setEnabled(false); mForward->setEnabled(false); const bool hasProviders = (mProviders->count() > 0); mHomeAction->setEnabled(hasProviders); mIdentifiers->setEnabled(hasProviders); if (hasProviders) { mProviders->setCurrentIndex(0); changedProvider(0); } else { updateView(); } } void DocumentationView::updateView() { if (mCurrent != mHistory.end()) { mProviders->setCurrentIndex(mProvidersModel->rowForProvider((*mCurrent)->provider())); mIdentifiers->completer()->setModel((*mCurrent)->provider()->indexModel()); mIdentifiers->setText((*mCurrent)->name()); mIdentifiers->completer()->setCompletionPrefix((*mCurrent)->name()); } else { mIdentifiers->clear(); } QLayoutItem* lastview = layout()->takeAt(0); Q_ASSERT(lastview); if (lastview->widget()->parent() == this) { lastview->widget()->deleteLater(); } delete lastview; mFindDoc->setEnabled(false); QWidget* w; if (mCurrent != mHistory.end()) { w = (*mCurrent)->documentationWidget(mFindDoc, this); Q_ASSERT(w); QWidget::setTabOrder(mIdentifiers, w); } else { // placeholder widget at location of doc view w = new QWidget(this); } mFind->setEnabled(mFindDoc->isEnabled()); if (!mFindDoc->isEnabled()) { mFindDoc->hide(); } QLayoutItem* findWidget = layout()->takeAt(0); layout()->addWidget(w); layout()->addItem(findWidget); } void DocumentationView::changedProvider(int row) { mIdentifiers->completer()->setModel(mProvidersModel->provider(row)->indexModel()); mIdentifiers->clear(); showHome(); } ////////////// ProvidersModel ////////////////// ProvidersModel::ProvidersModel(QObject* parent) : QAbstractListModel(parent) , mProviders(ICore::self()->documentationController()->documentationProviders()) { connect(ICore::self()->pluginController(), &IPluginController::unloadingPlugin, this, &ProvidersModel::unloaded); connect(ICore::self()->pluginController(), &IPluginController::pluginLoaded, this, &ProvidersModel::loaded); connect(ICore::self()->documentationController(), &IDocumentationController::providersChanged, this, &ProvidersModel::reloadProviders); } void ProvidersModel::reloadProviders() { beginResetModel(); mProviders = ICore::self()->documentationController()->documentationProviders(); std::sort(mProviders.begin(), mProviders.end(), [](const KDevelop::IDocumentationProvider* a, const KDevelop::IDocumentationProvider* b) { return a->name() < b->name(); }); endResetModel(); emit providersChanged(); } QVariant ProvidersModel::data(const QModelIndex& index, int role) const { if (index.row() >= mProviders.count() || index.row() < 0) return QVariant(); QVariant ret; switch (role) { case Qt::DisplayRole: ret = provider(index.row())->name(); break; case Qt::DecorationRole: ret = provider(index.row())->icon(); break; } return ret; } void ProvidersModel::addProvider(IDocumentationProvider* provider) { if (!provider || mProviders.contains(provider)) return; int pos = 0; while (pos < mProviders.size() && mProviders[pos]->name() < provider->name()) ++pos; beginInsertRows(QModelIndex(), pos, pos); mProviders.insert(pos, provider); endInsertRows(); emit providersChanged(); } void ProvidersModel::removeProvider(IDocumentationProvider* provider) { int pos; if (!provider || (pos = mProviders.indexOf(provider)) < 0) return; beginRemoveRows(QModelIndex(), pos, pos); mProviders.removeAt(pos); endRemoveRows(); emit providersChanged(); } void ProvidersModel::unloaded(IPlugin* plugin) { removeProvider(plugin->extension()); IDocumentationProviderProvider* providerProvider = plugin->extension(); if (providerProvider) { foreach(IDocumentationProvider* provider, providerProvider->providers()) removeProvider(provider); } } void ProvidersModel::loaded(IPlugin* plugin) { addProvider(plugin->extension()); IDocumentationProviderProvider* providerProvider = plugin->extension(); if (providerProvider) { foreach(IDocumentationProvider* provider, providerProvider->providers()) addProvider(provider); } } int ProvidersModel::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : mProviders.count(); } int ProvidersModel::rowForProvider(IDocumentationProvider* provider) { return mProviders.indexOf(provider); } IDocumentationProvider* ProvidersModel::provider(int pos) const { return mProviders[pos]; } QList ProvidersModel::providers() { return mProviders; } diff --git a/kdevplatform/documentation/documentationview.h b/kdevplatform/documentation/documentationview.h index 8d69bb483c..5932941e52 100644 --- a/kdevplatform/documentation/documentationview.h +++ b/kdevplatform/documentation/documentationview.h @@ -1,105 +1,103 @@ /* Copyright 2009 Aleix Pol Gonzalez Copyright 2010 Benjamin Port This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 KDEVPLATFORM_DOCUMENTATIONVIEW_H #define KDEVPLATFORM_DOCUMENTATIONVIEW_H #include #include #include #include "documentationexport.h" namespace KDevelop { class IPlugin; class DocumentationFindWidget; } class QModelIndex; class QLineEdit; class ProvidersModel; class QComboBox; class KDEVPLATFORMDOCUMENTATION_EXPORT DocumentationView : public QWidget { Q_OBJECT public: DocumentationView(QWidget* parent, ProvidersModel* m); - void showDocumentation(const KDevelop::IDocumentation::Ptr& doc); - public: QList contextMenuActions() const; public Q_SLOTS: void initialize(); - void addHistory(const KDevelop::IDocumentation::Ptr& doc); + void showDocumentation(const KDevelop::IDocumentation::Ptr& doc); void emptyHistory(); void browseForward(); void browseBack(); void changedSelection(const QModelIndex& idx); void changedProvider(int); void showHome(); private: void setupActions(); void updateView(); void returnPressed(); QAction* mForward; QAction* mBack; QAction* mHomeAction; QAction* mSeparatorBeforeFind; QAction* mFind; QLineEdit* mIdentifiers; QList< KDevelop::IDocumentation::Ptr > mHistory; QList< KDevelop::IDocumentation::Ptr >::iterator mCurrent; QComboBox* mProviders; ProvidersModel* mProvidersModel; KDevelop::DocumentationFindWidget* mFindDoc; }; class KDEVPLATFORMDOCUMENTATION_EXPORT ProvidersModel : public QAbstractListModel { Q_OBJECT public: explicit ProvidersModel(QObject* parent = nullptr); QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& idx = QModelIndex()) const override; QList providers(); KDevelop::IDocumentationProvider* provider(int pos) const; int rowForProvider(KDevelop::IDocumentationProvider* provider); public Q_SLOTS: void unloaded(KDevelop::IPlugin* p); void loaded(KDevelop::IPlugin* p); void reloadProviders(); private: void addProvider(KDevelop::IDocumentationProvider* provider); void removeProvider(KDevelop::IDocumentationProvider* provider); QList mProviders; Q_SIGNALS: void providersChanged(); }; #endif // KDEVPLATFORM_DOCUMENTATIONVIEW_H diff --git a/kdevplatform/interfaces/idocumentationprovider.h b/kdevplatform/interfaces/idocumentationprovider.h index 165abf092c..aa1e45e5b6 100644 --- a/kdevplatform/interfaces/idocumentationprovider.h +++ b/kdevplatform/interfaces/idocumentationprovider.h @@ -1,66 +1,63 @@ /* This file is part of KDevelop Copyright 2009 Aleix Pol 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 KDEVPLATFORM_IDOCUMENTATIONPROVIDER_H #define KDEVPLATFORM_IDOCUMENTATIONPROVIDER_H #include "interfacesexport.h" #include "idocumentation.h" class QIcon; class QModelIndex; class QAbstractItemModel; namespace KDevelop { class Declaration; class KDEVPLATFORMINTERFACES_EXPORT IDocumentationProvider { public: virtual ~IDocumentationProvider(); /** @returns an IDocument instance for the specified declaration or a null pointer if none could be found.*/ virtual IDocumentation::Ptr documentationForDeclaration(KDevelop::Declaration* declaration) const = 0; /** @returns an instance of an interface to create an index for all the items provided by this class. * Should have all items at the same level */ virtual QAbstractItemModel* indexModel() const = 0; /** @returns the documentation information related to the index in the model. */ virtual IDocumentation::Ptr documentationForIndex(const QModelIndex& idx) const = 0; /** @returns some icon associated to the provider. */ virtual QIcon icon() const = 0; /** @returns a name to identify the provider to the user. */ virtual QString name() const = 0; /** @returns a documentation item where we can show some home page information such a context index. */ virtual IDocumentation::Ptr homePage() const = 0; - -Q_SIGNALS: - virtual void addHistory(const KDevelop::IDocumentation::Ptr& doc) = 0; }; } Q_DECLARE_INTERFACE( KDevelop::IDocumentationProvider, "org.kdevelop.IDocumentationProvider") #endif diff --git a/plugins/cmake/cmakedocumentation.h b/plugins/cmake/cmakedocumentation.h index 8b7ceb435b..6e66d836c7 100644 --- a/plugins/cmake/cmakedocumentation.h +++ b/plugins/cmake/cmakedocumentation.h @@ -1,67 +1,64 @@ /* KDevelop CMake Support * * Copyright 2009 Aleix Pol * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef CMAKEDOCUMENTATION_H #define CMAKEDOCUMENTATION_H #include #include #include #include #include "icmakedocumentation.h" namespace KDevelop { class Declaration; } class CMakeManager; class QUrl; class CMakeCommandsContents; class KDescendantsProxyModel; class CMakeDocumentation : public KDevelop::IPlugin, public ICMakeDocumentation { Q_OBJECT Q_INTERFACES( ICMakeDocumentation ) Q_INTERFACES( KDevelop::IDocumentationProvider ) public: explicit CMakeDocumentation( QObject* parent = nullptr, const QVariantList& args = QVariantList() ); KDevelop::IDocumentation::Ptr description(const QString& identifier, const QUrl &file) const override; KDevelop::IDocumentation::Ptr documentationForDeclaration(KDevelop::Declaration* declaration) const override; QVector names(Type t) const override; CMakeCommandsContents* model() const { return m_index; } QAbstractItemModel* indexModel() const override; KDevelop::IDocumentation::Ptr documentationForIndex(const QModelIndex& idx) const override; QIcon icon() const override; QString name() const override; KDevelop::IDocumentation::Ptr homePage() const override; QString descriptionForIdentifier(const QString& identifier, Type t) const; - - Q_SIGNALS: - void addHistory(const KDevelop::IDocumentation::Ptr& doc) override; private: CMakeCommandsContents* m_index; KDescendantsProxyModel* m_flatIndex; }; #endif // CMAKEDOCUMENTATION_H diff --git a/plugins/manpage/manpageplugin.h b/plugins/manpage/manpageplugin.h index 8d20444ee8..4c70db8805 100644 --- a/plugins/manpage/manpageplugin.h +++ b/plugins/manpage/manpageplugin.h @@ -1,61 +1,58 @@ /* This file is part of KDevelop Copyright 2010 Yannick Motta Copyright 2010 Benjamin Port 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 MANPAGEPLUGIN_H #define MANPAGEPLUGIN_H #include "manpagemodel.h" #include #include #include #include class ManPageDocumentation; class ManPagePlugin : public KDevelop::IPlugin, public KDevelop::IDocumentationProvider { Q_OBJECT Q_INTERFACES( KDevelop::IDocumentationProvider ) public: explicit ManPagePlugin(QObject *parent, const QVariantList & args= QVariantList()); ~ManPagePlugin() override; KDevelop::IDocumentation::Ptr documentationForDeclaration (KDevelop::Declaration* dec) const override; QAbstractItemModel* indexModel() const override; KDevelop::IDocumentation::Ptr documentationForIndex(const QModelIndex& index) const override; QIcon icon() const override; QString name() const override; KDevelop::IDocumentation::Ptr homePage() const override; void deleteProgressBar(); ManPageModel* model() const; QProgressBar* progressBar() const; -Q_SIGNALS: - void addHistory(const KDevelop::IDocumentation::Ptr& doc) override; - private: KDevelop::IDocumentation::Ptr documentationForIdentifier(const QString& identifier) const; ManPageModel *m_model; }; #endif // MANPAGEPLUGIN_H diff --git a/plugins/qthelp/qthelpdocumentation.cpp b/plugins/qthelp/qthelpdocumentation.cpp index 63aec40c99..16bfdbc8c3 100644 --- a/plugins/qthelp/qthelpdocumentation.cpp +++ b/plugins/qthelp/qthelpdocumentation.cpp @@ -1,349 +1,347 @@ /* This file is part of KDevelop Copyright 2009 Aleix Pol Copyright 2009 David Nolden Copyright 2010 Benjamin Port 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 "qthelpdocumentation.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "qthelpnetwork.h" #include "qthelpproviderabstract.h" using namespace KDevelop; namespace { #if QT_VERSION >= 0x050500 int indexOf(const QString& str, const QRegularExpression& re, int from, QRegularExpressionMatch* rmatch) { return str.indexOf(re, from, rmatch); } int lastIndexOf(const QString& str, const QRegularExpression& re, int from, QRegularExpressionMatch* rmatch) { return str.lastIndexOf(re, from, rmatch); } #else int indexOf(const QString& str, const QRegularExpression& re, int from, QRegularExpressionMatch* rmatch) { if (!re.isValid()) { qCWarning(QTHELP) << "QString::indexOf: invalid QRegularExpression object"; return -1; } QRegularExpressionMatch match = re.match(str, from); if (match.hasMatch()) { const int ret = match.capturedStart(); if (rmatch) *rmatch = qMove(match); return ret; } return -1; } int lastIndexOf(const QString &str, const QRegularExpression &re, int from, QRegularExpressionMatch *rmatch) { if (!re.isValid()) { qCWarning(QTHELP) << "QString::lastIndexOf: invalid QRegularExpression object"; return -1; } int endpos = (from < 0) ? (str.size() + from + 1) : (from + 1); QRegularExpressionMatchIterator iterator = re.globalMatch(str); int lastIndex = -1; while (iterator.hasNext()) { QRegularExpressionMatch match = iterator.next(); int start = match.capturedStart(); if (start < endpos) { lastIndex = start; if (rmatch) *rmatch = qMove(match); } else { break; } } return lastIndex; } #endif } QtHelpProviderAbstract* QtHelpDocumentation::s_provider=nullptr; QtHelpDocumentation::QtHelpDocumentation(const QString& name, const QMap& info) : m_provider(s_provider), m_name(name), m_info(info), m_current(info.constBegin()), lastView(nullptr) {} QtHelpDocumentation::QtHelpDocumentation(const QString& name, const QMap& info, const QString& key) : m_provider(s_provider), m_name(name), m_info(info), m_current(m_info.find(key)), lastView(nullptr) { Q_ASSERT(m_current!=m_info.constEnd()); } QtHelpDocumentation::~QtHelpDocumentation() { delete m_lastStyleSheet.data(); } QString QtHelpDocumentation::description() const { const QUrl url(m_current.value()); //Extract a short description from the html data const QString dataString = QString::fromLatin1(m_provider->engine()->fileData(url)); ///@todo encoding const QString fragment = url.fragment(); const QString p = QStringLiteral("((\\\")|(\\\'))"); const QString optionalSpace = QStringLiteral(" *"); const QString exp = QString(QStringLiteral("< a name = ") + p + fragment + p + QStringLiteral(" > < / a >")).replace(' ', optionalSpace); const QRegularExpression findFragment(exp); QRegularExpressionMatch findFragmentMatch; int pos = indexOf(dataString, findFragment, 0, &findFragmentMatch); if(fragment.isEmpty()) { pos = 0; } else { //Check if there is a title opening-tag right before the fragment, and if yes add it, so we have a nicely formatted caption const QString titleRegExp = QStringLiteral("< h\\d class = \".*\" >").replace(' ', optionalSpace); const QRegularExpression findTitle(titleRegExp); const QRegularExpressionMatch match = findTitle.match(dataString, pos); const int titleStart = match.capturedStart(); const int titleEnd = titleStart + match.capturedEnd(); if(titleStart != -1) { const QStringRef between = dataString.midRef(titleEnd, pos-titleEnd).trimmed(); if(between.isEmpty()) pos = titleStart; } } if(pos != -1) { const QString exp = QString(QStringLiteral("< a name = ") + p + QStringLiteral("((\\S)*)") + p + QStringLiteral(" > < / a >")).replace(' ', optionalSpace); const QRegularExpression nextFragmentExpression(exp); int endPos = dataString.indexOf(nextFragmentExpression, pos+(fragment.size() ? findFragmentMatch.capturedLength() : 0)); if(endPos == -1) { endPos = dataString.size(); } { //Find the end of the last paragraph or newline, so we don't add prefixes of the following fragment const QString newLineRegExp = QStringLiteral ("< br / > | < / p >").replace(' ', optionalSpace); const QRegularExpression lastNewLine(newLineRegExp); QRegularExpressionMatch match; const int newEnd = lastIndexOf(dataString, lastNewLine, endPos, &match); if(match.isValid() && newEnd > pos) endPos = newEnd + match.capturedLength(); } { //Find the title, and start from there const QString titleRegExp = QStringLiteral("< h\\d class = \"title\" >").replace(' ', optionalSpace); const QRegularExpression findTitle(titleRegExp); const QRegularExpressionMatch match = findTitle.match(dataString); if (match.isValid()) pos = qBound(pos, match.capturedStart(), endPos); } QString thisFragment = dataString.mid(pos, endPos - pos); { //Completely remove the first large header found, since we don't need a header const QString headerRegExp = QStringLiteral("< h\\d.*>.*?< / h\\d >").replace(' ', optionalSpace); const QRegularExpression findHeader(headerRegExp); const QRegularExpressionMatch match = findHeader.match(thisFragment); if(match.isValid()) { thisFragment.remove(match.capturedStart(), match.capturedLength()); } } { //Replace all gigantic header-font sizes with { const QString sizeRegExp = QStringLiteral("< h\\d ").replace(' ', optionalSpace); const QRegularExpression findSize(sizeRegExp); thisFragment.replace(findSize, QStringLiteral("").replace(' ', optionalSpace); const QRegularExpression closeSize(sizeCloseRegExp); thisFragment.replace(closeSize, QStringLiteral("
")); } } { //Replace paragraphs by newlines const QString begin = QStringLiteral("< p >").replace(' ', optionalSpace); const QRegularExpression findBegin(begin); thisFragment.replace(findBegin, {}); const QString end = QStringLiteral("< /p >").replace(' ', optionalSpace); const QRegularExpression findEnd(end); thisFragment.replace(findEnd, QStringLiteral("
")); } { //Remove links, because they won't work const QString link = QString(QStringLiteral("< a href = ") + p + QStringLiteral(".*?") + p).replace(' ', optionalSpace); const QRegularExpression exp(link, QRegularExpression::CaseInsensitiveOption); thisFragment.replace(exp, QStringLiteral("open(); QTextStream ts(file); ts << "html { background: white !important; }\n"; if (url.scheme() == QLatin1String("qthelp") && url.host().startsWith(QLatin1String("com.trolltech.qt."))) { ts << ".content .toc + .title + p { clear:left; }\n" << "#qtdocheader .qtref { position: absolute !important; top: 5px !important; right: 0 !important; }\n"; } file->close(); view->setOverrideCss(QUrl::fromLocalFile(file->fileName())); delete m_lastStyleSheet.data(); m_lastStyleSheet = file; } QWidget* QtHelpDocumentation::documentationWidget(DocumentationFindWidget* findWidget, QWidget* parent) { if(m_info.isEmpty()) { //QtHelp sometimes has empty info maps. e.g. availableaudioeffects i 4.5.2 return new QLabel(i18n("Could not find any documentation for '%1'", m_name), parent); } else { StandardDocumentationView* view = new StandardDocumentationView(findWidget, parent); view->initZoom(m_provider->name()); view->setDelegateLinks(true); view->setNetworkAccessManager(m_provider->networkAccess()); view->setContextMenuPolicy(Qt::CustomContextMenu); QObject::connect(view, &StandardDocumentationView::linkClicked, this, &QtHelpDocumentation::jumpedTo); connect(view, &StandardDocumentationView::customContextMenuRequested, this, &QtHelpDocumentation::viewContextMenuRequested); setUserStyleSheet(view, m_current.value()); view->load(m_current.value()); lastView = view; return view; } } void QtHelpDocumentation::viewContextMenuRequested(const QPoint& pos) { StandardDocumentationView* view = qobject_cast(sender()); if (!view) return; auto menu = view->createStandardContextMenu(); if (m_info.count() > 1) { if (!menu->isEmpty()) { menu->addSeparator(); } QActionGroup* actionGroup = new QActionGroup(menu); foreach(const QString& name, m_info.keys()) { QtHelpAlternativeLink* act=new QtHelpAlternativeLink(name, this, actionGroup); act->setCheckable(true); act->setChecked(name==m_current.key()); menu->addAction(act); } } menu->setAttribute(Qt::WA_DeleteOnClose); menu->exec(view->mapToGlobal(pos)); } void QtHelpDocumentation::jumpedTo(const QUrl& newUrl) { Q_ASSERT(lastView); m_provider->jumpedTo(newUrl); - setUserStyleSheet(lastView, newUrl); - lastView->load(newUrl); } IDocumentationProvider* QtHelpDocumentation::provider() const { return m_provider; } QtHelpAlternativeLink::QtHelpAlternativeLink(const QString& name, const QtHelpDocumentation* doc, QObject* parent) : QAction(name, parent), mDoc(doc), mName(name) { connect(this, &QtHelpAlternativeLink::triggered, this, &QtHelpAlternativeLink::showUrl); } void QtHelpAlternativeLink::showUrl() { IDocumentation::Ptr newDoc(new QtHelpDocumentation(mName, mDoc->info(), mName)); ICore::self()->documentationController()->showDocumentation(newDoc); } HomeDocumentation::HomeDocumentation() : m_provider(QtHelpDocumentation::s_provider) { } QWidget* HomeDocumentation::documentationWidget(DocumentationFindWidget*, QWidget* parent) { QTreeView* w=new QTreeView(parent); w->header()->setVisible(false); w->setModel(m_provider->engine()->contentModel()); connect(w, &QTreeView::clicked, this, &HomeDocumentation::clicked); return w; } void HomeDocumentation::clicked(const QModelIndex& idx) { QHelpContentModel* model = m_provider->engine()->contentModel(); QHelpContentItem* it=model->contentItemAt(idx); QMap info; info.insert(it->title(), it->url()); IDocumentation::Ptr newDoc(new QtHelpDocumentation(it->title(), info)); ICore::self()->documentationController()->showDocumentation(newDoc); } QString HomeDocumentation::name() const { return i18n("QtHelp Home Page"); } IDocumentationProvider* HomeDocumentation::provider() const { return m_provider; } diff --git a/plugins/qthelp/qthelpproviderabstract.cpp b/plugins/qthelp/qthelpproviderabstract.cpp index 014d1c13cc..8a2fab25ce 100644 --- a/plugins/qthelp/qthelpproviderabstract.cpp +++ b/plugins/qthelp/qthelpproviderabstract.cpp @@ -1,117 +1,120 @@ /* This file is part of KDevelop Copyright 2009 Aleix Pol Copyright 2009 David Nolden Copyright 2010 Benjamin Port 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 "qthelpprovider.h" #include #include #include +#include +#include + #include #include #include #include #include "qthelpnetwork.h" #include "qthelpdocumentation.h" #include "debug.h" using namespace KDevelop; QtHelpProviderAbstract::QtHelpProviderAbstract(QObject *parent, const QString &collectionFileName, const QVariantList &args) : QObject(parent) , m_engine(QStandardPaths::writableLocation(QStandardPaths::DataLocation)+'/'+collectionFileName) , m_nam(new HelpNetworkAccessManager(&m_engine, this)) { Q_UNUSED(args); if( !m_engine.setupData() ) { qCWarning(QTHELP) << "Couldn't setup QtHelp Collection file"; } } QtHelpProviderAbstract::~QtHelpProviderAbstract() { } IDocumentation::Ptr QtHelpProviderAbstract::documentationForDeclaration(Declaration* dec) const { QtHelpDocumentation::s_provider = const_cast(this); if (dec) { static const IndexedString qmlJs("QML/JS"); QString id; { DUChainReadLocker lock; id = dec->qualifiedIdentifier().toString(RemoveTemplateInformation); if (dec->topContext()->parsingEnvironmentFile()->language() == qmlJs && !id.isEmpty()) id = QLatin1String("QML.") + id; } if (!id.isEmpty()) { QMap links = m_engine.linksForIdentifier(id); if(!links.isEmpty()) return IDocumentation::Ptr(new QtHelpDocumentation(id, links)); } } return {}; } QAbstractItemModel* QtHelpProviderAbstract::indexModel() const { QtHelpDocumentation::s_provider = const_cast(this); return m_engine.indexModel(); } IDocumentation::Ptr QtHelpProviderAbstract::documentationForIndex(const QModelIndex& idx) const { QtHelpDocumentation::s_provider = const_cast(this); QString name=idx.data(Qt::DisplayRole).toString(); return IDocumentation::Ptr(new QtHelpDocumentation(name, m_engine.indexModel()->linksForKeyword(name))); } void QtHelpProviderAbstract::jumpedTo(const QUrl& newUrl) { QtHelpDocumentation::s_provider = this; QMap info; info.insert(newUrl.toString(), newUrl); IDocumentation::Ptr doc(new QtHelpDocumentation(newUrl.toString(), info)); - emit addHistory(doc); + ICore::self()->documentationController()->showDocumentation(doc); } IDocumentation::Ptr QtHelpProviderAbstract::homePage() const { QtHelpDocumentation::s_provider = const_cast(this); return IDocumentation::Ptr(new HomeDocumentation); } bool QtHelpProviderAbstract::isValid() const { return !m_engine.registeredDocumentations().isEmpty(); } HelpNetworkAccessManager * QtHelpProviderAbstract::networkAccess() const { return m_nam; } diff --git a/plugins/qthelp/qthelpproviderabstract.h b/plugins/qthelp/qthelpproviderabstract.h index 1382f5ee44..185f2f62e4 100644 --- a/plugins/qthelp/qthelpproviderabstract.h +++ b/plugins/qthelp/qthelpproviderabstract.h @@ -1,61 +1,59 @@ /* This file is part of KDevelop Copyright 2009 Aleix Pol Copyright 2009 David Nolden Copyright 2010 Benjamin Port 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 QTHELPPROVIDERABSTRACT_H #define QTHELPPROVIDERABSTRACT_H #include #include #include #include class HelpNetworkAccessManager; class QtHelpProviderAbstract : public QObject, public KDevelop::IDocumentationProvider { Q_OBJECT Q_INTERFACES( KDevelop::IDocumentationProvider ) public: QtHelpProviderAbstract(QObject *parent, const QString &collectionFileName, const QVariantList & args); virtual ~QtHelpProviderAbstract(); KDevelop::IDocumentation::Ptr documentationForDeclaration (KDevelop::Declaration*) const override; KDevelop::IDocumentation::Ptr documentationForIndex(const QModelIndex& idx) const override; QAbstractItemModel* indexModel() const override; KDevelop::IDocumentation::Ptr homePage() const override; /// @return False in case we failed to load any documentation files, else true bool isValid() const; QHelpEngine* engine() { return &m_engine; } HelpNetworkAccessManager* networkAccess() const; public Q_SLOTS: void jumpedTo(const QUrl& newUrl); -Q_SIGNALS: - void addHistory(const KDevelop::IDocumentation::Ptr& doc) override; protected: QHelpEngine m_engine; HelpNetworkAccessManager* const m_nam; }; #endif // QTHELPPROVIDERABSTRACT_H