Plasma

dataengine.cpp
1 /*
2  SPDX-FileCopyrightText: 2006-2007 Aaron Seigo <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "dataengine.h"
8 #include "private/datacontainer_p.h"
9 #include "private/dataengine_p.h"
10 
11 #include <QAbstractItemModel>
12 #include <QQueue>
13 #include <QTime>
14 #include <QTimer>
15 #include <QTimerEvent>
16 #include <QVariant>
17 
18 #include <QDebug>
19 #include <QStandardPaths>
20 
21 #include <KLocalizedString>
22 
23 #include "datacontainer.h"
24 #include "package.h"
25 #include "pluginloader.h"
26 #include "scripting/dataenginescript.h"
27 #include "service.h"
28 
29 #include "config-plasma.h"
30 #include "private/service_p.h"
31 #include "private/storage_p.h"
32 
33 namespace Plasma
34 {
35 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 67)
37  : DataEngine(plugin.toMetaData(), parent)
38 {
39 }
40 #endif
41 
43  : QObject(parent)
44  , d(new DataEnginePrivate(this, plugin))
45 {
46  if (d->script) {
47  d->setupScriptSupport();
48  d->script->init();
49  } else {
50  // default implementation does nothing. this is for engines that have to
51  // start things in motion external to themselves before they can work
52  }
53 }
54 
55 DataEngine::DataEngine(QObject *parent, const QVariantList &args)
56  : QObject(parent)
57 {
58  KPluginMetaData data;
59  if (!args.isEmpty() && args.first().canConvert<KPluginMetaData>()) {
60  data = args.first().value<KPluginMetaData>();
61  }
62  d = new DataEnginePrivate(this, data, args);
63  if (d->script) {
64  d->setupScriptSupport();
65  d->script->init();
66  }
67 }
68 
69 DataEngine::~DataEngine()
70 {
71  // qCDebug(LOG_PLASMA) << objectName() << ": bye bye birdy! ";
72  delete d;
73 }
74 
76 {
77  if (d->script) {
78  return d->script->sources();
79  } else {
80  return d->sources.keys();
81  }
82 }
83 
85 {
86  if (d->script) {
87  Service *s = d->script->serviceForSource(source);
88  if (s) {
89  return s;
90  }
91  }
92 
93  return new NullService(source, this);
94 }
95 
96 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 67)
98 {
99  return KPluginInfo(d->dataEngineDescription);
100 }
101 #endif
102 
104 {
105  return d->dataEngineDescription;
106 }
107 
108 void DataEngine::connectSource(const QString &source, QObject *visualization, uint pollingInterval, Plasma::Types::IntervalAlignment intervalAlignment) const
109 {
110  // qCDebug(LOG_PLASMA) << "connectSource" << source;
111  bool newSource;
112  DataContainer *s = d->requestSource(source, &newSource);
113 
114  if (s) {
115  // we suppress the immediate invocation of dataUpdated here if the
116  // source was preexisting and they don't request delayed updates
117  // (we want to do an immediate update in that case so they don't
118  // have to wait for the first time out)
119  if (newSource && !s->data().isEmpty()) {
120  newSource = false;
121  }
122  d->connectSource(s, visualization, pollingInterval, intervalAlignment, !newSource || pollingInterval > 0);
123  // qCDebug(LOG_PLASMA) << " ==> source connected";
124  }
125 }
126 
127 void DataEngine::connectAllSources(QObject *visualization, uint pollingInterval, Plasma::Types::IntervalAlignment intervalAlignment) const
128 {
129  for (DataContainer *s : std::as_const(d->sources)) {
130  d->connectSource(s, visualization, pollingInterval, intervalAlignment);
131  }
132 }
133 
134 void DataEngine::disconnectSource(const QString &source, QObject *visualization) const
135 {
136  DataContainer *s = d->source(source, false);
137 
138  if (s) {
139  s->disconnectVisualization(visualization);
140  }
141 }
142 
144 {
145  return d->source(source, false);
146 }
147 
149 {
150  if (d->script) {
151  return d->script->sourceRequestEvent(name);
152  } else {
153  return false;
154  }
155 }
156 
158 {
159  if (d->script) {
160  return d->script->updateSourceEvent(source);
161  } else {
162  // qCDebug(LOG_PLASMA) << source;
163  return false; // TODO: should this be true to trigger, even needless, updates on every tick?
164  }
165 }
166 
167 void DataEngine::setData(const QString &source, const QVariant &value)
168 {
169  setData(source, source, value);
170 }
171 
172 void DataEngine::setData(const QString &source, const QString &key, const QVariant &value)
173 {
174  DataContainer *s = d->source(source, false);
175  bool isNew = !s;
176 
177  if (isNew) {
178  s = d->source(source);
179  }
180 
181  s->setData(key, value);
182 
183  if (isNew && source != d->waitingSourceRequest) {
184  Q_EMIT sourceAdded(source);
185  }
186 
187  d->scheduleSourcesUpdated();
188 }
189 
190 void DataEngine::setData(const QString &source, const QVariantMap &data)
191 {
192  DataContainer *s = d->source(source, false);
193  bool isNew = !s;
194 
195  if (isNew) {
196  s = d->source(source);
197  }
198 
199  Data::const_iterator it = data.constBegin();
200  while (it != data.constEnd()) {
201  s->setData(it.key(), it.value());
202  ++it;
203  }
204 
205  if (isNew && source != d->waitingSourceRequest) {
206  Q_EMIT sourceAdded(source);
207  }
208 
209  d->scheduleSourcesUpdated();
210 }
211 
213 {
214  DataContainer *s = d->source(source, false);
215  if (s) {
216  s->removeAllData();
217  d->scheduleSourcesUpdated();
218  }
219 }
220 
221 void DataEngine::removeData(const QString &source, const QString &key)
222 {
223  DataContainer *s = d->source(source, false);
224  if (s) {
225  s->setData(key, QVariant());
226  d->scheduleSourcesUpdated();
227  }
228 }
229 
231 {
232  if (model) {
233  setData(source, QStringLiteral("HasModel"), true);
234  } else {
235  removeData(source, QStringLiteral("HasModel"));
236  }
237 
239 
240  if (s) {
241  s->setModel(model);
242  }
243 }
244 
246 {
248 
249  if (s) {
250  return s->model();
251  } else {
252  return nullptr;
253  }
254 }
255 
257 {
258  if (d->sources.contains(source->objectName())) {
259 #ifndef NDEBUG
260  // qCDebug(LOG_PLASMA) << "source named \"" << source->objectName() << "\" already exists.";
261 #endif
262  return;
263  }
264 
265  QObject::connect(source, SIGNAL(updateRequested(DataContainer *)), this, SLOT(internalUpdateSource(DataContainer *)));
266  QObject::connect(source, SIGNAL(destroyed(QObject *)), this, SLOT(sourceDestroyed(QObject *)));
267  d->sources.insert(source->objectName(), source);
268  Q_EMIT sourceAdded(source->objectName());
269  d->scheduleSourcesUpdated();
270 }
271 
273 {
274  d->minPollingInterval = minimumMs;
275 }
276 
278 {
279  return d->minPollingInterval;
280 }
281 
282 void DataEngine::setPollingInterval(uint frequency)
283 {
284  killTimer(d->updateTimerId);
285  d->updateTimerId = 0;
286 
287  if (frequency > 0) {
288  d->updateTimerId = startTimer(frequency);
289  }
290 }
291 
293 {
294  QHash<QString, DataContainer *>::iterator it = d->sources.find(source);
295  if (it != d->sources.end()) {
296  DataContainer *s = it.value();
297  s->d->store();
298  Q_EMIT sourceRemoved(source);
299  d->sources.erase(it);
300  s->disconnect(this);
301  s->deleteLater();
302  }
303 }
304 
306 {
308  while (it.hasNext()) {
309  it.next();
310  Plasma::DataContainer *s = it.value();
311  const QString source = it.key();
312  Q_EMIT sourceRemoved(source);
313  it.remove();
314  s->disconnect(this);
315  s->deleteLater();
316  }
317 }
318 
320 {
321  return d->valid;
322 }
323 
325 {
326  return d->sources.isEmpty();
327 }
328 
329 void DataEngine::setValid(bool valid)
330 {
331  d->valid = valid;
332 }
333 
335 {
336  return d->sources;
337 }
338 
340 {
341  // qCDebug(LOG_PLASMA);
342  if (event->timerId() == d->updateTimerId) {
343  // if the freq update is less than 0, don't bother
344  if (d->minPollingInterval < 0) {
345  // qCDebug(LOG_PLASMA) << "uh oh.. no polling allowed!";
346  return;
347  }
348 
349  // minPollingInterval
350  if (d->updateTimer.elapsed() < d->minPollingInterval) {
351  // qCDebug(LOG_PLASMA) << "hey now.. slow down!";
352  return;
353  }
354 
355  d->updateTimer.start();
357  } else if (event->timerId() == d->checkSourcesTimerId) {
358  killTimer(d->checkSourcesTimerId);
359  d->checkSourcesTimerId = 0;
360 
362  while (it.hasNext()) {
363  it.next();
364  it.value()->checkForUpdate();
365  }
366  } else {
368  }
369 }
370 
372 {
374  while (it.hasNext()) {
375  it.next();
376  // qCDebug(LOG_PLASMA) << "updating" << it.key();
377  if (it.value()->isUsed()) {
378  updateSourceEvent(it.key());
379  }
380  }
381 
382  d->scheduleSourcesUpdated();
383 }
384 
386 {
387  for (DataContainer *source : std::as_const(d->sources)) {
388  if (source->isUsed()) {
389  source->forceImmediateUpdate();
390  }
391  }
392 }
393 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 83)
395 {
396  return d->package ? *d->package : Package();
397 }
398 #endif
399 
400 void DataEngine::setStorageEnabled(const QString &source, bool store)
401 {
402  DataContainer *s = d->source(source, false);
403  if (s) {
404  s->setStorageEnabled(store);
405  }
406 }
407 
408 // Private class implementations
409 DataEnginePrivate::DataEnginePrivate(DataEngine *e, const KPluginMetaData &md, const QVariantList &args)
410  : q(e)
411  , dataEngineDescription(md)
412  , refCount(-1)
413  , checkSourcesTimerId(0) // first ref
414  , updateTimerId(0)
415  , minPollingInterval(-1)
416  , valid(false)
417  , script(nullptr)
418 {
419  updateTimer.start();
420 
421  if (dataEngineDescription.isValid()) {
422  valid = true;
423  e->setObjectName(dataEngineDescription.name());
424  }
425 
426  if (dataEngineDescription.isValid()) {
427 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 83)
428 
429  QString api = dataEngineDescription.value(QStringLiteral("X-Plasma-API"));
430 
431  if (!api.isEmpty()) {
433  QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/dataengines/") + dataEngineDescription.pluginId(),
435  package = new Package(PluginLoader::self()->loadPackage(QStringLiteral("Plasma/DataEngine"), api));
436  package->setPath(path);
437 
438  if (package->isValid()) {
439  script = Plasma::loadScriptEngine(api, q, args);
440  }
441 
442  if (!script) {
443  delete package;
444  package = nullptr;
445  }
446  }
447 #endif
448  }
449 }
450 
451 DataEnginePrivate::~DataEnginePrivate()
452 {
453  delete script;
454  script = nullptr;
455 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 83)
456  delete package;
457  package = nullptr;
458 #endif
459 }
460 
461 void DataEnginePrivate::internalUpdateSource(DataContainer *source)
462 {
463  if (minPollingInterval > 0 && source->timeSinceLastUpdate() < (uint)minPollingInterval) {
464  // skip updating this source; it's been too soon
465  // qCDebug(LOG_PLASMA) << "internal update source is delaying" << source->timeSinceLastUpdate() << minPollingInterval;
466  // but fake an update so that the signalrelay that triggered this gets the data from the
467  // recent update. this way we don't have to worry about queuing - the relay will send a
468  // signal immediately and everyone else is undisturbed.
469  source->setNeedsUpdate();
470  return;
471  }
472 
473  if (q->updateSourceEvent(source->objectName())) {
474  // qCDebug(LOG_PLASMA) << "queuing an update";
475  scheduleSourcesUpdated();
476  }
477 }
478 
479 void DataEnginePrivate::ref()
480 {
481  --refCount;
482 }
483 
484 void DataEnginePrivate::deref()
485 {
486  ++refCount;
487 }
488 
489 bool DataEnginePrivate::isUsed() const
490 {
491  return refCount != 0;
492 }
493 
494 DataContainer *DataEnginePrivate::source(const QString &sourceName, bool createWhenMissing)
495 {
496  QHash<QString, DataContainer *>::const_iterator it = sources.constFind(sourceName);
497  if (it != sources.constEnd()) {
498  DataContainer *s = it.value();
499  return s;
500  }
501 
502  if (!createWhenMissing) {
503  return nullptr;
504  }
505 
506  // qCDebug(LOG_PLASMA) << "DataEngine " << q->objectName() << ": could not find DataContainer " << sourceName << ", creating";
507  DataContainer *s = new DataContainer(q);
508  s->setObjectName(sourceName);
509  sources.insert(sourceName, s);
510  QObject::connect(s, SIGNAL(destroyed(QObject *)), q, SLOT(sourceDestroyed(QObject *)));
511  QObject::connect(s, SIGNAL(updateRequested(DataContainer *)), q, SLOT(internalUpdateSource(DataContainer *)));
512 
513  return s;
514 }
515 
516 void DataEnginePrivate::connectSource(DataContainer *s,
517  QObject *visualization,
518  uint pollingInterval,
520  bool immediateCall)
521 {
522  // qCDebug(LOG_PLASMA) << "connect source called" << s->objectName() << "with interval" << pollingInterval;
523 
524  if (pollingInterval > 0) {
525  // never more frequently than allowed, never more than 20 times per second
526  uint min = qMax(50, minPollingInterval); // for qMax below
527  pollingInterval = qMax(min, pollingInterval);
528 
529  // align on the 50ms
530  pollingInterval = pollingInterval - (pollingInterval % 50);
531  }
532 
533  if (immediateCall) {
534  // we don't want to do an immediate call if we are simply
535  // reconnecting
536  // qCDebug(LOG_PLASMA) << "immediate call requested, we have:" << s->visualizationIsConnected(visualization);
537  immediateCall = !s->data().isEmpty() && !s->visualizationIsConnected(visualization);
538  }
539 
540  s->connectVisualization(visualization, pollingInterval, align);
541 
542  if (immediateCall) {
543  QMetaObject::invokeMethod(visualization, "dataUpdated", Q_ARG(QString, s->objectName()), Q_ARG(Plasma::DataEngine::Data, s->data()));
544  if (s->d->model) {
545  QMetaObject::invokeMethod(visualization, "modelChanged", Q_ARG(QString, s->objectName()), Q_ARG(QAbstractItemModel *, s->d->model.data()));
546  }
547  s->d->dirty = false;
548  }
549 }
550 
551 void DataEnginePrivate::sourceDestroyed(QObject *object)
552 {
554  while (it != sources.end()) {
555  if (it.value() == object) {
556  sources.erase(it);
557  Q_EMIT q->sourceRemoved(object->objectName());
558  break;
559  }
560  ++it;
561  }
562 }
563 
564 DataContainer *DataEnginePrivate::requestSource(const QString &sourceName, bool *newSource)
565 {
566  if (newSource) {
567  *newSource = false;
568  }
569 
570  // qCDebug(LOG_PLASMA) << "requesting source " << sourceName;
571  DataContainer *s = source(sourceName, false);
572 
573  if (!s) {
574  // we didn't find a data source, so give the engine an opportunity to make one
575  /*// qCDebug(LOG_PLASMA) << "DataEngine " << q->objectName()
576  << ": could not find DataContainer " << sourceName
577  << " will create on request" << endl;*/
578  waitingSourceRequest = sourceName;
579  if (q->sourceRequestEvent(sourceName)) {
580  s = source(sourceName, false);
581  if (s) {
582  // now we have a source; since it was created on demand, assume
583  // it should be removed when not used
584  if (newSource) {
585  *newSource = true;
586  }
588  Q_EMIT q->sourceAdded(sourceName);
589  }
590  }
591  waitingSourceRequest.clear();
592  }
593 
594  return s;
595 }
596 
597 // put all setup routines for script here. at this point we can assume that
598 // package exists and that we have a script engine
599 void DataEnginePrivate::setupScriptSupport()
600 {
601  /*
602  #ifndef NDEBUG
603  // qCDebug(LOG_PLASMA) << "sletting up script support, package is in" << package->path()
604  #endif
605  << "which is a" << package->structure()->type() << "package"
606  << ", main script is" << package->filePath("mainscript");
607  */
608 
609  // FIXME: Replace with ki18n functionality once semantics is clear.
610  // const QString translationsPath = package->filePath("translations");
611  // if (!translationsPath.isEmpty()) {
612  // KGlobal::dirs()->addResourceDir("locale", translationsPath);
613  // }
614 }
615 
616 void DataEnginePrivate::scheduleSourcesUpdated()
617 {
618  if (checkSourcesTimerId) {
619  return;
620  }
621 
622  checkSourcesTimerId = q->startTimer(0);
623 }
624 
625 }
626 
627 #include "moc_dataengine.cpp"
int startTimer(int interval, Qt::TimerType timerType)
Data provider for plasmoids (Plasma plugins)
Definition: dataengine.h:50
bool isEmpty() const
Returns true if the data engine is empty, which is to say that it has no data sources currently.
Definition: dataengine.cpp:324
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
Namespace for everything in libplasma.
Definition: datamodel.cpp:14
void sourceRemoved(const QString &source)
Emitted when a data source is removed.
Q_INVOKABLE void connectAllSources(QObject *visualization, uint pollingInterval=0, Plasma::Types::IntervalAlignment intervalAlignment=Types::NoAlignment) const
Connects all currently existing sources to an object for data updates.
Definition: dataengine.cpp:127
virtual QStringList sources() const
Definition: dataengine.cpp:75
void removeAllData()
Removes all data currently associated with this source.
Q_EMITQ_EMIT
QMutableHashIterator::Item next()
virtual void timerEvent(QTimerEvent *event)
QHash< QString, DataContainer * > containerDict() const
Definition: dataengine.cpp:334
virtual QVariant data(const QModelIndex &index, int role) const const=0
QAbstractItemModel * model()
void setStorageEnabled(const QString &source, bool store)
Sets a source to be stored for easy retrieval when the real source of the data (usually a network con...
Definition: dataengine.cpp:400
int minimumPollingInterval() const
Definition: dataengine.cpp:277
void setModel(QAbstractItemModel *model)
Associates a model with this DataContainer.
KPluginInfo pluginInfo() const
Definition: dataengine.cpp:97
Package package() const
Accessor for the associated Package object if any.
Definition: dataengine.cpp:394
virtual Q_INVOKABLE Service * serviceForSource(const QString &source)
Definition: dataengine.cpp:84
void setMinimumPollingInterval(int minimumMs)
Sets the minimum amount of time, in milliseconds, that must pass between successive updates of data.
Definition: dataengine.cpp:272
void timerEvent(QTimerEvent *event) override
Reimplemented from QObject.
Definition: dataengine.cpp:339
QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
const T & value() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Q_INVOKABLE void connectSource(const QString &source, QObject *visualization, uint pollingInterval=0, Plasma::Types::IntervalAlignment intervalAlignment=Types::NoAlignment) const
Connects a source to an object for data updates.
Definition: dataengine.cpp:108
void setData(const QString &source, const QVariant &value)
Sets a value for a data source.
Definition: dataengine.cpp:167
void destroyed(QObject *obj)
void deleteLater()
virtual bool event(QEvent *e)
const Key & key() const const
This class provides a generic API for write access to settings or services.
Definition: service.h:77
void sourceAdded(const QString &source)
Emitted when a new data source is created.
QHashIterator::Item next()
void killTimer(int id)
bool isEmpty() const const
const DataEngine::Data data() const
Returns the data for this DataContainer.
virtual bool updateSourceEvent(const QString &source)
Called by internal updating mechanisms to trigger the engine to refresh the data contained in a given...
Definition: dataengine.cpp:157
DataEngine(const KPluginInfo &plugin, QObject *parent=nullptr)
Constructor.
Definition: dataengine.cpp:36
AppletScript * loadScriptEngine(const QString &language, Applet *applet, const QVariantList &args)
Loads an Applet script engine for the given language.
virtual bool sourceRequestEvent(const QString &source)
When a source that does not currently exist is requested by the consumer, this method is called to gi...
Definition: dataengine.cpp:148
KPluginMetaData metadata() const
Definition: dataengine.cpp:103
void setData(const QString &key, const QVariant &value)
Set a value for a key.
QAbstractItemModel * modelForSource(const QString &source)
Definition: dataengine.cpp:245
void forceImmediateUpdateOfAllVisualizations()
Forces an immediate update to all connected sources, even those with timeouts that haven't yet expire...
Definition: dataengine.cpp:385
void setModel(const QString &source, QAbstractItemModel *model)
Associates a model to a data source.
Definition: dataengine.cpp:230
void updateAllSources()
Immediately updates all existing sources when called.
Definition: dataengine.cpp:371
void insert(int i, const T &value)
bool value(const QString &key, bool defaultValue) const
A set of data exported via a DataEngine.
Definition: datacontainer.h:51
bool hasNext() const const
Q_INVOKABLE DataContainer * containerForSource(const QString &source)
Retrieves a pointer to the DataContainer for a given source.
Definition: dataengine.cpp:143
bool isValid() const
Definition: package.cpp:100
QList::const_iterator constEnd() const const
QList::iterator erase(QList::iterator pos)
void removeAllSources()
Removes all data sources.
Definition: dataengine.cpp:305
void setValid(bool valid)
Sets whether or not this engine is valid, e.g.
Definition: dataengine.cpp:329
bool isValid() const
Returns true if this engine is valid, otherwise returns false.
Definition: dataengine.cpp:319
void removeSource(const QString &source)
Removes a data source.
Definition: dataengine.cpp:292
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
void becameUnused(const QString &source)
Emitted when the last visualization is disconnected.
void removeAllData(const QString &source)
Removes all the data associated with a data source.
Definition: dataengine.cpp:212
IntervalAlignment
Possible timing alignments.
Definition: plasma.h:223
QList::iterator begin()
void removeData(const QString &source, const QString &key)
Removes a data entry from a source.
Definition: dataengine.cpp:221
void addSource(DataContainer *source)
Adds an already constructed data source.
Definition: dataengine.cpp:256
void setPath(const QString &path)
Sets the path to the root of this package.
Definition: package.cpp:185
void disconnectVisualization(QObject *visualization)
Disconnects an object from this DataContainer.
object representing an installed Plasma package
Definition: package.h:76
void setPollingInterval(uint frequency)
Sets up an internal update tick for all data sources.
Definition: dataengine.cpp:282
QList::iterator end()
bool hasNext() const const
Q_INVOKABLE void disconnectSource(const QString &source, QObject *visualization) const
Disconnects a source from an object that was receiving data updates.
Definition: dataengine.cpp:134
const Key & key() const const
void setStorageEnabled(bool store)
sets this data container to be automatically stored.
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Feb 7 2023 04:15:01 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.