Plasma5Support

datacontainer.cpp
1/*
2 SPDX-FileCopyrightText: 2006-2007 Aaron Seigo <aseigo@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6#include "datacontainer.h"
7#include "private/datacontainer_p.h"
8#include "private/storage_p.h"
9
10#include <QAbstractItemModel>
11#include <QDebug>
12#include <QRandomGenerator>
13
14#include "debug_p.h"
15#include "plasma5support.h"
16
17namespace Plasma5Support
18{
20 : QObject(parent)
21 , d(new DataContainerPrivate(this))
22{
23}
24
25DataContainer::~DataContainer()
26{
27 delete d;
28}
29
31{
32 return d->data;
33}
34
35void DataContainer::setData(const QString &key, const QVariant &value)
36{
37 if (!value.isValid()) {
38 d->data.remove(key);
39 } else {
40 d->data.insert(key, value);
41 }
42
43 d->dirty = true;
44 d->updateTimer.start();
45
46 // check if storage is enabled and if storage is needed.
47 // If it is not set to be stored,then this is the first
48 // setData() since the last time it was stored. This
49 // gives us only one singleShot timer.
51 d->storageTimer.start(180000, this);
52 }
53
55}
56
58{
59 if (d->model.data() == model) {
60 return;
61 }
62
63 if (d->model) {
64 d->model.data()->deleteLater();
65 }
66
67 d->model = model;
68 model->setParent(this);
70}
71
73{
74 return d->model.data();
75}
76
78{
79 if (d->data.isEmpty()) {
80 // avoid an update if we don't have any data anyways
81 return;
82 }
83
84 d->data.clear();
85 d->dirty = true;
86 d->updateTimer.start();
87}
88
90{
91 return d->relayObjects.contains(visualization);
92}
93
95{
96 // qCDebug(LOG_PLASMA) << "connecting visualization" <<this<< visualization << "at interval of"
97 // << pollingInterval << "to" << objectName();
98 QMap<QObject *, SignalRelay *>::iterator objIt = d->relayObjects.find(visualization);
99 bool connected = objIt != d->relayObjects.end();
100 if (connected) {
101 // this visualization is already connected. just adjust the update
102 // frequency if necessary
103 SignalRelay *relay = objIt.value();
104 if (relay) {
105 // connected to a relay
106 // qCDebug(LOG_PLASMA) << " already connected, but to a relay";
107 if (relay->m_interval == pollingInterval && relay->m_align == alignment) {
108 // qCDebug(LOG_PLASMA) << " already connected to a relay of the same interval of"
109 // << pollingInterval << ", nothing to do";
110 return;
111 }
112
113 if (relay->receiverCount() == 1) {
114 // qCDebug(LOG_PLASMA) << " removing relay, as it is now unused";
115 d->relays.remove(relay->m_interval);
116 delete relay;
117 } else {
118 if (visualization->metaObject()->indexOfSlot("dataUpdated(QString,Plasma5Support::DataEngine::Data)") >= 0) {
119 disconnect(relay,
121 visualization,
123 }
124 // modelChanged is always emitted by the dataSource since there is no polling there
125 if (visualization->metaObject()->indexOfSlot("modelChanged(QString,QAbstractItemModel*)") >= 0) {
126 disconnect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
127 }
128 // relay->isUnused();
129 }
130 } else if (pollingInterval < 1) {
131 // the visualization was connected already, but not to a relay
132 // and it still doesn't want to connect to a relay, so we have
133 // nothing to do!
134 // qCDebug(LOG_PLASMA) << " already connected, nothing to do";
135 return;
136 } else {
137 if (visualization->metaObject()->indexOfSlot("dataUpdated(QString,Plasma5Support::DataEngine::Data)") >= 0) {
138 disconnect(this,
140 visualization,
142 }
143 if (visualization->metaObject()->indexOfSlot("modelChanged(QString,QAbstractItemModel*)") >= 0) {
144 disconnect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
145 }
146 }
147 } else {
148 connect(visualization, &QObject::destroyed, this, &DataContainer::disconnectVisualization); //, Qt::QueuedConnection);
149 }
150
151 if (pollingInterval < 1) {
152 // qCDebug(LOG_PLASMA) << " connecting directly";
153 d->relayObjects[visualization] = nullptr;
154 if (visualization->metaObject()->indexOfSlot("dataUpdated(QString,Plasma5Support::DataEngine::Data)") >= 0) {
155 connect(this,
157 visualization,
159 }
160 if (visualization->metaObject()->indexOfSlot("modelChanged(QString,QAbstractItemModel*)") >= 0) {
161 connect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
162 }
163 } else {
164 // qCDebug(LOG_PLASMA) << " connecting to a relay";
165 // we only want to do an immediate update if this is not the first object to connect to us
166 // if it is the first visualization, then the source will already have been populated
167 // engine's sourceRequested method
168 bool immediateUpdate = connected || d->relayObjects.count() > 1;
169 SignalRelay *relay = d->signalRelay(this, visualization, pollingInterval, alignment, immediateUpdate);
170 if (visualization->metaObject()->indexOfSlot("dataUpdated(QString,Plasma5Support::DataEngine::Data)") >= 0) {
171 connect(relay,
173 visualization,
175 }
176 // modelChanged is always emitted by the dataSource since there is no polling there
177 if (visualization->metaObject()->indexOfSlot("modelChanged(QString,QAbstractItemModel*)") >= 0) {
178 connect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
179 }
180 }
181}
182
184{
185 d->enableStorage = store;
186 if (store) {
187 QTimer::singleShot(QRandomGenerator::global()->bounded(2000 + 1), this, SLOT(retrieve()));
188 }
189}
190
192{
193 return d->enableStorage;
194}
195
197{
198 return !d->isStored;
199}
200
202{
203 d->isStored = !store;
204}
205
207{
208 QObject *o = this;
209 DataEngine *de = nullptr;
210 while (de == nullptr) {
211 o = dynamic_cast<QObject *>(o->parent());
212 if (o == nullptr) {
213 return nullptr;
214 }
215 de = dynamic_cast<DataEngine *>(o);
216 }
217 return de;
218}
219
220void DataContainerPrivate::store()
221{
222 if (!q->needsToBeStored() || !q->isStorageEnabled()) {
223 return;
224 }
225
226 DataEngine *de = q->getDataEngine();
227 if (!de) {
228 return;
229 }
230
231 q->setNeedsToBeStored(false);
232
233 if (!storage) {
234 storage = new Storage(q);
235 }
236
237 QVariantMap op = storage->operationDescription(QStringLiteral("save"));
238 op[QStringLiteral("group")] = q->objectName();
239 StorageJob *job = static_cast<StorageJob *>(storage->startOperationCall(op));
240 job->setData(data);
241 storageCount++;
242 QObject::connect(job, SIGNAL(finished(KJob *)), q, SLOT(storeJobFinished(KJob *)));
243}
244
245void DataContainerPrivate::storeJobFinished(KJob *)
246{
247 --storageCount;
248 if (storageCount < 1) {
249 storage->deleteLater();
250 storage = nullptr;
251 }
252}
253
254void DataContainerPrivate::retrieve()
255{
256 DataEngine *de = q->getDataEngine();
257 if (de == nullptr) {
258 return;
259 }
260
261 if (!storage) {
262 storage = new Storage(q);
263 }
264
265 QVariantMap retrieveGroup = storage->operationDescription(QStringLiteral("retrieve"));
266 retrieveGroup[QStringLiteral("group")] = q->objectName();
267 ServiceJob *retrieveJob = storage->startOperationCall(retrieveGroup);
268 QObject::connect(retrieveJob, SIGNAL(result(KJob *)), q, SLOT(populateFromStoredData(KJob *)));
269}
270
271void DataContainerPrivate::populateFromStoredData(KJob *job)
272{
273 if (job->error()) {
274 return;
275 }
276
277 StorageJob *ret = dynamic_cast<StorageJob *>(job);
278 if (!ret) {
279 return;
280 }
281
282 // Only fill the source with old stored
283 // data if it is not already populated with new data.
284 if (data.isEmpty() && !ret->data().isEmpty()) {
285 data = ret->data();
286 dirty = true;
287 q->forceImmediateUpdate();
288 }
289
290 QVariantMap expireGroup = storage->operationDescription(QStringLiteral("expire"));
291 // expire things older than 4 days
292 expireGroup[QStringLiteral("age")] = 345600;
293 storage->startOperationCall(expireGroup);
294}
295
297{
298 QMap<QObject *, SignalRelay *>::iterator objIt = d->relayObjects.find(visualization);
299 disconnect(visualization, &QObject::destroyed, this, &DataContainer::disconnectVisualization); //, Qt::QueuedConnection);
300
301 if (objIt == d->relayObjects.end() || !objIt.value()) {
302 // it is connected directly to the DataContainer itself
303 if (visualization->metaObject()->indexOfSlot("dataUpdated(QString,Plasma5Support::DataEngine::Data)") >= 0) {
304 disconnect(this,
306 visualization,
308 }
309 if (visualization->metaObject()->indexOfSlot("modelChanged(QString,QAbstractItemModel*)") >= 0) {
310 disconnect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
311 }
312 } else {
313 SignalRelay *relay = objIt.value();
314
315 if (relay->receiverCount() == 1) {
316 d->relays.remove(relay->m_interval);
317 delete relay;
318 } else {
319 if (visualization->metaObject()->indexOfSlot("dataUpdated(QString,Plasma5Support::DataEngine::Data)") >= 0) {
320 disconnect(relay,
322 visualization,
324 }
325 // modelChanged is always emitted by the dataSource since there is no polling there
326 if (visualization->metaObject()->indexOfSlot("modelChanged(QString,QAbstractItemModel*)") >= 0) {
327 disconnect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
328 }
329 }
330 }
331
332 if (objIt != d->relayObjects.end()) {
333 d->relayObjects.erase(objIt);
334 }
335
336 d->checkUsage();
337}
338
340{
341 // qCDebug(LOG_PLASMA) << objectName() << d->dirty;
342 if (d->dirty) {
343 Q_EMIT dataUpdated(objectName(), d->data);
344
345 // copy as checkQueueing can result in deletion of the relay
346 const auto relays = d->relays;
347 for (SignalRelay *relay : relays) {
348 relay->checkQueueing();
349 }
350
351 d->dirty = false;
352 }
353}
354
356{
357 if (d->dirty) {
358 d->dirty = false;
359 Q_EMIT dataUpdated(objectName(), d->data);
360 }
361
362 for (SignalRelay *relay : std::as_const(d->relays)) {
363 relay->forceImmediateUpdate();
364 }
365}
366
368{
369 return d->updateTimer.elapsed();
370}
371
373{
374 d->cached = update;
375}
376
378{
379 return !d->relays.isEmpty() || receivers(SIGNAL(dataUpdated(QString, Plasma5Support::DataEngine::Data))) > 0;
380}
381
382void DataContainerPrivate::checkUsage()
383{
384 if (!checkUsageTimer.isActive()) {
385 checkUsageTimer.start(10, q);
386 }
387}
388
390{
391 if (event->timerId() == d->checkUsageTimer.timerId()) {
392 if (!isUsed()) {
393 // DO NOT CALL ANYTHING AFTER THIS LINE AS IT MAY GET DELETED!
394 // qCDebug(LOG_PLASMA) << objectName() << "is unused";
395
396 // NOTE: Notifying visualization of the model destruction before actual deletion avoids crashes in some edge cases
397 if (d->model) {
398 d->model.clear();
399 Q_EMIT modelChanged(objectName(), nullptr);
400 }
402 }
403 d->checkUsageTimer.stop();
404 } else if (event->timerId() == d->storageTimer.timerId()) {
405 d->store();
406 d->storageTimer.stop();
407 }
408}
409
410} // Plasma namespace
411
412#include "moc_datacontainer.cpp"
int error() const
void disconnectVisualization(QObject *visualization)
Disconnects an object from this DataContainer.
void setModel(QAbstractItemModel *model)
Associates a model with this DataContainer.
void timerEvent(QTimerEvent *event) override
void setData(const QString &key, const QVariant &value)
Set a value for a key.
DataContainer(QObject *parent=nullptr)
Constructs a default DataContainer that has no name or data associated with it.
void connectVisualization(QObject *visualization, uint pollingInterval, Plasma5Support::Types::IntervalAlignment alignment)
Connects an object to this DataContainer.
QAbstractItemModel * model()
uint timeSinceLastUpdate() const
Returns how long ago, in msecs, that the data in this container was last updated.
bool visualizationIsConnected(QObject *visualization) const
const DataEngine::Data data() const
Returns the data for this DataContainer.
void setStorageEnabled(bool store)
sets this data container to be automatically stored.
void dataUpdated(const QString &source, const Plasma5Support::DataEngine::Data &data)
Emitted when the data has been updated, allowing visualizations to reflect the new data.
void checkForUpdate()
Checks whether any data has changed and, if so, emits dataUpdated().
void setNeedsUpdate(bool update=true)
Indicates that the data should be treated as dirty the next time hasUpdates() is called.
void removeAllData()
Removes all data currently associated with this source.
void becameUnused(const QString &source)
Emitted when the last visualization is disconnected.
void modelChanged(const QString &source, QAbstractItemModel *model)
A new model has been associated to this source, visualizations can safely use it as long they are con...
void setNeedsToBeStored(bool store)
sets that the data container needs to be stored or not.
void forceImmediateUpdate()
Forces immediate update signals to all visualizations.
Data provider for plasmoids (Plasma plugins)
Definition dataengine.h:45
IntervalAlignment
Possible timing alignments.
Namespace for everything in libplasma.
Definition datamodel.cpp:15
int indexOfSlot(const char *slot) const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void destroyed(QObject *obj)
bool disconnect(const QMetaObject::Connection &connection)
virtual bool event(QEvent *e)
virtual const QMetaObject * metaObject() const const
QObject * parent() const const
int receivers(const char *signal) const const
void setParent(QObject *parent)
QRandomGenerator * global()
bool isValid() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:08:57 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.