• Skip to content
  • Skip to link menu
KDE 4.2 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     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     //kDebug() << objectName() << ": bye bye birdy! ";
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     //kDebug() << "connectSource" << source;
00087     bool newSource;
00088     DataContainer *s = d->requestSource(source, &newSource);
00089 
00090     if (s) {
00091         // we suppress the immediate invocation of dataUpdated here if the
00092         // source was prexisting and they don't request delayed updates
00093         // (we want to do an immediate update in that case so they don't
00094         // have to wait for the first time out)
00095         d->connectSource(s, visualization, pollingInterval, intervalAlignment,
00096                          !newSource || pollingInterval > 0);
00097         //kDebug() << " ==> source connected";
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         // kDebug() << "called";
00148         // default implementation does nothing. this is for engines that have to
00149         // start things in motion external to themselves before they can work
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         //kDebug() << source;
00168         return false; //TODO: should this be true to trigger, even needless, updates on every tick?
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 NOTE: This is not implemented to prevent having to store the value internally.
00295       When there is a good use case for needing access to this value, we can
00296       add another member to the Private class and add this method.
00297 
00298 void DataEngine::pollingInterval()
00299 {
00300     return d->pollingInterval;
00301 }
00302 */
00303 
00304 void DataEngine::removeSource(const QString &source)
00305 {
00306     //kDebug() << "removing source " << source;
00307     SourceDict::iterator it = d->sources.find(source);
00308     if (it != d->sources.end()) {
00309         DataContainer *s = it.value();
00310 
00311         // remove it from the limit queue if we're keeping one
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     // if the freq update is less than 0, don't bother
00370     if (d->minPollingInterval < 0) {
00371         //kDebug() << "uh oh.. no polling allowed!";
00372         return;
00373     }
00374 
00375     // minPollingInterval
00376     if (d->updateTimestamp.elapsed() < d->minPollingInterval) {
00377         //kDebug() << "hey now.. slow down!";
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         //kDebug() << "updating" << it.key();
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 // Private class implementations
00442 DataEnginePrivate::DataEnginePrivate(DataEngine *e, KService::Ptr service)
00443     : q(e),
00444       dataEngineDescription(service),
00445       refCount(-1), // first ref
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         // skip updating this source; it's been too soon
00505         //kDebug() << "internal update source is delaying" << source->timeSinceLastUpdate() << minPollingInterval;
00506         //but fake an update so that the signalrelay that triggered this gets the data from the
00507         //recent update. this way we don't have to worry about queuing - the relay will send a
00508         //signal immediately and everyone else is undisturbed.
00509         source->setNeedsUpdate();
00510         return;
00511     }
00512 
00513     if (q->updateSourceEvent(source->objectName())) {
00514         //kDebug() << "queuing an update";
00515         queueUpdate();
00516     }/* else {
00517         kDebug() << "no update";
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     /*kDebug() << "DataEngine " << q->objectName()
00560                 << ": could not find DataContainer " << sourceName
00561                 << ", creating" << endl;*/
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     //kDebug() << "connect source called" << s->objectName() << "with interval" << pollingInterval;
00581     if (pollingInterval > 0) {
00582         // never more frequently than allowed, never more than 20 times per second
00583         uint min = qMax(50, minPollingInterval); // for qMax below
00584         pollingInterval = qMax(min, pollingInterval);
00585 
00586         // align on the 50ms
00587         pollingInterval = pollingInterval - (pollingInterval % 50);
00588     }
00589 
00590     if (immediateCall) {
00591         // we don't want to do an immediate call if we are simply
00592         // reconnecting
00593         //kDebug() << "immediate call requested, we have:" << s->visualizationIsConnected(visualization);
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     //kDebug() << "requesting source " << sourceName;
00613     DataContainer *s = source(sourceName, false);
00614 
00615     if (!s) {
00616         // we didn't find a data source, so give the engine an opportunity to make one
00617         /*kDebug() << "DataEngine " << q->objectName()
00618             << ": could not find DataContainer " << sourceName
00619             << " will create on request" << endl;*/
00620         if (q->sourceRequestEvent(sourceName)) {
00621             s = source(sourceName, false);
00622             if (s) {
00623                 // now we have a source; since it was created on demand, assume
00624                 // it should be removed when not used
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"

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
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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