File indexing completed on 2024-05-12 05:53:14

0001 /*
0002  * Copyright 2020 Tomaz Canabrava <tcanabrava@kde.org>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) version 3, or any
0008  * later version accepted by the membership of KDE e.V. (or its
0009  * successor approved by the membership of KDE e.V.), which shall
0010  * act as a proxy defined in Section 6 of version 3 of the license.
0011  *
0012  * This library is distributed in the hope that it will be useful,
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015  * Lesser General Public License for more details.
0016  *
0017  * You should have received a copy of the GNU Lesser General Public
0018  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
0019  */
0020 
0021 #include "abstractpluginmodel.h"
0022 #include "debug.h"
0023 
0024 #include <KPluginFactory>
0025 
0026 #include <QCoreApplication>
0027 #include <QMetaEnum>
0028 
0029 namespace Kirogi
0030 {
0031 class Q_DECL_HIDDEN AbstractPluginModel::Private
0032 {
0033 public:
0034     explicit Private(AbstractPluginModel *q);
0035     ~Private();
0036 
0037     QVector<KPluginMetaData> plugins;
0038 
0039     // This is QMap so `AbstractPluginModel::loadedPlugins` returns a stable sort.
0040     QMap<QString, QObject *> loadedPlugins;
0041 
0042     void loadPluginByType(const QString &serviceType);
0043 
0044 private:
0045     AbstractPluginModel *m_q;
0046 };
0047 
0048 AbstractPluginModel::Private::Private(AbstractPluginModel *q)
0049     : m_q(q)
0050 {
0051 }
0052 AbstractPluginModel::Private::~Private() = default;
0053 
0054 AbstractPluginModel::AbstractPluginModel(QObject *parent)
0055     : QAbstractListModel(parent)
0056     , d(new AbstractPluginModel::Private(this))
0057 {
0058 }
0059 
0060 AbstractPluginModel::~AbstractPluginModel()
0061 {
0062     unloadAllPlugins();
0063     delete d;
0064 }
0065 
0066 void AbstractPluginModel::Private::loadPluginByType(const QString &type)
0067 {
0068     plugins = KPluginMetaData::findPlugins(QLatin1String("kirogi/") + type);
0069 
0070     // Unload plugins that apparently got uninstalled at runtime.
0071     for (const QString &id : loadedPlugins.keys()) {
0072         const bool found = std::any_of(plugins.constBegin(), plugins.constEnd(), [id](const auto &md) {
0073             return md.pluginId() == id;
0074         });
0075         if (!found) {
0076             loadedPlugins.take(id)->deleteLater();
0077         }
0078     }
0079 }
0080 
0081 void AbstractPluginModel::loadPluginByType(const QString &serviceType)
0082 {
0083     d->loadPluginByType(serviceType);
0084 }
0085 
0086 bool AbstractPluginModel::loadPluginByIndex(int row)
0087 {
0088     const KPluginMetaData &md = d->plugins.at(row);
0089 
0090     if (d->loadedPlugins.contains(md.pluginId())) {
0091         return false;
0092     }
0093 
0094     const auto result = KPluginFactory::loadFactory(md);
0095 
0096     if (!result) {
0097         qCWarning(KIROGI_CORE) << "Error loading plugin:" << md.pluginId() << "-" << result.errorString;
0098         return false;
0099     }
0100 
0101     QObject *plugin = requestFromFactory(result.plugin);
0102 
0103     if (!plugin) {
0104         return false;
0105     }
0106 
0107     qCWarning(KIROGI_CORE) << "Loaded plugin with id:" << md.pluginId();
0108     d->loadedPlugins[md.pluginId()] = plugin;
0109 
0110     emit pluginLoaded(md.pluginId(), md.name(), plugin);
0111     const QModelIndex &idx = index(row, 0);
0112     emit dataChanged(idx, idx);
0113 
0114     return true;
0115 }
0116 
0117 bool AbstractPluginModel::loadPluginById(const QString &id)
0118 {
0119     for (int i = 0; i < d->plugins.count(); ++i) {
0120         const KPluginMetaData &md = d->plugins.at(i);
0121 
0122         if (md.pluginId() == id) {
0123             return loadPluginByIndex(i);
0124         }
0125     }
0126 
0127     return false;
0128 }
0129 
0130 bool AbstractPluginModel::unloadPlugin(int row)
0131 {
0132     const QString &id = d->plugins.at(row).pluginId();
0133 
0134     if (!d->loadedPlugins.contains(id)) {
0135         return false;
0136     }
0137 
0138     d->loadedPlugins.take(id)->deleteLater();
0139 
0140     const QModelIndex &idx = index(row, 0);
0141     emit dataChanged(idx, idx);
0142 
0143     return true;
0144 }
0145 
0146 bool AbstractPluginModel::unloadAllPlugins()
0147 {
0148     if (!d->loadedPlugins.count()) {
0149         return false;
0150     }
0151 
0152     for (int i = 0; i < d->plugins.count(); ++i) {
0153         const KPluginMetaData &md = d->plugins.at(i);
0154 
0155         QObject *plugin = d->loadedPlugins.take(md.pluginId());
0156 
0157         if (plugin) {
0158             plugin->deleteLater();
0159 
0160             const QModelIndex &idx = index(i, 0);
0161             emit dataChanged(idx, idx);
0162         }
0163     }
0164 
0165     return true;
0166 }
0167 
0168 QHash<int, QByteArray> AbstractPluginModel::roleNames() const
0169 {
0170     QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
0171 
0172     QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("AdditionalRoles"));
0173 
0174     auto desCapitalize = [](const char *input) -> QByteArray {
0175         QByteArray array(input);
0176         return array.left(1).toLower() + array.mid(1);
0177     };
0178 
0179     for (int i = 0; i < e.keyCount(); ++i) {
0180         roles.insert(e.value(i), desCapitalize(e.key(i)));
0181     }
0182 
0183     return roles;
0184 }
0185 
0186 int AbstractPluginModel::rowCount(const QModelIndex &parent) const
0187 {
0188     if (!checkIndex(parent, CheckIndexOption::ParentIsInvalid)) {
0189         return 0;
0190     }
0191 
0192     return d->plugins.count();
0193 }
0194 
0195 KPluginMetaData AbstractPluginModel::metadataAt(int row) const
0196 {
0197     Q_ASSERT(rowCount() > row);
0198     return d->plugins.at(row);
0199 }
0200 
0201 QObject *AbstractPluginModel::pluginForId(const QString &id) const
0202 {
0203     return d->loadedPlugins.value(id, nullptr);
0204 }
0205 
0206 }