File indexing completed on 2024-05-12 17:08:53
0001 /* 0002 SPDX-FileCopyrightText: 2015 Martin Klapetek <mklapetek@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "eventpluginsmanager.h" 0008 0009 #include <QAbstractListModel> 0010 #include <QCoreApplication> 0011 #include <QDebug> 0012 #include <QDir> 0013 #include <QJsonObject> 0014 #include <QPluginLoader> 0015 0016 #include <KPluginMetaData> 0017 0018 class EventPluginsManagerPrivate 0019 { 0020 public: 0021 explicit EventPluginsManagerPrivate(); 0022 ~EventPluginsManagerPrivate(); 0023 0024 friend class EventPluginsModel; 0025 struct PluginData { 0026 QString name; 0027 QString desc; 0028 QString icon; 0029 QString configUi; 0030 }; 0031 0032 std::unique_ptr<EventPluginsModel> model; 0033 QList<CalendarEvents::CalendarEventsPlugin *> plugins; 0034 QMap<QString, PluginData> availablePlugins; 0035 QStringList enabledPlugins; 0036 }; 0037 0038 class EventPluginsModel : public QAbstractListModel 0039 { 0040 Q_OBJECT 0041 public: 0042 EventPluginsModel(EventPluginsManagerPrivate *d) 0043 : d(d) 0044 , m_roles(QAbstractListModel::roleNames()) 0045 { 0046 m_roles.insert(Qt::EditRole, QByteArrayLiteral("checked")); 0047 m_roles.insert(Qt::UserRole, QByteArrayLiteral("configUi")); 0048 m_roles.insert(Qt::UserRole + 1, QByteArrayLiteral("pluginPath")); 0049 } 0050 0051 // make these two available to the manager 0052 void beginResetModel() 0053 { 0054 QAbstractListModel::beginResetModel(); 0055 } 0056 0057 void endResetModel() 0058 { 0059 QAbstractListModel::endResetModel(); 0060 } 0061 0062 QHash<int, QByteArray> roleNames() const override 0063 { 0064 return m_roles; 0065 } 0066 0067 Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const override 0068 { 0069 Q_UNUSED(parent); 0070 return d->availablePlugins.size(); 0071 } 0072 0073 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override 0074 { 0075 if (!index.isValid() || !d) { 0076 return QVariant(); 0077 } 0078 0079 const auto it = std::next(d->availablePlugins.cbegin(), index.row()); 0080 const QString currentPlugin = it.key(); 0081 const EventPluginsManagerPrivate::PluginData metadata = it.value(); 0082 0083 switch (role) { 0084 case Qt::DisplayRole: 0085 return metadata.name; 0086 case Qt::ToolTipRole: 0087 return metadata.desc; 0088 case Qt::DecorationRole: 0089 return metadata.icon; 0090 case Qt::UserRole: { 0091 // The currentPlugin path contains the full path including 0092 // the plugin filename, so it needs to be cut off from the last '/' 0093 const QStringView prefix = QStringView(currentPlugin).left(currentPlugin.lastIndexOf(QDir::separator())); 0094 const QString qmlFilePath = metadata.configUi; 0095 return QStringLiteral("%1%2%3").arg(prefix, QDir::separator(), qmlFilePath); 0096 } 0097 case Qt::UserRole + 1: 0098 return currentPlugin; 0099 case Qt::EditRole: 0100 return d->enabledPlugins.contains(currentPlugin); 0101 } 0102 0103 return QVariant(); 0104 } 0105 0106 bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override 0107 { 0108 if (role != Qt::EditRole || !index.isValid()) { 0109 return false; 0110 } 0111 0112 bool enabled = value.toBool(); 0113 const QString pluginPath = d->availablePlugins.keys().at(index.row()); 0114 0115 if (enabled) { 0116 if (!d->enabledPlugins.contains(pluginPath)) { 0117 d->enabledPlugins << pluginPath; 0118 } 0119 } else { 0120 d->enabledPlugins.removeOne(pluginPath); 0121 } 0122 0123 Q_EMIT dataChanged(index, index); 0124 0125 return true; 0126 } 0127 0128 Q_INVOKABLE QVariant get(int row, const QByteArray &role) 0129 { 0130 return data(createIndex(row, 0), roleNames().key(role)); 0131 } 0132 0133 private: 0134 EventPluginsManagerPrivate *d; 0135 QHash<int, QByteArray> m_roles; 0136 }; 0137 0138 EventPluginsManagerPrivate::EventPluginsManagerPrivate() 0139 : model(std::make_unique<EventPluginsModel>(this)) 0140 { 0141 auto plugins = KPluginMetaData::findPlugins(QStringLiteral("plasmacalendarplugins"), [](const KPluginMetaData &md) { 0142 return md.rawData().contains(QStringLiteral("KPlugin")); 0143 }); 0144 for (const KPluginMetaData &plugin : std::as_const(plugins)) { 0145 availablePlugins.insert(plugin.fileName(), 0146 {plugin.name(), plugin.description(), plugin.iconName(), plugin.value(QStringLiteral("X-KDE-PlasmaCalendar-ConfigUi"))}); 0147 } 0148 0149 // Fallback for legacy pre-KPlugin plugins so we can still load them 0150 const QStringList paths = QCoreApplication::libraryPaths(); 0151 for (const QString &libraryPath : paths) { 0152 const QString path(libraryPath + QStringLiteral("/plasmacalendarplugins")); 0153 QDir dir(path); 0154 0155 if (!dir.exists()) { 0156 continue; 0157 } 0158 0159 const QStringList entryList = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); 0160 0161 for (const QString &fileName : entryList) { 0162 const QString absolutePath = dir.absoluteFilePath(fileName); 0163 if (availablePlugins.contains(absolutePath)) { 0164 continue; 0165 } 0166 0167 QPluginLoader loader(absolutePath); 0168 // Load only our own plugins 0169 if (loader.metaData().value(QStringLiteral("IID")) == QLatin1String("org.kde.CalendarEventsPlugin")) { 0170 const auto md = loader.metaData().value(QStringLiteral("MetaData")).toObject(); 0171 availablePlugins.insert(absolutePath, 0172 {md.value(QStringLiteral("Name")).toString(), 0173 md.value(QStringLiteral("Description")).toString(), 0174 md.value(QStringLiteral("Icon")).toString(), 0175 md.value(QStringLiteral("ConfigUi")).toString()}); 0176 } 0177 } 0178 } 0179 } 0180 0181 EventPluginsManagerPrivate::~EventPluginsManagerPrivate() 0182 { 0183 qDeleteAll(plugins); 0184 } 0185 0186 EventPluginsManager::EventPluginsManager(QObject *parent) 0187 : QObject(parent) 0188 , d(new EventPluginsManagerPrivate) 0189 { 0190 } 0191 0192 EventPluginsManager::~EventPluginsManager() 0193 { 0194 delete d; 0195 } 0196 0197 void EventPluginsManager::populateEnabledPluginsList(const QStringList &pluginsList) 0198 { 0199 d->model->beginResetModel(); 0200 d->enabledPlugins = pluginsList; 0201 d->model->endResetModel(); 0202 } 0203 0204 void EventPluginsManager::setEnabledPlugins(QStringList &pluginsList) 0205 { 0206 d->model->beginResetModel(); 0207 d->enabledPlugins = pluginsList; 0208 0209 // Remove all already loaded plugins from the pluginsList 0210 // and unload those plugins that are not in the pluginsList 0211 auto i = d->plugins.begin(); 0212 while (i != d->plugins.end()) { 0213 const QString pluginPath = (*i)->property("pluginPath").toString(); 0214 if (pluginsList.contains(pluginPath)) { 0215 pluginsList.removeAll(pluginPath); 0216 ++i; 0217 } else { 0218 (*i)->deleteLater(); 0219 i = d->plugins.erase(i); 0220 } 0221 } 0222 0223 // Now load all the plugins left in pluginsList 0224 for (const QString &pluginPath : std::as_const(pluginsList)) { 0225 loadPlugin(pluginPath); 0226 } 0227 0228 d->model->endResetModel(); 0229 Q_EMIT pluginsChanged(); 0230 } 0231 0232 QStringList EventPluginsManager::enabledPlugins() const 0233 { 0234 return d->enabledPlugins; 0235 } 0236 0237 void EventPluginsManager::loadPlugin(const QString &absolutePath) 0238 { 0239 QPluginLoader loader(absolutePath); 0240 0241 if (!loader.load()) { 0242 qWarning() << "Could not create Plasma Calendar Plugin: " << absolutePath; 0243 qWarning() << loader.errorString(); 0244 return; 0245 } 0246 0247 QObject *obj = loader.instance(); 0248 if (obj) { 0249 CalendarEvents::CalendarEventsPlugin *eventsPlugin = qobject_cast<CalendarEvents::CalendarEventsPlugin *>(obj); 0250 if (eventsPlugin) { 0251 qDebug() << "Loading Calendar plugin" << eventsPlugin; 0252 eventsPlugin->setProperty("pluginPath", absolutePath); 0253 d->plugins << eventsPlugin; 0254 0255 // Connect the relay signals 0256 connect(eventsPlugin, &CalendarEvents::CalendarEventsPlugin::dataReady, this, &EventPluginsManager::dataReady); 0257 connect(eventsPlugin, &CalendarEvents::CalendarEventsPlugin::eventModified, this, &EventPluginsManager::eventModified); 0258 connect(eventsPlugin, &CalendarEvents::CalendarEventsPlugin::eventRemoved, this, &EventPluginsManager::eventRemoved); 0259 connect(eventsPlugin, &CalendarEvents::CalendarEventsPlugin::alternateDateReady, this, &EventPluginsManager::alternateDateReady); 0260 connect(eventsPlugin, &CalendarEvents::CalendarEventsPlugin::subLabelReady, this, &EventPluginsManager::subLabelReady); 0261 } else { 0262 // not our/valid plugin, so unload it 0263 loader.unload(); 0264 } 0265 } else { 0266 loader.unload(); 0267 } 0268 } 0269 0270 QList<CalendarEvents::CalendarEventsPlugin *> EventPluginsManager::plugins() const 0271 { 0272 return d->plugins; 0273 } 0274 0275 QAbstractListModel *EventPluginsManager::pluginsModel() const 0276 { 0277 return d->model.get(); 0278 } 0279 0280 #include "eventpluginsmanager.moc"