1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 2020 David Faure <faure@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "partloader.h"
9
10#include "kparts_logging.h"
11
12#include <KConfigGroup>
13#include <KLocalizedString>
14#include <KService>
15#include <KSharedConfig>
16
17#include <QMimeDatabase>
18#include <QMimeType>
19
20static QList<KPluginMetaData> partsFromUserPreference(const QString &mimeType)
21{
22 auto config = KSharedConfig::openConfig(QStringLiteral("kpartsrc"), mode: KConfig::NoGlobals);
23 const QStringList pluginIds = config->group(QStringLiteral("Added KDE Part Associations")).readXdgListEntry(pKey: mimeType);
24 QList<KPluginMetaData> plugins;
25 plugins.reserve(asize: pluginIds.size());
26 for (const QString &pluginId : pluginIds) {
27 if (KPluginMetaData data(QLatin1String("kf6/parts/") + pluginId); data.isValid()) {
28 plugins << data;
29 }
30 }
31 return plugins;
32}
33
34// A plugin can support N mimetypes. Pick the one that is closest to @parent in the inheritance tree
35// and return how far it is from that parent (0 = same mimetype, 1 = direct child, etc.)
36static int pluginDistanceToMimeType(const KPluginMetaData &md, const QString &parent)
37{
38 QMimeDatabase db;
39 auto distanceToMimeType = [&](const QString &mime) {
40 if (mime == parent) {
41 return 0;
42 }
43 const QStringList ancestors = db.mimeTypeForName(nameOrAlias: mime).allAncestors();
44 const int dist = ancestors.indexOf(str: parent);
45 return dist == -1 ? 50 : dist + 1;
46 };
47 const QStringList mimes = md.mimeTypes();
48 int minDistance = 50;
49 for (const QString &mime : mimes) {
50 minDistance = std::min(a: minDistance, b: distanceToMimeType(mime));
51 }
52 return minDistance;
53}
54
55QList<KPluginMetaData> KParts::PartLoader::partsForMimeType(const QString &mimeType)
56{
57 auto supportsMime = [&mimeType](const KPluginMetaData &md) {
58 if (md.supportsMimeType(mimeType)) {
59 return true;
60 }
61 auto pluginJson = md.rawData();
62 auto pluginNamespace = pluginJson.value(key: QLatin1String("KParts")).toObject().value(key: QLatin1String("PluginNamespace")).toString();
63 if (pluginNamespace.isEmpty()) {
64 return false;
65 }
66 auto plugins = KPluginMetaData::findPlugins(directory: pluginNamespace, filter: [&mimeType](const KPluginMetaData &pluginMd) {
67 return pluginMd.supportsMimeType(mimeType);
68 });
69 return !plugins.isEmpty();
70 };
71 QList<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("kf6/parts"), filter: supportsMime);
72 auto orderPredicate = [&](const KPluginMetaData &left, const KPluginMetaData &right) {
73 // We filtered based on "supports mimetype", but this didn't order from most-specific to least-specific.
74 const int leftDistance = pluginDistanceToMimeType(md: left, parent: mimeType);
75 const int rightDistance = pluginDistanceToMimeType(md: right, parent: mimeType);
76 if (leftDistance < rightDistance) {
77 return true;
78 }
79 if (leftDistance > rightDistance) {
80 return false;
81 }
82 // Plugins who support the same mimetype are then sorted by initial preference
83 const auto getInitialPreference = [](const KPluginMetaData &data) {
84 return data.rawData().value(key: QLatin1String("KPlugin")).toObject().value(key: QLatin1String("InitialPreference")).toInt();
85 };
86 return getInitialPreference(left) > getInitialPreference(right);
87 };
88 std::sort(first: plugins.begin(), last: plugins.end(), comp: orderPredicate);
89
90 const QList<KPluginMetaData> userParts = partsFromUserPreference(mimeType);
91 if (!userParts.isEmpty()) {
92 plugins = userParts;
93 }
94
95 // for (const KPluginMetaData &plugin : plugins) {
96 // qDebug() << plugin.fileName() << plugin.initialPreference();
97 //}
98 return plugins;
99}
100
101void KParts::PartLoader::Private::getErrorStrings(QString *errorString, QString *errorText, const QString &argument, ErrorType type)
102{
103 switch (type) {
104 case CouldNotLoadPlugin:
105 *errorString = i18n("KPluginFactory could not load the plugin: %1", argument);
106 *errorText = QStringLiteral("KPluginFactory could not load the plugin: %1").arg(a: argument);
107 break;
108 case NoPartFoundForMimeType:
109 *errorString = i18n("No part was found for mimeType %1", argument);
110 *errorText = QStringLiteral("No part was found for mimeType %1").arg(a: argument);
111 break;
112 case NoPartInstantiatedForMimeType:
113 *errorString = i18n("No part could be instantiated for mimeType %1", argument);
114 *errorText = QStringLiteral("No part could be instantiated for mimeType %1").arg(a: argument);
115 break;
116 default:
117 qCWarning(KPARTSLOG) << "PartLoader::Private::getErrorStrings got unexpected error type" << type;
118 break;
119 }
120};
121

source code of kparts/src/partloader.cpp