• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • 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 #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"

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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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