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 | |
20 | static 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.) |
36 | static 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 | |
55 | QList<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 | |
101 | void 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 | |