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 }