diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,11 +23,12 @@ find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS KIO + Crash I18n Config DBusAddons Notifications - Runner + DBusRunner WindowSystem Activities ) @@ -52,11 +53,11 @@ option(COPY_MESSAGING_HOST_FILE_HOME "Copy the native messaging hosts json file to user home dir" FALSE) add_feature_info(COPY_MESSAGING_HOST_FILE_HOME ${COPY_MESSAGING_HOST_FILE_HOME} "Enable this option to copy the native messaging hosts json file to home dir if you install plasma-browser-integration to custom prefix (non-/usr)") -set(MOZILLA_DIR "${CMAKE_INSTALL_PREFIX}/lib/mozilla" CACHE STRING "Mozilla directory") +set(MOZILLA_DIR "${CMAKE_INSTALL_PREFIX}/${KDE_INSTALL_LIBDIR}/mozilla" CACHE STRING "Mozilla directory") add_feature_info(MOZILLA_DIR On "Mozilla directory is '${MOZILLA_DIR}'") add_subdirectory(host) -add_subdirectory(tabsrunner) +#add_subdirectory(tabsrunner) add_subdirectory(reminder) if(NOT DEFINED CHROME_EXTENSION_ID) @@ -94,3 +95,9 @@ # TODO firefox feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) + +install( + FILES plasma-runner-browsertabs.desktop + DESTINATION ${KDE_INSTALL_KSERVICES5DIR} +) + diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -9,9 +9,6 @@ tabsrunnerplugin.cpp ) -qt5_add_dbus_adaptor(HOST_SOURCES ../dbus/org.kde.plasma.browser_integration.TabsRunner.xml tabsrunnerplugin.h TabsRunnerPlugin) -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) qt5_add_dbus_adaptor(HOST_SOURCES ../dbus/org.mpris.MediaPlayer2.Player.xml mprisplugin.h MPrisPlugin mprisplayer MPrisPlayer) @@ -26,6 +23,8 @@ KF5::Notifications KF5::WindowSystem KF5::Activities + KF5::DBusRunner + KF5::Crash ) install(TARGETS plasma-browser-integration-host ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/host/settings.cpp b/host/settings.cpp --- a/host/settings.cpp +++ b/host/settings.cpp @@ -73,9 +73,6 @@ Settings::Settings() : AbstractBrowserPlugin(QStringLiteral("settings"), 1, nullptr) { - new SettingsAdaptor(this); - QDBusConnection::sessionBus().registerObject(QStringLiteral("/Settings"), this); - } Settings &Settings::self() diff --git a/host/tabsrunnerplugin.h b/host/tabsrunnerplugin.h --- a/host/tabsrunnerplugin.h +++ b/host/tabsrunnerplugin.h @@ -25,25 +25,26 @@ #include "abstractbrowserplugin.h" -#include -#include +#include +#include -class TabsRunnerPlugin : public AbstractBrowserPlugin, protected QDBusContext +class TabsRunnerAdaptor; + +class TabsRunnerPlugin : public AbstractBrowserPlugin { Q_OBJECT public: explicit TabsRunnerPlugin(QObject *parent); + ~TabsRunnerPlugin() override; bool onLoad() override; bool onUnload() override; void handleData(const QString &event, const QJsonObject &data) override; - // dbus-exported - QList> GetTabs(); - void Activate(int tabId); - void SetMuted(int tabId, bool muted); + void refreshTabs(); + void activate(int tabId); + void setMuted(int tabId, bool muted); private: - QDBusMessage m_tabsReplyMessage; - + QScopedPointer m_adaptor; }; diff --git a/host/tabsrunnerplugin.cpp b/host/tabsrunnerplugin.cpp --- a/host/tabsrunnerplugin.cpp +++ b/host/tabsrunnerplugin.cpp @@ -24,68 +24,223 @@ #include "tabsrunnerplugin.h" #include "connection.h" +#include "settings.h" -#include #include #include #include #include #include -#include "tabsrunneradaptor.h" +#include +#include +#include + +#include +#include +#include +#include +#include + +class TabsRunnerAdaptor: KDBusRunner::AbstractRunner +{ + Q_OBJECT +public: + TabsRunnerAdaptor(TabsRunnerPlugin *parent); + ~TabsRunnerAdaptor() override; + KDBusRunner::Actions actions() const override; + void handleMatchRequest(const KDBusRunner::MatchReply::Ptr &matchReply) override; + void run(const QString &matchId, const QString &actionId) override; + void setTabs(const QJsonArray &tabs); +private: + TabsRunnerPlugin *q; + KDBusRunner::MatchReply::Ptr m_currentQuery; +}; + +TabsRunnerAdaptor::TabsRunnerAdaptor(TabsRunnerPlugin *parent) + :KDBusRunner::AbstractRunner(QStringLiteral("/runner"), QString(), parent), + q(parent) +{ + setEnabled(true); +} + +TabsRunnerAdaptor::~TabsRunnerAdaptor() +{ +} + +KDBusRunner::Actions TabsRunnerAdaptor::actions() const +{ + return KDBusRunner::Actions({}); +} + +void TabsRunnerAdaptor::handleMatchRequest(const KDBusRunner::MatchReply::Ptr &matchReply) +{ + m_currentQuery = matchReply; + q->refreshTabs(); +} + +void TabsRunnerAdaptor::run(const QString &matchId, const QString &actionId) +{ + bool rc; + int idx = matchId.toInt(&rc); + if (!rc) { + return; + } + + if (actionId.isEmpty()) { + q->activate(idx); + } +} + +void TabsRunnerAdaptor::setTabs(const QJsonArray &tabs) +{ + if (m_currentQuery.isNull()) { + return; + } + for(const QJsonValue &tabValue: tabs) { + auto tab = tabValue.toObject(); + KDBusRunner::QueryMatch match; //DAVE maybe make this a QSharedDataPointer + + // add browser name or window name or so to it maybe? + const QString &text = tab.value(QStringLiteral("title")).toString(); + if (text.isEmpty()) { // shouldn't happen? + continue; + } + + // will be used to raise the tab eventually + int tabId = tab.value(QStringLiteral("id")).toInt(); + if (!tabId) { + continue; + } + + const QUrl url(tab.value(QStringLiteral("url")).toString()); + if (!url.isValid()) { + continue; + } + + const bool incognito = tab.value(QStringLiteral("incognito")).toBool(); + const bool audible = tab.value(QStringLiteral("audible")).toBool(); + + match.text = text; + match.id = QString::number(tabId); + + qreal relevance = 0; + + const QString term = m_currentQuery->query(); + + // someone was really busy here, typing the *exact* title or url :D + if (text.compare(term, Qt::CaseInsensitive) == 0 || url.toString().compare(term, Qt::CaseInsensitive) == 0) { + match.type = KDBusRunner::QueryMatch::ExactMatch; + relevance = 1; + } else { + match.type = KDBusRunner::QueryMatch::PossibleMatch; + + if (text.contains(term, Qt::CaseInsensitive)) { + relevance = 0.9; + if (text.startsWith(term, Qt::CaseInsensitive)) { + relevance += 0.1; + } + } else if (url.host().contains(term, Qt::CaseInsensitive)) { + relevance = 0.7; + if (url.host().startsWith(term, Qt::CaseInsensitive)) { + relevance += 0.1; + } + } else if (url.path().contains(term, Qt::CaseInsensitive)) { + relevance = 0.5; + if (url.path().startsWith(term, Qt::CaseInsensitive)) { + relevance += 0.1; + } + } + } + + if (!relevance) { + continue; + } + + match.relevance = relevance; + + QByteArray iconData; + + const QString favIconData = tab.value(QStringLiteral("favIconData")).toString(); + const int b64start = favIconData.indexOf(QLatin1Char(',')); + if (b64start != -1) { + const QByteArray b64 = favIconData.rightRef(favIconData.size() - b64start - 1).toLatin1(); + iconData = QByteArray::fromBase64(b64); + } + + QString iconName; + auto browser = Settings::self().environment(); + if (iconData.isNull()) { + if (incognito) { + iconName = QStringLiteral("face-smirk");// TODO QStringLiteral("incognito"); + } else if (browser == Settings::Environment::Chrome) { + iconName = QStringLiteral("google-chrome"); + } else if (browser == Settings::Environment::Chromium) { + iconName = QStringLiteral("chromium-browser"); + } else if (browser == Settings::Environment::Firefox) { + iconName = QStringLiteral("firefox"); + } else if (browser == Settings::Environment::Opera) { + iconName = QStringLiteral("opera"); + } else if (browser == Settings::Environment::Vivaldi) { + iconName = QStringLiteral("vivaldi"); + } + } + + if (audible) { + if (false) { + iconName = QStringLiteral("audio-volume-muted"); + } else { + iconName = QStringLiteral("audio-volume-high"); + } + } + + if (!iconName.isEmpty()) { + match.iconName = iconName; + } + if(!iconData.isNull()) { + match.properties[QStringLiteral("iconData")] = iconData; + } + m_currentQuery->addMatch(match); + } + m_currentQuery->submit(); + m_currentQuery.clear(); +} TabsRunnerPlugin::TabsRunnerPlugin(QObject* parent) : AbstractBrowserPlugin(QStringLiteral("tabsrunner"), 1, parent) { - new TabsRunnerAdaptor(this); +} + +TabsRunnerPlugin::~TabsRunnerPlugin() +{ } bool TabsRunnerPlugin::onLoad() { - return QDBusConnection::sessionBus().registerObject(QStringLiteral("/TabsRunner"), this); + m_adaptor.reset(new TabsRunnerAdaptor(this)); //or keep & user setEnabled in the Abstractrunner? + return true; } bool TabsRunnerPlugin::onUnload() { - QDBusConnection::sessionBus().unregisterObject(QStringLiteral("/TabsRunner")); + m_adaptor.reset(); return true; } -// FIXME We really should enforce some kind of security policy, so only e.g. plasmashell and krunner -// may access your tabs -QList TabsRunnerPlugin::GetTabs() +void TabsRunnerPlugin::refreshTabs() { - // already a get tabs request pending, abort it and then start anew - // TODO would be lovely to marshall that stuff somehow, ie. every GetTabs call gets - // its own reply instead of just aborting and serving the one who came last - // however, the TabsRunner blocks waiting for a reply, so in practise this isn't as urgent - if (m_tabsReplyMessage.type() != QDBusMessage::InvalidMessage) { - QDBusConnection::sessionBus().send( - m_tabsReplyMessage.createErrorReply( - QStringLiteral("org.kde.plasma.browser_integration.TabsRunner.Error.Cancelled"), - QStringLiteral("GetTabs got cancelled because a another request came in") - ) - ); - return {}; - } - - m_tabsReplyMessage = message(); - setDelayedReply(true); - sendData(QStringLiteral("getTabs")); - - return {}; } -void TabsRunnerPlugin::Activate(int tabId) +void TabsRunnerPlugin::activate(int tabId) { sendData(QStringLiteral("activate"), { {QStringLiteral("tabId"), tabId} }); } -void TabsRunnerPlugin::SetMuted(int tabId, bool muted) +void TabsRunnerPlugin::setMuted(int tabId, bool muted) { sendData(QStringLiteral("setMuted"), { {QStringLiteral("tabId"), tabId}, @@ -96,28 +251,12 @@ void TabsRunnerPlugin::handleData(const QString& event, const QJsonObject& json) { if (event == QLatin1String("gotTabs")) { - if (m_tabsReplyMessage.type() != QDBusMessage::InvalidMessage) { - + if (m_adaptor) { const QJsonArray &tabs = json.value(QStringLiteral("tabs")).toArray(); - - QList tabsReply; - tabsReply.reserve(tabs.count()); - - for (auto it = tabs.constBegin(), end = tabs.constEnd(); it != end; ++it) { - const QJsonObject &tab = it->toObject(); - - tabsReply.append(tab.toVariantHash()); - } - - QList reply; - reply.append(QVariant(tabsReply)); - - QDBusConnection::sessionBus().send( - // TODO why does it unwrap this? didn't we want a a(a{sv}) instead of a{sv}a{sv}a{sv}..? :/ - m_tabsReplyMessage.createReply(QList{tabsReply}) - ); - m_tabsReplyMessage = QDBusMessage(); + m_adaptor->setTabs(tabs); } } } +#include "tabsrunnerplugin.moc" + diff --git a/tabsrunner/CMakeLists.txt b/tabsrunner/CMakeLists.txt deleted file mode 100644 --- a/tabsrunner/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -add_definitions(-DTRANSLATION_DOMAIN=\"plasma_runner_browsertabs\") - -set(krunner_browsertabs_SRCS - tabsrunner.cpp -) - -add_library(krunner_browsertabs MODULE ${krunner_browsertabs_SRCS}) -target_link_libraries(krunner_browsertabs - Qt5::DBus - KF5::I18n - KF5::Runner -) - -install(TARGETS krunner_browsertabs DESTINATION ${KDE_INSTALL_PLUGINDIR} ) - -install(FILES plasma-runner-browsertabs.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) - diff --git a/tabsrunner/Messages.sh b/tabsrunner/Messages.sh deleted file mode 100755 --- a/tabsrunner/Messages.sh +++ /dev/null @@ -1,2 +0,0 @@ -#! /usr/bin/env bash -$XGETTEXT *.cpp -o $podir/plasma_runner_browsertabs.pot diff --git a/tabsrunner/plasma-runner-browsertabs.desktop b/tabsrunner/plasma-runner-browsertabs.desktop deleted file mode 100644 --- a/tabsrunner/plasma-runner-browsertabs.desktop +++ /dev/null @@ -1,83 +0,0 @@ -[Desktop Entry] -Name=Browser Tabs -Name[ar]=ألسنة المتصفّح -Name[ca]=Pestanyes del navegador -Name[ca@valencia]=Pestanyes del navegador -Name[cs]=Karty prohlížeče -Name[da]=Browser-faneblade -Name[de]=Browser-Unterfenster -Name[el]=Καρτέλες περιηγητή -Name[en_GB]=Browser Tabs -Name[es]=Pestañas del navegador -Name[eu]=Arakatzailearen fitxak -Name[fi]=Selainvälilehdet -Name[fr]=Onglets du navigateur -Name[gl]=Lapelas do navegador -Name[hu]=Böngészőlapok -Name[it]=Schede del browser -Name[ko]=브라우저 탭 -Name[nb]=Nettleserfaner -Name[nl]=Browsertabbladen -Name[nn]=Nettlesarfaner -Name[pl]=Karty przeglądarki -Name[pt]=Páginas do Navegador -Name[pt_BR]=Abas do navegador -Name[ru]=Вкладки браузера -Name[sk]=Karty prehliadača -Name[sl]=Zavihki brskalnika -Name[sr]=Језичци прегледача -Name[sr@ijekavian]=Језичци прегледача -Name[sr@ijekavianlatin]=Jezičci pregledača -Name[sr@latin]=Jezičci pregledača -Name[sv]=Webbläsarflikar -Name[tr]=Tarayıcı Sekmeleri -Name[uk]=Вкладки навігатора -Name[x-test]=xxBrowser Tabsxx -Name[zh_CN]=浏览器标签 -Name[zh_TW]=瀏覽器分頁 -Comment=Find and activate browser tabs -Comment[ar]=جِد ألسنة المتصفّح ونشّطها -Comment[ca]=Cerca i activa les pestanyes del navegador -Comment[ca@valencia]=Cerca i activa les pestanyes del navegador -Comment[cs]=Najít a aktivovat karty prohlížeče -Comment[de]=Browser-Unterfenster suchen und aktivieren -Comment[el]=Εύρεση και ενεργοποίηση καρτελών περιηγητή -Comment[en_GB]=Find and activate browser tabs -Comment[es]=Buscar y activar pestañas del navegador -Comment[eu]=Aurkitu eta aktibatu arakatzailearen fitxak -Comment[fi]=Etsi ja aktivoi selainvälilehtiä -Comment[fr]=Chercher et activer les onglets du navigateur -Comment[gl]=Atopar e activar lapelas do navegador -Comment[hu]=Böngészőlapok keresése és aktiválása -Comment[it]=Cerca e attiva le schede del browser -Comment[ko]=브라우저 탭을 찾고 활성화 -Comment[nb]=Finner og aktiverer nettleserfaner -Comment[nl]=Zoek en activeer browsertabbladen -Comment[nn]=Finn og aktiver nettlesarfaner -Comment[pl]=Znajdź i aktywuj karty przeglądarki -Comment[pt]=Procurar e activar páginas do navegador -Comment[pt_BR]=Encontre e ative as abas do navegador -Comment[ru]=Список вкладок браузера с возможностью переключения между ними -Comment[sk]=Nájsť a aktivovať karty prehliadača -Comment[sl]=Najdite in omogočite zavihke brskalnika -Comment[sr]=Нађите и активирајте језичке прегледача -Comment[sr@ijekavian]=Нађите и активирајте језичке прегледача -Comment[sr@ijekavianlatin]=Nađite i aktivirajte jezičke pregledača -Comment[sr@latin]=Nađite i aktivirajte jezičke pregledača -Comment[sv]=Sök efter och aktivera webbläsarflikar -Comment[tr]=Tarayıcı sekmelerini bul ve etkinleştir -Comment[uk]=Пошук і задіяння вкладок навігатора -Comment[x-test]=xxFind and activate browser tabsxx -Comment[zh_CN]=找到并激活浏览器标签 -Comment[zh_TW]=尋找並開啟瀏覽器分頁 -X-KDE-ServiceTypes=Plasma/Runner -Type=Service -Icon=internet-web-browser -X-KDE-Library=krunner_browsertabs -X-KDE-PluginInfo-Author=Kai Uwe Broulik -X-KDE-PluginInfo-Email=kde@privat.broulik.de -X-KDE-PluginInfo-Name=browsertabs -X-KDE-PluginInfo-Version=1.0 -X-KDE-PluginInfo-License=LGPL -X-KDE-PluginInfo-EnabledByDefault=true -X-Plasma-AdvertiseSingleRunnerQueryMode=true diff --git a/tabsrunner/tabsrunner.h b/tabsrunner/tabsrunner.h deleted file mode 100644 --- a/tabsrunner/tabsrunner.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2017 Kai Uwe Broulik - * - * 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 3 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, see . - */ - -#pragma once - -#include - -#include - -class TabsRunner : public Plasma::AbstractRunner -{ - Q_OBJECT - -public: - TabsRunner(QObject *parent, const QVariantList &args); - ~TabsRunner() override; - - void match(Plasma::RunnerContext &context) override; - void run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &action) override; - -protected slots: - QMimeData *mimeDataForMatch(const Plasma::QueryMatch &match) override; - QList actionsForMatch(const Plasma::QueryMatch &match) override; - -private: - static QDBusMessage createMessage(const QString &service, const QString &method); - - // accessed in match() - QHash m_serviceToBrowser; - -}; diff --git a/tabsrunner/tabsrunner.cpp b/tabsrunner/tabsrunner.cpp deleted file mode 100644 --- a/tabsrunner/tabsrunner.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright (C) 2017 Kai Uwe Broulik - * - * 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 3 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, see . - */ - -#include "tabsrunner.h" - -#include - -#include - -#include -#include -#include -#include - -#include - -static const QString s_muteTab = QStringLiteral("mute"); -static const QString s_unmuteTab = QStringLiteral("unmute"); - -TabsRunner::TabsRunner(QObject *parent, const QVariantList &args) - : Plasma::AbstractRunner(parent, args) -{ - Q_UNUSED(args) - - setObjectName(QStringLiteral("BrowserTabs")); - setPriority(AbstractRunner::HighestPriority); - - addSyntax(Plasma::RunnerSyntax(QStringLiteral(":q:"), i18n("Finds browser tabs whose title match :q:"))); - - // should we actually show the current state instead of what the button will do? - addAction(s_muteTab, QIcon::fromTheme(QStringLiteral("audio-volume-muted")), i18n("Mute Tab")); - addAction(s_unmuteTab, QIcon::fromTheme(QStringLiteral("audio-volume-high")), i18n("Unmute Tab")); - -} - -TabsRunner::~TabsRunner() = default; - -void TabsRunner::match(Plasma::RunnerContext &context) -{ - const QString &term = context.query(); - if (term.length() < 3 && !context.singleRunnerQueryMode()) { - return; - } - - // first look for all running hosts, there can be multiple browsers running - QDBusReply servicesReply = QDBusConnection::sessionBus().interface()->registeredServiceNames(); - QStringList services; - if (servicesReply.isValid()) { - services = servicesReply.value(); - } - - for (const QString &service: services) { - if (!service.startsWith(QLatin1String("org.kde.plasma.browser_integration"))) { - continue; - } - - QString browser = m_serviceToBrowser.value(service); - if (browser.isEmpty()) { // now ask what browser we're dealing with - // FIXME can we use our dbus xml for this? - QDBusMessage message = QDBusMessage::createMethodCall(service, - QStringLiteral("/Settings"), - QStringLiteral("org.freedesktop.DBus.Properties"), - QStringLiteral("Get")); - message.setArguments({ - QStringLiteral("org.kde.plasma.browser_integration.Settings"), - QStringLiteral("Environment") - }); - - QDBusMessage reply = QDBusConnection::sessionBus().call(message); - - if (reply.type() != QDBusMessage::ReplyMessage || reply.arguments().count() != 1) { - continue; - } - - // what a long tail of calls... - browser = reply.arguments().at(0).value().variant().toString(); - m_serviceToBrowser.insert(service, browser); - } - - QDBusMessage message = - QDBusMessage::createMethodCall(service, - QStringLiteral("/TabsRunner"), - QStringLiteral("org.kde.plasma.browser_integration.TabsRunner"), - QStringLiteral("GetTabs") - ); - - QDBusMessage reply = QDBusConnection::sessionBus().call(message); - - if (reply.type() != QDBusMessage::ReplyMessage || reply.arguments().length() != 1) { - continue; - } - - QList matches; - - auto arg = reply.arguments().at(0).value(); - auto tabvs = qdbus_cast>(arg); - - for (const QVariant &tabv : tabvs) - { - auto tab = qdbus_cast(tabv.value()); - - // add browser name or window name or so to it maybe? - const QString &text = tab.value(QStringLiteral("title")).toString(); - if (text.isEmpty()) { // shouldn't happen? - continue; - } - - // will be used to raise the tab eventually - int tabId = tab.value(QStringLiteral("id")).toInt(); - if (!tabId) { - continue; - } - - const QUrl url(tab.value(QStringLiteral("url")).toString()); - if (!url.isValid()) { - continue; - } - - const bool incognito = tab.value(QStringLiteral("incognito")).toBool(); - const bool audible = tab.value(QStringLiteral("audible")).toBool(); - - QVariantHash mutedInfo; - tab.value(QStringLiteral("mutedInfo")).value() >> mutedInfo; - - const bool muted = mutedInfo.value(QStringLiteral("muted")).toBool(); - - const QVariantHash tabData = { - {QStringLiteral("service"), service}, - {QStringLiteral("tabId"), tabId}, - {QStringLiteral("audible"), audible}, - {QStringLiteral("muted"), muted} - }; - - Plasma::QueryMatch match(this); - match.setText(text); - match.setData(tabData); - - qreal relevance = 0; - - // someone was really busy here, typing the *exact* title or url :D - if (text.compare(term, Qt::CaseInsensitive) == 0 || url.toString().compare(term, Qt::CaseInsensitive) == 0) { - match.setType(Plasma::QueryMatch::ExactMatch); - relevance = 1; - } else { - match.setType(Plasma::QueryMatch::PossibleMatch); - - if (text.contains(term, Qt::CaseInsensitive)) { - relevance = 0.9; - if (text.startsWith(term, Qt::CaseInsensitive)) { - relevance += 0.1; - } - } else if (url.host().contains(term, Qt::CaseInsensitive)) { - relevance = 0.7; - if (url.host().startsWith(term, Qt::CaseInsensitive)) { - relevance += 0.1; - } - } else if (url.path().contains(term, Qt::CaseInsensitive)) { - relevance = 0.5; - if (url.path().startsWith(term, Qt::CaseInsensitive)) { - relevance += 0.1; - } - } - } - - if (!relevance) { - continue; - } - - match.setRelevance(relevance); - - QString iconName; - QIcon icon; - - const QString favIconData = tab.value(QStringLiteral("favIconData")).toString(); - const int b64start = favIconData.indexOf(QLatin1Char(',')); - if (b64start != -1) { - QByteArray b64 = favIconData.rightRef(favIconData.size() - b64start - 1).toLatin1(); - QByteArray data = QByteArray::fromBase64(b64); - QPixmap pixmap; - if (pixmap.loadFromData(data)) - icon = QIcon(pixmap); - } - - if (icon.isNull()) { - if (incognito) { - iconName = QStringLiteral("face-smirk");// TODO QStringLiteral("incognito"); - } else if (browser == QLatin1String("chrome")) { - iconName = QStringLiteral("google-chrome"); - } else if (browser == QLatin1String("chromium")) { - iconName = QStringLiteral("chromium-browser"); - } else if (browser == QLatin1String("firefox")) { - iconName = QStringLiteral("firefox"); - } else if (browser == QLatin1String("opera")) { - iconName = QStringLiteral("opera"); - } else if (browser == QLatin1String("vivaldi")) { - iconName = QStringLiteral("vivaldi"); - } - } - - if (audible) { - if (muted) { - iconName = QStringLiteral("audio-volume-muted"); - } else { - iconName = QStringLiteral("audio-volume-high"); - } - } - - if (!iconName.isEmpty()) { - match.setIconName(iconName); - } else if(!icon.isNull()) { - match.setIcon(icon); - } - - matches << match; - } - - context.addMatches(matches); - } -} - -void TabsRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match) -{ - Q_UNUSED(context); - - const QVariantHash &tabData = match.data().toHash(); - - const QString &service = tabData.value(QStringLiteral("service")).toString(); - const int tabId = tabData.value(QStringLiteral("tabId")).toInt(); - - if (match.selectedAction() == action(s_unmuteTab)) { - QDBusMessage message = createMessage(service, QStringLiteral("SetMuted")); - message.setArguments({tabId, false}); - QDBusConnection::sessionBus().call(message); // asyncCall? - return; - } - - if (match.selectedAction() == action(s_muteTab)) { - QDBusMessage message = createMessage(service, QStringLiteral("SetMuted")); - message.setArguments({tabId, true}); - QDBusConnection::sessionBus().call(message); // asyncCall? - return; - } - - QDBusMessage message = createMessage(service, QStringLiteral("Activate")); - message.setArguments({tabId}); - QDBusConnection::sessionBus().call(message); // asyncCall? -} - -QDBusMessage TabsRunner::createMessage(const QString &service, const QString &method) -{ - return QDBusMessage::createMethodCall(service, - QStringLiteral("/TabsRunner"), - QStringLiteral("org.kde.plasma.browser_integration.TabsRunner"), - method); -} - -QMimeData *TabsRunner::mimeDataForMatch(const Plasma::QueryMatch &match) -{ - Q_UNUSED(match); - // TODO return tab url or maybe for firefox a magic "dragging tab off a window" mime? - return nullptr; -} - -QList TabsRunner::actionsForMatch(const Plasma::QueryMatch &match) -{ - QList actions; - - const QVariantHash &tabData = match.data().toHash(); - - const bool audible = tabData.value(QStringLiteral("audible")).toBool(); - const bool muted = tabData.value(QStringLiteral("muted")).toBool(); - - if (audible) { - if (muted) { - actions << action(s_unmuteTab); - } else { - actions << action(s_muteTab); - } - } - - return actions; -} - -K_EXPORT_PLASMA_RUNNER(browsertabs, TabsRunner) - -#include "tabsrunner.moc"