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