File indexing completed on 2024-05-19 03:59:07

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2020 David Faure <faure@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "partloader.h"
0009 
0010 #include "kparts_logging.h"
0011 
0012 #include <KConfigGroup>
0013 #include <KLocalizedString>
0014 #include <KService>
0015 #include <KSharedConfig>
0016 
0017 #include <QMimeDatabase>
0018 #include <QMimeType>
0019 
0020 static QList<KPluginMetaData> partsFromUserPreference(const QString &mimeType)
0021 {
0022     auto config = KSharedConfig::openConfig(QStringLiteral("kpartsrc"), KConfig::NoGlobals);
0023     const QStringList pluginIds = config->group(QStringLiteral("Added KDE Part Associations")).readXdgListEntry(mimeType);
0024     QList<KPluginMetaData> plugins;
0025     plugins.reserve(pluginIds.size());
0026     for (const QString &pluginId : pluginIds) {
0027         if (KPluginMetaData data(QLatin1String("kf6/parts/") + pluginId); data.isValid()) {
0028             plugins << data;
0029         }
0030     }
0031     return plugins;
0032 }
0033 
0034 // A plugin can support N mimetypes. Pick the one that is closest to @parent in the inheritance tree
0035 // and return how far it is from that parent (0 = same mimetype, 1 = direct child, etc.)
0036 static int pluginDistanceToMimeType(const KPluginMetaData &md, const QString &parent)
0037 {
0038     QMimeDatabase db;
0039     auto distanceToMimeType = [&](const QString &mime) {
0040         if (mime == parent) {
0041             return 0;
0042         }
0043         const QStringList ancestors = db.mimeTypeForName(mime).allAncestors();
0044         const int dist = ancestors.indexOf(parent);
0045         return dist == -1 ? 50 : dist + 1;
0046     };
0047     const QStringList mimes = md.mimeTypes();
0048     int minDistance = 50;
0049     for (const QString &mime : mimes) {
0050         minDistance = std::min(minDistance, distanceToMimeType(mime));
0051     }
0052     return minDistance;
0053 }
0054 
0055 QList<KPluginMetaData> KParts::PartLoader::partsForMimeType(const QString &mimeType)
0056 {
0057     auto supportsMime = [&mimeType](const KPluginMetaData &md) {
0058         if (md.supportsMimeType(mimeType)) {
0059             return true;
0060         }
0061         auto pluginJson = md.rawData();
0062         auto pluginNamespace = pluginJson.value(QLatin1String("KParts")).toObject().value(QLatin1String("PluginNamespace")).toString();
0063         if (pluginNamespace.isEmpty()) {
0064             return false;
0065         }
0066         auto plugins = KPluginMetaData::findPlugins(pluginNamespace, [&mimeType](const KPluginMetaData &pluginMd) {
0067             return pluginMd.supportsMimeType(mimeType);
0068         });
0069         return !plugins.isEmpty();
0070     };
0071     QList<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("kf6/parts"), supportsMime);
0072     auto orderPredicate = [&](const KPluginMetaData &left, const KPluginMetaData &right) {
0073         // We filtered based on "supports mimetype", but this didn't order from most-specific to least-specific.
0074         const int leftDistance = pluginDistanceToMimeType(left, mimeType);
0075         const int rightDistance = pluginDistanceToMimeType(right, mimeType);
0076         if (leftDistance < rightDistance) {
0077             return true;
0078         }
0079         if (leftDistance > rightDistance) {
0080             return false;
0081         }
0082         // Plugins who support the same mimetype are then sorted by initial preference
0083         const auto getInitialPreference = [](const KPluginMetaData &data) {
0084             return data.rawData().value(QLatin1String("KPlugin")).toObject().value(QLatin1String("InitialPreference")).toInt();
0085         };
0086         return getInitialPreference(left) > getInitialPreference(right);
0087     };
0088     std::sort(plugins.begin(), plugins.end(), orderPredicate);
0089 
0090     const QList<KPluginMetaData> userParts = partsFromUserPreference(mimeType);
0091     if (!userParts.isEmpty()) {
0092         plugins = userParts;
0093     }
0094 
0095     // for (const KPluginMetaData &plugin : plugins) {
0096     //    qDebug() << plugin.fileName() << plugin.initialPreference();
0097     //}
0098     return plugins;
0099 }
0100 
0101 void KParts::PartLoader::Private::getErrorStrings(QString *errorString, QString *errorText, const QString &argument, ErrorType type)
0102 {
0103     switch (type) {
0104     case CouldNotLoadPlugin:
0105         *errorString = i18n("KPluginFactory could not load the plugin: %1", argument);
0106         *errorText = QStringLiteral("KPluginFactory could not load the plugin: %1").arg(argument);
0107         break;
0108     case NoPartFoundForMimeType:
0109         *errorString = i18n("No part was found for mimeType %1", argument);
0110         *errorText = QStringLiteral("No part was found for mimeType %1").arg(argument);
0111         break;
0112     case NoPartInstantiatedForMimeType:
0113         *errorString = i18n("No part could be instantiated for mimeType %1", argument);
0114         *errorText = QStringLiteral("No part could be instantiated for mimeType %1").arg(argument);
0115         break;
0116     default:
0117         qCWarning(KPARTSLOG) << "PartLoader::Private::getErrorStrings got unexpected error type" << type;
0118         break;
0119     }
0120 };