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) { 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) { 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 const QString source = it.key(); 00334 Plasma::DataContainer *s = it.value(); 00335 it.remove(); 00336 emit sourceRemoved(source); 00337 delete s; 00338 } 00339 } 00340 00341 bool DataEngine::isValid() const 00342 { 00343 return d->valid; 00344 } 00345 00346 bool DataEngine::isEmpty() const 00347 { 00348 return d->sources.isEmpty(); 00349 } 00350 00351 void DataEngine::setValid(bool valid) 00352 { 00353 d->valid = valid; 00354 } 00355 00356 DataEngine::SourceDict DataEngine::containerDict() const 00357 { 00358 return d->sources; 00359 } 00360 00361 void DataEngine::timerEvent(QTimerEvent *event) 00362 { 00363 //kDebug(); 00364 if (event->timerId() == d->updateTimerId) { 00365 // if the freq update is less than 0, don't bother 00366 if (d->minPollingInterval < 0) { 00367 //kDebug() << "uh oh.. no polling allowed!"; 00368 return; 00369 } 00370 00371 // minPollingInterval 00372 if (d->updateTimestamp.elapsed() < d->minPollingInterval) { 00373 //kDebug() << "hey now.. slow down!"; 00374 return; 00375 } 00376 00377 d->updateTimestamp.restart(); 00378 updateAllSources(); 00379 } else if (event->timerId() == d->checkSourcesTimerId) { 00380 killTimer(d->checkSourcesTimerId); 00381 d->checkSourcesTimerId = 0; 00382 00383 QHashIterator<QString, Plasma::DataContainer*> it(d->sources); 00384 while (it.hasNext()) { 00385 it.next(); 00386 it.value()->checkForUpdate(); 00387 } 00388 } else { 00389 QObject::timerEvent(event); 00390 } 00391 } 00392 00393 void DataEngine::updateAllSources() 00394 { 00395 QHashIterator<QString, Plasma::DataContainer*> it(d->sources); 00396 while (it.hasNext()) { 00397 it.next(); 00398 //kDebug() << "updating" << it.key(); 00399 updateSourceEvent(it.key()); 00400 } 00401 00402 scheduleSourcesUpdated(); 00403 } 00404 00405 void DataEngine::forceImmediateUpdateOfAllVisualizations() 00406 { 00407 foreach (DataContainer *source, d->sources) { 00408 source->forceImmediateUpdate(); 00409 } 00410 } 00411 00412 void DataEngine::setIcon(const QString &icon) 00413 { 00414 d->icon = icon; 00415 } 00416 00417 QString DataEngine::icon() const 00418 { 00419 return d->icon; 00420 } 00421 00422 QString DataEngine::pluginName() const 00423 { 00424 if (!d->dataEngineDescription.isValid()) { 00425 return QString(); 00426 } 00427 00428 return d->dataEngineDescription.pluginName(); 00429 } 00430 00431 void DataEngine::setDefaultService(const QString &serviceName) 00432 { 00433 d->serviceName = serviceName; 00434 } 00435 00436 Service* DataEngine::createDefaultService(QObject *parent) 00437 { 00438 QVariantList args; 00439 args << QVariant::fromValue<DataEngine*>(this); 00440 return Service::load(d->serviceName, args, parent); 00441 } 00442 00443 void DataEnginePrivate::publish(AnnouncementMethods methods, const QString &name) 00444 { 00445 if (!publishedService) { 00446 publishedService = new DataEngineService(q); 00447 } 00448 00449 //QString resourceName = 00450 //i18nc("%1 is the name of a dataengine, %2 the name of the machine that engine is published 00451 //on", 00452 //"%1 dataengine on %2", name(), AuthorizationManager::self()->d->myCredentials.name()); 00453 kDebug() << "name: " << name; 00454 publishedService->d->publish(methods, name); 00455 } 00456 00457 void DataEnginePrivate::unpublish(const QString &name) 00458 { 00459 Q_UNUSED(name) 00460 00461 if (publishedService) { 00462 publishedService->d->unpublish(); 00463 } 00464 } 00465 00466 bool DataEnginePrivate::isPublished() const 00467 { 00468 if (publishedService) { 00469 return publishedService->d->isPublished(); 00470 } else { 00471 return false; 00472 } 00473 } 00474 00475 const Package *DataEngine::package() const 00476 { 00477 return d->package; 00478 } 00479 00480 void DataEngine::scheduleSourcesUpdated() 00481 { 00482 if (d->checkSourcesTimerId) { 00483 return; 00484 } 00485 00486 d->checkSourcesTimerId = startTimer(0); 00487 } 00488 00489 QString DataEngine::name() const 00490 { 00491 return d->engineName; 00492 } 00493 00494 void DataEngine::setName(const QString &name) 00495 { 00496 d->engineName = name; 00497 setObjectName(name); 00498 } 00499 00500 void DataEngine::setStorageEnabled(const QString &source, bool store) 00501 { 00502 DataContainer *s = d->source(source, false); 00503 if (s) { 00504 s->setStorageEnabled(store); 00505 } 00506 } 00507 00508 // Private class implementations 00509 DataEnginePrivate::DataEnginePrivate(DataEngine *e, const KPluginInfo &info) 00510 : q(e), 00511 dataEngineDescription(info), 00512 refCount(-1), // first ref 00513 checkSourcesTimerId(0), 00514 updateTimerId(0), 00515 minPollingInterval(-1), 00516 limit(0), 00517 valid(true), 00518 script(0), 00519 package(0), 00520 publishedService(0) 00521 { 00522 updateTimestamp.start(); 00523 00524 if (!info.isValid()) { 00525 engineName = i18n("Unnamed"); 00526 return; 00527 } 00528 00529 engineName = info.name(); 00530 if (engineName.isEmpty()) { 00531 engineName = i18n("Unnamed"); 00532 } 00533 e->setObjectName(engineName); 00534 icon = info.icon(); 00535 00536 if (dataEngineDescription.isValid()) { 00537 QString api = dataEngineDescription.property("X-Plasma-API").toString(); 00538 00539 if (!api.isEmpty()) { 00540 const QString path = 00541 KStandardDirs::locate("data", 00542 "plasma/dataengines/" + dataEngineDescription.pluginName() + '/'); 00543 PackageStructure::Ptr structure = Plasma::packageStructure(api, Plasma::DataEngineComponent); 00544 structure->setPath(path); 00545 package = new Package(path, structure); 00546 00547 script = Plasma::loadScriptEngine(api, q); 00548 if (!script) { 00549 kDebug() << "Could not create a" << api << "ScriptEngine for the" 00550 << dataEngineDescription.name() << "DataEngine."; 00551 delete package; 00552 package = 0; 00553 } 00554 } 00555 } 00556 } 00557 00558 DataEnginePrivate::~DataEnginePrivate() 00559 { 00560 delete script; 00561 script = 0; 00562 delete package; 00563 package = 0; 00564 } 00565 00566 void DataEnginePrivate::internalUpdateSource(DataContainer *source) 00567 { 00568 if (minPollingInterval > 0 && 00569 source->timeSinceLastUpdate() < (uint)minPollingInterval) { 00570 // skip updating this source; it's been too soon 00571 //kDebug() << "internal update source is delaying" << source->timeSinceLastUpdate() << minPollingInterval; 00572 //but fake an update so that the signalrelay that triggered this gets the data from the 00573 //recent update. this way we don't have to worry about queuing - the relay will send a 00574 //signal immediately and everyone else is undisturbed. 00575 source->setNeedsUpdate(); 00576 return; 00577 } 00578 00579 if (q->updateSourceEvent(source->objectName())) { 00580 //kDebug() << "queuing an update"; 00581 q->scheduleSourcesUpdated(); 00582 }/* else { 00583 kDebug() << "no update"; 00584 }*/ 00585 } 00586 00587 void DataEnginePrivate::ref() 00588 { 00589 --refCount; 00590 } 00591 00592 void DataEnginePrivate::deref() 00593 { 00594 ++refCount; 00595 } 00596 00597 bool DataEnginePrivate::isUsed() const 00598 { 00599 return refCount != 0; 00600 } 00601 00602 DataContainer *DataEnginePrivate::source(const QString &sourceName, bool createWhenMissing) 00603 { 00604 DataEngine::SourceDict::const_iterator it = sources.constFind(sourceName); 00605 if (it != sources.constEnd()) { 00606 DataContainer *s = it.value(); 00607 if (limit > 0) { 00608 QQueue<DataContainer*>::iterator it = sourceQueue.begin(); 00609 while (it != sourceQueue.end()) { 00610 if (*it == s) { 00611 sourceQueue.erase(it); 00612 break; 00613 } 00614 ++it; 00615 } 00616 sourceQueue.enqueue(s); 00617 } 00618 return s; 00619 } 00620 00621 if (!createWhenMissing) { 00622 return 0; 00623 } 00624 00625 //kDebug() << "DataEngine " << q->objectName() << ": could not find DataContainer " << sourceName << ", creating"; 00626 DataContainer *s = new DataContainer(q); 00627 s->setObjectName(sourceName); 00628 sources.insert(sourceName, s); 00629 QObject::connect(s, SIGNAL(destroyed(QObject *)), q, SLOT(sourceDestroyed(QObject *))); 00630 QObject::connect(s, SIGNAL(updateRequested(DataContainer*)), 00631 q, SLOT(internalUpdateSource(DataContainer*))); 00632 00633 if (limit > 0) { 00634 trimQueue(); 00635 sourceQueue.enqueue(s); 00636 } 00637 return s; 00638 } 00639 00640 void DataEnginePrivate::connectSource(DataContainer *s, QObject *visualization, 00641 uint pollingInterval, 00642 Plasma::IntervalAlignment align, 00643 bool immediateCall) 00644 { 00645 //kDebug() << "connect source called" << s->objectName() << "with interval" << pollingInterval; 00646 00647 //FIXME: at the moment a remote dataengine can only poll, a push mechanism will be needed instead 00648 if (pollingInterval == 0 && qobject_cast<RemoteDataEngine *>(q)) { 00649 pollingInterval = 5000; 00650 } 00651 if (pollingInterval > 0) { 00652 // never more frequently than allowed, never more than 20 times per second 00653 uint min = qMax(50, minPollingInterval); // for qMax below 00654 pollingInterval = qMax(min, pollingInterval); 00655 00656 // align on the 50ms 00657 pollingInterval = pollingInterval - (pollingInterval % 50); 00658 } 00659 00660 if (immediateCall) { 00661 // we don't want to do an immediate call if we are simply 00662 // reconnecting 00663 //kDebug() << "immediate call requested, we have:" << s->visualizationIsConnected(visualization); 00664 immediateCall = !s->data().isEmpty() && 00665 !s->visualizationIsConnected(visualization); 00666 } 00667 00668 s->connectVisualization(visualization, pollingInterval, align); 00669 00670 if (immediateCall) { 00671 QMetaObject::invokeMethod(visualization, "dataUpdated", 00672 Q_ARG(QString, s->objectName()), 00673 Q_ARG(Plasma::DataEngine::Data, s->data())); 00674 s->d->dirty = false; 00675 } 00676 } 00677 00678 void DataEnginePrivate::sourceDestroyed(QObject *object) 00679 { 00680 DataEngine::SourceDict::iterator it = sources.begin(); 00681 while (it != sources.end()) { 00682 if (it.value() == object) { 00683 sources.erase(it); 00684 break; 00685 } 00686 ++it; 00687 } 00688 } 00689 00690 DataContainer *DataEnginePrivate::requestSource(const QString &sourceName, bool *newSource) 00691 { 00692 if (newSource) { 00693 *newSource = false; 00694 } 00695 00696 //kDebug() << "requesting source " << sourceName; 00697 DataContainer *s = source(sourceName, false); 00698 00699 if (!s) { 00700 // we didn't find a data source, so give the engine an opportunity to make one 00701 /*kDebug() << "DataEngine " << q->objectName() 00702 << ": could not find DataContainer " << sourceName 00703 << " will create on request" << endl;*/ 00704 if (q->sourceRequestEvent(sourceName)) { 00705 s = source(sourceName, false); 00706 if (s) { 00707 // now we have a source; since it was created on demand, assume 00708 // it should be removed when not used 00709 if (newSource) { 00710 *newSource = true; 00711 } 00712 QObject::connect(s, SIGNAL(becameUnused(QString)), q, SLOT(removeSource(QString))); 00713 } 00714 } 00715 } 00716 00717 return s; 00718 } 00719 00720 void DataEnginePrivate::trimQueue() 00721 { 00722 uint queueCount = sourceQueue.count(); 00723 while (queueCount >= limit && !sourceQueue.isEmpty()) { 00724 DataContainer *punted = sourceQueue.dequeue(); 00725 q->removeSource(punted->objectName()); 00726 queueCount = sourceQueue.count(); 00727 } 00728 } 00729 00730 // put all setup routines for script here. at this point we can assume that 00731 // package exists and that we have a script engine 00732 void DataEnginePrivate::setupScriptSupport() 00733 { 00734 if (!package) { 00735 return; 00736 } 00737 00738 /* 00739 kDebug() << "sletting up script support, package is in" << package->path() 00740 << "which is a" << package->structure()->type() << "package" 00741 << ", main script is" << package->filePath("mainscript"); 00742 */ 00743 00744 QString translationsPath = package->filePath("translations"); 00745 if (!translationsPath.isEmpty()) { 00746 //FIXME: we should _probably_ use a KComponentData to segregate the applets 00747 // from each other; but I want to get the basics working first :) 00748 KGlobal::dirs()->addResourceDir("locale", translationsPath); 00749 KGlobal::locale()->insertCatalog(package->metadata().pluginName()); 00750 } 00751 } 00752 00753 } 00754 00755 #include "dataengine.moc"
KDE 4.6 API Reference