diff --git a/extension/_locales/en/messages.json b/extension/_locales/en/messages.json --- a/extension/_locales/en/messages.json +++ b/extension/_locales/en/messages.json @@ -50,6 +50,15 @@ "message": "Show downloads in notification area" }, + "options_plugin_bookmarksrunner_title": { + "description": "Title for Bookmarks KRunner plugin", + "message": "Find bookmarks in “Run Command” window" + }, + "options_plugin_bookmarksrunner_description": { + "description": "Description for Bookmarks KRunner plugin", + "message": "Make sure the “Bookmarks” module is enabled in Plasma Search settings." + }, + "options_plugin_tabsrunner_title": { "description": "Title for Browser Tabs KRunner plugin", "message": "Find browser tabs in “Run Command” window" diff --git a/extension/constants.js b/extension/constants.js --- a/extension/constants.js +++ b/extension/constants.js @@ -25,6 +25,9 @@ downloads: { enabled: true }, + bookmarksrunner: { + enabled: true + }, tabsrunner: { enabled: true }, diff --git a/extension/extension.js b/extension/extension.js --- a/extension/extension.js +++ b/extension/extension.js @@ -462,6 +462,27 @@ } }); +// Bookmarks Runner +// ------------------------------------------------------------------------ +// + +// only forward certain tab properties back to our host +var whitelistedBookmarkProperties = [ + "id", "url", "title" +]; + +addCallback("bookmarksrunner", "search", function (message) { + chrome.bookmarks.search(message.query, function (bookmarks) { + var filteredBookmarks = filterArrayObjects(bookmarks, whitelistedBookmarkProperties); + + port.postMessage({ + subsystem: "bookmarksrunner", + event: "gotBookmarks", + bookmarks: filteredBookmarks + }); + }); +}); + // Tabs Runner // ------------------------------------------------------------------------ // diff --git a/extension/manifest.json b/extension/manifest.json --- a/extension/manifest.json +++ b/extension/manifest.json @@ -47,6 +47,7 @@ "downloads.shelf", "tabs", "history", + "bookmarks", "", "contextMenus" diff --git a/extension/options.html b/extension/options.html --- a/extension/options.html +++ b/extension/options.html @@ -39,6 +39,12 @@
  • +

    I18N

    +
  • +
  • +

    I18N

    diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -10,11 +10,15 @@ kdeconnectplugin.cpp downloadplugin.cpp downloadjob.cpp + bookmarksrunnerplugin.cpp tabsrunnerplugin.cpp slcplugin.cpp ) qt5_add_dbus_adaptor(HOST_SOURCES ../dbus/org.kde.plasma.browser_integration.TabsRunner.xml tabsrunnerplugin.h TabsRunnerPlugin) + +qt5_add_dbus_adaptor(HOST_SOURCES "org.kde.krunner1.xml" bookmarksrunnerplugin.h BookmarksRunnerPlugin) + qt5_add_dbus_adaptor(HOST_SOURCES ../dbus/org.kde.plasma.browser_integration.Settings.xml settings.h Settings) qt5_add_dbus_adaptor(HOST_SOURCES ../dbus/org.mpris.MediaPlayer2.xml mprisplugin.h MPrisPlugin mprisroot MPrisRoot) @@ -31,8 +35,11 @@ KF5::Notifications KF5::WindowSystem KF5::Activities + KF5::Runner ) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) install(TARGETS plasma-browser-integration-host ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) + +install(FILES plasma-runner-bookmarks.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} RENAME plasma-runner-bookmarkspbi.desktop) #FIXME diff --git a/host/bookmarksrunnerplugin.h b/host/bookmarksrunnerplugin.h new file mode 100644 --- /dev/null +++ b/host/bookmarksrunnerplugin.h @@ -0,0 +1,55 @@ +/* + Copyright (C) 2018 by Kai Uwe Broulik + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#pragma once + +#include "abstractbrowserplugin.h" + +#include +#include + +#include "dbusutils_p.h" + +class BookmarksRunnerPlugin : public AbstractBrowserPlugin, protected QDBusContext +{ + Q_OBJECT + +public: + BookmarksRunnerPlugin(QObject *parent); + + bool onLoad() override; + bool onUnload() override; + void handleData(const QString &event, const QJsonObject &data) override; + + RemoteActions Actions(); + RemoteMatches Match(const QString &searchTerm); + void Run(const QString &id, const QString &actionId); + +private: + void performMatch(); + RemoteMatches matchInternal(const QString &searchTerm, const QString &type, + const QString &category); + + QDBusMessage m_lastRequest; + QString m_searchTerm; + +}; diff --git a/host/bookmarksrunnerplugin.cpp b/host/bookmarksrunnerplugin.cpp new file mode 100644 --- /dev/null +++ b/host/bookmarksrunnerplugin.cpp @@ -0,0 +1,133 @@ +/* + Copyright (C) 2018 by Kai Uwe Broulik + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "bookmarksrunnerplugin.h" + +#include "connection.h" + +#include + +#include + +#include "dbusutils_p.h" +#include "krunner1adaptor.h" + +BookmarksRunnerPlugin::BookmarksRunnerPlugin(QObject *parent) + : AbstractBrowserPlugin(QStringLiteral("bookmarksrunner"), 1, parent) +{ + new Krunner1Adaptor(this); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); +} + +bool BookmarksRunnerPlugin::onLoad() +{ + return QDBusConnection::sessionBus().registerObject("/BookmarksRunner", this); +} + +bool BookmarksRunnerPlugin::onUnload() +{ + QDBusConnection::sessionBus().unregisterObject(QStringLiteral("/BookmarksRunner")); + return true; +} + +RemoteActions BookmarksRunnerPlugin::Actions() +{ + return {}; +} + +RemoteMatches BookmarksRunnerPlugin::Match(const QString &searchTerm) +{ + setDelayedReply(true); + + if (m_lastRequest.type() != QDBusMessage::InvalidMessage) { + QDBusConnection::sessionBus().send(m_lastRequest.createReply(QVariantList())); + } + + m_lastRequest = message(); + m_searchTerm = searchTerm; + + // TODO enforce minimum length? + sendData(QStringLiteral("search"), { + {QStringLiteral("query"), searchTerm} + }); + + return RemoteMatches(); +} + +void BookmarksRunnerPlugin::handleData(const QString &event, const QJsonObject &json) +{ + if (event != QLatin1String("gotBookmarks")) { + return; + } + + if (m_lastRequest.type() == QDBusMessage::InvalidMessage) { + return; + } + + RemoteMatches matches; + + const QJsonArray &bookmarks = json.value(QStringLiteral("bookmarks")).toArray(); + + qreal relevance = 0.75; // FIXME + + for (auto it = bookmarks.constBegin(), end = bookmarks.constEnd(); it != end; ++it) { + const QJsonObject &bookmark = it->toObject(); + + const QString urlString = bookmark.value(QStringLiteral("url")).toString(); + const QString title = bookmark.value(QStringLiteral("title")).toString(); + // folders don't have a URL + if (urlString.isEmpty()) { + continue; + } + + RemoteMatch match; + match.id = urlString; + match.text = title; + // TODO bookmarks API doesn't expose bookmarks, at least show browser icon instead? + match.iconName = QStringLiteral("text-html"); + match.relevance = relevance; + match.type = Plasma::QueryMatch::PossibleMatch; // TODO when's an exact match? + match.properties = QVariantMap{ + {QStringLiteral("urls"), QStringList(urlString)}, + {QStringLiteral("subtext"), QUrl(urlString).toDisplayString()} + }; + + relevance -= 0.05; + + // TODO should we limit the number of results? I often get like 70 of them + matches << match; + } + + QDBusConnection::sessionBus().send(m_lastRequest.createReply(QVariant::fromValue(matches))); + m_lastRequest = QDBusMessage(); +} + +void BookmarksRunnerPlugin::Run(const QString &id, const QString &actionId) +{ + if (actionId.isEmpty()) { + // didn't want to pull in KIOWidgets just for KRun + QDesktopServices::openUrl(QUrl(id)); + } +} diff --git a/host/dbusutils_p.h b/host/dbusutils_p.h new file mode 100644 --- /dev/null +++ b/host/dbusutils_p.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include +#include +#include + +struct RemoteMatch +{ + //sssuda{sv} + QString id; + QString text; + QString iconName; + Plasma::QueryMatch::Type type = Plasma::QueryMatch::NoMatch; + qreal relevance = 0; + QVariantMap properties; +}; + +typedef QList RemoteMatches; + +struct RemoteAction +{ + QString id; + QString text; + QString iconName; +}; + +typedef QList RemoteActions; + +inline QDBusArgument &operator<< (QDBusArgument &argument, const RemoteMatch &match) { + argument.beginStructure(); + argument << match.id; + argument << match.text; + argument << match.iconName; + argument << match.type; + argument << match.relevance; + argument << match.properties; + argument.endStructure(); + return argument; +} + +inline const QDBusArgument &operator>>(const QDBusArgument &argument, RemoteMatch &match) { + argument.beginStructure(); + argument >> match.id; + argument >> match.text; + argument >> match.iconName; + uint type; + argument >> type; + match.type = (Plasma::QueryMatch::Type)type; + argument >> match.relevance; + argument >> match.properties; + argument.endStructure(); + + return argument; +} + +inline QDBusArgument &operator<< (QDBusArgument &argument, const RemoteAction &action) +{ + argument.beginStructure(); + argument << action.id; + argument << action.text; + argument << action.iconName; + argument.endStructure(); + return argument; +} + +inline const QDBusArgument &operator>>(const QDBusArgument &argument, RemoteAction &action) { + argument.beginStructure(); + argument >> action.id; + argument >> action.text; + argument >> action.iconName; + argument.endStructure(); + return argument; +} + +Q_DECLARE_METATYPE(RemoteMatch); +Q_DECLARE_METATYPE(RemoteMatches); +Q_DECLARE_METATYPE(RemoteAction); +Q_DECLARE_METATYPE(RemoteActions); + diff --git a/host/main.cpp b/host/main.cpp --- a/host/main.cpp +++ b/host/main.cpp @@ -33,6 +33,7 @@ #include "incognitoplugin.h" #include "kdeconnectplugin.h" #include "downloadplugin.h" +#include "bookmarksrunnerplugin.h" #include "tabsrunnerplugin.h" #include "mprisplugin.h" #include "slcplugin.h" @@ -81,6 +82,7 @@ m_plugins << new IncognitoPlugin(&a); m_plugins << new KDEConnectPlugin(&a); m_plugins << new DownloadPlugin(&a); + m_plugins << new BookmarksRunnerPlugin(&a); m_plugins << new TabsRunnerPlugin(&a); m_plugins << new MPrisPlugin(&a); m_plugins << new SlcPlugin(&a); diff --git a/host/org.kde.krunner1.xml b/host/org.kde.krunner1.xml new file mode 100644 --- /dev/null +++ b/host/org.kde.krunner1.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/host/plasma-runner-bookmarks.desktop b/host/plasma-runner-bookmarks.desktop new file mode 100644 --- /dev/null +++ b/host/plasma-runner-bookmarks.desktop @@ -0,0 +1,17 @@ +[Desktop Entry] +Name=Bookmarks (p-b-i) +Comment=Find and open bookmarks +X-KDE-ServiceTypes=Plasma/Runner +Type=Service +Icon=bookmarks +X-KDE-PluginInfo-Author=Kai Uwe Broulik +X-KDE-PluginInfo-Email=kde@privat.broulik.de +X-KDE-PluginInfo-Name=bookmarkspbi +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-License=LGPL +X-Plasma-AdvertiseSingleRunnerQueryMode=true +X-KDE-PluginInfo-EnabledByDefault=true +X-Plasma-API=DBus +# FIXME there can be multiple +X-Plasma-DBusRunner-Service=org.kde.plasma.browser_integration +X-Plasma-DBusRunner-Path=/BookmarksRunner