[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
This commit is contained in:
Martin Klapetek 2015-11-04 13:55:32 -05:00
parent fe0384f0d4
commit d0d0a27bac
7 changed files with 383 additions and 54 deletions

View File

@ -8,6 +8,7 @@ set(calendar_SRCS
#datetimerangefiltermodel.cpp
daysmodel.cpp
eventdatadecorator.cpp
eventpluginsmanager.cpp
)
add_library(calendarplugin SHARED ${calendar_SRCS})

View File

@ -21,11 +21,22 @@
#include "calendardata.h"
#include "calendar.h"
#include "eventdatadecorator.h"
#include "eventpluginsmanager.h"
#include <QtQml>
#include <QAbstractItemModel>
#include <QQmlEngine>
#include <QAbstractListModel>
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<QAbstractItemModel>();
qmlRegisterType<QAbstractListModel>();
qmlRegisterUncreatableType<EventDataDecorator>();
qmlRegisterSingletonType<EventPluginsManager>(uri, 2, 0, "EventPluginsManager", event_plugins_manager_provider);
}
//Q_EXPORT_PLUGIN2(calendarplugin, CalendarPlugin)
#include "moc_calendarplugin.cpp"

View File

@ -18,60 +18,18 @@
*/
#include "daysmodel.h"
#include "eventdatadecorator.h"
#include "eventpluginsmanager.h"
#include <QDebug>
#include <QByteArray>
#include <QPluginLoader>
#include <QDir>
#include <QCoreApplication>
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<Plasma::CalendarEventsPlugin*>(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<int, QByteArray> roleNames;
roleNames.insert(isCurrent, "isCurrent");
@ -142,9 +100,11 @@ 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) {
if (m_pluginsManager) {
Q_FOREACH (Plasma::CalendarEventsPlugin *eventsPlugin, m_pluginsManager->plugins()) {
eventsPlugin->loadEventsForDateRange(modelFirstDay, modelFirstDay.addDays(42));
}
}
}
void DaysModel::onDataReady(const QMultiHash<QDate, Plasma::EventData> &data)
@ -245,3 +205,30 @@ QModelIndex DaysModel::indexForDate(const QDate &date)
return createIndex(daysTo, 0);
}
void DaysModel::setPluginsManager(QObject *manager)
{
EventPluginsManager *m = qobject_cast<EventPluginsManager*>(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();
}

View File

@ -23,13 +23,14 @@
#include <QAbstractListModel>
#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<QObject*> eventsForDate(const QDate &date);
Q_SIGNALS:
@ -62,6 +65,7 @@ private Q_SLOTS:
private:
QModelIndex indexForDate(const QDate &date);
EventPluginsManager *m_pluginsManager;
QList<DayData> *m_data;
QList<QObject*> m_qmlData;
QDate m_lastRequestedAgendaDate;

View File

@ -0,0 +1,248 @@
/*
Copyright (C) 2015 Martin Klapetek <mklapetek@kde.org>
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 <QCoreApplication>
#include <QAbstractListModel>
#include <QJsonObject>
#include <QPluginLoader>
#include <QDir>
#include <QDebug>
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<int, QByteArray> 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<int, QByteArray> 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<Plasma::CalendarEventsPlugin*>(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<Plasma::CalendarEventsPlugin*> EventPluginsManager::plugins() const
{
return m_plugins;
}
QAbstractListModel* EventPluginsManager::pluginsModel() const
{
return m_model;
}
#include "eventpluginsmanager.moc"

View File

@ -0,0 +1,76 @@
/*
Copyright (C) 2015 Martin Klapetek <mklapetek@kde.org>
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 <QObject>
#include <QMap>
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<Plasma::CalendarEventsPlugin*> 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<QDate, Plasma::EventData> &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<Plasma::CalendarEventsPlugin*> m_plugins;
QMap<QString, QJsonObject> m_availablePlugins;
QStringList m_enabledPlugins;
};
#endif

View File

@ -123,6 +123,10 @@ PinchArea {
firstDayOfWeek: Qt.locale().firstDayOfWeek
today: root.today
Component.onCompleted: {
daysModel.setPluginsManager(EventPluginsManager);
}
onYearChanged: {
updateYearOverview()
updateDecadeOverview()