diff --git a/src/plasma/pluginloader.cpp b/src/plasma/pluginloader.cpp index 298c89a53..fc9602c18 100644 --- a/src/plasma/pluginloader.cpp +++ b/src/plasma/pluginloader.cpp @@ -1,917 +1,896 @@ /* * Copyright 2010 Ryan Rix * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "pluginloader.h" #include #include #include #include #include #include #include #include #include "config-plasma.h" #include "applet.h" #include "containment.h" #include "containmentactions.h" #include "dataengine.h" #include "package.h" #include "private/applet_p.h" #include "private/service_p.h" // for NullService #include "private/storage_p.h" #include "private/package_p.h" #include "private/packagestructure_p.h" #include #include "debug_p.h" namespace Plasma { static PluginLoader *s_pluginLoader = nullptr; class PluginLoaderPrivate { public: PluginLoaderPrivate() : isDefaultLoader(false) { } static QSet knownCategories(); - static QString parentAppConstraint(const QString &parentApp = QString()); static QSet s_customCategories; QHash > structures; bool isDefaultLoader; static QString s_dataEnginePluginDir; static QString s_packageStructurePluginDir; static QString s_plasmoidsPluginDir; static QString s_servicesPluginDir; static QString s_containmentActionsPluginDir; // We only use this cache during start of the process to speed up many consecutive calls // After that, we're too afraid to produce race conditions and it's not that time-critical anyway // the 20 seconds here means that the cache is only used within 20sec during startup, after that, // complexity goes up and we'd have to update the cache in order to avoid subtle bugs // just not using the cache is way easier then, since it doesn't make *that* much of a difference, // anyway int maxCacheAge = 20; qint64 pluginCacheAge = 0; QHash> pluginCache; }; QSet PluginLoaderPrivate::s_customCategories; QString PluginLoaderPrivate::s_dataEnginePluginDir = QStringLiteral("plasma/dataengine"); QString PluginLoaderPrivate::s_packageStructurePluginDir = QStringLiteral("plasma/packagestructure"); QString PluginLoaderPrivate::s_plasmoidsPluginDir = QStringLiteral("plasma/applets"); QString PluginLoaderPrivate::s_servicesPluginDir = QStringLiteral("plasma/services"); QString PluginLoaderPrivate::s_containmentActionsPluginDir = QStringLiteral("plasma/containmentactions"); QSet PluginLoaderPrivate::knownCategories() { // this is to trick the translation tools into making the correct // strings for translation QSet categories = s_customCategories; categories << QStringLiteral(I18N_NOOP("Accessibility")).toLower() << QStringLiteral(I18N_NOOP("Application Launchers")).toLower() << QStringLiteral(I18N_NOOP("Astronomy")).toLower() << QStringLiteral(I18N_NOOP("Date and Time")).toLower() << QStringLiteral(I18N_NOOP("Development Tools")).toLower() << QStringLiteral(I18N_NOOP("Education")).toLower() << QStringLiteral(I18N_NOOP("Environment and Weather")).toLower() << QStringLiteral(I18N_NOOP("Examples")).toLower() << QStringLiteral(I18N_NOOP("File System")).toLower() << QStringLiteral(I18N_NOOP("Fun and Games")).toLower() << QStringLiteral(I18N_NOOP("Graphics")).toLower() << QStringLiteral(I18N_NOOP("Language")).toLower() << QStringLiteral(I18N_NOOP("Mapping")).toLower() << QStringLiteral(I18N_NOOP("Miscellaneous")).toLower() << QStringLiteral(I18N_NOOP("Multimedia")).toLower() << QStringLiteral(I18N_NOOP("Online Services")).toLower() << QStringLiteral(I18N_NOOP("Productivity")).toLower() << QStringLiteral(I18N_NOOP("System Information")).toLower() << QStringLiteral(I18N_NOOP("Utilities")).toLower() << QStringLiteral(I18N_NOOP("Windows and Tasks")).toLower() << QStringLiteral(I18N_NOOP("Clipboard")).toLower() << QStringLiteral(I18N_NOOP("Tasks")).toLower(); return categories; } -QString PluginLoaderPrivate::parentAppConstraint(const QString &parentApp) -{ - if (parentApp.isEmpty()) { - QCoreApplication *app = QCoreApplication::instance(); - if (!app) { - return QString(); - } - - return QStringLiteral("((not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '') or [X-KDE-ParentApp] == '%1')") - .arg(app->applicationName()); - } - - return QStringLiteral("[X-KDE-ParentApp] == '%1'").arg(parentApp); -} - PluginLoader::PluginLoader() : d(new PluginLoaderPrivate) { } PluginLoader::~PluginLoader() { typedef QPointer pswp; foreach (pswp wp, d->structures) { delete wp.data(); } delete d; } void PluginLoader::setPluginLoader(PluginLoader *loader) { if (!s_pluginLoader) { s_pluginLoader = loader; } else { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Cannot set pluginLoader, already set!" << s_pluginLoader; #endif } } PluginLoader *PluginLoader::self() { if (!s_pluginLoader) { // we have been called before any PluginLoader was set, so just use the default // implementation. this prevents plugins from nefariously injecting their own // plugin loader if the app doesn't s_pluginLoader = new PluginLoader; s_pluginLoader->d->isDefaultLoader = true; } return s_pluginLoader; } Applet *PluginLoader::loadApplet(const QString &name, uint appletId, const QVariantList &args) { if (name.isEmpty()) { return nullptr; } Applet *applet = d->isDefaultLoader ? nullptr : internalLoadApplet(name, appletId, args); if (applet) { return applet; } if (appletId == 0) { appletId = ++AppletPrivate::s_maxAppletId; } const qint64 now = qRound64(QDateTime::currentMSecsSinceEpoch() / 1000.0); bool useRuntimeCache = true; if (now - d->pluginCacheAge > d->maxCacheAge && d->pluginCacheAge != 0) { // cache is old and we're not within a few seconds of startup anymore useRuntimeCache = false; d->pluginCache.clear(); } if (d->pluginCacheAge == 0) { // Find all the plugins now, but only once d->pluginCacheAge = now; auto insertIntoCache = [this](const QString &pluginPath) { KPluginMetaData metadata(pluginPath); if (!metadata.isValid()) { return; } d->pluginCache[metadata.pluginId()].append(metadata); }; KPluginLoader::forEachPlugin(PluginLoaderPrivate::s_plasmoidsPluginDir, insertIntoCache); // COMPAT CODE for applets installed into the toplevel plugins dir by mistake. KPluginLoader::forEachPlugin(QString(), insertIntoCache); } //if name wasn't a path, pluginName == name const QString pluginName = name.section(QLatin1Char('/'), -1); QVector plugins; if (useRuntimeCache) { auto it = d->pluginCache.constFind(pluginName); if (it != d->pluginCache.constEnd()) { plugins = *it; } } else { plugins = KPluginLoader::findPluginsById(PluginLoaderPrivate::s_plasmoidsPluginDir, pluginName); // COMPAT CODE for applets installed into the toplevel plugins dir by mistake. if (plugins.isEmpty()) { plugins = KPluginLoader::findPluginsById(QString(), pluginName); } } const KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"), name); if (!plugins.isEmpty()) { KPluginLoader loader(plugins.first().fileName()); if (!isPluginVersionCompatible(loader)) { return nullptr; } KPluginFactory *factory = loader.factory(); if (factory) { QVariantList allArgs; allArgs << QVariant::fromValue(p) << loader.metaData().toVariantMap() << appletId << args; applet = factory->create(nullptr, allArgs); } } if (applet) { return applet; } if (!applet) { //qCDebug(LOG_PLASMA) << name << "not a C++ applet: Falling back to an empty one"; QVariantList allArgs; allArgs << QVariant::fromValue(p) << p.metadata().fileName() << appletId << args; if (p.metadata().serviceTypes().contains(QStringLiteral("Plasma/Containment"))) { applet = new Containment(nullptr, allArgs); } else { applet = new Applet(nullptr, allArgs); } } const QString localePath = p.filePath("translations"); if (!localePath.isEmpty()) { KLocalizedString::addDomainLocaleDir(QByteArray("plasma_applet_") + name.toLatin1(), localePath); } return applet; } DataEngine *PluginLoader::loadDataEngine(const QString &name) { DataEngine *engine = d->isDefaultLoader ? nullptr : internalLoadDataEngine(name); if (engine) { return engine; } // Look for C++ plugins first auto filter = [&name](const KPluginMetaData &md) -> bool { return md.pluginId() == name; }; QVector plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filter); if (!plugins.isEmpty()) { KPluginLoader loader(plugins.first().fileName()); const QVariantList argsWithMetaData = QVariantList() << loader.metaData().toVariantMap(); KPluginFactory *factory = loader.factory(); if (factory) { engine = factory->create(nullptr, argsWithMetaData); } } if (engine) { return engine; } const KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/DataEngine"), name); if (!p.isValid()) { return nullptr; } return new DataEngine(KPluginInfo(p.metadata().fileName()), nullptr); } QStringList PluginLoader::listAllEngines(const QString &parentApp) { QStringList engines; // Look for C++ plugins first auto filter = [&parentApp](const KPluginMetaData &md) -> bool { return md.value(QStringLiteral("X-KDE-ParentApp")) == parentApp; }; QVector plugins; if (parentApp.isEmpty()) { plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir); } else { plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filter); } foreach (auto& plugin, plugins) { engines << plugin.pluginId(); } const QList packagePlugins = KPackage::PackageLoader::self()->listPackages(QStringLiteral("Plasma/DataEngine")); for (auto& plugin : packagePlugins) { engines << plugin.pluginId(); } return engines; } KPluginInfo::List PluginLoader::listEngineInfo(const QString &parentApp) { return PluginLoader::self()->listDataEngineInfo(parentApp); } KPluginInfo::List PluginLoader::listEngineInfoByCategory(const QString &category, const QString &parentApp) { KPluginInfo::List list; // Look for C++ plugins first auto filterNormal = [&category](const KPluginMetaData &md) -> bool { return md.value(QStringLiteral("X-KDE-PluginInfo-Category")) == category; }; auto filterParentApp = [&category, &parentApp](const KPluginMetaData &md) -> bool { return md.value(QStringLiteral("X-KDE-ParentApp")) == parentApp && md.value(QStringLiteral("X-KDE-PluginInfo-Category")) == category; }; QVector plugins; if (parentApp.isEmpty()) { plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filterNormal); } else { plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filterParentApp); } list = KPluginInfo::fromMetaData(plugins); //TODO FIXME: PackageLoader needs to have a function to inject packageStructures const QList packagePlugins = KPackage::PackageLoader::self()->listPackages(QStringLiteral("Plasma/DataEngine")); list << KPluginInfo::fromMetaData(packagePlugins.toVector()); return list; } Service *PluginLoader::loadService(const QString &name, const QVariantList &args, QObject *parent) { Service *service = d->isDefaultLoader ? nullptr : internalLoadService(name, args, parent); if (service) { return service; } //TODO: scripting API support if (name.isEmpty()) { return new NullService(QString(), parent); } else if (name == QLatin1String("org.kde.servicestorage")) { return new Storage(parent); } // Look for C++ plugins first auto filter = [&name](const KPluginMetaData &md) -> bool { return md.pluginId() == name; }; QVector plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_servicesPluginDir, filter); if (!plugins.isEmpty()) { KPluginLoader loader(plugins.first().fileName()); if (!isPluginVersionCompatible(loader)) { return nullptr; } KPluginFactory *factory = loader.factory(); if (factory) { service = factory->create(nullptr, args); } } if (service) { if (service->name().isEmpty()) { service->setName(name); } return service; } else { return new NullService(name, parent); } } ContainmentActions *PluginLoader::loadContainmentActions(Containment *parent, const QString &name, const QVariantList &args) { if (name.isEmpty()) { return nullptr; } ContainmentActions *actions = d->isDefaultLoader ? nullptr : internalLoadContainmentActions(parent, name, args); if (actions) { return actions; } // Look for C++ plugins first auto filter = [&name](const KPluginMetaData &md) -> bool { return md.pluginId() == name; }; QVector plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_containmentActionsPluginDir, filter); if (!plugins.isEmpty()) { KPluginLoader loader(plugins.first().fileName()); const QVariantList argsWithMetaData = QVariantList() << loader.metaData().toVariantMap(); KPluginFactory *factory = loader.factory(); if (factory) { actions = factory->create(nullptr, argsWithMetaData); } } if (actions) { return actions; } //FIXME: this is only for backwards compatibility, but probably will have to stay //for the time being QString constraint = QStringLiteral("[X-KDE-PluginInfo-Name] == '%1'").arg(name); KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("Plasma/ContainmentActions"), constraint); if (offers.isEmpty()) { #ifndef NDEBUG qCDebug(LOG_PLASMA) << "offers is empty for " << name; #endif return nullptr; } KService::Ptr offer = offers.first(); KPluginLoader plugin(*offer); if (!isPluginVersionCompatible(plugin)) { return nullptr; } QVariantList allArgs; allArgs << offer->storageId() << args; QString error; actions = offer->createInstance(parent, allArgs, &error); if (!actions) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Couldn't load containmentActions \"" << name << "\"! reason given: " << error; #endif } return actions; } #ifndef PLASMA_NO_DEPRECATED Package PluginLoader::loadPackage(const QString &packageFormat, const QString &specialization) { if (!d->isDefaultLoader) { Package p = internalLoadPackage(packageFormat, specialization); if (p.hasValidStructure()) { return p; } } if (packageFormat.isEmpty()) { return Package(); } const QString hashkey = packageFormat + QLatin1Char('%') + specialization; PackageStructure *structure = d->structures.value(hashkey).data(); if (structure) { return Package(structure); } KPackage::PackageStructure *internalStructure = KPackage::PackageLoader::self()->loadPackageStructure(packageFormat); if (internalStructure) { structure = new PackageStructure(); structure->d->internalStructure = internalStructure; //fallback to old structures } else { const QString constraint = QStringLiteral("[X-KDE-PluginInfo-Name] == '%1'").arg(packageFormat); structure = KPluginTrader::createInstanceFromQuery(PluginLoaderPrivate::s_packageStructurePluginDir, QStringLiteral("Plasma/PackageStructure"), constraint, nullptr); if (structure) { structure->d->internalStructure = new PackageStructureWrapper(structure); } } if (structure) { d->structures.insert(hashkey, structure); return Package(structure); } #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Couldn't load Package for" << packageFormat << "! reason given: " << error; #endif return Package(); } #endif QList PluginLoader::listAppletMetaData(const QString &category, const QString &parentApp) { //FIXME: this assumes we are always use packages.. no pure c++ std::function filter; if (category.isEmpty()) { //use all but the excluded categories KConfigGroup group(KSharedConfig::openConfig(), "General"); QStringList excluded = group.readEntry("ExcludeCategories", QStringList()); filter = [excluded, parentApp](const KPluginMetaData &md) -> bool { const QString pa = md.value(QStringLiteral("X-KDE-ParentApp")); - return (pa.isEmpty() || pa == parentApp) && !excluded.contains(md.category()); + return (parentApp.isEmpty() || pa == parentApp) && !excluded.contains(md.category()); }; } else { //specific category (this could be an excluded one - is that bad?) filter = [category, parentApp](const KPluginMetaData &md) -> bool { const QString pa = md.value(QStringLiteral("X-KDE-ParentApp")); if (category == QLatin1String("Miscellaneous")) { - return (pa.isEmpty() || pa == parentApp) && (md.category() == category || md.category().isEmpty()); + return (parentApp.isEmpty() || pa == parentApp) && (md.category() == category || md.category().isEmpty()); } else { - return (pa.isEmpty() || pa == parentApp) && md.category() == category; + return (parentApp.isEmpty() || pa == parentApp) && md.category() == category; } }; } QList list; if (!d->isDefaultLoader && (parentApp.isEmpty() || parentApp == QCoreApplication::instance()->applicationName())) { list = KPluginInfo::toMetaData(internalAppletInfo(category)).toList(); } return KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter); } KPluginInfo::List PluginLoader::listAppletInfo(const QString &category, const QString &parentApp) { KPluginInfo::List list; const auto plugins = listAppletMetaData(category, parentApp); //NOTE: it still produces kplugininfos from KServices because some user code expects //info.service() to be valid and would crash otherwise foreach (auto& md, plugins) { auto pi = md.metaDataFileName().endsWith(QLatin1String(".json")) ? KPluginInfo(md) : KPluginInfo(KService::serviceByStorageId(md.metaDataFileName())); if (!pi.isValid()) { qCWarning(LOG_PLASMA) << "Could not load plugin info for plugin :" << md.pluginId() << "skipping plugin"; continue; } list << pi; } return list; } QList PluginLoader::listAppletMetaDataForMimeType(const QString &mimeType) { auto filter = [&mimeType](const KPluginMetaData &md) -> bool { return KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-DropMimeTypes")).contains(mimeType); }; return KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter); } KPluginInfo::List PluginLoader::listAppletInfoForMimeType(const QString &mimeType) { return KPluginInfo::fromMetaData(listAppletMetaDataForMimeType(mimeType).toVector()); } QList PluginLoader::listAppletMetaDataForUrl(const QUrl &url) { QString parentApp; QCoreApplication *app = QCoreApplication::instance(); if (app) { parentApp = app->applicationName(); } auto filter = [&parentApp](const KPluginMetaData &md) -> bool { const QString pa = md.value(QStringLiteral("X-KDE-ParentApp")); - return (pa.isEmpty() || pa == parentApp) && !KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-DropUrlPatterns")).isEmpty(); + return (parentApp.isEmpty() || pa == parentApp) && !KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-DropUrlPatterns")).isEmpty(); }; const QList allApplets = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter); QList filtered; foreach (const KPluginMetaData &md, allApplets) { QStringList urlPatterns = KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-DropUrlPatterns")); foreach (const QString &glob, urlPatterns) { QRegExp rx(glob); rx.setPatternSyntax(QRegExp::Wildcard); if (rx.exactMatch(url.toString())) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << md.name() << "matches" << glob << url; #endif filtered << md; } } } return filtered; } KPluginInfo::List PluginLoader::listAppletInfoForUrl(const QUrl &url) { return KPluginInfo::fromMetaData(listAppletMetaDataForUrl(url).toVector()); } QStringList PluginLoader::listAppletCategories(const QString &parentApp, bool visibleOnly) { KConfigGroup group(KSharedConfig::openConfig(), "General"); const QStringList excluded = group.readEntry("ExcludeCategories", QStringList()); auto filter = [&parentApp, &excluded, visibleOnly](const KPluginMetaData &md) -> bool { const QString pa = md.value(QStringLiteral("X-KDE-ParentApp")); - return (pa.isEmpty() || pa == parentApp) + return (parentApp.isEmpty() || pa == parentApp) && (excluded.isEmpty() || excluded.contains(md.value(QStringLiteral("X-KDE-PluginInfo-Category")))) && (!visibleOnly || !md.isHidden()); }; const QList allApplets = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter); QStringList categories; foreach (auto& plugin, allApplets) { if (plugin.category().isEmpty()) { if (!categories.contains(i18nc("misc category", "Miscellaneous"))) { categories << i18nc("misc category", "Miscellaneous"); } } else { categories << plugin.category(); } } categories.sort(); return categories; } void PluginLoader::setCustomAppletCategories(const QStringList &categories) { PluginLoaderPrivate::s_customCategories = QSet::fromList(categories); } QStringList PluginLoader::customAppletCategories() const { return PluginLoaderPrivate::s_customCategories.toList(); } QString PluginLoader::appletCategory(const QString &appletName) { if (appletName.isEmpty()) { return QString(); } const KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"), appletName); if (!p.isValid()) { return QString(); } return p.metadata().category(); } KPluginInfo::List PluginLoader::listContainments(const QString &category, const QString &parentApp) { return listContainmentsOfType(QString(), category, parentApp); } KPluginInfo::List PluginLoader::listContainmentsOfType(const QString &type, const QString &category, const QString &parentApp) { KConfigGroup group(KSharedConfig::openConfig(), "General"); auto filter = [&type, &category, &parentApp](const KPluginMetaData &md) -> bool { if (!md.serviceTypes().contains(QStringLiteral("Plasma/Containment"))) { return false; } - const QString pa = md.value(QStringLiteral("X-KDE-ParentApp")); - if (!pa.isEmpty() && pa != parentApp) { + if (!parentApp.isEmpty() && md.value(QStringLiteral("X-KDE-ParentApp")) != parentApp) { return false; } if (!type.isEmpty() && md.value(QStringLiteral("X-Plasma-ContainmentType")) != type) { return false; } if (!category.isEmpty() && md.value(QStringLiteral("X-KDE-PluginInfo-Category")) != category) { return false; } return true; }; return KPluginInfo::fromMetaData(KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter).toVector()); } KPluginInfo::List PluginLoader::listContainmentsForMimeType(const QString &mimeType) { auto filter = [&mimeType](const KPluginMetaData &md) -> bool { return md.serviceTypes().contains(QLatin1String("Plasma/Containment")) && KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-DropMimeTypes")).contains(mimeType); }; return KPluginInfo::fromMetaData(KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter).toVector()); } QStringList PluginLoader::listContainmentTypes() { KPluginInfo::List containmentInfos = listContainments(); QSet types; foreach (const KPluginInfo &containmentInfo, containmentInfos) { QStringList theseTypes = containmentInfo.service()->property(QStringLiteral("X-Plasma-ContainmentType")).toStringList(); foreach (const QString &type, theseTypes) { types.insert(type); } } return types.toList(); } KPluginInfo::List PluginLoader::listDataEngineInfo(const QString &parentApp) { KPluginInfo::List list; if (!d->isDefaultLoader && (parentApp.isEmpty() || parentApp == QCoreApplication::instance()->applicationName())) { list = internalDataEngineInfo(); } QString constraint; - if (parentApp.isEmpty()) { - constraint = QStringLiteral("not exist [X-KDE-ParentApp]"); - } else { + if (!parentApp.isEmpty()) { constraint = QLatin1String("[X-KDE-ParentApp] == '") + parentApp + QLatin1Char('\''); } list.append(KPluginTrader::self()->query(PluginLoaderPrivate::s_dataEnginePluginDir, QStringLiteral("Plasma/DataEngine"), constraint)); return list; } KPluginInfo::List PluginLoader::listContainmentActionsInfo(const QString &parentApp) { KPluginInfo::List list; if (!d->isDefaultLoader && (parentApp.isEmpty() || parentApp == QCoreApplication::instance()->applicationName())) { list = internalContainmentActionsInfo(); } QString constraint; - if (parentApp.isEmpty()) { - constraint = QStringLiteral("not exist [X-KDE-ParentApp]"); - } else { + if (!parentApp.isEmpty()) { constraint = QLatin1String("[X-KDE-ParentApp] == '") + parentApp + QLatin1Char('\''); } list.append(KPluginTrader::self()->query(PluginLoaderPrivate::s_containmentActionsPluginDir, QStringLiteral("Plasma/ContainmentActions"), constraint)); QSet knownPlugins; foreach (const KPluginInfo &p, list) { knownPlugins.insert(p.pluginName()); } //FIXME: this is only for backwards compatibility, but probably will have to stay //for the time being KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("Plasma/ContainmentActions"), constraint); foreach (KService::Ptr s, offers) { if (!knownPlugins.contains(s->pluginKeyword())) { list.append(KPluginInfo(s)); } } return list; } Applet *PluginLoader::internalLoadApplet(const QString &name, uint appletId, const QVariantList &args) { Q_UNUSED(name) Q_UNUSED(appletId) Q_UNUSED(args) return nullptr; } DataEngine *PluginLoader::internalLoadDataEngine(const QString &name) { Q_UNUSED(name) return nullptr; } ContainmentActions *PluginLoader::internalLoadContainmentActions(Containment *containment, const QString &name, const QVariantList &args) { Q_UNUSED(containment) Q_UNUSED(name) Q_UNUSED(args) return nullptr; } Service *PluginLoader::internalLoadService(const QString &name, const QVariantList &args, QObject *parent) { Q_UNUSED(name) Q_UNUSED(args) Q_UNUSED(parent) return nullptr; } #ifndef PLASMA_NO_DEPRECATED Package PluginLoader::internalLoadPackage(const QString &name, const QString &specialization) { Q_UNUSED(name); Q_UNUSED(specialization); return Package(); } #endif KPluginInfo::List PluginLoader::internalAppletInfo(const QString &category) const { Q_UNUSED(category) return KPluginInfo::List(); } KPluginInfo::List PluginLoader::internalDataEngineInfo() const { return KPluginInfo::List(); } KPluginInfo::List PluginLoader::internalServiceInfo() const { return KPluginInfo::List(); } KPluginInfo::List PluginLoader::internalContainmentActionsInfo() const { return KPluginInfo::List(); } static KPluginInfo::List standardInternalInfo(const QString &type, const QString &category = QString()) { QStringList files = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/internal/") + type + QLatin1String("/*.desktop"), QStandardPaths::LocateFile); KPluginInfo::List allInfo = KPluginInfo::fromFiles(files); if (category.isEmpty() || allInfo.isEmpty()) { return allInfo; } KPluginInfo::List matchingInfo; foreach (const KPluginInfo &info, allInfo) { if (info.category().compare(category, Qt::CaseInsensitive) == 0) { matchingInfo << info; } } return matchingInfo; } KPluginInfo::List PluginLoader::standardInternalAppletInfo(const QString &category) const { return standardInternalInfo(QStringLiteral("applets"), category); } KPluginInfo::List PluginLoader::standardInternalDataEngineInfo() const { return standardInternalInfo(QStringLiteral("dataengines")); } KPluginInfo::List PluginLoader::standardInternalServiceInfo() const { return standardInternalInfo(QStringLiteral("services")); } bool PluginLoader::isPluginVersionCompatible(KPluginLoader &loader) { const quint32 version = loader.pluginVersion(); if (version == quint32(-1)) { // unversioned, just let it through qCWarning(LOG_PLASMA) << loader.fileName() << "unversioned plugin detected, may result in instability"; return true; } // we require PLASMA_VERSION_MAJOR and PLASMA_VERSION_MINOR const quint32 minVersion = PLASMA_MAKE_VERSION(PLASMA_VERSION_MAJOR, 0, 0); const quint32 maxVersion = PLASMA_MAKE_VERSION(PLASMA_VERSION_MAJOR, PLASMA_VERSION_MINOR, 60); if (version < minVersion || version > maxVersion) { qCWarning(LOG_PLASMA) << loader.fileName() << ": this plugin is compiled against incompatible Plasma version" << version << "This build is compatible with" << PLASMA_VERSION_MAJOR << ".0.0 (" << minVersion << ") to" << PLASMA_VERSION_STRING << "(" << maxVersion << ")"; return false; } return true; } } // Plasma Namespace diff --git a/src/plasma/pluginloader.h b/src/plasma/pluginloader.h index 31b1b464c..7861e9914 100644 --- a/src/plasma/pluginloader.h +++ b/src/plasma/pluginloader.h @@ -1,532 +1,522 @@ /* * Copyright 2010 by Ryan Rix * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PLUGIN_LOADER_H #define PLUGIN_LOADER_H #include #include #include namespace Plasma { class Applet; class Containment; class ContainmentActions; class DataEngine; class Service; class PluginLoaderPrivate; //TODO: // * add loadWallpaper // * add KPluginInfo listing support for Containments (already loaded via the applet loading code) /** * @class PluginLoader plasma/pluginloader.h * * This is an abstract base class which defines an interface to which Plasma's * Applet Loading logic can communicate with a parent application. The plugin loader * must be set before any plugins are loaded, otherwise (for safety reasons), the * default PluginLoader implementation will be used. The reimplemented version should * not do more than simply returning a loaded plugin. It should not init() it, and it should not * hang on to it. The associated methods will be called only when a component of Plasma * needs to load a _new_ plugin. (e.g. DataEngine does its own caching). * * @author Ryan Rix * @since 4.6 **/ class PLASMA_EXPORT PluginLoader { public: /** * Load an Applet plugin. * * @param name the plugin name, as returned by KPluginInfo::pluginName() * @param appletId unique ID to assign the applet, or zero to have one * assigned automatically. * @param args to send the applet extra arguments * @return a pointer to the loaded applet, or 0 on load failure **/ Applet *loadApplet(const QString &name, uint appletId = 0, const QVariantList &args = QVariantList()); /** * Load a dataengine plugin. * * @param name the name of the engine * @return the dataengine that was loaded, or the NullEngine on failure. **/ DataEngine *loadDataEngine(const QString &name); /** * @return a listing of all known dataengines by name * * @param parentApp the application to filter dataengines on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a - * list containing only dataengines not specifically - * registered to an application. + * list of all dataengines. */ static QStringList listAllEngines(const QString &parentApp = QString()); /** * Returns a list of all known dataengines. * * @param parentApp the application to filter dataengines on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a - * list containing only dataengines not specifically - * registered to an application. + * list of all dataengines. * @return list of dataengines **/ static KPluginInfo::List listEngineInfo(const QString &parentApp = QString()); /** * Returns a list of all known dataengines filtering by category. * * @param category the category to filter dataengines on. Uses the * X-KDE-PluginInfo-Category entry (if any) in the * plugin info. The value of QString() will * result in a list of dataengines with an empty category. * * @param parentApp the application to filter dataengines on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a - * list containing only dataengines not specifically - * registered to an application. + * list of all dataengines in specified categories. * @return list of dataengines * @since 4.3 **/ static KPluginInfo::List listEngineInfoByCategory(const QString &category, const QString &parentApp = QString()); /** * Load a Service plugin. * * @param name the plugin name of the service to load * @param args a list of arguments to supply to the service plugin when loading it * @param parent the parent object, if any, for the service * * @return a Service object, unlike Plasma::Service::loadService, this can return null. **/ Service *loadService(const QString &name, const QVariantList &args, QObject *parent = nullptr); /** * Load a ContainmentActions plugin. * * Returns a pointer to the containmentactions if successful. * The caller takes responsibility for the containmentactions, including * deleting it when no longer needed. * * @param parent the parent containment. @since 4.6 null is allowed. * @param name the plugin name, as returned by KPluginInfo::pluginName() * @param args to send the containmentactions extra arguments * @return a ContainmentActions object **/ ContainmentActions *loadContainmentActions(Containment *parent, const QString &containmentActionsName, const QVariantList &args = QVariantList()); /** * Load a Package plugin. * * @param name the plugin name of the package to load * @param specialization used to find script extensions for the given format, e.g. "QML" for "Plasma/Applet" * * @return a Package object matching name, or an invalid package on failure * @deprecated since 5.29 use KPackage::PackageLoader::loadPackage **/ #ifndef PLASMA_NO_DEPRECATED PLASMA_DEPRECATED Package loadPackage(const QString &packageFormat, const QString &specialization = QString()); #endif /** * Returns a list of all known applets. * This may skip applets based on security settings and ExcludeCategories in the application's config. * * @param category Only applets matching this category will be returned. * Useful in conjunction with knownCategories. * If "Misc" is passed in, then applets without a * Categories= entry are also returned. * If an empty string is passed in, all applets are * returned. * @param parentApp the application to filter applets on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a - * list containing only applets not specifically - * registered to an application. + * list of all applets in specified category. * @return list of applets * * @deprecated use listAppletMetaData. Doesn't support metadata.json packages. **/ PLASMA_DEPRECATED KPluginInfo::List listAppletInfo(const QString &category, const QString &parentApp = QString()); /** * Returns a list of all known applets. * This may skip applets based on security settings and ExcludeCategories in the application's config. * * @param category Only applets matchin this category will be returned. * Useful in conjunction with knownCategories. * If "Misc" is passed in, then applets without a * Categories= entry are also returned. * If an empty string is passed in, all applets are * returned. * @param parentApp the application to filter applets on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a - * list containing only applets not specifically - * registered to an application. + * list of all applets in specified categories. * @return list of applets * * @since 5.28 **/ QList listAppletMetaData(const QString &category, const QString &parentApp = QString()); /** * Returns a list of all known applets associated with a certain mimetype. * * @return list of applets * * @deprecated use listAppletMetaDataForMimeType instead **/ PLASMA_DEPRECATED KPluginInfo::List listAppletInfoForMimeType(const QString &mimetype); /** * Returns a list of all known applets associated with a certain mimetype. * * @return list of applets **/ QList listAppletMetaDataForMimeType(const QString &mimetype); /** * Returns a list of all known applets associated with a certain URL. * * @return list of applets * * @deprecated use listAppletMetaDataForUrl instead **/ PLASMA_DEPRECATED KPluginInfo::List listAppletInfoForUrl(const QUrl &url); /** * Returns a list of all known applets associated with a certain URL. * * @return list of applets **/ QList listAppletMetaDataForUrl(const QUrl &url); /** * Returns a list of all the categories used by installed applets. * * @param parentApp the application to filter applets on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a - * list containing only applets not specifically - * registered to an application. + * list of all Applets. * @return list of categories * @param visibleOnly true if it should only return applets that are marked as visible */ QStringList listAppletCategories(const QString &parentApp = QString(), bool visibleOnly = true); /** * Sets the list of custom categories that are used in addition to the default * set of categories known to libplasma for applets. * @param categories a list of categories * @since 4.3 */ void setCustomAppletCategories(const QStringList &categories); /** * @return the list of custom categories known to libplasma * @since 4.3 */ QStringList customAppletCategories() const; /** * Get the category of the given applet * * @param appletName the name of the applet */ QString appletCategory(const QString &appletName); /** * Returns a list of all known containments. * * @param category Only containments matching this category will be returned. * Useful in conjunction with knownCategories. * If "Miscellaneous" is passed in, then containments without a * Categories= entry are also returned. * If an empty string is passed in, all containments are * returned. * @param parentApp the application to filter containments on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a - * list containing only containments not specifically - * registered to an application. + * list of all containments. * @return list of containments **/ static KPluginInfo::List listContainments(const QString &category = QString(), const QString &parentApp = QString()); /** * Returns a list of all known containments that match the parameters. * * @param type Only containments with this string in X-Plasma-ContainmentType * in their .desktop files will be returned. Common values are panel and * desktop * @param category Only containments matching this category will be returned. * Useful in conjunction with knownCategories. * If "Miscellaneous" is passed in, then containments without a * Categories= entry are also returned. * If an empty string is passed in, all containments are * returned. * @param parentApp the application to filter containments on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a - * list containing only containments not specifically - * registered to an application. + * list of all containments, matching categories/type. * @return list of containments **/ static KPluginInfo::List listContainmentsOfType(const QString &type, const QString &category = QString(), const QString &parentApp = QString()); /** * @return a list of all known types of containments on this system */ static QStringList listContainmentTypes(); /** * Returns a list of all known containments associated with a certain MimeType * * @return list of containments **/ static KPluginInfo::List listContainmentsForMimeType(const QString &mimeType); /** * Returns a list of all known dataengines. * * @param parentApp the application to filter dataengines on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a - * list containing only dataengines not specifically - * registered to an application. + * list of all dataengines * @return list of dataengines **/ KPluginInfo::List listDataEngineInfo(const QString &parentApp = QString()); /** * Returns a list of all known ContainmentActions. * * @param parentApp the application to filter ContainmentActions on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a - * list containing only ContainmentActions not specifically - * registered to an application. + * list of all ContainmentActions. * @return list of ContainmentActions **/ KPluginInfo::List listContainmentActionsInfo(const QString &parentApp); /** * Set the plugin loader which will be queried for all loads. * * @param loader A subclass of PluginLoader which will be supplied * by the application **/ static void setPluginLoader(PluginLoader *loader); /** * Return the active plugin loader **/ static PluginLoader *self(); protected: /** * A re-implementable method that allows subclasses to override * the default behaviour of loadApplet. If the applet requested is not recognized, * then the implementation should return a NULL pointer. This method is called * by loadApplet prior to attempting to load an applet using the standard Plasma * plugin mechanisms. * * @param name the plugin name, as returned by KPluginInfo::pluginName() * @param appletId unique ID to assign the applet, or zero to have one * assigned automatically. * @param args to send the applet extra arguments * @return a pointer to the loaded applet, or 0 on load failure **/ virtual Applet *internalLoadApplet(const QString &name, uint appletId = 0, const QVariantList &args = QVariantList()); /** * A re-implementable method that allows subclasses to override * the default behaviour of loadDataEngine. If the engine requested is not recognized, * then the implementation should return a NULL pointer. This method is called * by loadDataEngine prior to attempting to load a DataEgine using the standard Plasma * plugin mechanisms. * * @param name the name of the engine * @return the data engine that was loaded, or the NullEngine on failure. **/ virtual DataEngine *internalLoadDataEngine(const QString &name); /** * A re-implementable method that allows subclasses to override * the default behaviour of loadService. If the service requested is not recognized, * then the implementation should return a NULL pointer. This method is called * by loadService prior to attempting to load a Service using the standard Plasma * plugin mechanisms. * * @param name the plugin name of the service to load * @param args a list of arguments to supply to the service plugin when loading it * @param parent the parent object, if any, for the service * * @return a Service object, unlike Plasma::Service::loadService, this can return null. **/ virtual Service *internalLoadService(const QString &name, const QVariantList &args, QObject *parent = nullptr); /** * A re-implementable method that allows subclasses to override * the default behaviour of loadContainmentActions. If the ContainmentActions requested is not recognized, * then the implementation should return a NULL pointer. This method is called * by loadService prior to attempting to load a Service using the standard Plasma * plugin mechanisms. * * Returns a pointer to the containmentactions if successful. * The caller takes responsibility for the containmentactions, including * deleting it when no longer needed. * * @param parent the parent containment. @since 4.6 null is allowed. * @param name the plugin name, as returned by KPluginInfo::pluginName() * @param args to send the containmentactions extra arguments * @return a ContainmentActions object **/ virtual ContainmentActions *internalLoadContainmentActions(Containment *parent, const QString &containmentActionsName, const QVariantList &args); /** * A re-implementable method that allows subclasses to override * the default behaviour of loadPackage. If the service requested is not recognized, * then the implementation should return a NULL pointer. This method is called * by loadService prior to attempting to load a Service using the standard Plasma * plugin mechanisms. * * @param name the plugin name of the service to load * @param args a list of arguments to supply to the service plugin when loading it * @param parent the parent object, if any, for the service * * @return a Service object, unlike Plasma::Service::loadService, this can return null. * @deprecated since 5.29 **/ #ifndef PLASMA_NO_DEPRECATED virtual PLASMA_DEPRECATED Package internalLoadPackage(const QString &name, const QString &specialization); #endif /** * A re-implementable method that allows subclasses to provide additional applets * for listAppletInfo. If the application has no applets to give to the application, * then the implementation should return an empty list. * * This method is called by listAppletInfo prior to generating the list of applets installed * on the system using the standard Plasma plugin mechanisms, and will try to find .desktop * files for your applets. * * @param category Only applets matching this category will be returned. * Useful in conjunction with knownCategories. * If "Misc" is passed in, then applets without a * Categories= entry are also returned. * If an empty string is passed in, all applets are * returned. * @return list of applets **/ virtual KPluginInfo::List internalAppletInfo(const QString &category) const; /** * A re-implementable method that allows subclasses to provide additional dataengines * for DataEngine::listDataEngines. * * @return list of dataengine info, or an empty list if none **/ virtual KPluginInfo::List internalDataEngineInfo() const; /** * Returns a list of all known Service implementations * * @return list of Service info, or an empty list if none */ virtual KPluginInfo::List internalServiceInfo() const; /** * Returns a list of all known ContainmentActions implementations * * @return list of ContainmentActions info, or an empty list if none */ virtual KPluginInfo::List internalContainmentActionsInfo() const; /** * Standardized mechanism for providing internal applets by install .desktop files * in $APPPDATA/plasma/internal/applets/ * * For applications that do this, internalAppletInfo can be implemented as a one-liner * call to this method. * * @param category Only applets matching this category will be returned. * Useful in conjunction with knownCategories. * If "Misc" is passed in, then applets without a * Categories= entry are also returned. * If an empty string is passed in, all applets are * returned. * @return list of applets, or an empty list if none */ KPluginInfo::List standardInternalAppletInfo(const QString &category) const; /** * Standardized mechanism for providing internal dataengines by install .desktop files * in $APPPDATA/plasma/internal/dataengines/ * * For applications that do this, internalDataEngineInfo can be implemented as a one-liner * call to this method. * * @return list of dataengines */ KPluginInfo::List standardInternalDataEngineInfo() const; /** * Standardized mechanism for providing internal services by install .desktop files * in $APPPDATA/plasma/internal/services/ * * For applications that do this, internalServiceInfo can be implemented as a one-liner * call to this method. * * @return list of services */ KPluginInfo::List standardInternalServiceInfo() const; PluginLoader(); virtual ~PluginLoader(); private: bool isPluginVersionCompatible(KPluginLoader &loader); PluginLoaderPrivate *const d; }; } Q_DECLARE_METATYPE(Plasma::PluginLoader *) #endif