From eb47805d72bee6524777280d11af7a38e9adc373 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 24 Dec 2013 13:51:20 +0100 Subject: [PATCH 01/12] DataContainer can carry a QAbstractItemModel aroun same rules of sharing and memory management apply. The model will be shared between all visualizations and deleted when nobody is connected to the source anymore --- src/plasma/datacontainer.cpp | 27 +++++++++++++++++++++++++++ src/plasma/datacontainer.h | 25 +++++++++++++++++++++++++ src/plasma/private/datacontainer_p.h | 3 +++ 3 files changed, 55 insertions(+) diff --git a/src/plasma/datacontainer.cpp b/src/plasma/datacontainer.cpp index 3ebc66a3b..58a34bfc2 100644 --- a/src/plasma/datacontainer.cpp +++ b/src/plasma/datacontainer.cpp @@ -21,6 +21,7 @@ #include "private/storage_p.h" #include +#include #include "plasma.h" @@ -65,6 +66,17 @@ void DataContainer::setData(const QString &key, const QVariant &value) setNeedsToBeStored(true); } +void DataContainer::setModel(QAbstractItemModel *model) +{ + if (d->model) { + d->model.data()->deleteLater(); + } + + d->model = model; + model->setParent(this); + emit modelChanged(objectName(), model); +} + void DataContainer::removeAllData() { if (d->data.isEmpty()) { @@ -109,6 +121,9 @@ void DataContainer::connectVisualization(QObject *visualization, uint pollingInt } else { disconnect(relay, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); + //modelChanged is always emitted by the dataSource since there is no polling there + disconnect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), + visualization, SLOT(modelChanged(QString, QAbstractItemModel *))); //relay->isUnused(); } } else if (pollingInterval < 1) { @@ -120,6 +135,8 @@ void DataContainer::connectVisualization(QObject *visualization, uint pollingInt } else { disconnect(this, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); + disconnect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), + visualization, SLOT(modelChanged(QString, QAbstractItemModel *))); } } else { connect(visualization, SIGNAL(destroyed(QObject*)), @@ -131,6 +148,8 @@ void DataContainer::connectVisualization(QObject *visualization, uint pollingInt d->relayObjects[visualization] = 0; connect(this, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); + connect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), + visualization, SLOT(modelChanged(QString, QAbstractItemModel *))); } else { //qDebug() << " connecting to a relay"; // we only want to do an imediate update if this is not the first object to connect to us @@ -141,6 +160,9 @@ void DataContainer::connectVisualization(QObject *visualization, uint pollingInt alignment, immediateUpdate); connect(relay, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); + //modelChanged is always emitted by the dataSource since there is no polling there + connect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), + visualization, SLOT(modelChanged(QString, QAbstractItemModel *))); } } @@ -271,6 +293,8 @@ void DataContainer::disconnectVisualization(QObject *visualization) // it is connected directly to the DataContainer itself disconnect(this, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); + disconnect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), + visualization, SLOT(modelChanged(QString, QAbstractItemModel *))); } else { SignalRelay *relay = objIt.value(); @@ -280,6 +304,9 @@ void DataContainer::disconnectVisualization(QObject *visualization) } else { disconnect(relay, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); + //modelChanged is always emitted by the dataSource since there is no polling there + disconnect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), + visualization, SLOT(modelChanged(QString, QAbstractItemModel *))); } } diff --git a/src/plasma/datacontainer.h b/src/plasma/datacontainer.h index f7a165980..2a3841427 100644 --- a/src/plasma/datacontainer.h +++ b/src/plasma/datacontainer.h @@ -28,6 +28,8 @@ #include #include +class QAbstractItemModel; + namespace Plasma { @@ -106,6 +108,19 @@ class PLASMA_EXPORT DataContainer : public QObject **/ void removeAllData(); + /** + * Associates a model with this DataContainer. Use this for data + * that is intended to be a long list of items. + * + * The ownership of the model is transferred to the DataContainer, + * so the model will be deletd when a new one is set or when the + * DataContainer itself is deleted, so it will be deleted when there won't be any + * visualization associated to this source. + * + * @param model the model that will be associated with this DataContainer + */ + void setModel(QAbstractItemModel *model); + /** * @return true if the visualization is currently connected */ @@ -188,6 +203,16 @@ class PLASMA_EXPORT DataContainer : public QObject **/ void dataUpdated(const QString &source, const Plasma::DataEngine::Data &data); + /** + * A new model has been associated to this source, + * visualizations can safely use it as long they are connected to this source. + * + * @param source the objectName() of the DataContainer (and hence the name + * of the source) that owns the model + * @param model the QAbstractItemModel instance + */ + void modelChanged(const QString &source, QAbstractItemModel *model); + /** * Emitted when the last visualization is disconnected. * diff --git a/src/plasma/private/datacontainer_p.h b/src/plasma/private/datacontainer_p.h index 19cd3516a..a4760dcda 100644 --- a/src/plasma/private/datacontainer_p.h +++ b/src/plasma/private/datacontainer_p.h @@ -27,6 +27,8 @@ #include #include +#include + class QTimer; namespace Plasma @@ -87,6 +89,7 @@ public: Storage* storage; QBasicTimer storageTimer; QBasicTimer checkUsageTimer; + QWeakPointer model; int storageCount; bool dirty : 1; bool cached : 1; From 98d8bacf2b6b9dd1a49ef81c30b4ae4297cb291c Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 24 Dec 2013 14:02:20 +0100 Subject: [PATCH 02/12] Revert "remove unused typedefs" This reverts commit 104e8d9e1fba2c1bade727dc1691365f93ff783b. --- src/plasma/dataengine.h | 2 ++ src/plasma/private/dataengine_p.h | 2 +- src/plasma/scripting/dataenginescript.cpp | 4 ++-- src/plasma/scripting/dataenginescript.h | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/plasma/dataengine.h b/src/plasma/dataengine.h index 35920a317..ffe6b6ead 100644 --- a/src/plasma/dataengine.h +++ b/src/plasma/dataengine.h @@ -62,6 +62,8 @@ class PLASMA_EXPORT DataEngine : public QObject public: typedef QHash Dict; typedef QMap Data; + typedef QMapIterator DataIterator; + typedef QHash SourceDict; /** * Constructor. diff --git a/src/plasma/private/dataengine_p.h b/src/plasma/private/dataengine_p.h index 53907cd89..d8ade83af 100644 --- a/src/plasma/private/dataengine_p.h +++ b/src/plasma/private/dataengine_p.h @@ -98,7 +98,7 @@ class DataEnginePrivate int updateTimerId; int minPollingInterval; QTime updateTimestamp; - QHash sources; + DataEngine::SourceDict sources; bool valid; DataEngineScript *script; QString serviceName; diff --git a/src/plasma/scripting/dataenginescript.cpp b/src/plasma/scripting/dataenginescript.cpp index 0b25b1baa..ac45e4a48 100644 --- a/src/plasma/scripting/dataenginescript.cpp +++ b/src/plasma/scripting/dataenginescript.cpp @@ -166,12 +166,12 @@ void DataEngineScript::addSource(DataContainer *source) } } -QHash DataEngineScript::containerDict() const +DataEngine::SourceDict DataEngineScript::containerDict() const { if (d->dataEngine) { return d->dataEngine->containerDict(); } - return QHash(); + return DataEngine::SourceDict(); } void DataEngineScript::removeSource(const QString &source) diff --git a/src/plasma/scripting/dataenginescript.h b/src/plasma/scripting/dataenginescript.h index 6c0fbc78c..4618eacc0 100644 --- a/src/plasma/scripting/dataenginescript.h +++ b/src/plasma/scripting/dataenginescript.h @@ -134,7 +134,7 @@ protected: void setPollingInterval(uint frequency); void removeAllSources(); void addSource(DataContainer *source); - QHash containerDict() const; + DataEngine::SourceDict containerDict() const; void removeSource(const QString &source); void updateAllSources(); void forceImmediateUpdateOfAllVisualizations(); From 05a62e56521d5f0925e76d5944ae805c0c116483 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 24 Dec 2013 14:29:37 +0100 Subject: [PATCH 03/12] install in the proper folder --- examples/dataengines/customDataContainers/CMakeLists.txt | 2 +- examples/dataengines/simpleEngine/CMakeLists.txt | 2 +- examples/dataengines/sourcesOnRequest/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/dataengines/customDataContainers/CMakeLists.txt b/examples/dataengines/customDataContainers/CMakeLists.txt index add3c7154..c48ea081a 100644 --- a/examples/dataengines/customDataContainers/CMakeLists.txt +++ b/examples/dataengines/customDataContainers/CMakeLists.txt @@ -12,5 +12,5 @@ target_link_libraries(plasma_dataengine_example_customDataContainers KF5::Service ) -install(TARGETS plasma_dataengine_example_customDataContainers DESTINATION ${PLUGIN_INSTALL_DIR}) +install(TARGETS plasma_dataengine_example_customDataContainers DESTINATION ${PLUGIN_INSTALL_DIR}/plasma/dataengine) install(FILES plasma-dataengine-example-customDataContainers.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) diff --git a/examples/dataengines/simpleEngine/CMakeLists.txt b/examples/dataengines/simpleEngine/CMakeLists.txt index fb3050044..27ae5eef2 100644 --- a/examples/dataengines/simpleEngine/CMakeLists.txt +++ b/examples/dataengines/simpleEngine/CMakeLists.txt @@ -12,5 +12,5 @@ target_link_libraries(plasma_dataengine_example_simpleEngine KF5::I18n ) -install(TARGETS plasma_dataengine_example_simpleEngine DESTINATION ${PLUGIN_INSTALL_DIR}) +install(TARGETS plasma_dataengine_example_simpleEngine DESTINATION ${PLUGIN_INSTALL_DIR}/plasma/dataengine) install(FILES plasma-dataengine-example-simpleEngine.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) diff --git a/examples/dataengines/sourcesOnRequest/CMakeLists.txt b/examples/dataengines/sourcesOnRequest/CMakeLists.txt index 43932efed..9c0a039aa 100644 --- a/examples/dataengines/sourcesOnRequest/CMakeLists.txt +++ b/examples/dataengines/sourcesOnRequest/CMakeLists.txt @@ -11,5 +11,5 @@ target_link_libraries(plasma_dataengine_example_sourcesOnRequest KF5::Service ) -install(TARGETS plasma_dataengine_example_sourcesOnRequest DESTINATION ${PLUGIN_INSTALL_DIR}) +install(TARGETS plasma_dataengine_example_sourcesOnRequest DESTINATION ${PLUGIN_INSTALL_DIR}/plasma/dataengine) install(FILES plasma-dataengine-example-sourcesOnRequest.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) From c318acb242f12ea23626cfd1f10f115955a928f8 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 24 Dec 2013 14:29:53 +0100 Subject: [PATCH 04/12] make use of the model embedding --- .../sourcesOnRequest/sourcesOnRequest.cpp | 24 ++++++++++++++++++- src/plasma/datacontainer.cpp | 5 ++++ src/plasma/datacontainer.h | 5 ++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/examples/dataengines/sourcesOnRequest/sourcesOnRequest.cpp b/examples/dataengines/sourcesOnRequest/sourcesOnRequest.cpp index 912a275b8..68a251e95 100644 --- a/examples/dataengines/sourcesOnRequest/sourcesOnRequest.cpp +++ b/examples/dataengines/sourcesOnRequest/sourcesOnRequest.cpp @@ -27,9 +27,12 @@ #include +#include + /* This DataEngine shows how to created sources on demand as they are requested and update them on visualization-requested update intervals. + It also shows how to bind and arbitrary QAbstractItemModel to a source */ SourcesOnRequestEngine::SourcesOnRequestEngine(QObject *parent, const QVariantList &args) @@ -61,6 +64,15 @@ bool SourcesOnRequestEngine::sourceRequestEvent(const QString &source) // expects. So ALWAYS key the new data by the source string as below: setData(source, "Update Count", 0); + Plasma::DataContainer *s = containerForSource(source); + if (!s->model()) { + QStandardItemModel *m = new QStandardItemModel; + m->appendRow(new QStandardItem("Item1, first update")); + m->appendRow(new QStandardItem("Item2, first update")); + m->appendRow(new QStandardItem("Item3, first update")); + s->setModel(m); + } + // as we successfully set up the source, return true return true; } @@ -81,7 +93,17 @@ bool SourcesOnRequestEngine::updateSourceEvent(const QString &source) // sourceRequestEvent, however, this will result in expected behavior: visualizations // connected to the sources which have setData called for them will be notified // of these changes. - setData(source, "Update Count", containerForSource(source)->data().value("Update Count").toInt() + 1); + const int updateCount = containerForSource(source)->data().value("Update Count").toInt() + 1; + setData(source, "Update Count", updateCount); + + Plasma::DataContainer *s = containerForSource(source); + if (!s->model()) { + QStandardItemModel *m = new QStandardItemModel; + m->appendRow(new QStandardItem("Item1, update " + updateCount)); + m->appendRow(new QStandardItem("Item2, update " + updateCount)); + m->appendRow(new QStandardItem("Item3, update " + updateCount)); + s->setModel(m); + } // Since we updated the source immediately here, we need to return true so the DataEngine // knows to continue with the update notification for visualizations. diff --git a/src/plasma/datacontainer.cpp b/src/plasma/datacontainer.cpp index 58a34bfc2..8891d8964 100644 --- a/src/plasma/datacontainer.cpp +++ b/src/plasma/datacontainer.cpp @@ -77,6 +77,11 @@ void DataContainer::setModel(QAbstractItemModel *model) emit modelChanged(objectName(), model); } +QAbstractItemModel *DataContainer::model() +{ + return d->model.data(); +} + void DataContainer::removeAllData() { if (d->data.isEmpty()) { diff --git a/src/plasma/datacontainer.h b/src/plasma/datacontainer.h index 2a3841427..07d098ca3 100644 --- a/src/plasma/datacontainer.h +++ b/src/plasma/datacontainer.h @@ -121,6 +121,11 @@ class PLASMA_EXPORT DataContainer : public QObject */ void setModel(QAbstractItemModel *model); + /** + * @return the model owned by this DataSource + */ + QAbstractItemModel *model(); + /** * @return true if the visualization is currently connected */ From 124139c2bfe1f4e373f61ff201943756d2f5a3aa Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 24 Dec 2013 15:21:21 +0100 Subject: [PATCH 05/12] working models, add an example applet --- examples/applets/CMakeLists.txt | 1 + .../dataenginemodel/contents/ui/main.qml | 55 +++++++++++++++++++ .../applets/dataenginemodel/metadata.desktop | 20 +++++++ .../sourcesOnRequest/sourcesOnRequest.cpp | 12 ++-- src/declarativeimports/core/datasource.cpp | 10 ++++ src/declarativeimports/core/datasource.h | 11 +++- src/plasma/datacontainer.cpp | 4 ++ src/plasma/dataengine.cpp | 5 ++ 8 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 examples/applets/dataenginemodel/contents/ui/main.qml create mode 100644 examples/applets/dataenginemodel/metadata.desktop diff --git a/examples/applets/CMakeLists.txt b/examples/applets/CMakeLists.txt index ff9498720..56916fd24 100644 --- a/examples/applets/CMakeLists.txt +++ b/examples/applets/CMakeLists.txt @@ -1,5 +1,6 @@ plasma_install_package(config org.kde.example.configuration) +plasma_install_package(config org.kde.example.dataenginemodel) plasma_install_package(notes org.kde.example.notes) plasma_install_package(widgetgallery org.kde.example.widgetgallery) plasma_install_package(qmltasks org.kde.example.tasks) diff --git a/examples/applets/dataenginemodel/contents/ui/main.qml b/examples/applets/dataenginemodel/contents/ui/main.qml new file mode 100644 index 000000000..d64e71f48 --- /dev/null +++ b/examples/applets/dataenginemodel/contents/ui/main.qml @@ -0,0 +1,55 @@ +// -*- coding: iso-8859-1 -*- +/* + * Copyright 2012 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library 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. + */ + +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras + +Column { + width: 500 + height: 500 + property int minimumWidth: 200 + property int minimumHeight: 300 + + PlasmaCore.DataSource { + id: source + dataEngine: "org.kde.examples.sourcesOnRequest" + interval: 1000 + connectedSources: "test" + } + + PlasmaComponents.Label { + text: source.data.test["Update Count"] + } + PlasmaExtras.ScrollArea { + anchors { + left: parent.left + right: parent.right + } + height: 500 + ListView { + model: source.models.test + delegate: PlasmaComponents.Label { + text: model.display + } + } + } +} diff --git a/examples/applets/dataenginemodel/metadata.desktop b/examples/applets/dataenginemodel/metadata.desktop new file mode 100644 index 000000000..73e30a6c5 --- /dev/null +++ b/examples/applets/dataenginemodel/metadata.desktop @@ -0,0 +1,20 @@ +[Desktop Entry] +Comment=Example applet that shows how to use Models embeeded in DataEngines +Encoding=UTF-8 +Keywords= +Name=Dataengine model +Type=Service +Icon= +X-KDE-ParentApp= +X-KDE-PluginInfo-Author=Marco Martin +X-KDE-PluginInfo-Category=Miscellaneous +X-KDE-PluginInfo-Email=mart@kde.org +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-Name=org.kde.examples.dataenginemodel +X-KDE-PluginInfo-Version= +X-KDE-PluginInfo-Website= +X-KDE-ServiceTypes=Plasma/Applet +X-Plasma-API=declarativeappletscript + +X-Plasma-MainScript=ui/main.qml +X-Plasma-RemoteLocation= diff --git a/examples/dataengines/sourcesOnRequest/sourcesOnRequest.cpp b/examples/dataengines/sourcesOnRequest/sourcesOnRequest.cpp index 68a251e95..db35a91de 100644 --- a/examples/dataengines/sourcesOnRequest/sourcesOnRequest.cpp +++ b/examples/dataengines/sourcesOnRequest/sourcesOnRequest.cpp @@ -97,12 +97,12 @@ bool SourcesOnRequestEngine::updateSourceEvent(const QString &source) setData(source, "Update Count", updateCount); Plasma::DataContainer *s = containerForSource(source); - if (!s->model()) { - QStandardItemModel *m = new QStandardItemModel; - m->appendRow(new QStandardItem("Item1, update " + updateCount)); - m->appendRow(new QStandardItem("Item2, update " + updateCount)); - m->appendRow(new QStandardItem("Item3, update " + updateCount)); - s->setModel(m); + QStandardItemModel *m = qobject_cast(s->model()); + if (s->model()) { + m->clear(); + m->appendRow(new QStandardItem(QString("Item1, update %1").arg(updateCount))); + m->appendRow(new QStandardItem(QString("Item2, update %1").arg(updateCount))); + m->appendRow(new QStandardItem(QString("Item3, update %1").arg(updateCount))); } // Since we updated the source immediately here, we need to return true so the DataEngine diff --git a/src/declarativeimports/core/datasource.cpp b/src/declarativeimports/core/datasource.cpp index b44ce109f..64cd38f5b 100644 --- a/src/declarativeimports/core/datasource.cpp +++ b/src/declarativeimports/core/datasource.cpp @@ -148,6 +148,16 @@ void DataSource::dataUpdated(const QString &sourceName, const Plasma::DataEngine } } +void DataSource::modelChanged(const QString &sourceName, QAbstractItemModel *model) +{ + m_models[sourceName] = QVariant::fromValue(model); + connect(model, &QObject::destroyed, [=]() { + m_models.remove(sourceName); + emit modelsChanged(); + }); + emit modelsChanged(); +} + void DataSource::removeSource(const QString &source) { m_data.remove(source); diff --git a/src/declarativeimports/core/datasource.h b/src/declarativeimports/core/datasource.h index 01c613a17..16b11b3bd 100644 --- a/src/declarativeimports/core/datasource.h +++ b/src/declarativeimports/core/datasource.h @@ -89,11 +89,17 @@ public: /** * All the data fetched by this dataengine. - * This is a map of maps. At the first level, there are the source names, at the secons, they keys set by the DataEngine + * This is a map of maps. At the first level, there are the source names, at the second, they keys set by the DataEngine */ Q_PROPERTY(QVariantMap data READ data NOTIFY dataChanged); QVariantMap data() const {return m_data;} + /** + * All the models associated to this DataEngine, indexed by source + */ + Q_PROPERTY(QVariantMap models READ models NOTIFY modelsChanged); + QVariantMap models() const {return m_models;} + /** * @returns a Plasma::Service given a source name * @arg QString source source name we want a service of @@ -112,6 +118,7 @@ public: protected Q_SLOTS: void dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data); + void modelChanged(const QString &sourceName, QAbstractItemModel *model); void removeSource(const QString &source); void setupData(); @@ -124,6 +131,7 @@ Q_SIGNALS: void intervalChanged(); void engineChanged(); void dataChanged(); + void modelsChanged(); void connectedSourcesChanged(); void sourcesChanged(); @@ -132,6 +140,7 @@ private: int m_interval; QString m_engine; QVariantMap m_data; + QVariantMap m_models; Plasma::DataEngine* m_dataEngine; Plasma::DataEngineConsumer* m_dataEngineConsumer; QStringList m_connectedSources; diff --git a/src/plasma/datacontainer.cpp b/src/plasma/datacontainer.cpp index 8891d8964..218c8627d 100644 --- a/src/plasma/datacontainer.cpp +++ b/src/plasma/datacontainer.cpp @@ -68,6 +68,10 @@ void DataContainer::setData(const QString &key, const QVariant &value) void DataContainer::setModel(QAbstractItemModel *model) { + if (d->model.data() == model) { + return; + } + if (d->model) { d->model.data()->deleteLater(); } diff --git a/src/plasma/dataengine.cpp b/src/plasma/dataengine.cpp index a56448229..b58723318 100644 --- a/src/plasma/dataengine.cpp +++ b/src/plasma/dataengine.cpp @@ -521,6 +521,11 @@ void DataEnginePrivate::connectSource(DataContainer *s, QObject *visualization, QMetaObject::invokeMethod(visualization, "dataUpdated", Q_ARG(QString, s->objectName()), Q_ARG(Plasma::DataEngine::Data, s->data())); + if (s->d->model) { + QMetaObject::invokeMethod(visualization, "modelChanged", + Q_ARG(QString, s->objectName()), + Q_ARG(QAbstractItemModel *, s->d->model.data())); + } s->d->dirty = false; } } From a721878fda0de83e913f4f31e27ddecbcbba89b2 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 24 Dec 2013 15:42:44 +0100 Subject: [PATCH 06/12] use q QQmlPropertyMap, crash-- and more efficient --- src/declarativeimports/core/datasource.cpp | 7 +++---- src/declarativeimports/core/datasource.h | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/declarativeimports/core/datasource.cpp b/src/declarativeimports/core/datasource.cpp index 64cd38f5b..2278803b3 100644 --- a/src/declarativeimports/core/datasource.cpp +++ b/src/declarativeimports/core/datasource.cpp @@ -30,6 +30,7 @@ DataSource::DataSource(QObject* parent) m_dataEngine(0), m_dataEngineConsumer(0) { + m_models = new QQmlPropertyMap(this); setObjectName("DataSource"); } @@ -150,12 +151,10 @@ void DataSource::dataUpdated(const QString &sourceName, const Plasma::DataEngine void DataSource::modelChanged(const QString &sourceName, QAbstractItemModel *model) { - m_models[sourceName] = QVariant::fromValue(model); + m_models->insert(sourceName, QVariant::fromValue(model)); connect(model, &QObject::destroyed, [=]() { - m_models.remove(sourceName); - emit modelsChanged(); + m_models->clear(sourceName); }); - emit modelsChanged(); } void DataSource::removeSource(const QString &source) diff --git a/src/declarativeimports/core/datasource.h b/src/declarativeimports/core/datasource.h index 16b11b3bd..5008c934e 100644 --- a/src/declarativeimports/core/datasource.h +++ b/src/declarativeimports/core/datasource.h @@ -97,8 +97,8 @@ public: /** * All the models associated to this DataEngine, indexed by source */ - Q_PROPERTY(QVariantMap models READ models NOTIFY modelsChanged); - QVariantMap models() const {return m_models;} + Q_PROPERTY(QQmlPropertyMap *models READ models CONSTANT); + QQmlPropertyMap *models() const {return m_models;} /** * @returns a Plasma::Service given a source name @@ -131,7 +131,6 @@ Q_SIGNALS: void intervalChanged(); void engineChanged(); void dataChanged(); - void modelsChanged(); void connectedSourcesChanged(); void sourcesChanged(); @@ -140,7 +139,7 @@ private: int m_interval; QString m_engine; QVariantMap m_data; - QVariantMap m_models; + QQmlPropertyMap *m_models; Plasma::DataEngine* m_dataEngine; Plasma::DataEngineConsumer* m_dataEngineConsumer; QStringList m_connectedSources; From 4e2db852dc3c2c0706f2856d08d07eab5e966b90 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 24 Dec 2013 15:46:22 +0100 Subject: [PATCH 07/12] apidocs++ --- src/declarativeimports/core/datasource.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/declarativeimports/core/datasource.h b/src/declarativeimports/core/datasource.h index 5008c934e..868ab63b6 100644 --- a/src/declarativeimports/core/datasource.h +++ b/src/declarativeimports/core/datasource.h @@ -95,7 +95,9 @@ public: QVariantMap data() const {return m_data;} /** - * All the models associated to this DataEngine, indexed by source + * All the models associated to this DataEngine, indexed by source. + * In order for a model to be present, besides being implemented in the DataEngine, + * The user has to be connected to its source, so the source name has to be present in the connectedSources property. */ Q_PROPERTY(QQmlPropertyMap *models READ models CONSTANT); QQmlPropertyMap *models() const {return m_models;} From e272c313004be1f4bcfd5e14cb5677c6aa51eaf2 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 24 Dec 2013 16:58:08 +0100 Subject: [PATCH 08/12] OR, not AND a source is unused if there anre no relays OR nothing directly connected --- src/plasma/datacontainer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plasma/datacontainer.cpp b/src/plasma/datacontainer.cpp index 218c8627d..b017cf1bc 100644 --- a/src/plasma/datacontainer.cpp +++ b/src/plasma/datacontainer.cpp @@ -106,7 +106,7 @@ bool DataContainer::visualizationIsConnected(QObject *visualization) const void DataContainer::connectVisualization(QObject *visualization, uint pollingInterval, Plasma::Types::IntervalAlignment alignment) { - //qDebug() << "connecting visualization" << visualization << "at interval of" + //qDebug() << "connecting visualization" <::iterator objIt = d->relayObjects.find(visualization); bool connected = objIt != d->relayObjects.end(); @@ -363,7 +363,7 @@ void DataContainer::setNeedsUpdate(bool update) bool DataContainer::isUsed() const { - return !d->relays.isEmpty() && + return !d->relays.isEmpty() || receivers(SIGNAL(dataUpdated(QString, Plasma::DataEngine::Data))) > 0; } From 3184551ddb14faab67ded0a3479bc5e65c76ce17 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 24 Dec 2013 16:58:47 +0100 Subject: [PATCH 09/12] install the example in the proper place --- examples/applets/CMakeLists.txt | 2 +- examples/applets/dataenginemodel/metadata.desktop | 2 +- src/declarativeimports/core/datasource.cpp | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/applets/CMakeLists.txt b/examples/applets/CMakeLists.txt index 56916fd24..1d61e3305 100644 --- a/examples/applets/CMakeLists.txt +++ b/examples/applets/CMakeLists.txt @@ -1,6 +1,6 @@ plasma_install_package(config org.kde.example.configuration) -plasma_install_package(config org.kde.example.dataenginemodel) +plasma_install_package(dataenginemodel org.kde.example.dataenginemodel) plasma_install_package(notes org.kde.example.notes) plasma_install_package(widgetgallery org.kde.example.widgetgallery) plasma_install_package(qmltasks org.kde.example.tasks) diff --git a/examples/applets/dataenginemodel/metadata.desktop b/examples/applets/dataenginemodel/metadata.desktop index 73e30a6c5..3327b26f9 100644 --- a/examples/applets/dataenginemodel/metadata.desktop +++ b/examples/applets/dataenginemodel/metadata.desktop @@ -10,7 +10,7 @@ X-KDE-PluginInfo-Author=Marco Martin X-KDE-PluginInfo-Category=Miscellaneous X-KDE-PluginInfo-Email=mart@kde.org X-KDE-PluginInfo-License=GPL -X-KDE-PluginInfo-Name=org.kde.examples.dataenginemodel +X-KDE-PluginInfo-Name=org.kde.example.dataenginemodel X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website= X-KDE-ServiceTypes=Plasma/Applet diff --git a/src/declarativeimports/core/datasource.cpp b/src/declarativeimports/core/datasource.cpp index 2278803b3..8aeb4679e 100644 --- a/src/declarativeimports/core/datasource.cpp +++ b/src/declarativeimports/core/datasource.cpp @@ -160,6 +160,7 @@ void DataSource::modelChanged(const QString &sourceName, QAbstractItemModel *mod void DataSource::removeSource(const QString &source) { m_data.remove(source); + m_models->clear(source); //TODO: emit those signals as last thing if (m_connectedSources.contains(source)) { From f692b5aa745a846cc9df481b13e0d6d38bde43ba Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 24 Dec 2013 17:11:27 +0100 Subject: [PATCH 10/12] crash-- --- src/declarativeimports/core/datasource.cpp | 6 ++++++ src/plasma/datacontainer.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/declarativeimports/core/datasource.cpp b/src/declarativeimports/core/datasource.cpp index 8aeb4679e..6b0336299 100644 --- a/src/declarativeimports/core/datasource.cpp +++ b/src/declarativeimports/core/datasource.cpp @@ -151,7 +151,13 @@ void DataSource::dataUpdated(const QString &sourceName, const Plasma::DataEngine void DataSource::modelChanged(const QString &sourceName, QAbstractItemModel *model) { + if (!model) { + m_models->clear(sourceName); + return; + } + m_models->insert(sourceName, QVariant::fromValue(model)); + //FIXME: this will break in the case a second model is set connect(model, &QObject::destroyed, [=]() { m_models->clear(sourceName); }); diff --git a/src/plasma/datacontainer.cpp b/src/plasma/datacontainer.cpp index b017cf1bc..e8463927a 100644 --- a/src/plasma/datacontainer.cpp +++ b/src/plasma/datacontainer.cpp @@ -380,6 +380,12 @@ void DataContainer::timerEvent(QTimerEvent * event) if (!isUsed()) { // DO NOT CALL ANYTHING AFTER THIS LINE AS IT MAY GET DELETED! //qDebug() << objectName() << "is unused"; + + //NOTE: Notifying visualization of the model destruction before actual deletion avoids crashes in some edge cases + if (d->model) { + d->model.clear(); + emit modelChanged(objectName(), 0); + } emit becameUnused(objectName()); } d->checkUsageTimer.stop(); From dea794253382d2cddc8278a5838b623c5c6e9fbc Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 30 Dec 2013 10:21:36 +0100 Subject: [PATCH 11/12] add DataEngine::setModel --- .../sourcesOnRequest/sourcesOnRequest.cpp | 10 +++---- src/plasma/dataengine.cpp | 27 +++++++++++++++++++ src/plasma/dataengine.h | 23 ++++++++++++++++ 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/examples/dataengines/sourcesOnRequest/sourcesOnRequest.cpp b/examples/dataengines/sourcesOnRequest/sourcesOnRequest.cpp index db35a91de..14a23dda0 100644 --- a/examples/dataengines/sourcesOnRequest/sourcesOnRequest.cpp +++ b/examples/dataengines/sourcesOnRequest/sourcesOnRequest.cpp @@ -64,13 +64,12 @@ bool SourcesOnRequestEngine::sourceRequestEvent(const QString &source) // expects. So ALWAYS key the new data by the source string as below: setData(source, "Update Count", 0); - Plasma::DataContainer *s = containerForSource(source); - if (!s->model()) { + if (!model(source)) { QStandardItemModel *m = new QStandardItemModel; m->appendRow(new QStandardItem("Item1, first update")); m->appendRow(new QStandardItem("Item2, first update")); m->appendRow(new QStandardItem("Item3, first update")); - s->setModel(m); + setModel(source, m); } // as we successfully set up the source, return true @@ -96,9 +95,8 @@ bool SourcesOnRequestEngine::updateSourceEvent(const QString &source) const int updateCount = containerForSource(source)->data().value("Update Count").toInt() + 1; setData(source, "Update Count", updateCount); - Plasma::DataContainer *s = containerForSource(source); - QStandardItemModel *m = qobject_cast(s->model()); - if (s->model()) { + QStandardItemModel *m = qobject_cast(model(source)); + if (m) { m->clear(); m->appendRow(new QStandardItem(QString("Item1, update %1").arg(updateCount))); m->appendRow(new QStandardItem(QString("Item2, update %1").arg(updateCount))); diff --git a/src/plasma/dataengine.cpp b/src/plasma/dataengine.cpp index b58723318..194df428d 100644 --- a/src/plasma/dataengine.cpp +++ b/src/plasma/dataengine.cpp @@ -21,6 +21,7 @@ #include "private/dataengine_p.h" #include "private/datacontainer_p.h" +#include #include #include #include @@ -227,6 +228,32 @@ void DataEngine::removeData(const QString &source, const QString &key) } } +void DataEngine::setModel(const QString &source, QAbstractItemModel *model) +{ + if (model) { + setData(source, "HasModel", true); + } else { + removeData(source, "HasModel"); + } + + Plasma::DataContainer *s = containerForSource(source); + + if (s) { + s->setModel(model); + } +} + +QAbstractItemModel *DataEngine::model(const QString &source) +{ + Plasma::DataContainer *s = containerForSource(source); + + if (s) { + return s->model(); + } else { + return 0; + } +} + void DataEngine::addSource(DataContainer *source) { if (d->sources.contains(source->objectName())) { diff --git a/src/plasma/dataengine.h b/src/plasma/dataengine.h index ffe6b6ead..c0e6a9a59 100644 --- a/src/plasma/dataengine.h +++ b/src/plasma/dataengine.h @@ -31,6 +31,8 @@ #include #include +class QAbstractItemModel; + namespace Plasma { @@ -306,6 +308,27 @@ Types::NoAlignment) const; **/ void removeData(const QString &source, const QString &key); + /** + * Associates a model to a data source. If the source + * doesn't exist then it is created. The source will have the key "HasModel" to easily indicate there is a model present. + * + * The ownership of the model is transferred to the DataContainer, + * so the model will be deletd when a new one is set or when the + * DataContainer itself is deleted. As the DataContainer, it will be + * deleted when there won't be any + * visualization associated to this source. + * + * @param source the name of the data source + * @param model the model instance + */ + void setModel(const QString &source, QAbstractItemModel *model); + + /** + * @return The model associated to a source if any. The ownership of the model stays with the DataContainer. + * Returns 0 if there isn't any model associated or if the source doesn't exists. + */ + QAbstractItemModel *model(const QString &source); + /** * Adds an already constructed data source. The DataEngine takes * ownership of the DataContainer object. The objectName of the source From a7470063a0f73ff9029b77718ea883c75cd54ed8 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 30 Dec 2013 10:41:14 +0100 Subject: [PATCH 12/12] model()->modelForSource() --- .../sourcesOnRequest/sourcesOnRequest.cpp | 4 ++-- src/plasma/datacontainer.h | 2 ++ src/plasma/dataengine.cpp | 2 +- src/plasma/dataengine.h | 12 ++++++------ 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/examples/dataengines/sourcesOnRequest/sourcesOnRequest.cpp b/examples/dataengines/sourcesOnRequest/sourcesOnRequest.cpp index 14a23dda0..96aa96d63 100644 --- a/examples/dataengines/sourcesOnRequest/sourcesOnRequest.cpp +++ b/examples/dataengines/sourcesOnRequest/sourcesOnRequest.cpp @@ -64,7 +64,7 @@ bool SourcesOnRequestEngine::sourceRequestEvent(const QString &source) // expects. So ALWAYS key the new data by the source string as below: setData(source, "Update Count", 0); - if (!model(source)) { + if (!modelForSource(source)) { QStandardItemModel *m = new QStandardItemModel; m->appendRow(new QStandardItem("Item1, first update")); m->appendRow(new QStandardItem("Item2, first update")); @@ -95,7 +95,7 @@ bool SourcesOnRequestEngine::updateSourceEvent(const QString &source) const int updateCount = containerForSource(source)->data().value("Update Count").toInt() + 1; setData(source, "Update Count", updateCount); - QStandardItemModel *m = qobject_cast(model(source)); + QStandardItemModel *m = qobject_cast(modelForSource(source)); if (m) { m->clear(); m->appendRow(new QStandardItem(QString("Item1, update %1").arg(updateCount))); diff --git a/src/plasma/datacontainer.h b/src/plasma/datacontainer.h index 07d098ca3..57fac3f59 100644 --- a/src/plasma/datacontainer.h +++ b/src/plasma/datacontainer.h @@ -117,6 +117,8 @@ class PLASMA_EXPORT DataContainer : public QObject * DataContainer itself is deleted, so it will be deleted when there won't be any * visualization associated to this source. * + * Normally you should set the model from DataEngine::setModel instead from here. + * * @param model the model that will be associated with this DataContainer */ void setModel(QAbstractItemModel *model); diff --git a/src/plasma/dataengine.cpp b/src/plasma/dataengine.cpp index 194df428d..312802906 100644 --- a/src/plasma/dataengine.cpp +++ b/src/plasma/dataengine.cpp @@ -243,7 +243,7 @@ void DataEngine::setModel(const QString &source, QAbstractItemModel *model) } } -QAbstractItemModel *DataEngine::model(const QString &source) +QAbstractItemModel *DataEngine::modelForSource(const QString &source) { Plasma::DataContainer *s = containerForSource(source); diff --git a/src/plasma/dataengine.h b/src/plasma/dataengine.h index c0e6a9a59..1552d2ab3 100644 --- a/src/plasma/dataengine.h +++ b/src/plasma/dataengine.h @@ -177,6 +177,12 @@ Types::NoAlignment) const; **/ Q_INVOKABLE DataContainer *containerForSource(const QString &source); + /** + * @return The model associated to a source if any. The ownership of the model stays with the DataContainer. + * Returns 0 if there isn't any model associated or if the source doesn't exists. + */ + QAbstractItemModel *modelForSource(const QString &source); + /** * Returns true if this engine is valid, otherwise returns false * @@ -323,12 +329,6 @@ Types::NoAlignment) const; */ void setModel(const QString &source, QAbstractItemModel *model); - /** - * @return The model associated to a source if any. The ownership of the model stays with the DataContainer. - * Returns 0 if there isn't any model associated or if the source doesn't exists. - */ - QAbstractItemModel *model(const QString &source); - /** * Adds an already constructed data source. The DataEngine takes * ownership of the DataContainer object. The objectName of the source