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()