Plasma

datacontainer.cpp
1 /*
2  SPDX-FileCopyrightText: 2006-2007 Aaron Seigo <[email protected]>
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 "plasma.h"
16 
17 namespace Plasma
18 {
20  : QObject(parent)
21  , d(new DataContainerPrivate(this))
22 {
23 }
24 
25 DataContainer::~DataContainer()
26 {
27  delete d;
28 }
29 
31 {
32  return d->data;
33 }
34 
35 void 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.
50  if (isStorageEnabled() || !needsToBeStored()) {
51  d->storageTimer.start(180000, this);
52  }
53 
54  setNeedsToBeStored(true);
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 
94 void DataContainer::connectVisualization(QObject *visualization, uint pollingInterval, Plasma::Types::IntervalAlignment alignment)
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,Plasma::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,Plasma::DataEngine::Data)") >= 0) {
139  }
140  if (visualization->metaObject()->indexOfSlot("modelChanged(QString,QAbstractItemModel*)") >= 0) {
141  disconnect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
142  }
143  }
144  } else {
145  connect(visualization, &QObject::destroyed, this, &DataContainer::disconnectVisualization); //, Qt::QueuedConnection);
146  }
147 
148  if (pollingInterval < 1) {
149  // qCDebug(LOG_PLASMA) << " connecting directly";
150  d->relayObjects[visualization] = nullptr;
151  if (visualization->metaObject()->indexOfSlot("dataUpdated(QString,Plasma::DataEngine::Data)") >= 0) {
153  }
154  if (visualization->metaObject()->indexOfSlot("modelChanged(QString,QAbstractItemModel*)") >= 0) {
155  connect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
156  }
157  } else {
158  // qCDebug(LOG_PLASMA) << " connecting to a relay";
159  // we only want to do an immediate update if this is not the first object to connect to us
160  // if it is the first visualization, then the source will already have been populated
161  // engine's sourceRequested method
162  bool immediateUpdate = connected || d->relayObjects.count() > 1;
163  SignalRelay *relay = d->signalRelay(this, visualization, pollingInterval, alignment, immediateUpdate);
164  if (visualization->metaObject()->indexOfSlot("dataUpdated(QString,Plasma::DataEngine::Data)") >= 0) {
166  }
167  // modelChanged is always emitted by the dataSource since there is no polling there
168  if (visualization->metaObject()->indexOfSlot("modelChanged(QString,QAbstractItemModel*)") >= 0) {
169  connect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
170  }
171  }
172 }
173 
175 {
176  d->enableStorage = store;
177  if (store) {
178  QTimer::singleShot(QRandomGenerator::global()->bounded(2000 + 1), this, SLOT(retrieve()));
179  }
180 }
181 
183 {
184  return d->enableStorage;
185 }
186 
188 {
189  return !d->isStored;
190 }
191 
193 {
194  d->isStored = !store;
195 }
196 
198 {
199  QObject *o = this;
200  DataEngine *de = nullptr;
201  while (de == nullptr) {
202  o = dynamic_cast<QObject *>(o->parent());
203  if (o == nullptr) {
204  return nullptr;
205  }
206  de = dynamic_cast<DataEngine *>(o);
207  }
208  return de;
209 }
210 
211 void DataContainerPrivate::store()
212 {
213  if (!q->needsToBeStored() || !q->isStorageEnabled()) {
214  return;
215  }
216 
217  DataEngine *de = q->getDataEngine();
218  if (!de) {
219  return;
220  }
221 
222  q->setNeedsToBeStored(false);
223 
224  if (!storage) {
225  storage = new Storage(q);
226  }
227 
228  QVariantMap op = storage->operationDescription(QStringLiteral("save"));
229  op[QStringLiteral("group")] = q->objectName();
230  StorageJob *job = static_cast<StorageJob *>(storage->startOperationCall(op));
231  job->setData(data);
232  storageCount++;
233  QObject::connect(job, SIGNAL(finished(KJob *)), q, SLOT(storeJobFinished(KJob *)));
234 }
235 
236 void DataContainerPrivate::storeJobFinished(KJob *)
237 {
238  --storageCount;
239  if (storageCount < 1) {
240  storage->deleteLater();
241  storage = nullptr;
242  }
243 }
244 
245 void DataContainerPrivate::retrieve()
246 {
247  DataEngine *de = q->getDataEngine();
248  if (de == nullptr) {
249  return;
250  }
251 
252  if (!storage) {
253  storage = new Storage(q);
254  }
255 
256  QVariantMap retrieveGroup = storage->operationDescription(QStringLiteral("retrieve"));
257  retrieveGroup[QStringLiteral("group")] = q->objectName();
258  ServiceJob *retrieveJob = storage->startOperationCall(retrieveGroup);
259  QObject::connect(retrieveJob, SIGNAL(result(KJob *)), q, SLOT(populateFromStoredData(KJob *)));
260 }
261 
262 void DataContainerPrivate::populateFromStoredData(KJob *job)
263 {
264  if (job->error()) {
265  return;
266  }
267 
268  StorageJob *ret = dynamic_cast<StorageJob *>(job);
269  if (!ret) {
270  return;
271  }
272 
273  // Only fill the source with old stored
274  // data if it is not already populated with new data.
275  if (data.isEmpty() && !ret->data().isEmpty()) {
276  data = ret->data();
277  dirty = true;
278  q->forceImmediateUpdate();
279  }
280 
281  QVariantMap expireGroup = storage->operationDescription(QStringLiteral("expire"));
282  // expire things older than 4 days
283  expireGroup[QStringLiteral("age")] = 345600;
284  storage->startOperationCall(expireGroup);
285 }
286 
288 {
289  QMap<QObject *, SignalRelay *>::iterator objIt = d->relayObjects.find(visualization);
290  disconnect(visualization, &QObject::destroyed, this, &DataContainer::disconnectVisualization); //, Qt::QueuedConnection);
291 
292  if (objIt == d->relayObjects.end() || !objIt.value()) {
293  // it is connected directly to the DataContainer itself
294  if (visualization->metaObject()->indexOfSlot("dataUpdated(QString,Plasma::DataEngine::Data)") >= 0) {
296  }
297  if (visualization->metaObject()->indexOfSlot("modelChanged(QString,QAbstractItemModel*)") >= 0) {
298  disconnect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
299  }
300  } else {
301  SignalRelay *relay = objIt.value();
302 
303  if (relay->receiverCount() == 1) {
304  d->relays.remove(relay->m_interval);
305  delete relay;
306  } else {
307  if (visualization->metaObject()->indexOfSlot("dataUpdated(QString,Plasma::DataEngine::Data)") >= 0) {
309  }
310  // modelChanged is always emitted by the dataSource since there is no polling there
311  if (visualization->metaObject()->indexOfSlot("modelChanged(QString,QAbstractItemModel*)") >= 0) {
312  disconnect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)), visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
313  }
314  }
315  }
316 
317  d->relayObjects.erase(objIt);
318  d->checkUsage();
319 }
320 
322 {
323  // qCDebug(LOG_PLASMA) << objectName() << d->dirty;
324  if (d->dirty) {
325  Q_EMIT dataUpdated(objectName(), d->data);
326 
327  // copy as checkQueueing can result in deletion of the relay
328  const auto relays = d->relays;
329  for (SignalRelay *relay : relays) {
330  relay->checkQueueing();
331  }
332 
333  d->dirty = false;
334  }
335 }
336 
338 {
339  if (d->dirty) {
340  d->dirty = false;
341  Q_EMIT dataUpdated(objectName(), d->data);
342  }
343 
344  for (SignalRelay *relay : std::as_const(d->relays)) {
345  relay->forceImmediateUpdate();
346  }
347 }
348 
350 {
351  return d->updateTimer.elapsed();
352 }
353 
355 {
356  d->cached = update;
357 }
358 
360 {
361  return !d->relays.isEmpty() || receivers(SIGNAL(dataUpdated(QString, Plasma::DataEngine::Data))) > 0;
362 }
363 
364 void DataContainerPrivate::checkUsage()
365 {
366  if (!checkUsageTimer.isActive()) {
367  checkUsageTimer.start(10, q);
368  }
369 }
370 
372 {
373  if (event->timerId() == d->checkUsageTimer.timerId()) {
374  if (!isUsed()) {
375  // DO NOT CALL ANYTHING AFTER THIS LINE AS IT MAY GET DELETED!
376  // qCDebug(LOG_PLASMA) << objectName() << "is unused";
377 
378  // NOTE: Notifying visualization of the model destruction before actual deletion avoids crashes in some edge cases
379  if (d->model) {
380  d->model.clear();
381  Q_EMIT modelChanged(objectName(), nullptr);
382  }
384  }
385  d->checkUsageTimer.stop();
386  } else if (event->timerId() == d->storageTimer.timerId()) {
387  d->store();
388  d->storageTimer.stop();
389  }
390 }
391 
392 } // Plasma namespace
393 
394 #include "moc_datacontainer.cpp"
void checkForUpdate()
Checks whether any data has changed and, if so, emits dataUpdated().
int indexOfSlot(const char *slot) const const
bool isValid() const const
Data provider for plasmoids (Plasma plugins)
Definition: dataengine.h:50
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
Namespace for everything in libplasma.
Definition: datamodel.cpp:14
uint timeSinceLastUpdate() const
Returns how long ago, in msecs, that the data in this container was last updated.
void removeAllData()
Removes all data currently associated with this source.
Q_EMITQ_EMIT
QAbstractItemModel * model()
void connectVisualization(QObject *visualization, uint pollingInterval, Plasma::Types::IntervalAlignment alignment)
Connects an object to this DataContainer.
void setModel(QAbstractItemModel *model)
Associates a model with this DataContainer.
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...
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void dataUpdated(const QString &source, const Plasma::DataEngine::Data &data)
Emitted when the data has been updated, allowing visualizations to reflect the new data.
void destroyed(QObject *obj)
virtual bool event(QEvent *e)
bool needsToBeStored() const
void timerEvent(QTimerEvent *event) override
void setNeedsToBeStored(bool store)
sets that the data container needs to be stored or not.
const DataEngine::Data data() const
Returns the data for this DataContainer.
void forceImmediateUpdate()
Forces immediate update signals to all visualizations.
void setData(const QString &key, const QVariant &value)
Set a value for a key.
int receivers(const char *signal) const const
virtual const QMetaObject * metaObject() const const
void setNeedsUpdate(bool update=true)
Indicates that the data should be treated as dirty the next time hasUpdates() is called.
void setParent(QObject *parent)
void becameUnused(const QString &source)
Emitted when the last visualization is disconnected.
IntervalAlignment
Possible timing alignments.
Definition: plasma.h:223
DataContainer(QObject *parent=nullptr)
Constructs a default DataContainer that has no name or data associated with it.
void disconnectVisualization(QObject *visualization)
Disconnects an object from this DataContainer.
int error() const
bool visualizationIsConnected(QObject *visualization) const
QObject * parent() const const
DataEngine * getDataEngine()
QRandomGenerator * global()
bool isStorageEnabled() const
void setStorageEnabled(bool store)
sets this data container to be automatically stored.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Mar 26 2023 04:14:47 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.