Plasma
dataengine.cpp
Go to the documentation of this file.
00001 /* 00002 * Copyright 2006-2007 Aaron Seigo <aseigo@kde.org> 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU Library General Public License as 00006 * published by the Free Software Foundation; either version 2, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details 00013 * 00014 * You should have received a copy of the GNU Library General Public 00015 * License along with this program; if not, write to the 00016 * Free Software Foundation, Inc., 00017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "dataengine.h" 00021 #include "private/dataengine_p.h" 00022 #include "private/datacontainer_p.h" 00023 00024 #include <QQueue> 00025 #include <QTimer> 00026 #include <QTime> 00027 #include <QTimerEvent> 00028 #include <QVariant> 00029 00030 #include <kdebug.h> 00031 #include <kplugininfo.h> 00032 #include <kservice.h> 00033 #include <kstandarddirs.h> 00034 00035 #include "authorizationmanager.h" 00036 #include "datacontainer.h" 00037 #include "package.h" 00038 #include "service.h" 00039 #include "scripting/dataenginescript.h" 00040 00041 #include "private/authorizationmanager_p.h" 00042 #include "private/dataengineservice_p.h" 00043 #include "private/remotedataengine_p.h" 00044 #include "private/service_p.h" 00045 #include "private/storage_p.h" 00046 00047 namespace Plasma 00048 { 00049 00050 DataEngine::DataEngine(QObject *parent, KService::Ptr service) 00051 : QObject(parent), 00052 d(new DataEnginePrivate(this, KPluginInfo(service))) 00053 { 00054 } 00055 00056 DataEngine::DataEngine(QObject *parent, const QVariantList &args) 00057 : QObject(parent), 00058 d(new DataEnginePrivate(this, KPluginInfo(KService::serviceByStorageId(args.count() > 0 ? args[0].toString() : QString())))) 00059 { 00060 } 00061 00062 DataEngine::~DataEngine() 00063 { 00064 //kDebug() << objectName() << ": bye bye birdy! "; 00065 delete d; 00066 } 00067 00068 QStringList DataEngine::sources() const 00069 { 00070 if (d->script) { 00071 return d->script->sources(); 00072 } else { 00073 return d->sources.keys(); 00074 } 00075 } 00076 00077 Service *DataEngine::serviceForSource(const QString &source) 00078 { 00079 if (d->script) { 00080 Service * s = d->script->serviceForSource(source); 00081 if (s) { 00082 return s; 00083 } 00084 } 00085 00086 return new NullService(source, this); 00087 } 00088 00089 void DataEngine::connectSource(const QString &source, QObject *visualization, 00090 uint pollingInterval, 00091 Plasma::IntervalAlignment intervalAlignment) const 00092 { 00093 //kDebug() << "connectSource" << source; 00094 bool newSource; 00095 DataContainer *s = d->requestSource(source, &newSource); 00096 00097 if (s) { 00098 // we suppress the immediate invocation of dataUpdated here if the 00099 // source was prexisting and they don't request delayed updates 00100 // (we want to do an immediate update in that case so they don't 00101 // have to wait for the first time out) 00102 if (newSource && !s->data().isEmpty()) { 00103 newSource = false; 00104 } 00105 d->connectSource(s, visualization, pollingInterval, intervalAlignment, 00106 !newSource || pollingInterval > 0); 00107 //kDebug() << " ==> source connected"; 00108 } 00109 } 00110 00111 void DataEngine::connectAllSources(QObject *visualization, uint pollingInterval, 00112 Plasma::IntervalAlignment intervalAlignment) const 00113 { 00114 foreach (DataContainer *s, d->sources) { 00115 d->connectSource(s, visualization, pollingInterval, intervalAlignment); 00116 } 00117 } 00118 00119 void DataEngine::disconnectSource(const QString &source, QObject *visualization) const 00120 { 00121 DataContainer *s = d->source(source, false); 00122 00123 if (s) { 00124 s->disconnectVisualization(visualization); 00125 } 00126 } 00127 00128 DataContainer *DataEngine::containerForSource(const QString &source) 00129 { 00130 return d->source(source, false); 00131 } 00132 00133 DataEngine::Data DataEngine::query(const QString &source) const 00134 { 00135 bool newSource; 00136 DataContainer *s = d->requestSource(source, &newSource); 00137 00138 if (!s) { 00139 return DataEngine::Data(); 00140 } else if (!newSource && d->minPollingInterval >= 0 && 00141 s->timeSinceLastUpdate() >= uint(d->minPollingInterval)) { 00142 DataEngine *unconstThis = const_cast<DataEngine*>(this); 00143 if (unconstThis->updateSourceEvent(source)) { 00144 unconstThis->scheduleSourcesUpdated(); 00145 } 00146 } 00147 00148 DataEngine::Data data = s->data(); 00149 s->checkUsage(); 00150 return data; 00151 } 00152 00153 void DataEngine::init() 00154 { 00155 if (d->script) { 00156 d->setupScriptSupport(); 00157 d->script->init(); 00158 } else { 00159 // kDebug() << "called"; 00160 // default implementation does nothing. this is for engines that have to 00161 // start things in motion external to themselves before they can work 00162 } 00163 } 00164 00165 bool DataEngine::sourceRequestEvent(const QString &name) 00166 { 00167 if (d->script) { 00168 return d->script->sourceRequestEvent(name); 00169 } else { 00170 return false; 00171 } 00172 } 00173 00174 bool DataEngine::updateSourceEvent(const QString &source) 00175 { 00176 if (d->script) { 00177 return d->script->updateSourceEvent(source); 00178 } else { 00179 //kDebug() << source; 00180 return false; //TODO: should this be true to trigger, even needless, updates on every tick? 00181 } 00182 } 00183 00184 void DataEngine::setData(const QString &source, const QVariant &value) 00185 { 00186 setData(source, source, value); 00187 } 00188 00189 void DataEngine::setData(const QString &source, const QString &key, const QVariant &value) 00190 { 00191 DataContainer *s = d->source(source, false); 00192 bool isNew = !s; 00193 00194 if (isNew) { 00195 s = d->source(source); 00196 } 00197 00198 s->setData(key, value); 00199 00200 if (isNew && source != d->waitingSourceRequest) { 00201 emit sourceAdded(source); 00202 } 00203 00204 scheduleSourcesUpdated(); 00205 } 00206 00207 void DataEngine::setData(const QString &source, const Data &data) 00208 { 00209 DataContainer *s = d->source(source, false); 00210 bool isNew = !s; 00211 00212 if (isNew) { 00213 s = d->source(source); 00214 } 00215 00216 Data::const_iterator it = data.constBegin(); 00217 while (it != data.constEnd()) { 00218 s->setData(it.key(), it.value()); 00219 ++it; 00220 } 00221 00222 if (isNew && source != d->waitingSourceRequest) { 00223 emit sourceAdded(source); 00224 } 00225 00226 scheduleSourcesUpdated(); 00227 } 00228 00229 void DataEngine::removeAllData(const QString &source) 00230 { 00231 DataContainer *s = d->source(source, false); 00232 if (s) { 00233 s->removeAllData(); 00234 scheduleSourcesUpdated(); 00235 } 00236 } 00237 00238 void DataEngine::removeData(const QString &source, const QString &key) 00239 { 00240 DataContainer *s = d->source(source, false); 00241 if (s) { 00242 s->setData(key, QVariant()); 00243 scheduleSourcesUpdated(); 00244 } 00245 } 00246 00247 void DataEngine::addSource(DataContainer *source) 00248 { 00249 if (d->sources.contains(source->objectName())) { 00250 kDebug() << "source named \"" << source->objectName() << "\" already exists."; 00251 return; 00252 } 00253 00254 QObject::connect(source, SIGNAL(updateRequested(DataContainer*)), 00255 this, SLOT(internalUpdateSource(DataContainer*))); 00256 QObject::connect(source, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*))); 00257 d->sources.insert(source->objectName(), source); 00258 emit sourceAdded(source->objectName()); 00259 scheduleSourcesUpdated(); 00260 } 00261 00262 void DataEngine::setMaxSourceCount(uint limit) 00263 { 00264 if (d->limit == limit) { 00265 return; 00266 } 00267 00268 d->limit = limit; 00269 00270 if (d->limit > 0) { 00271 d->trimQueue(); 00272 } else { 00273 d->sourceQueue.clear(); 00274 } 00275 } 00276 00277 uint DataEngine::maxSourceCount() const 00278 { 00279 return d->limit; 00280 } 00281 00282 void DataEngine::setMinimumPollingInterval(int minimumMs) 00283 { 00284 d->minPollingInterval = minimumMs; 00285 } 00286 00287 int DataEngine::minimumPollingInterval() const 00288 { 00289 return d->minPollingInterval; 00290 } 00291 00292 void DataEngine::setPollingInterval(uint frequency) 00293 { 00294 killTimer(d->updateTimerId); 00295 d->updateTimerId = 0; 00296 00297 if (frequency > 0) { 00298 d->updateTimerId = startTimer(frequency); 00299 } 00300 } 00301 00302 void DataEngine::removeSource(const QString &source) 00303 { 00304 SourceDict::iterator it = d->sources.find(source); 00305 if (it != d->sources.end()) { 00306 DataContainer *s = it.value(); 00307 00308 // remove it from the limit queue if we're keeping one 00309 if (d->limit > 0) { 00310 QQueue<DataContainer*>::iterator it = d->sourceQueue.begin(); 00311 while (it != d->sourceQueue.end()) { 00312 if (*it == s) { 00313 d->sourceQueue.erase(it); 00314 break; 00315 } 00316 ++it; 00317 } 00318 } 00319 00320 s->d->store(); 00321 s->disconnect(this); 00322 s->deleteLater(); 00323 d->sources.erase(it); 00324 emit sourceRemoved(source); 00325 } 00326 } 00327 00328 void DataEngine::removeAllSources() 00329 { 00330 QMutableHashIterator<QString, Plasma::DataContainer*> it(d->sources); 00331 while (it.hasNext()) { 00332 it.next(); 00333 Plasma::DataContainer *s = it.value(); 00334 const QString source = it.key(); 00335 it.remove(); 00336 s->disconnect(this); 00337 s->deleteLater(); 00338 emit sourceRemoved(source); 00339 } 00340 } 00341 00342 bool DataEngine::isValid() const 00343 { 00344 return d->valid; 00345 } 00346 00347 bool DataEngine::isEmpty() const 00348 { 00349 return d->sources.isEmpty(); 00350 } 00351 00352 void DataEngine::setValid(bool valid) 00353 { 00354 d->valid = valid; 00355 } 00356 00357 DataEngine::SourceDict DataEngine::containerDict() const 00358 { 00359 return d->sources; 00360 } 00361 00362 void DataEngine::timerEvent(QTimerEvent *event) 00363 { 00364 //kDebug(); 00365 if (event->timerId() == d->updateTimerId) { 00366 // if the freq update is less than 0, don't bother 00367 if (d->minPollingInterval < 0) { 00368 //kDebug() << "uh oh.. no polling allowed!"; 00369 return; 00370 } 00371 00372 // minPollingInterval 00373 if (d->updateTimestamp.elapsed() < d->minPollingInterval) { 00374 //kDebug() << "hey now.. slow down!"; 00375 return; 00376 } 00377 00378 d->updateTimestamp.restart(); 00379 updateAllSources(); 00380 } else if (event->timerId() == d->checkSourcesTimerId) { 00381 killTimer(d->checkSourcesTimerId); 00382 d->checkSourcesTimerId = 0; 00383 00384 QHashIterator<QString, Plasma::DataContainer*> it(d->sources); 00385 while (it.hasNext()) { 00386 it.next(); 00387 it.value()->checkForUpdate(); 00388 } 00389 } else { 00390 QObject::timerEvent(event); 00391 } 00392 } 00393 00394 void DataEngine::updateAllSources() 00395 { 00396 QHashIterator<QString, Plasma::DataContainer*> it(d->sources); 00397 while (it.hasNext()) { 00398 it.next(); 00399 //kDebug() << "updating" << it.key(); 00400 updateSourceEvent(it.key()); 00401 } 00402 00403 scheduleSourcesUpdated(); 00404 } 00405 00406 void DataEngine::forceImmediateUpdateOfAllVisualizations() 00407 { 00408 foreach (DataContainer *source, d->sources) { 00409 source->forceImmediateUpdate(); 00410 } 00411 } 00412 00413 void DataEngine::setIcon(const QString &icon) 00414 { 00415 d->icon = icon; 00416 } 00417 00418 QString DataEngine::icon() const 00419 { 00420 return d->icon; 00421 } 00422 00423 QString DataEngine::pluginName() const 00424 { 00425 if (!d->dataEngineDescription.isValid()) { 00426 return QString(); 00427 } 00428 00429 return d->dataEngineDescription.pluginName(); 00430 } 00431 00432 void DataEngine::setDefaultService(const QString &serviceName) 00433 { 00434 d->serviceName = serviceName; 00435 } 00436 00437 Service* DataEngine::createDefaultService(QObject *parent) 00438 { 00439 QVariantList args; 00440 args << QVariant::fromValue<DataEngine*>(this); 00441 return Service::load(d->serviceName, args, parent); 00442 } 00443 00444 void DataEnginePrivate::publish(AnnouncementMethods methods, const QString &name) 00445 { 00446 if (!publishedService) { 00447 publishedService = new DataEngineService(q); 00448 } 00449 00450 //QString resourceName = 00451 //i18nc("%1 is the name of a dataengine, %2 the name of the machine that engine is published 00452 //on", 00453 //"%1 dataengine on %2", name(), AuthorizationManager::self()->d->myCredentials.name()); 00454 kDebug() << "name: " << name; 00455 publishedService->d->publish(methods, name); 00456 } 00457 00458 void DataEnginePrivate::unpublish(const QString &name) 00459 { 00460 Q_UNUSED(name) 00461 00462 if (publishedService) { 00463 publishedService->d->unpublish(); 00464 } 00465 } 00466 00467 bool DataEnginePrivate::isPublished() const 00468 { 00469 if (publishedService) { 00470 return publishedService->d->isPublished(); 00471 } else { 00472 return false; 00473 } 00474 } 00475 00476 const Package *DataEngine::package() const 00477 { 00478 return d->package; 00479 } 00480 00481 void DataEngine::scheduleSourcesUpdated() 00482 { 00483 if (d->checkSourcesTimerId) { 00484 return; 00485 } 00486 00487 d->checkSourcesTimerId = startTimer(0); 00488 } 00489 00490 QString DataEngine::name() const 00491 { 00492 return d->engineName; 00493 } 00494 00495 void DataEngine::setName(const QString &name) 00496 { 00497 d->engineName = name; 00498 setObjectName(name); 00499 } 00500 00501 void DataEngine::setStorageEnabled(const QString &source, bool store) 00502 { 00503 DataContainer *s = d->source(source, false); 00504 if (s) { 00505 s->setStorageEnabled(store); 00506 } 00507 } 00508 00509 // Private class implementations 00510 DataEnginePrivate::DataEnginePrivate(DataEngine *e, const KPluginInfo &info) 00511 : q(e), 00512 dataEngineDescription(info), 00513 refCount(-1), // first ref 00514 checkSourcesTimerId(0), 00515 updateTimerId(0), 00516 minPollingInterval(-1), 00517 limit(0), 00518 valid(true), 00519 script(0), 00520 package(0), 00521 publishedService(0) 00522 { 00523 updateTimestamp.start(); 00524 00525 if (!info.isValid()) { 00526 engineName = i18n("Unnamed"); 00527 return; 00528 } 00529 00530 engineName = info.name(); 00531 if (engineName.isEmpty()) { 00532 engineName = i18n("Unnamed"); 00533 } 00534 e->setObjectName(engineName); 00535 icon = info.icon(); 00536 00537 if (dataEngineDescription.isValid()) { 00538 QString api = dataEngineDescription.property("X-Plasma-API").toString(); 00539 00540 if (!api.isEmpty()) { 00541 const QString path = 00542 KStandardDirs::locate("data", 00543 "plasma/dataengines/" + dataEngineDescription.pluginName() + '/'); 00544 PackageStructure::Ptr structure = Plasma::packageStructure(api, Plasma::DataEngineComponent); 00545 structure->setPath(path); 00546 package = new Package(path, structure); 00547 00548 script = Plasma::loadScriptEngine(api, q); 00549 if (!script) { 00550 kDebug() << "Could not create a" << api << "ScriptEngine for the" 00551 << dataEngineDescription.name() << "DataEngine."; 00552 delete package; 00553 package = 0; 00554 } 00555 } 00556 } 00557 } 00558 00559 DataEnginePrivate::~DataEnginePrivate() 00560 { 00561 delete script; 00562 script = 0; 00563 delete package; 00564 package = 0; 00565 } 00566 00567 void DataEnginePrivate::internalUpdateSource(DataContainer *source) 00568 { 00569 if (minPollingInterval > 0 && 00570 source->timeSinceLastUpdate() < (uint)minPollingInterval) { 00571 // skip updating this source; it's been too soon 00572 //kDebug() << "internal update source is delaying" << source->timeSinceLastUpdate() << minPollingInterval; 00573 //but fake an update so that the signalrelay that triggered this gets the data from the 00574 //recent update. this way we don't have to worry about queuing - the relay will send a 00575 //signal immediately and everyone else is undisturbed. 00576 source->setNeedsUpdate(); 00577 return; 00578 } 00579 00580 if (q->updateSourceEvent(source->objectName())) { 00581 //kDebug() << "queuing an update"; 00582 q->scheduleSourcesUpdated(); 00583 }/* else { 00584 kDebug() << "no update"; 00585 }*/ 00586 } 00587 00588 void DataEnginePrivate::ref() 00589 { 00590 --refCount; 00591 } 00592 00593 void DataEnginePrivate::deref() 00594 { 00595 ++refCount; 00596 } 00597 00598 bool DataEnginePrivate::isUsed() const 00599 { 00600 return refCount != 0; 00601 } 00602 00603 DataContainer *DataEnginePrivate::source(const QString &sourceName, bool createWhenMissing) 00604 { 00605 DataEngine::SourceDict::const_iterator it = sources.constFind(sourceName); 00606 if (it != sources.constEnd()) { 00607 DataContainer *s = it.value(); 00608 if (limit > 0) { 00609 QQueue<DataContainer*>::iterator it = sourceQueue.begin(); 00610 while (it != sourceQueue.end()) { 00611 if (*it == s) { 00612 sourceQueue.erase(it); 00613 break; 00614 } 00615 ++it; 00616 } 00617 sourceQueue.enqueue(s); 00618 } 00619 return s; 00620 } 00621 00622 if (!createWhenMissing) { 00623 return 0; 00624 } 00625 00626 //kDebug() << "DataEngine " << q->objectName() << ": could not find DataContainer " << sourceName << ", creating"; 00627 DataContainer *s = new DataContainer(q); 00628 s->setObjectName(sourceName); 00629 sources.insert(sourceName, s); 00630 QObject::connect(s, SIGNAL(destroyed(QObject*)), q, SLOT(sourceDestroyed(QObject*))); 00631 QObject::connect(s, SIGNAL(updateRequested(DataContainer*)), 00632 q, SLOT(internalUpdateSource(DataContainer*))); 00633 00634 if (limit > 0) { 00635 trimQueue(); 00636 sourceQueue.enqueue(s); 00637 } 00638 return s; 00639 } 00640 00641 void DataEnginePrivate::connectSource(DataContainer *s, QObject *visualization, 00642 uint pollingInterval, 00643 Plasma::IntervalAlignment align, 00644 bool immediateCall) 00645 { 00646 //kDebug() << "connect source called" << s->objectName() << "with interval" << pollingInterval; 00647 00648 //FIXME: at the moment a remote dataengine can only poll, a push mechanism will be needed instead 00649 if (pollingInterval == 0 && qobject_cast<RemoteDataEngine *>(q)) { 00650 pollingInterval = 5000; 00651 } 00652 if (pollingInterval > 0) { 00653 // never more frequently than allowed, never more than 20 times per second 00654 uint min = qMax(50, minPollingInterval); // for qMax below 00655 pollingInterval = qMax(min, pollingInterval); 00656 00657 // align on the 50ms 00658 pollingInterval = pollingInterval - (pollingInterval % 50); 00659 } 00660 00661 if (immediateCall) { 00662 // we don't want to do an immediate call if we are simply 00663 // reconnecting 00664 //kDebug() << "immediate call requested, we have:" << s->visualizationIsConnected(visualization); 00665 immediateCall = !s->data().isEmpty() && 00666 !s->visualizationIsConnected(visualization); 00667 } 00668 00669 s->connectVisualization(visualization, pollingInterval, align); 00670 00671 if (immediateCall) { 00672 QMetaObject::invokeMethod(visualization, "dataUpdated", 00673 Q_ARG(QString, s->objectName()), 00674 Q_ARG(Plasma::DataEngine::Data, s->data())); 00675 s->d->dirty = false; 00676 } 00677 } 00678 00679 void DataEnginePrivate::sourceDestroyed(QObject *object) 00680 { 00681 DataEngine::SourceDict::iterator it = sources.begin(); 00682 while (it != sources.end()) { 00683 if (it.value() == object) { 00684 sources.erase(it); 00685 emit q->sourceRemoved(object->objectName()); 00686 break; 00687 } 00688 ++it; 00689 } 00690 } 00691 00692 DataContainer *DataEnginePrivate::requestSource(const QString &sourceName, bool *newSource) 00693 { 00694 if (newSource) { 00695 *newSource = false; 00696 } 00697 00698 //kDebug() << "requesting source " << sourceName; 00699 DataContainer *s = source(sourceName, false); 00700 00701 if (!s) { 00702 // we didn't find a data source, so give the engine an opportunity to make one 00703 /*kDebug() << "DataEngine " << q->objectName() 00704 << ": could not find DataContainer " << sourceName 00705 << " will create on request" << endl;*/ 00706 waitingSourceRequest = sourceName; 00707 if (q->sourceRequestEvent(sourceName)) { 00708 s = source(sourceName, false); 00709 if (s) { 00710 // now we have a source; since it was created on demand, assume 00711 // it should be removed when not used 00712 if (newSource) { 00713 *newSource = true; 00714 } 00715 QObject::connect(s, SIGNAL(becameUnused(QString)), q, SLOT(removeSource(QString))); 00716 emit q->sourceAdded(sourceName); 00717 } 00718 } 00719 waitingSourceRequest.clear(); 00720 } 00721 00722 return s; 00723 } 00724 00725 void DataEnginePrivate::trimQueue() 00726 { 00727 uint queueCount = sourceQueue.count(); 00728 while (queueCount >= limit && !sourceQueue.isEmpty()) { 00729 DataContainer *punted = sourceQueue.dequeue(); 00730 q->removeSource(punted->objectName()); 00731 queueCount = sourceQueue.count(); 00732 } 00733 } 00734 00735 // put all setup routines for script here. at this point we can assume that 00736 // package exists and that we have a script engine 00737 void DataEnginePrivate::setupScriptSupport() 00738 { 00739 if (!package) { 00740 return; 00741 } 00742 00743 /* 00744 kDebug() << "sletting up script support, package is in" << package->path() 00745 << "which is a" << package->structure()->type() << "package" 00746 << ", main script is" << package->filePath("mainscript"); 00747 */ 00748 00749 QString translationsPath = package->filePath("translations"); 00750 if (!translationsPath.isEmpty()) { 00751 //FIXME: we should _probably_ use a KComponentData to segregate the applets 00752 // from each other; but I want to get the basics working first :) 00753 KGlobal::dirs()->addResourceDir("locale", translationsPath); 00754 KGlobal::locale()->insertCatalog(package->metadata().pluginName()); 00755 } 00756 } 00757 00758 } 00759 00760 #include "dataengine.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 17:36:03 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 17:36:03 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.