File indexing completed on 2024-05-05 09:57:15
0001 /* 0002 SPDX-FileCopyrightText: 2006 Aaron Seigo <aseigo@kde.org> 0003 SPDX-FileCopyrightText: 2014 Vishesh Handa <vhanda@kde.org> 0004 SPDX-FileCopyrightText: 2016-2020 Harald Sitter <sitter@kde.org> 0005 SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de> 0006 SPDX-FileCopyrightText: 2022 Natalie Clarius <natalie_clarius@yahoo.de> 0007 0008 SPDX-License-Identifier: LGPL-2.0-only 0009 */ 0010 0011 #include "systemsettingsrunner.h" 0012 0013 #include <algorithm> 0014 0015 #include <QMimeData> 0016 #include <QUrl> 0017 #include <QUrlQuery> 0018 0019 #include <PlasmaActivities/ResourceInstance> 0020 #include <KIO/CommandLauncherJob> 0021 #include <KLocalizedString> 0022 #include <KNotificationJobUiDelegate> 0023 #include <KSycoca> 0024 0025 #include "../core/kcmmetadatahelpers.h" 0026 0027 K_PLUGIN_CLASS_WITH_JSON(SystemsettingsRunner, "systemsettingsrunner.json") 0028 0029 SystemsettingsRunner::SystemsettingsRunner(QObject *parent, const KPluginMetaData &metaData) 0030 : KRunner::AbstractRunner(parent, metaData) 0031 { 0032 addSyntax(QStringLiteral(":q:"), i18n("Finds system settings modules whose names or descriptions match :q:")); 0033 connect(this, &SystemsettingsRunner::prepare, this, [this]() { 0034 m_modules = findKCMsMetaData(MetaDataSource::All); 0035 }); 0036 connect(this, &SystemsettingsRunner::teardown, this, [this]() { 0037 m_modules.clear(); 0038 }); 0039 } 0040 0041 void SystemsettingsRunner::match(KRunner::RunnerContext &context) 0042 { 0043 QList<KRunner::QueryMatch> matches; 0044 const QString query = context.query(); 0045 const QStringList queryWords{query.split(QLatin1Char(' '))}; 0046 for (const KPluginMetaData &data : std::as_const(m_modules)) { 0047 qreal relevance = -1; 0048 const auto checkMatchAndRelevance = [&query, &relevance, &queryWords](const QString &value, qreal relevanceValue) { 0049 if (value.startsWith(query, Qt::CaseInsensitive)) { 0050 relevance = relevanceValue + 0.1; 0051 return true; 0052 } 0053 for (const QString &queryWord : queryWords) { 0054 if (relevance == -1 && queryWord.length() > 3 && value.contains(queryWord, Qt::CaseInsensitive)) { 0055 relevance = relevanceValue; 0056 return true; 0057 } 0058 } 0059 return false; 0060 }; 0061 0062 const QString name = data.name(); 0063 const QString description = data.description(); 0064 const QStringList keywords = data.value(QStringLiteral("X-KDE-Keywords")).split(QLatin1Char(',')); 0065 // check for matches and set relevance 0066 if (query.length() < 3) { 0067 if (name.startsWith(query, Qt::CaseInsensitive)) { 0068 relevance = 0.9; 0069 } else { 0070 continue; 0071 } 0072 } else if (checkMatchAndRelevance(name, 0.8)) { // name starts with query or contains any query word 0073 } else if (checkMatchAndRelevance(description, 0.5)) { // description starts with query or contains any query word 0074 } else if (std::any_of(keywords.begin(), keywords.end(), [&query](const QString &keyword) { 0075 return keyword.startsWith(query, Qt::CaseInsensitive); 0076 })) { 0077 if (keywords.contains(query, Qt::CaseInsensitive)) { // any of the keywords matches query 0078 relevance = 0.5; 0079 } else { // any of the keywords starts with query 0080 relevance = 0.2; 0081 } 0082 } else { // none of the properties matches 0083 continue; // skip this KCM 0084 } 0085 0086 KRunner::QueryMatch::CategoryRelevance categoryRelevance = KRunner::QueryMatch::CategoryRelevance::Low; 0087 if (name.compare(query, Qt::CaseInsensitive) == 0) { // name matches exactly 0088 categoryRelevance = KRunner::QueryMatch::CategoryRelevance::Highest; 0089 } else if (name.startsWith(query, Qt::CaseInsensitive) || description.startsWith(query, Qt::CaseInsensitive)) { // name or description matches as start 0090 categoryRelevance = KRunner::QueryMatch::CategoryRelevance::Moderate; 0091 } else if (keywords.contains(query, Qt::CaseInsensitive)) { // any of the keywords matches exactly 0092 categoryRelevance = KRunner::QueryMatch::CategoryRelevance::Moderate; 0093 } 0094 0095 KRunner::QueryMatch match(this); 0096 match.setText(name); 0097 match.setUrls({QUrl(QLatin1String("applications://") + data.pluginId())}); 0098 match.setSubtext(description); 0099 match.setIconName(data.iconName()); // If it is not set, KRunner will fall back to the runner's icon 0100 match.setId(data.pluginId()); // KRunner needs the id to adjust the relevance for often launched KCMs 0101 match.setData(QVariant::fromValue(data)); 0102 match.setRelevance(relevance); 0103 match.setCategoryRelevance(categoryRelevance); 0104 0105 if (isKinfoCenterKcm(data)) { 0106 match.setMatchCategory(i18nd("systemsettings", "System Information")); 0107 } else { 0108 match.setMatchCategory(i18nd("systemsettings", "System Settings")); 0109 } 0110 0111 matches << match; 0112 } 0113 context.addMatches(matches); 0114 } 0115 0116 void SystemsettingsRunner::run(const KRunner::RunnerContext & /*context*/, const KRunner::QueryMatch &match) 0117 { 0118 const auto data = match.data().value<KPluginMetaData>(); 0119 0120 KIO::CommandLauncherJob *job = nullptr; 0121 if (isKinfoCenterKcm(data)) { 0122 job = new KIO::CommandLauncherJob(QStringLiteral("kinfocenter"), {data.pluginId()}); 0123 job->setDesktopName(QStringLiteral("org.kde.kinfocenter")); 0124 } else if (!data.value(QStringLiteral("X-KDE-System-Settings-Parent-Category")).isEmpty()) { 0125 job = new KIO::CommandLauncherJob(QStringLiteral("systemsettings"), {data.pluginId()}); 0126 job->setDesktopName(QStringLiteral("systemsettings")); 0127 } else { 0128 // Systemsettings only uses predefined namespaces that kcmshell5/6 also knows about 0129 job = new KIO::CommandLauncherJob(QStringLiteral("kcmshell6"), {data.pluginId()}); 0130 } 0131 auto delegate = new KNotificationJobUiDelegate; 0132 delegate->setAutoErrorHandlingEnabled(true); 0133 job->setUiDelegate(delegate); 0134 job->start(); 0135 0136 KActivities::ResourceInstance::notifyAccessed(QUrl(QStringLiteral("systemsettings:") + data.pluginId()), QStringLiteral("org.kde.krunner")); 0137 } 0138 0139 QMimeData *SystemsettingsRunner::mimeDataForMatch(const KRunner::QueryMatch &match) 0140 { 0141 const auto value = match.data().value<KPluginMetaData>(); 0142 if (value.isValid()) { 0143 if (KService::Ptr ptr = KService::serviceByStorageId(value.pluginId() + QLatin1String(".desktop"))) { 0144 auto data = new QMimeData(); 0145 data->setUrls(QList<QUrl>{QUrl::fromLocalFile(ptr->entryPath())}); 0146 return data; 0147 } 0148 } 0149 return nullptr; 0150 } 0151 0152 #include "systemsettingsrunner.moc" 0153 0154 #include "moc_systemsettingsrunner.cpp"