From d0d0a27bac0ff9ee84320dff1ff8b42d9696dccf Mon Sep 17 00:00:00 2001 From: Martin Klapetek Date: Wed, 4 Nov 2015 13:55:32 -0500 Subject: [PATCH] [calendar] Move the plugins handling to a separate class This is also made a QML singleton that will be used for the applet config view where it will add the plugin configs once we add that possibility. The same instance is then set to the DaysModel from QML. REVIEW: 125951 --- .../calendar/CMakeLists.txt | 1 + .../calendar/calendarplugin.cpp | 17 +- src/declarativeimports/calendar/daysmodel.cpp | 83 +++--- src/declarativeimports/calendar/daysmodel.h | 8 +- .../calendar/eventpluginsmanager.cpp | 248 ++++++++++++++++++ .../calendar/eventpluginsmanager.h | 76 ++++++ .../calendar/qml/MonthView.qml | 4 + 7 files changed, 383 insertions(+), 54 deletions(-) create mode 100644 src/declarativeimports/calendar/eventpluginsmanager.cpp create mode 100644 src/declarativeimports/calendar/eventpluginsmanager.h diff --git a/src/declarativeimports/calendar/CMakeLists.txt b/src/declarativeimports/calendar/CMakeLists.txt index 7497bda98..4aac02d5e 100644 --- a/src/declarativeimports/calendar/CMakeLists.txt +++ b/src/declarativeimports/calendar/CMakeLists.txt @@ -8,6 +8,7 @@ set(calendar_SRCS #datetimerangefiltermodel.cpp daysmodel.cpp eventdatadecorator.cpp + eventpluginsmanager.cpp ) add_library(calendarplugin SHARED ${calendar_SRCS}) diff --git a/src/declarativeimports/calendar/calendarplugin.cpp b/src/declarativeimports/calendar/calendarplugin.cpp index a4014bdcd..6014398db 100644 --- a/src/declarativeimports/calendar/calendarplugin.cpp +++ b/src/declarativeimports/calendar/calendarplugin.cpp @@ -21,11 +21,22 @@ #include "calendardata.h" #include "calendar.h" #include "eventdatadecorator.h" +#include "eventpluginsmanager.h" #include -#include +#include #include +static QObject *event_plugins_manager_provider(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(scriptEngine) + + EventPluginsManager *manager = new EventPluginsManager(); + engine->setObjectOwnership(manager, QQmlEngine::CppOwnership); + + return manager; +} + void CalendarPlugin::registerTypes(const char *uri) { Q_ASSERT(uri == QLatin1String("org.kde.plasma.calendar")); @@ -34,7 +45,5 @@ void CalendarPlugin::registerTypes(const char *uri) qmlRegisterType(); qmlRegisterType(); qmlRegisterUncreatableType(); + qmlRegisterSingletonType(uri, 2, 0, "EventPluginsManager", event_plugins_manager_provider); } - -//Q_EXPORT_PLUGIN2(calendarplugin, CalendarPlugin) -#include "moc_calendarplugin.cpp" diff --git a/src/declarativeimports/calendar/daysmodel.cpp b/src/declarativeimports/calendar/daysmodel.cpp index 6572321e6..8500b1f35 100644 --- a/src/declarativeimports/calendar/daysmodel.cpp +++ b/src/declarativeimports/calendar/daysmodel.cpp @@ -18,60 +18,18 @@ */ #include "daysmodel.h" +#include "eventdatadecorator.h" +#include "eventpluginsmanager.h" #include #include -#include #include -#include DaysModel::DaysModel(QObject *parent) : QAbstractListModel(parent), - m_agendaNeedsUpdate(false) + m_agendaNeedsUpdate(false), + m_pluginsManager(0) { - QString pluginPath; - - const QStringList paths = QCoreApplication::libraryPaths(); - Q_FOREACH (const QString &libraryPath, paths) { - const QString path(libraryPath + QStringLiteral("/plasmacalendarplugins")); - QDir dir(path); - - if (!dir.exists()) { - continue; - } - - QStringList entryList = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); - Q_FOREACH (const QString &fileName, entryList) { - QPluginLoader loader(dir.absoluteFilePath(fileName)); - - if (!loader.load()) { - qWarning() << "Could not create Plasma Calendar Plugin: " << pluginPath; - qWarning() << loader.errorString(); - continue; - } - - QObject *obj = loader.instance(); - if (obj) { - Plasma::CalendarEventsPlugin *eventsPlugin = qobject_cast(obj); - if (eventsPlugin) { - qDebug() << "Adding Calendar plugin" << eventsPlugin; - connect(eventsPlugin, &Plasma::CalendarEventsPlugin::dataReady, - this, &DaysModel::onDataReady); - connect(eventsPlugin, &Plasma::CalendarEventsPlugin::eventModified, - this, &DaysModel::onEventModified); - connect(eventsPlugin, &Plasma::CalendarEventsPlugin::eventRemoved, - this, &DaysModel::onEventRemoved); - m_eventPlugins << eventsPlugin; - } else { - // not our/valid plugin, so unload it - loader.unload(); - } - } else { - loader.unload(); - } - } - } - QHash roleNames; roleNames.insert(isCurrent, "isCurrent"); @@ -142,8 +100,10 @@ void DaysModel::update() const QDate modelFirstDay(m_data->at(0).yearNumber, m_data->at(0).monthNumber, m_data->at(0).dayNumber); - Q_FOREACH (Plasma::CalendarEventsPlugin *eventsPlugin, m_eventPlugins) { - eventsPlugin->loadEventsForDateRange(modelFirstDay, modelFirstDay.addDays(42)); + if (m_pluginsManager) { + Q_FOREACH (Plasma::CalendarEventsPlugin *eventsPlugin, m_pluginsManager->plugins()) { + eventsPlugin->loadEventsForDateRange(modelFirstDay, modelFirstDay.addDays(42)); + } } } @@ -245,3 +205,30 @@ QModelIndex DaysModel::indexForDate(const QDate &date) return createIndex(daysTo, 0); } + +void DaysModel::setPluginsManager(QObject *manager) +{ + EventPluginsManager *m = qobject_cast(manager); + + if (!m) { + return; + } + + if (m_pluginsManager != 0) { + m_pluginsManager->deleteLater(); + m_pluginsManager = 0; + } + + m_pluginsManager = m; + + connect(m_pluginsManager, &EventPluginsManager::dataReady, + this, &DaysModel::onDataReady); + connect(m_pluginsManager, &EventPluginsManager::eventModified, + this, &DaysModel::onEventModified); + connect(m_pluginsManager, &EventPluginsManager::eventRemoved, + this, &DaysModel::onEventRemoved); + connect(m_pluginsManager, &EventPluginsManager::pluginsChanged, + this, &DaysModel::update); + + update(); +} diff --git a/src/declarativeimports/calendar/daysmodel.h b/src/declarativeimports/calendar/daysmodel.h index 8d97c79be..9f4439005 100644 --- a/src/declarativeimports/calendar/daysmodel.h +++ b/src/declarativeimports/calendar/daysmodel.h @@ -23,13 +23,14 @@ #include #include "daydata.h" -#include "eventdatadecorator.h" - #include "plasmacalendarintegration/calendareventsplugin.h" +class EventPluginsManager; + class DaysModel : public QAbstractListModel { Q_OBJECT + public: enum Roles { isCurrent = Qt::UserRole + 1, @@ -49,6 +50,8 @@ public: QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; void update(); + Q_INVOKABLE void setPluginsManager(QObject *manager); + Q_INVOKABLE QList eventsForDate(const QDate &date); Q_SIGNALS: @@ -62,6 +65,7 @@ private Q_SLOTS: private: QModelIndex indexForDate(const QDate &date); + EventPluginsManager *m_pluginsManager; QList *m_data; QList m_qmlData; QDate m_lastRequestedAgendaDate; diff --git a/src/declarativeimports/calendar/eventpluginsmanager.cpp b/src/declarativeimports/calendar/eventpluginsmanager.cpp new file mode 100644 index 000000000..20f648f88 --- /dev/null +++ b/src/declarativeimports/calendar/eventpluginsmanager.cpp @@ -0,0 +1,248 @@ +/* + Copyright (C) 2015 Martin Klapetek + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +*/ + +#include "eventpluginsmanager.h" +#include "plasmacalendarintegration/calendareventsplugin.h" + +#include +#include +#include +#include +#include +#include + +class EventPluginsModel : public QAbstractListModel +{ + Q_OBJECT +public: + EventPluginsModel(EventPluginsManager *manager) : QAbstractListModel(manager) + { + m_manager = manager; + m_roles = QAbstractListModel::roleNames(); + m_roles.insert(Qt::EditRole, QByteArrayLiteral("checked")); + m_roles.insert(Qt::UserRole, QByteArrayLiteral("configUi")); + }; + + // make these two available to the manager + void beginResetModel() + { + QAbstractListModel::beginResetModel(); + } + + void endResetModel() + { + QAbstractListModel::endResetModel(); + } + + virtual QHash roleNames() const Q_DECL_OVERRIDE + { + return m_roles; + } + + Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE + { + Q_UNUSED(parent); + return m_manager->m_availablePlugins.size(); + } + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE + { + if (!index.isValid() && !m_manager) { + return QVariant(); + } + + const QString currentPlugin = m_manager->m_availablePlugins.keys().at(index.row()); + const QJsonObject metadata = m_manager->m_availablePlugins.value(currentPlugin).value(QStringLiteral("MetaData")).toObject(); + + switch (role) { + case Qt::DisplayRole: + return metadata.value(QStringLiteral("Name")); + case Qt::DecorationRole: + return metadata.value(QStringLiteral("Icon")); + case Qt::UserRole: + { + // The currentPlugin path contains the full path including + // the plugin filename, so it needs to be cut off from the last '/' + const QStringRef pathRef = currentPlugin.leftRef(currentPlugin.lastIndexOf('/')); + const QString qmlFilePath = metadata.value(QStringLiteral("ConfigUi")).toString(); + return QString(pathRef % '/' % qmlFilePath); + } + case Qt::EditRole: + return m_manager->m_enabledPlugins.contains(currentPlugin); + } + + return QVariant(); + + } + + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) Q_DECL_OVERRIDE + { + if (role != Qt::EditRole || !index.isValid()) { + return false; + } + + bool enabled = value.toBool(); + const QString pluginPath = m_manager->m_availablePlugins.keys().at(index.row()); + + if (enabled) { + if (!m_manager->m_enabledPlugins.contains(pluginPath)) { + m_manager->m_enabledPlugins << pluginPath; + } + } else { + m_manager->m_enabledPlugins.removeOne(pluginPath); + } + + emit dataChanged(index, index); + + return true; + } + + Q_INVOKABLE QVariant get(int row, const QByteArray &role) + { + return data(createIndex(row, 0), roleNames().key(role)); + } + +private: + EventPluginsManager *m_manager; + QHash m_roles; +}; + +EventPluginsManager::EventPluginsManager(QObject *parent) + : QObject(parent) +{ + QString pluginPath; + + // First of all get a list of available plugins + // and get their metadata. This alone is enough + // for the applet config to work + const QStringList paths = QCoreApplication::libraryPaths(); + Q_FOREACH (const QString &libraryPath, paths) { + const QString path(libraryPath + QStringLiteral("/plasmacalendarplugins")); + QDir dir(path); + + if (!dir.exists()) { + continue; + } + + QStringList entryList = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); + + Q_FOREACH (const QString &fileName, entryList) { + const QString absolutePath = dir.absoluteFilePath(fileName); + QPluginLoader loader(absolutePath); + // Load only our own plugins + if (loader.metaData().value(QStringLiteral("IID")) == QLatin1String("org.kde.plasma.CalendarEventsPlugin")) { + m_availablePlugins.insert(absolutePath, loader.metaData()); + } + } + } + + m_model = new EventPluginsModel(this); + Q_EMIT pluginsChanged(); +} + +EventPluginsManager::~EventPluginsManager() +{ + qDeleteAll(m_plugins); +} + +void EventPluginsManager::populateEnabledPluginsList(const QStringList &pluginsList) +{ + m_model->beginResetModel(); + m_enabledPlugins = pluginsList; + m_model->endResetModel(); +} + +void EventPluginsManager::setEnabledPlugins(QStringList &pluginsList) +{ + m_model->beginResetModel(); + m_enabledPlugins = pluginsList; + + // Remove all already loaded plugins from the pluginsList + // and unload those plugins that are not in the pluginsList + auto i = m_plugins.begin(); + while (i != m_plugins.end()) { + const QString pluginPath = (*i)->property("pluginPath").toString(); + if (pluginsList.contains(pluginPath)) { + pluginsList.removeAll(pluginPath); + ++i; + } else { + (*i)->deleteLater(); + i = m_plugins.erase(i); + } + } + + // Now load all the plugins left in pluginsList + Q_FOREACH (const QString &pluginPath, pluginsList) { + loadPlugin(pluginPath); + } + + m_model->endResetModel(); + Q_EMIT pluginsChanged(); +} + +QStringList EventPluginsManager::enabledPlugins() const +{ + return m_enabledPlugins; +} + +void EventPluginsManager::loadPlugin(const QString &absolutePath) +{ + QPluginLoader loader(absolutePath); + + if (!loader.load()) { + qWarning() << "Could not create Plasma Calendar Plugin: " << absolutePath; + qWarning() << loader.errorString(); + return; + } + + QObject *obj = loader.instance(); + if (obj) { + Plasma::CalendarEventsPlugin *eventsPlugin = qobject_cast(obj); + if (eventsPlugin) { + qDebug() << "Loading Calendar plugin" << eventsPlugin; + eventsPlugin->setProperty("pluginPath", absolutePath); + m_plugins << eventsPlugin; + + // Connect the relay signals + connect(eventsPlugin, &Plasma::CalendarEventsPlugin::dataReady, + this, &EventPluginsManager::dataReady); + connect(eventsPlugin, &Plasma::CalendarEventsPlugin::eventModified, + this, &EventPluginsManager::eventModified); + connect(eventsPlugin, &Plasma::CalendarEventsPlugin::eventRemoved, + this, &EventPluginsManager::eventRemoved); + } else { + // not our/valid plugin, so unload it + loader.unload(); + } + } else { + loader.unload(); + } +} + +QList EventPluginsManager::plugins() const +{ + return m_plugins; +} + +QAbstractListModel* EventPluginsManager::pluginsModel() const +{ + return m_model; +} + +#include "eventpluginsmanager.moc" diff --git a/src/declarativeimports/calendar/eventpluginsmanager.h b/src/declarativeimports/calendar/eventpluginsmanager.h new file mode 100644 index 000000000..d736487a4 --- /dev/null +++ b/src/declarativeimports/calendar/eventpluginsmanager.h @@ -0,0 +1,76 @@ +/* + Copyright (C) 2015 Martin Klapetek + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +*/ + +#ifndef EVENTPLUGINSMANAGER_H +#define EVENTPLUGINSMANAGER_H + +#include +#include + +namespace Plasma { +class CalendarEventsPlugin; +class EventData; +} +class EventPluginsModel; +class QAbstractListModel; + +class EventPluginsManager : public QObject +{ + Q_OBJECT + Q_PROPERTY(QAbstractListModel *model READ pluginsModel NOTIFY pluginsChanged) + Q_PROPERTY(QStringList enabledPlugins READ enabledPlugins WRITE setEnabledPlugins NOTIFY pluginsChanged) + +public: + EventPluginsManager(QObject *parent = 0); + ~EventPluginsManager(); + + QList plugins() const; + QAbstractListModel* pluginsModel() const; + + // This is a helper function to set which plugins + // are enabled without needing to go through setEnabledPlugins + // which also loads the plugins; from the Applet config + // the plugins are not required to be actually loaded + Q_INVOKABLE void populateEnabledPluginsList(const QStringList &pluginsList); + + void setEnabledPlugins(QStringList &pluginsList); + QStringList enabledPlugins() const; + +Q_SIGNALS: + void pluginsChanged(); + + // These three signals below are used for relaying the + // plugin signals so that the EventPluginsManager don't + // have to worry about connecting to newly loaded plugins + void dataReady(const QMultiHash &data); + void eventModified(const Plasma::EventData &modifiedEvent); + void eventRemoved(const QString &uid); + +private: + void loadPlugin(const QString &absolutePath); + + friend class EventPluginsModel; + EventPluginsModel *m_model; + QList m_plugins; + QMap m_availablePlugins; + QStringList m_enabledPlugins; +}; + +#endif + diff --git a/src/declarativeimports/calendar/qml/MonthView.qml b/src/declarativeimports/calendar/qml/MonthView.qml index 535b6e438..59e084daa 100644 --- a/src/declarativeimports/calendar/qml/MonthView.qml +++ b/src/declarativeimports/calendar/qml/MonthView.qml @@ -123,6 +123,10 @@ PinchArea { firstDayOfWeek: Qt.locale().firstDayOfWeek today: root.today + Component.onCompleted: { + daysModel.setPluginsManager(EventPluginsManager); + } + onYearChanged: { updateYearOverview() updateDecadeOverview()