00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "dataengine.h"
00021 #include "private/dataengine_p.h"
00022
00023 #include <QQueue>
00024 #include <QTimer>
00025 #include <QTime>
00026 #include <QTimerEvent>
00027 #include <QVariant>
00028
00029 #include <kdebug.h>
00030 #include <kplugininfo.h>
00031 #include <kservice.h>
00032 #include <kstandarddirs.h>
00033
00034 #include "datacontainer.h"
00035 #include "package.h"
00036 #include "service.h"
00037 #include "scripting/dataenginescript.h"
00038
00039 #include "private/service_p.h"
00040
00041 namespace Plasma
00042 {
00043
00044 DataEngine::DataEngine(QObject *parent, KService::Ptr service)
00045 : QObject(parent),
00046 d(new DataEnginePrivate(this, service))
00047 {
00048 connect(d->updateTimer, SIGNAL(timeout()), this, SLOT(scheduleSourcesUpdated()));
00049 }
00050
00051 DataEngine::DataEngine(QObject *parent, const QVariantList &args)
00052 : QObject(parent),
00053 d(new DataEnginePrivate(this, KService::serviceByStorageId(args.count() > 0 ? args[0].toString() : QString())))
00054 {
00055 connect(d->updateTimer, SIGNAL(timeout()), this, SLOT(scheduleSourcesUpdated()));
00056 }
00057
00058 DataEngine::~DataEngine()
00059 {
00060
00061 delete d;
00062 }
00063
00064 QStringList DataEngine::sources() const
00065 {
00066 if (d->script) {
00067 return d->script->sources();
00068 } else {
00069 return d->sources.keys();
00070 }
00071 }
00072
00073 Service *DataEngine::serviceForSource(const QString &source)
00074 {
00075 if (d->script) {
00076 return d->script->serviceForSource(source);
00077 } else {
00078 return new NullService(source, this);
00079 }
00080 }
00081
00082 void DataEngine::connectSource(const QString &source, QObject *visualization,
00083 uint pollingInterval,
00084 Plasma::IntervalAlignment intervalAlignment) const
00085 {
00086
00087 bool newSource;
00088 DataContainer *s = d->requestSource(source, &newSource);
00089
00090 if (s) {
00091
00092
00093
00094
00095 d->connectSource(s, visualization, pollingInterval, intervalAlignment,
00096 !newSource || pollingInterval > 0);
00097
00098 }
00099 }
00100
00101 void DataEngine::connectAllSources(QObject *visualization, uint pollingInterval,
00102 Plasma::IntervalAlignment intervalAlignment) const
00103 {
00104 foreach (DataContainer *s, d->sources) {
00105 d->connectSource(s, visualization, pollingInterval, intervalAlignment);
00106 }
00107 }
00108
00109 void DataEngine::disconnectSource(const QString &source, QObject *visualization) const
00110 {
00111 DataContainer *s = d->source(source, false);
00112
00113 if (s) {
00114 s->disconnectVisualization(visualization);
00115 }
00116 }
00117
00118 DataContainer *DataEngine::containerForSource(const QString &source)
00119 {
00120 return d->source(source, false);
00121 }
00122
00123 DataEngine::Data DataEngine::query(const QString &source) const
00124 {
00125 bool newSource;
00126 DataContainer *s = d->requestSource(source, &newSource);
00127
00128 if (!s) {
00129 return DataEngine::Data();
00130 } else if (!newSource && d->minPollingInterval >= 0 &&
00131 s->timeSinceLastUpdate() >= uint(d->minPollingInterval)) {
00132 if (const_cast<DataEngine*>(this)->updateSourceEvent(source)) {
00133 d->queueUpdate();
00134 }
00135 }
00136
00137 DataEngine::Data data = s->data();
00138 s->checkUsage();
00139 return data;
00140 }
00141
00142 void DataEngine::init()
00143 {
00144 if (d->script) {
00145 d->script->init();
00146 } else {
00147
00148
00149
00150 }
00151 }
00152
00153 bool DataEngine::sourceRequestEvent(const QString &name)
00154 {
00155 if (d->script) {
00156 return d->script->sourceRequestEvent(name);
00157 } else {
00158 return false;
00159 }
00160 }
00161
00162 bool DataEngine::updateSourceEvent(const QString &source)
00163 {
00164 if (d->script) {
00165 return d->script->updateSourceEvent(source);
00166 } else {
00167
00168 return false;
00169 }
00170 }
00171
00172 void DataEngine::setData(const QString &source, const QVariant &value)
00173 {
00174 setData(source, source, value);
00175 }
00176
00177 void DataEngine::setData(const QString &source, const QString &key, const QVariant &value)
00178 {
00179 DataContainer *s = d->source(source, false);
00180 bool isNew = !s;
00181
00182 if (isNew) {
00183 s = d->source(source);
00184 }
00185
00186 s->setData(key, value);
00187
00188 if (isNew) {
00189 emit sourceAdded(source);
00190 }
00191
00192 d->queueUpdate();
00193 }
00194
00195 void DataEngine::setData(const QString &source, const Data &data)
00196 {
00197 DataContainer *s = d->source(source, false);
00198 bool isNew = !s;
00199
00200 if (isNew) {
00201 s = d->source(source);
00202 }
00203
00204 Data::const_iterator it = data.constBegin();
00205 while (it != data.constEnd()) {
00206 s->setData(it.key(), it.value());
00207 ++it;
00208 }
00209
00210 if (isNew) {
00211 emit sourceAdded(source);
00212 }
00213
00214 d->queueUpdate();
00215 }
00216
00217 void DataEngine::removeAllData(const QString &source)
00218 {
00219 DataContainer *s = d->source(source, false);
00220 if (s) {
00221 s->removeAllData();
00222 d->queueUpdate();
00223 }
00224 }
00225
00226 void DataEngine::removeData(const QString &source, const QString &key)
00227 {
00228 DataContainer *s = d->source(source, false);
00229 if (s) {
00230 s->setData(key, QVariant());
00231 d->queueUpdate();
00232 }
00233 }
00234
00235 void DataEngine::addSource(DataContainer *source)
00236 {
00237 if (d->sources.contains(source->objectName())) {
00238 kDebug() << "source named \"" << source->objectName() << "\" already exists.";
00239 return;
00240 }
00241
00242 QObject::connect(source, SIGNAL(updateRequested(DataContainer*)),
00243 this, SLOT(internalUpdateSource(DataContainer*)));
00244 d->sources.insert(source->objectName(), source);
00245 emit sourceAdded(source->objectName());
00246 d->queueUpdate();
00247 }
00248
00249 void DataEngine::setMaxSourceCount(uint limit)
00250 {
00251 if (d->limit == limit) {
00252 return;
00253 }
00254
00255 d->limit = limit;
00256
00257 if (d->limit > 0) {
00258 d->trimQueue();
00259 } else {
00260 d->sourceQueue.clear();
00261 }
00262 }
00263
00264 uint DataEngine::maxSourceCount() const
00265 {
00266 return d->limit;
00267 }
00268
00269 void DataEngine::setMinimumPollingInterval(int minimumMs)
00270 {
00271 if (minimumMs < 0) {
00272 minimumMs = 0;
00273 }
00274
00275 d->minPollingInterval = minimumMs;
00276 }
00277
00278 int DataEngine::minimumPollingInterval() const
00279 {
00280 return d->minPollingInterval;
00281 }
00282
00283 void DataEngine::setPollingInterval(uint frequency)
00284 {
00285 killTimer(d->updateTimerId);
00286 d->updateTimerId = 0;
00287
00288 if (frequency > 0) {
00289 d->updateTimerId = startTimer(frequency);
00290 }
00291 }
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304 void DataEngine::removeSource(const QString &source)
00305 {
00306
00307 SourceDict::iterator it = d->sources.find(source);
00308 if (it != d->sources.end()) {
00309 DataContainer *s = it.value();
00310
00311
00312 if (d->limit > 0) {
00313 QQueue<DataContainer*>::iterator it = d->sourceQueue.begin();
00314 while (it != d->sourceQueue.end()) {
00315 if (*it == s) {
00316 d->sourceQueue.erase(it);
00317 break;
00318 }
00319 ++it;
00320 }
00321 }
00322
00323 s->deleteLater();
00324 d->sources.erase(it);
00325 emit sourceRemoved(source);
00326 }
00327 }
00328
00329 void DataEngine::removeAllSources()
00330 {
00331 QMutableHashIterator<QString, Plasma::DataContainer*> it(d->sources);
00332 while (it.hasNext()) {
00333 it.next();
00334 emit sourceRemoved(it.key());
00335 delete it.value();
00336 it.remove();
00337 }
00338 }
00339
00340 bool DataEngine::isValid() const
00341 {
00342 return d->valid;
00343 }
00344
00345 bool DataEngine::isEmpty() const
00346 {
00347 return d->sources.isEmpty();
00348 }
00349
00350 void DataEngine::setValid(bool valid)
00351 {
00352 d->valid = valid;
00353 }
00354
00355 DataEngine::SourceDict DataEngine::containerDict() const
00356 {
00357 return d->sources;
00358 }
00359
00360 void DataEngine::timerEvent(QTimerEvent *event)
00361 {
00362 if (event->timerId() != d->updateTimerId) {
00363 kDebug() << "bzzzt";
00364 return;
00365 }
00366
00367 event->accept();
00368
00369
00370 if (d->minPollingInterval < 0) {
00371
00372 return;
00373 }
00374
00375
00376 if (d->updateTimestamp.elapsed() < d->minPollingInterval) {
00377
00378 return;
00379 }
00380
00381 d->updateTimestamp.restart();
00382 updateAllSources();
00383 }
00384
00385 void DataEngine::updateAllSources()
00386 {
00387 QHashIterator<QString, Plasma::DataContainer*> it(d->sources);
00388 while (it.hasNext()) {
00389 it.next();
00390
00391 updateSourceEvent(it.key());
00392 }
00393
00394 scheduleSourcesUpdated();
00395 }
00396
00397 void DataEngine::setIcon(const QString &icon)
00398 {
00399 d->icon = icon;
00400 }
00401
00402 QString DataEngine::icon() const
00403 {
00404 return d->icon;
00405 }
00406
00407 QString DataEngine::pluginName() const
00408 {
00409 if (!d->dataEngineDescription.isValid()) {
00410 return QString();
00411 }
00412
00413 return d->dataEngineDescription.pluginName();
00414 }
00415
00416 const Package *DataEngine::package() const
00417 {
00418 return d->package;
00419 }
00420
00421 void DataEngine::scheduleSourcesUpdated()
00422 {
00423 QHashIterator<QString, Plasma::DataContainer*> it(d->sources);
00424 while (it.hasNext()) {
00425 it.next();
00426 it.value()->checkForUpdate();
00427 }
00428 }
00429
00430 QString DataEngine::name() const
00431 {
00432 return d->engineName;
00433 }
00434
00435 void DataEngine::setName(const QString &name)
00436 {
00437 d->engineName = name;
00438 setObjectName(name);
00439 }
00440
00441
00442 DataEnginePrivate::DataEnginePrivate(DataEngine *e, KService::Ptr service)
00443 : q(e),
00444 dataEngineDescription(service),
00445 refCount(-1),
00446 updateTimerId(0),
00447 minPollingInterval(-1),
00448 limit(0),
00449 valid(true),
00450 script(0),
00451 package(0)
00452 {
00453 updateTimer = new QTimer(q);
00454 updateTimer->setSingleShot(true);
00455 updateTimestamp.start();
00456
00457 if (!service) {
00458 engineName = i18n("Unnamed");
00459 return;
00460 }
00461
00462 engineName = service->name();
00463 if (engineName.isEmpty()) {
00464 engineName = i18n("Unnamed");
00465 }
00466 e->setObjectName(engineName);
00467 icon = service->icon();
00468
00469 if (dataEngineDescription.isValid()) {
00470 QString api = dataEngineDescription.property("X-Plasma-API").toString();
00471
00472 if (!api.isEmpty()) {
00473 const QString path =
00474 KStandardDirs::locate("data",
00475 "plasma/dataengines/" + dataEngineDescription.pluginName() + '/');
00476 PackageStructure::Ptr structure =
00477 Plasma::packageStructure(api, Plasma::DataEngineComponent);
00478 structure->setPath(path);
00479 package = new Package(path, structure);
00480
00481 script = Plasma::loadScriptEngine(api, q);
00482 if (!script) {
00483 kDebug() << "Could not create a" << api << "ScriptEngine for the"
00484 << dataEngineDescription.name() << "DataEngine.";
00485 delete package;
00486 package = 0;
00487 }
00488 }
00489 }
00490 }
00491
00492 DataEnginePrivate::~DataEnginePrivate()
00493 {
00494 delete script;
00495 script = 0;
00496 delete package;
00497 package = 0;
00498 }
00499
00500 void DataEnginePrivate::internalUpdateSource(DataContainer *source)
00501 {
00502 if (minPollingInterval > 0 &&
00503 source->timeSinceLastUpdate() < (uint)minPollingInterval) {
00504
00505
00506
00507
00508
00509 source->setNeedsUpdate();
00510 return;
00511 }
00512
00513 if (q->updateSourceEvent(source->objectName())) {
00514
00515 queueUpdate();
00516 }
00517
00518
00519 }
00520
00521 void DataEnginePrivate::ref()
00522 {
00523 --refCount;
00524 }
00525
00526 void DataEnginePrivate::deref()
00527 {
00528 ++refCount;
00529 }
00530
00531 bool DataEnginePrivate::isUsed() const
00532 {
00533 return refCount != 0;
00534 }
00535
00536 DataContainer *DataEnginePrivate::source(const QString &sourceName, bool createWhenMissing)
00537 {
00538 DataEngine::SourceDict::const_iterator it = sources.constFind(sourceName);
00539 if (it != sources.constEnd()) {
00540 DataContainer *s = it.value();
00541 if (limit > 0) {
00542 QQueue<DataContainer*>::iterator it = sourceQueue.begin();
00543 while (it != sourceQueue.end()) {
00544 if (*it == s) {
00545 sourceQueue.erase(it);
00546 break;
00547 }
00548 ++it;
00549 }
00550 sourceQueue.enqueue(s);
00551 }
00552 return it.value();
00553 }
00554
00555 if (!createWhenMissing) {
00556 return 0;
00557 }
00558
00559
00560
00561
00562 DataContainer *s = new DataContainer(q);
00563 s->setObjectName(sourceName);
00564 sources.insert(sourceName, s);
00565 QObject::connect(s, SIGNAL(updateRequested(DataContainer*)),
00566 q, SLOT(internalUpdateSource(DataContainer*)));
00567
00568 if (limit > 0) {
00569 trimQueue();
00570 sourceQueue.enqueue(s);
00571 }
00572 return s;
00573 }
00574
00575 void DataEnginePrivate::connectSource(DataContainer *s, QObject *visualization,
00576 uint pollingInterval,
00577 Plasma::IntervalAlignment align,
00578 bool immediateCall)
00579 {
00580
00581 if (pollingInterval > 0) {
00582
00583 uint min = qMax(50, minPollingInterval);
00584 pollingInterval = qMax(min, pollingInterval);
00585
00586
00587 pollingInterval = pollingInterval - (pollingInterval % 50);
00588 }
00589
00590 if (immediateCall) {
00591
00592
00593
00594 immediateCall = !s->visualizationIsConnected(visualization);
00595 }
00596
00597 s->connectVisualization(visualization, pollingInterval, align);
00598
00599 if (immediateCall) {
00600 QMetaObject::invokeMethod(visualization, "dataUpdated",
00601 Q_ARG(QString, s->objectName()),
00602 Q_ARG(Plasma::DataEngine::Data, s->data()));
00603 }
00604 }
00605
00606 DataContainer *DataEnginePrivate::requestSource(const QString &sourceName, bool *newSource)
00607 {
00608 if (newSource) {
00609 *newSource = false;
00610 }
00611
00612
00613 DataContainer *s = source(sourceName, false);
00614
00615 if (!s) {
00616
00617
00618
00619
00620 if (q->sourceRequestEvent(sourceName)) {
00621 s = source(sourceName, false);
00622 if (s) {
00623
00624
00625 if (newSource) {
00626 *newSource = true;
00627 }
00628 QObject::connect(s, SIGNAL(becameUnused(QString)), q, SLOT(removeSource(QString)));
00629 }
00630 }
00631 }
00632
00633 return s;
00634 }
00635
00636 void DataEnginePrivate::trimQueue()
00637 {
00638 uint queueCount = sourceQueue.count();
00639 while (queueCount >= limit) {
00640 DataContainer *punted = sourceQueue.dequeue();
00641 q->removeSource(punted->objectName());
00642 }
00643 }
00644
00645 void DataEnginePrivate::queueUpdate()
00646 {
00647 if (updateTimer->isActive()) {
00648 return;
00649 }
00650 updateTimer->start(0);
00651 }
00652
00653 }
00654
00655 #include "dataengine.moc"