Libplasma

pluginloader.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Ryan Rix <ry@n.rix.si>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "pluginloader.h"
8
9#include <QDebug>
10#include <QRegularExpression>
11
12#include <KConfigGroup>
13#include <KLocalizedString>
14#include <KPackage/PackageLoader>
15#include <KPluginFactory>
16#include <KRuntimePlatform>
17#include <KSharedConfig>
18
19#include "applet.h"
20#include "containment.h"
21#include "containmentactions.h"
22#include "private/applet_p.h"
23
24using namespace Qt::Literals;
25
26namespace Plasma
27{
28inline bool isContainmentMetaData(const KPluginMetaData &md)
29{
30 return md.rawData().contains(QStringLiteral("X-Plasma-ContainmentType"));
31}
32
33PluginLoader *PluginLoader::self()
34{
35 static PluginLoader self;
36 return &self;
37}
38
39Applet *PluginLoader::loadApplet(const QString &name, uint appletId, const QVariantList &args)
40{
41 if (name.isEmpty()) {
42 return nullptr;
43 }
44
45 if (appletId == 0) {
46 appletId = ++AppletPrivate::s_maxAppletId;
47 }
48
49 // name can be either an actual applet name or an absolute path, in the
50 // latter case, ensure we only use the name part of the path.
51 const QString pluginName = name.section(QLatin1Char('/'), -1);
52
53 /*
54 * Cases:
55 * - Pure KPackage
56 * - KPackage + C++
57 * - KPackage + C++ (with X-Plasma-RootPath)
58 * - C++ with embedded QML
59 */
60
61 KPluginMetaData plugin(u"plasma/applets/" + name, KPluginMetaData::AllowEmptyMetaData);
62 const KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(u"Plasma/Applet"_s, name);
63
64 // If the applet is using another applet package, search for the plugin of the other applet
65 const QString parentPlugin = package.metadata().value(u"X-Plasma-RootPath");
66 if (!parentPlugin.isEmpty()) {
67 plugin = KPluginMetaData(u"plasma/applets/" + parentPlugin, KPluginMetaData::AllowEmptyMetaData);
68 }
69
70 // pure KPackage
71 if (package.isValid() && !plugin.isValid()) {
72 QVariantList allArgs;
73 allArgs << QVariant::fromValue(package) << appletId << args;
74
75 Applet *applet = nullptr;
76
77 if (isContainmentMetaData(package.metadata())) {
78 applet = new Containment(nullptr, package.metadata(), allArgs);
79 } else {
80 applet = new Applet(nullptr, package.metadata(), allArgs);
81 }
82
83 const QString localePath = package.filePath("translations");
84 if (!localePath.isEmpty()) {
85 KLocalizedString::addDomainLocaleDir(QByteArray("plasma_applet_") + name.toLatin1(), localePath);
86 }
87
88 return applet;
89 }
90
91 // KPackage + C++
92 if (package.isValid()) {
93 QVariantList allArgs;
94 allArgs << QVariant::fromValue(package) << appletId << args;
95
96 KPluginFactory *factory = KPluginFactory::loadFactory(plugin).plugin;
97
98 if (plugin.rawData().isEmpty()) {
99 // Plugin has empty metadata, use metadata from KPackage
100 factory->setMetaData(package.metadata());
101 } else {
102 // Plugin has its own metadata
103 factory->setMetaData(plugin);
104 }
105 return factory->create<Plasma::Applet>(nullptr, allArgs);
106 }
107
108 // C++ with embedded QML
109 if (plugin.isValid()) {
110 return KPluginFactory::instantiatePlugin<Plasma::Applet>(plugin, nullptr, {{}, appletId}).plugin;
111 }
112
113 // Add fake extension to parse completeBaseName() as pluginId
114 // without having to construct a fake JSON metadata object.
115 // This would help with better error messages which would
116 // at least show the missing applet's ID.
117 const auto fakeFileName = name + u'.';
118 // metadata = KPluginMetaData(QJsonObject(), fakeFileName);
119 return new Applet(nullptr, KPluginMetaData(QJsonObject(), fakeFileName), {{}, appletId});
120}
121
122ContainmentActions *PluginLoader::loadContainmentActions(Containment *parent, const QString &name, const QVariantList &args)
123{
124 Q_UNUSED(parent)
125 Q_UNUSED(args)
126 if (name.isEmpty()) {
127 return nullptr;
128 }
129
130 KPluginMetaData plugin(QStringLiteral("plasma/containmentactions/") + name, KPluginMetaData::AllowEmptyMetaData);
131
132 if (plugin.isValid()) {
133 if (auto res = KPluginFactory::instantiatePlugin<Plasma::ContainmentActions>(plugin, nullptr, {QVariant::fromValue(plugin)})) {
134 return res.plugin;
135 }
136 }
137
138 return nullptr;
139}
140
142{
143 auto platforms = KRuntimePlatform::runtimePlatform();
144 // For now desktop always lists everything
145 if (platforms.contains(QStringLiteral("desktop"))) {
146 platforms.clear();
147 }
148
149 // FIXME: this assumes we are always use packages.. no pure c++
150 std::function<bool(const KPluginMetaData &)> filter;
151 if (category.isEmpty()) { // use all but the excluded categories
152 KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("General"));
153 QStringList excluded = group.readEntry("ExcludeCategories", QStringList());
154
155 filter = [excluded, platforms](const KPluginMetaData &md) -> bool {
156 if (!platforms.isEmpty() && !md.formFactors().isEmpty()) {
157 bool found = false;
158 for (const auto &plat : platforms) {
159 if (md.formFactors().contains(plat)) {
160 found = true;
161 break;
162 }
163 }
164
165 if (!found) {
166 return false;
167 }
168 }
169
170 return !excluded.contains(md.category());
171 };
172 } else { // specific category (this could be an excluded one - is that bad?)
173
174 filter = [category, platforms](const KPluginMetaData &md) -> bool {
175 if (!platforms.isEmpty() && !md.formFactors().isEmpty()) {
176 bool found = false;
177 for (const auto &plat : platforms) {
178 if (md.formFactors().contains(plat)) {
179 found = true;
180 break;
181 }
182 }
183
184 if (!found) {
185 return false;
186 }
187 }
188
189 if (category == QLatin1String("Miscellaneous")) {
190 return md.category() == category || md.category().isEmpty();
191 } else {
192 return md.category() == category;
193 }
194 };
195 }
196
197 const auto kpackages = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter);
198
199 const auto plugins = KPluginMetaData::findPlugins(QStringLiteral("plasma/applets/"), {}, KPluginMetaData::AllowEmptyMetaData);
200
201 QList<KPluginMetaData> extraPlugins;
202
203 for (const KPluginMetaData &plugin : plugins) {
204 auto it = std::find_if(kpackages.cbegin(), kpackages.cend(), [plugin](const KPluginMetaData &md) {
205 return md.pluginId() == plugin.pluginId();
206 });
207
208 if (it != kpackages.cend()) {
209 continue;
210 }
211
212 extraPlugins << plugin;
213 }
214
215 return kpackages + extraPlugins;
216}
217
219{
220 auto filter = [&mimeType](const KPluginMetaData &md) -> bool {
221 return md.value(u"X-Plasma-DropMimeTypes", QStringList()).contains(mimeType);
222 };
223 return KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter);
224}
225
227{
228 auto filter = [](const KPluginMetaData &md) -> bool {
229 return !md.value(u"X-Plasma-DropUrlPatterns", QStringList()).isEmpty();
230 };
231 const QList<KPluginMetaData> allApplets = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter);
232
233 QList<KPluginMetaData> filtered;
234 for (const KPluginMetaData &md : allApplets) {
235 const QStringList urlPatterns = md.value(u"X-Plasma-DropUrlPatterns", QStringList());
236 for (const QString &glob : urlPatterns) {
238 if (rx.match(url.toString()).hasMatch()) {
239 filtered << md;
240 }
241 }
242 }
243
244 return filtered;
245}
246
248{
249 auto ownFilter = [filter](const KPluginMetaData &md) -> bool {
250 return isContainmentMetaData(md) && filter(md);
251 };
252
253 return KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), ownFilter);
254}
255
257{
258 auto filter = [type](const KPluginMetaData &md) -> bool {
259 return md.value(u"X-Plasma-ContainmentType") == type;
260 };
261
262 return listContainmentsMetaData(filter);
263}
264
266{
267 auto filter = [&parentApp](const KPluginMetaData &md) -> bool {
268 return md.value(u"X-KDE-ParentApp") == parentApp;
269 };
270
272 if (parentApp.isEmpty()) {
273 plugins = KPluginMetaData::findPlugins(QStringLiteral("plasma/containmentactions"));
274 } else {
275 plugins = KPluginMetaData::findPlugins(QStringLiteral("plasma/containmentactions"), filter);
276 }
277
278 return plugins;
279}
280
281} // Plasma Namespace
QString readEntry(const char *key, const char *aDefault=nullptr) const
QList< KPluginMetaData > findPackages(const QString &packageFormat, const QString &packageRoot=QString(), std::function< bool(const KPluginMetaData &)> filter=std::function< bool(const KPluginMetaData &)>())
static PackageLoader * self()
QString category() const
bool value(QStringView key, bool defaultValue) const
QJsonObject rawData() const
static QList< KPluginMetaData > findPlugins(const QString &directory, std::function< bool(const KPluginMetaData &)> filter={}, KPluginMetaDataOptions options={})
bool isValid() const
QStringList formFactors() const
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
The base Applet class.
Definition applet.h:64
The base ContainmentActions class.
The base class for plugins that provide backgrounds and applet grouping containers.
Definition containment.h:47
QList< KPluginMetaData > listAppletMetaDataForUrl(const QUrl &url)
Returns a list of all known applets associated with a certain URL.
static QList< KPluginMetaData > listContainmentsMetaDataOfType(const QString &type)
Returns a list of containments of the specified type.
static QList< KPluginMetaData > listContainmentsMetaData(std::function< bool(const KPluginMetaData &)> filter={})
Returns a list of all known containments.
ContainmentActions * loadContainmentActions(Containment *parent, const QString &containmentActionsName, const QVariantList &args=QVariantList())
Load a ContainmentActions plugin.
Applet * loadApplet(const QString &name, uint appletId=0, const QVariantList &args=QVariantList())
Load an Applet plugin.
QList< KPluginMetaData > listContainmentActionsMetaData(const QString &parentApp)
Returns a list of all known ContainmentActions.
QList< KPluginMetaData > listAppletMetaData(const QString &category)
Returns a list of all known applets.
QList< KPluginMetaData > listAppletMetaDataForMimeType(const QString &mimetype)
Returns a list of all known applets associated with a certain mimetype.
static PluginLoader * self()
Return the active plugin loader.
KCOREADDONS_EXPORT QStringList runtimePlatform()
Namespace for everything in libplasma.
bool contains(QLatin1StringView key) const const
bool isEmpty() const const
QRegularExpressionMatch match(QStringView subjectView, qsizetype offset, MatchType matchType, MatchOptions matchOptions) const const
QString anchoredPattern(QStringView expression)
QString wildcardToRegularExpression(QStringView pattern, WildcardConversionOptions options)
bool hasMatch() const const
bool isEmpty() const const
QString section(QChar sep, qsizetype start, qsizetype end, SectionFlags flags) const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QString toString(FormattingOptions options) const const
QVariant fromValue(T &&value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 25 2025 11:51:31 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.