File indexing completed on 2024-05-05 05:35:45
0001 /* 0002 SPDX-FileCopyrightText: 2017 Kai Uwe Broulik <kde@privat.broulik.de> 0003 SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org> 0004 0005 SPDX-License-Identifier: MIT 0006 */ 0007 0008 #include "tabsrunnerplugin.h" 0009 0010 #include "connection.h" 0011 0012 #include <QDBusConnection> 0013 #include <QGuiApplication> 0014 #include <QHash> 0015 #include <QIcon> 0016 #include <QImage> 0017 #include <QJsonArray> 0018 #include <QJsonObject> 0019 #include <QVariant> 0020 0021 #include <KApplicationTrader> 0022 #include <KLocalizedString> 0023 0024 #include "settings.h" 0025 0026 static const auto s_actionIdMute = QLatin1String("MUTE"); 0027 static const auto s_actionIdUnmute = QLatin1String("UNMUTE"); 0028 0029 TabsRunnerPlugin::TabsRunnerPlugin(QObject *parent) 0030 : AbstractKRunnerPlugin(QStringLiteral("/TabsRunner"), QStringLiteral("tabsrunner"), 1, parent) 0031 { 0032 } 0033 0034 RemoteActions TabsRunnerPlugin::Actions() 0035 { 0036 RemoteAction muteAction{ 0037 s_actionIdMute, 0038 i18n("Mute Tab"), 0039 QStringLiteral("audio-volume-muted"), 0040 }; 0041 RemoteAction unmuteAction{ 0042 s_actionIdUnmute, 0043 i18n("Unmute Tab"), 0044 QStringLiteral("audio-volume-high"), 0045 }; 0046 0047 return {muteAction, unmuteAction}; 0048 } 0049 0050 RemoteMatches TabsRunnerPlugin::Match(const QString &searchTerm) 0051 { 0052 if (searchTerm.length() < 3) { 0053 sendErrorReply(QDBusError::InvalidArgs, QStringLiteral("Search term too short")); 0054 return {}; 0055 } 0056 0057 setDelayedReply(true); 0058 0059 const bool runQuery = m_requests.isEmpty(); 0060 0061 m_requests.insert(searchTerm, message()); 0062 0063 if (runQuery) { 0064 sendData(QStringLiteral("getTabs")); 0065 } 0066 0067 return {}; 0068 } 0069 0070 void TabsRunnerPlugin::Run(const QString &id, const QString &actionId) 0071 { 0072 bool ok = false; 0073 const int tabId = id.toInt(&ok); 0074 if (!ok || tabId < 0) { 0075 sendErrorReply(QDBusError::InvalidArgs, QStringLiteral("Invalid tab ID")); 0076 return; 0077 } 0078 0079 if (actionId.isEmpty()) { 0080 sendData(QStringLiteral("activate"), 0081 { 0082 {QStringLiteral("tabId"), tabId}, 0083 }); 0084 return; 0085 } 0086 0087 if (actionId == s_actionIdMute || actionId == s_actionIdUnmute) { 0088 sendData(QStringLiteral("setMuted"), 0089 { 0090 {QStringLiteral("tabId"), tabId}, 0091 {QStringLiteral("muted"), actionId == s_actionIdMute}, 0092 }); 0093 return; 0094 } 0095 0096 sendErrorReply(QDBusError::InvalidArgs, QStringLiteral("Unknown action ID")); 0097 } 0098 0099 void TabsRunnerPlugin::handleData(const QString &event, const QJsonObject &json) 0100 { 0101 if (event == QLatin1String("gotTabs")) { 0102 const QJsonArray &tabs = json.value(QStringLiteral("tabs")).toArray(); 0103 0104 for (auto it = m_requests.constBegin(), end = m_requests.constEnd(); it != end; ++it) { 0105 const QString query = it.key(); 0106 const QDBusMessage request = it.value(); 0107 0108 RemoteMatches matches; 0109 0110 for (auto jt = tabs.constBegin(), jend = tabs.constEnd(); jt != jend; ++jt) { 0111 const QJsonObject tab = jt->toObject(); 0112 0113 RemoteMatch match; 0114 0115 const int tabId = tab.value(QStringLiteral("id")).toInt(); 0116 const QString text = tab.value(QStringLiteral("title")).toString(); 0117 const QUrl url(tab.value(QStringLiteral("url")).toString()); 0118 0119 QStringList actions; 0120 0121 qreal relevance = 0; 0122 // someone was really busy here, typing the *exact* title or url :D 0123 if (text.compare(query, Qt::CaseInsensitive) == 0 || url.toString().compare(query, Qt::CaseInsensitive) == 0) { 0124 match.categoryRelevance = qToUnderlying(KRunner::QueryMatch::CategoryRelevance::Highest); 0125 relevance = 1; 0126 } else { 0127 match.categoryRelevance = qToUnderlying(KRunner::QueryMatch::CategoryRelevance::High); 0128 0129 if (KApplicationTrader::isSubsequence(query, text, Qt::CaseInsensitive)) { 0130 relevance = 0.85; 0131 if (text.contains(query, Qt::CaseInsensitive)) { 0132 relevance += 0.05; 0133 if (text.startsWith(query, Qt::CaseInsensitive)) { 0134 relevance += 0.05; 0135 } 0136 } 0137 } else if (url.host().contains(query, Qt::CaseInsensitive)) { 0138 relevance = 0.7; 0139 if (url.host().startsWith(query, Qt::CaseInsensitive)) { 0140 relevance += 0.1; 0141 } 0142 } else if (url.path().contains(query, Qt::CaseInsensitive)) { 0143 relevance = 0.5; 0144 if (url.path().startsWith(query, Qt::CaseInsensitive)) { 0145 relevance += 0.1; 0146 } 0147 } 0148 } 0149 0150 if (!relevance) { 0151 continue; 0152 } 0153 0154 match.id = QString::number(tabId); 0155 match.text = text; 0156 match.properties.insert(QStringLiteral("subtext"), url.toDisplayString()); 0157 match.relevance = relevance; 0158 0159 QUrl urlWithoutPassword = url; 0160 urlWithoutPassword.setPassword({}); 0161 match.properties.insert(QStringLiteral("urls"), QUrl::toStringList(QList<QUrl>{urlWithoutPassword})); 0162 0163 const bool audible = tab.value(QStringLiteral("audible")).toBool(); 0164 0165 const QJsonObject mutedInfo = tab.value(QStringLiteral("mutedInfo")).toObject(); 0166 const bool muted = mutedInfo.value(QStringLiteral("muted")).toBool(); 0167 0168 if (audible) { 0169 if (muted) { 0170 match.iconName = QStringLiteral("audio-volume-muted"); 0171 actions.append(s_actionIdUnmute); 0172 } else { 0173 match.iconName = QStringLiteral("audio-volume-high"); 0174 actions.append(s_actionIdMute); 0175 } 0176 } else { 0177 match.iconName = qApp->windowIcon().name(); 0178 0179 const QImage favIcon = imageFromDataUrl(tab.value(QStringLiteral("favIconData")).toString()); 0180 if (!favIcon.isNull()) { 0181 const RemoteImage remoteImage = serializeImage(favIcon); 0182 match.properties.insert(QStringLiteral("icon-data"), QVariant::fromValue(remoteImage)); 0183 } 0184 } 0185 0186 // Has to always be present so it knows we handle actions ourself 0187 match.properties.insert(QStringLiteral("actions"), actions); 0188 0189 matches.append(match); 0190 } 0191 0192 QDBusConnection::sessionBus().send(request.createReply(QVariant::fromValue(matches))); 0193 } 0194 0195 m_requests.clear(); 0196 } 0197 } 0198 0199 #include "moc_tabsrunnerplugin.cpp"