• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

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 
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 }
00049 
00050 DataEngine::DataEngine(QObject *parent, const QVariantList &args)
00051     : QObject(parent),
00052       d(new DataEnginePrivate(this, KService::serviceByStorageId(args.count() > 0 ? args[0].toString() : QString())))
00053 {
00054 }
00055 
00056 DataEngine::~DataEngine()
00057 {
00058     //kDebug() << objectName() << ": bye bye birdy! ";
00059     delete d;
00060 }
00061 
00062 QStringList DataEngine::sources() const
00063 {
00064     if (d->script) {
00065         return d->script->sources();
00066     } else {
00067         return d->sources.keys();
00068     }
00069 }
00070 
00071 Service *DataEngine::serviceForSource(const QString &source)
00072 {
00073     if (d->script) {
00074         return d->script->serviceForSource(source);
00075     } else {
00076         return new NullService(source, this);
00077     }
00078 }
00079 
00080 void DataEngine::connectSource(const QString &source, QObject *visualization,
00081                                uint pollingInterval,
00082                                Plasma::IntervalAlignment intervalAlignment) const
00083 {
00084     //kDebug() << "connectSource" << source;
00085     bool newSource;
00086     DataContainer *s = d->requestSource(source, &newSource);
00087 
00088     if (s) {
00089         // we suppress the immediate invocation of dataUpdated here if the
00090         // source was prexisting and they don't request delayed updates
00091         // (we want to do an immediate update in that case so they don't
00092         // have to wait for the first time out)
00093         d->connectSource(s, visualization, pollingInterval, intervalAlignment,
00094                          !newSource || pollingInterval > 0);
00095         //kDebug() << " ==> source connected";
00096     }
00097 }
00098 
00099 void DataEngine::connectAllSources(QObject *visualization, uint pollingInterval,
00100                                    Plasma::IntervalAlignment intervalAlignment) const
00101 {
00102     foreach (DataContainer *s, d->sources) {
00103         d->connectSource(s, visualization, pollingInterval, intervalAlignment);
00104     }
00105 }
00106 
00107 void DataEngine::disconnectSource(const QString &source, QObject *visualization) const
00108 {
00109     DataContainer *s = d->source(source, false);
00110 
00111     if (s) {
00112         s->disconnectVisualization(visualization);
00113     }
00114 }
00115 
00116 DataContainer *DataEngine::containerForSource(const QString &source)
00117 {
00118     return d->source(source, false);
00119 }
00120 
00121 DataEngine::Data DataEngine::query(const QString &source) const
00122 {
00123     bool newSource;
00124     DataContainer *s = d->requestSource(source, &newSource);
00125 
00126     if (!s) {
00127         return DataEngine::Data();
00128     } else if (!newSource && d->minPollingInterval >= 0 &&
00129                s->timeSinceLastUpdate() >= uint(d->minPollingInterval)) {
00130         DataEngine *unconstThis = const_cast<DataEngine*>(this);
00131         if (unconstThis->updateSourceEvent(source)) {
00132             unconstThis->scheduleSourcesUpdated();
00133         }
00134     }
00135 
00136     DataEngine::Data data = s->data();
00137     s->checkUsage();
00138     return data;
00139 }
00140 
00141 void DataEngine::init()
00142 {
00143     if (d->script) {
00144         d->script->init();
00145     } else {
00146         // kDebug() << "called";
00147         // default implementation does nothing. this is for engines that have to
00148         // start things in motion external to themselves before they can work
00149     }
00150 }
00151 
00152 bool DataEngine::sourceRequestEvent(const QString &name)
00153 {
00154     if (d->script) {
00155         return d->script->sourceRequestEvent(name);
00156     } else {
00157         return false;
00158     }
00159 }
00160 
00161 bool DataEngine::updateSourceEvent(const QString &source)
00162 {
00163     if (d->script) {
00164         return d->script->updateSourceEvent(source);
00165     } else {
00166         //kDebug() << source;
00167         return false; //TODO: should this be true to trigger, even needless, updates on every tick?
00168     }
00169 }
00170 
00171 void DataEngine::setData(const QString &source, const QVariant &value)
00172 {
00173     setData(source, source, value);
00174 }
00175 
00176 void DataEngine::setData(const QString &source, const QString &key, const QVariant &value)
00177 {
00178     DataContainer *s = d->source(source, false);
00179     bool isNew = !s;
00180 
00181     if (isNew) {
00182         s = d->source(source);
00183     }
00184 
00185     s->setData(key, value);
00186 
00187     if (isNew) {
00188         emit sourceAdded(source);
00189     }
00190 
00191     scheduleSourcesUpdated();
00192 }
00193 
00194 void DataEngine::setData(const QString &source, const Data &data)
00195 {
00196     DataContainer *s = d->source(source, false);
00197     bool isNew = !s;
00198 
00199     if (isNew) {
00200         s = d->source(source);
00201     }
00202 
00203     Data::const_iterator it = data.constBegin();
00204     while (it != data.constEnd()) {
00205         s->setData(it.key(), it.value());
00206         ++it;
00207     }
00208 
00209     if (isNew) {
00210         emit sourceAdded(source);
00211     }
00212 
00213     scheduleSourcesUpdated();
00214 }
00215 
00216 void DataEngine::removeAllData(const QString &source)
00217 {
00218     DataContainer *s = d->source(source, false);
00219     if (s) {
00220         s->removeAllData();
00221         scheduleSourcesUpdated();
00222     }
00223 }
00224 
00225 void DataEngine::removeData(const QString &source, const QString &key)
00226 {
00227     DataContainer *s = d->source(source, false);
00228     if (s) {
00229         s->setData(key, QVariant());
00230         scheduleSourcesUpdated();
00231     }
00232 }
00233 
00234 void DataEngine::addSource(DataContainer *source)
00235 {
00236     if (d->sources.contains(source->objectName())) {
00237         kDebug() << "source named \"" << source->objectName() << "\" already exists.";
00238         return;
00239     }
00240 
00241     QObject::connect(source, SIGNAL(updateRequested(DataContainer*)),
00242                      this, SLOT(internalUpdateSource(DataContainer*)));
00243     d->sources.insert(source->objectName(), source);
00244     emit sourceAdded(source->objectName());
00245     scheduleSourcesUpdated();
00246 }
00247 
00248 void DataEngine::setMaxSourceCount(uint limit)
00249 {
00250     if (d->limit == limit) {
00251         return;
00252     }
00253 
00254     d->limit = limit;
00255 
00256     if (d->limit > 0) {
00257         d->trimQueue();
00258     } else {
00259         d->sourceQueue.clear();
00260     }
00261 }
00262 
00263 uint DataEngine::maxSourceCount() const
00264 {
00265     return d->limit;
00266 }
00267 
00268 void DataEngine::setMinimumPollingInterval(int minimumMs)
00269 {
00270     if (minimumMs < 0) {
00271         minimumMs = 0;
00272     }
00273 
00274     d->minPollingInterval = minimumMs;
00275 }
00276 
00277 int DataEngine::minimumPollingInterval() const
00278 {
00279     return d->minPollingInterval;
00280 }
00281 
00282 void DataEngine::setPollingInterval(uint frequency)
00283 {
00284     killTimer(d->updateTimerId);
00285     d->updateTimerId = 0;
00286 
00287     if (frequency > 0) {
00288         d->updateTimerId = startTimer(frequency);
00289     }
00290 }
00291 
00292 /*
00293 NOTE: This is not implemented to prevent having to store the value internally.
00294       When there is a good use case for needing access to this value, we can
00295       add another member to the Private class and add this method.
00296 
00297 void DataEngine::pollingInterval()
00298 {
00299     return d->pollingInterval;
00300 }
00301 */
00302 
00303 void DataEngine::removeSource(const QString &source)
00304 {
00305     //kDebug() << "removing source " << source;
00306     SourceDict::iterator it = d->sources.find(source);
00307     if (it != d->sources.end()) {
00308         DataContainer *s = it.value();
00309 
00310         // remove it from the limit queue if we're keeping one
00311         if (d->limit > 0) {
00312             QQueue<DataContainer*>::iterator it = d->sourceQueue.begin();
00313             while (it != d->sourceQueue.end()) {
00314                 if (*it == s) {
00315                     d->sourceQueue.erase(it);
00316                     break;
00317                 }
00318                 ++it;
00319             }
00320         }
00321 
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         emit sourceRemoved(it.key());
00334         delete it.value();
00335         it.remove();
00336     }
00337 }
00338 
00339 bool DataEngine::isValid() const
00340 {
00341     return d->valid;
00342 }
00343 
00344 bool DataEngine::isEmpty() const
00345 {
00346     return d->sources.isEmpty();
00347 }
00348 
00349 void DataEngine::setValid(bool valid)
00350 {
00351     d->valid = valid;
00352 }
00353 
00354 DataEngine::SourceDict DataEngine::containerDict() const
00355 {
00356     return d->sources;
00357 }
00358 
00359 void DataEngine::timerEvent(QTimerEvent *event)
00360 {
00361     if (event->timerId() == d->updateTimerId) {
00362         // if the freq update is less than 0, don't bother
00363         if (d->minPollingInterval < 0) {
00364             //kDebug() << "uh oh.. no polling allowed!";
00365             return;
00366         }
00367 
00368         // minPollingInterval
00369         if (d->updateTimestamp.elapsed() < d->minPollingInterval) {
00370             //kDebug() << "hey now.. slow down!";
00371             return;
00372         }
00373 
00374         d->updateTimestamp.restart();
00375         d->updateTimerId = 0;
00376         updateAllSources();
00377     } else if (event->timerId() == d->checkSourcesTimerId) {
00378         QHashIterator<QString, Plasma::DataContainer*> it(d->sources);
00379         while (it.hasNext()) {
00380             it.next();
00381             it.value()->checkForUpdate();
00382         }
00383 
00384         killTimer(d->checkSourcesTimerId);
00385         d->checkSourcesTimerId = 0;
00386     } else {
00387         QObject::timerEvent(event);
00388     }
00389 }
00390 
00391 void DataEngine::updateAllSources()
00392 {
00393     QHashIterator<QString, Plasma::DataContainer*> it(d->sources);
00394     while (it.hasNext()) {
00395         it.next();
00396         //kDebug() << "updating" << it.key();
00397         updateSourceEvent(it.key());
00398     }
00399 
00400     scheduleSourcesUpdated();
00401 }
00402 
00403 void DataEngine::setIcon(const QString &icon)
00404 {
00405     d->icon = icon;
00406 }
00407 
00408 QString DataEngine::icon() const
00409 {
00410     return d->icon;
00411 }
00412 
00413 QString DataEngine::pluginName() const
00414 {
00415     if (!d->dataEngineDescription.isValid()) {
00416         return QString();
00417     }
00418 
00419     return d->dataEngineDescription.pluginName();
00420 }
00421 
00422 const Package *DataEngine::package() const
00423 {
00424     return d->package;
00425 }
00426 
00427 void DataEngine::scheduleSourcesUpdated()
00428 {
00429     if (d->checkSourcesTimerId) {
00430         return;
00431     }
00432 
00433     d->checkSourcesTimerId = startTimer(0);
00434 }
00435 
00436 QString DataEngine::name() const
00437 {
00438     return d->engineName;
00439 }
00440 
00441 void DataEngine::setName(const QString &name)
00442 {
00443     d->engineName = name;
00444     setObjectName(name);
00445 }
00446 
00447 // Private class implementations
00448 DataEnginePrivate::DataEnginePrivate(DataEngine *e, KService::Ptr service)
00449     : q(e),
00450       dataEngineDescription(service),
00451       refCount(-1), // first ref
00452       checkSourcesTimerId(0),
00453       updateTimerId(0),
00454       minPollingInterval(-1),
00455       limit(0),
00456       valid(true),
00457       script(0),
00458       package(0)
00459 {
00460     updateTimestamp.start();
00461 
00462     if (!service) {
00463         engineName = i18n("Unnamed");
00464         return;
00465     }
00466 
00467     engineName = service->name();
00468     if (engineName.isEmpty()) {
00469         engineName = i18n("Unnamed");
00470     }
00471     e->setObjectName(engineName);
00472     icon = service->icon();
00473 
00474     if (dataEngineDescription.isValid()) {
00475         QString api = dataEngineDescription.property("X-Plasma-API").toString();
00476 
00477         if (!api.isEmpty()) {
00478             const QString path =
00479                 KStandardDirs::locate("data",
00480                                       "plasma/dataengines/" + dataEngineDescription.pluginName() + '/');
00481             PackageStructure::Ptr structure =
00482                 Plasma::packageStructure(api, Plasma::DataEngineComponent);
00483             structure->setPath(path);
00484             package = new Package(path, structure);
00485 
00486             script = Plasma::loadScriptEngine(api, q);
00487             if (!script) {
00488                 kDebug() << "Could not create a" << api << "ScriptEngine for the"
00489                         << dataEngineDescription.name() << "DataEngine.";
00490                 delete package;
00491                 package = 0;
00492             }
00493         }
00494     }
00495 }
00496 
00497 DataEnginePrivate::~DataEnginePrivate()
00498 {
00499     delete script;
00500     script = 0;
00501     delete package;
00502     package = 0;
00503 }
00504 
00505 void DataEnginePrivate::internalUpdateSource(DataContainer *source)
00506 {
00507     if (minPollingInterval > 0 &&
00508         source->timeSinceLastUpdate() < (uint)minPollingInterval) {
00509         // skip updating this source; it's been too soon
00510         //kDebug() << "internal update source is delaying" << source->timeSinceLastUpdate() << minPollingInterval;
00511         //but fake an update so that the signalrelay that triggered this gets the data from the
00512         //recent update. this way we don't have to worry about queuing - the relay will send a
00513         //signal immediately and everyone else is undisturbed.
00514         source->setNeedsUpdate();
00515         return;
00516     }
00517 
00518     if (q->updateSourceEvent(source->objectName())) {
00519         //kDebug() << "queuing an update";
00520         q->scheduleSourcesUpdated();
00521     }/* else {
00522         kDebug() << "no update";
00523     }*/
00524 }
00525 
00526 void DataEnginePrivate::ref()
00527 {
00528     --refCount;
00529 }
00530 
00531 void DataEnginePrivate::deref()
00532 {
00533     ++refCount;
00534 }
00535 
00536 bool DataEnginePrivate::isUsed() const
00537 {
00538     return refCount != 0;
00539 }
00540 
00541 DataContainer *DataEnginePrivate::source(const QString &sourceName, bool createWhenMissing)
00542 {
00543     DataEngine::SourceDict::const_iterator it = sources.constFind(sourceName);
00544     if (it != sources.constEnd()) {
00545         DataContainer *s = it.value();
00546         if (limit > 0) {
00547             QQueue<DataContainer*>::iterator it = sourceQueue.begin();
00548             while (it != sourceQueue.end()) {
00549                 if (*it == s) {
00550                     sourceQueue.erase(it);
00551                     break;
00552                 }
00553                 ++it;
00554             }
00555             sourceQueue.enqueue(s);
00556         }
00557         return it.value();
00558     }
00559 
00560     if (!createWhenMissing) {
00561         return 0;
00562     }
00563 
00564     /*kDebug() << "DataEngine " << q->objectName()
00565                 << ": could not find DataContainer " << sourceName
00566                 << ", creating" << endl;*/
00567     DataContainer *s = new DataContainer(q);
00568     s->setObjectName(sourceName);
00569     sources.insert(sourceName, s);
00570     QObject::connect(s, SIGNAL(updateRequested(DataContainer*)),
00571                      q, SLOT(internalUpdateSource(DataContainer*)));
00572 
00573     if (limit > 0) {
00574         trimQueue();
00575         sourceQueue.enqueue(s);
00576     }
00577     return s;
00578 }
00579 
00580 void DataEnginePrivate::connectSource(DataContainer *s, QObject *visualization,
00581                                       uint pollingInterval,
00582                                       Plasma::IntervalAlignment align,
00583                                       bool immediateCall)
00584 {
00585     //kDebug() << "connect source called" << s->objectName() << "with interval" << pollingInterval;
00586     if (pollingInterval > 0) {
00587         // never more frequently than allowed, never more than 20 times per second
00588         uint min = qMax(50, minPollingInterval); // for qMax below
00589         pollingInterval = qMax(min, pollingInterval);
00590 
00591         // align on the 50ms
00592         pollingInterval = pollingInterval - (pollingInterval % 50);
00593     }
00594 
00595     if (immediateCall) {
00596         // we don't want to do an immediate call if we are simply
00597         // reconnecting
00598         //kDebug() << "immediate call requested, we have:" << s->visualizationIsConnected(visualization);
00599         immediateCall = !s->visualizationIsConnected(visualization);
00600     }
00601 
00602     s->connectVisualization(visualization, pollingInterval, align);
00603 
00604     if (immediateCall) {
00605         QMetaObject::invokeMethod(visualization, "dataUpdated",
00606                                   Q_ARG(QString, s->objectName()),
00607                                   Q_ARG(Plasma::DataEngine::Data, s->data()));
00608     }
00609 }
00610 
00611 DataContainer *DataEnginePrivate::requestSource(const QString &sourceName, bool *newSource)
00612 {
00613     if (newSource) {
00614         *newSource = false;
00615     }
00616 
00617     //kDebug() << "requesting source " << sourceName;
00618     DataContainer *s = source(sourceName, false);
00619 
00620     if (!s) {
00621         // we didn't find a data source, so give the engine an opportunity to make one
00622         /*kDebug() << "DataEngine " << q->objectName()
00623             << ": could not find DataContainer " << sourceName
00624             << " will create on request" << endl;*/
00625         if (q->sourceRequestEvent(sourceName)) {
00626             s = source(sourceName, false);
00627             if (s) {
00628                 // now we have a source; since it was created on demand, assume
00629                 // it should be removed when not used
00630                 if (newSource) {
00631                     *newSource = true;
00632                 }
00633                 QObject::connect(s, SIGNAL(becameUnused(QString)), q, SLOT(removeSource(QString)));
00634             }
00635         }
00636     }
00637 
00638     return s;
00639 }
00640 
00641 void DataEnginePrivate::trimQueue()
00642 {
00643     uint queueCount = sourceQueue.count();
00644     while (queueCount >= limit && !sourceQueue.isEmpty()) {
00645         DataContainer *punted = sourceQueue.dequeue();
00646         q->removeSource(punted->objectName());
00647         queueCount = sourceQueue.count();
00648     }
00649 }
00650 
00651 }
00652 
00653 #include "dataengine.moc"

Plasma

Skip menu "Plasma"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal