• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

Plasma

wallpaper.cpp

Go to the documentation of this file.
00001 /*
00002  *   Copyright 2008 by Aaron Seigo <aseigo@kde.org>
00003  *   Copyright 2008 by Petri Damsten <damu@iki.fi>
00004  *
00005  *   This program is free software; you can redistribute it and/or modify
00006  *   it under the terms of the GNU Library General Public License as
00007  *   published by the Free Software Foundation; either version 2, or
00008  *   (at your option) any later version.
00009  *
00010  *   This program is distributed in the hope that it will be useful,
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *   GNU General Public License for more details
00014  *
00015  *   You should have received a copy of the GNU Library General Public
00016  *   License along with this program; if not, write to the
00017  *   Free Software Foundation, Inc.,
00018  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00019  */
00020 
00021 #include "wallpaper.h"
00022 
00023 #include "config-plasma.h"
00024 
00025 #include <QColor>
00026 #include <QFile>
00027 #include <QFileInfo>
00028 #include <QImage>
00029 #include <QAction>
00030 #include <QQueue>
00031 #include <QTimer>
00032 #include <QRunnable>
00033 #include <QThreadPool>
00034 
00035 #include <kdebug.h>
00036 #include <kglobal.h>
00037 #include <kservicetypetrader.h>
00038 #include <kstandarddirs.h>
00039 
00040 #ifndef PLASMA_NO_KIO
00041 #include <kio/job.h>
00042 #endif
00043 
00044 #ifndef PLASMA_NO_SOLID
00045 #include <solid/device.h>
00046 #include <solid/deviceinterface.h>
00047 #endif
00048 
00049 #include <version.h>
00050 
00051 #include "plasma/package.h"
00052 #include "plasma/private/dataengineconsumer_p.h"
00053 #include "plasma/private/packages_p.h"
00054 #include "plasma/private/wallpaper_p.h"
00055 
00056 namespace Plasma
00057 {
00058 
00059 class SaveImageThread : public QRunnable
00060 {
00061     QImage m_image;
00062     QString m_filePath;
00063 
00064     public:
00065     SaveImageThread(const QImage &image, const QString &filePath)
00066     {
00067         m_image = image;
00068         m_filePath = filePath;
00069     }
00070 
00071     void run()
00072     {
00073         m_image.save(m_filePath);
00074     }
00075 };
00076 
00077 LoadImageThread::LoadImageThread(const QString &filePath)
00078 {
00079     m_filePath = filePath;
00080 }
00081 
00082 void LoadImageThread::run()
00083 {
00084     QImage image;
00085     image.load(m_filePath);
00086     emit done(image);
00087 }
00088 
00089 class WallpaperWithPaint : public Wallpaper
00090 {
00091 public:
00092     WallpaperWithPaint(QObject *parent, const QVariantList &args)
00093         : Wallpaper(parent, args)
00094     {
00095     }
00096 
00097     virtual void paint(QPainter *painter, const QRectF &exposedRect)
00098     {
00099         if (d->script) {
00100             d->script->paint(painter, exposedRect);
00101         }
00102     }
00103 };
00104 
00105 QList<WallpaperRenderThread *> WallpaperPrivate::s_renderers;
00106 QQueue<WallpaperPrivate::RenderRequest> WallpaperPrivate::s_renderQueue;
00107 PackageStructure::Ptr WallpaperPrivate::s_packageStructure(0);
00108 
00109 Wallpaper::Wallpaper(QObject * parentObject)
00110     : d(new WallpaperPrivate(KService::serviceByStorageId(QString()), this))
00111 {
00112     setParent(parentObject);
00113 }
00114 
00115 Wallpaper::Wallpaper(QObject *parentObject, const QVariantList &args)
00116     : d(new WallpaperPrivate(KService::serviceByStorageId(args.count() > 0 ?
00117                              args[0].toString() : QString()), this))
00118 {
00119     // now remove first item since those are managed by Wallpaper and subclasses shouldn't
00120     // need to worry about them. yes, it violates the constness of this var, but it lets us add
00121     // or remove items later while applets can just pretend that their args always start at 0
00122     QVariantList &mutableArgs = const_cast<QVariantList &>(args);
00123     if (!mutableArgs.isEmpty()) {
00124         mutableArgs.removeFirst();
00125     }
00126 
00127     setParent(parentObject);
00128 }
00129 
00130 Wallpaper::~Wallpaper()
00131 {
00132     delete d;
00133 }
00134 
00135 KPluginInfo::List Wallpaper::listWallpaperInfo(const QString &formFactor)
00136 {
00137     QString constraint;
00138     if (!formFactor.isEmpty()) {
00139         constraint.append("[X-Plasma-FormFactors] ~~ '").append(formFactor).append("'");
00140     }
00141 
00142     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Wallpaper", constraint);
00143     return KPluginInfo::fromServices(offers);
00144 }
00145 
00146 KPluginInfo::List Wallpaper::listWallpaperInfoForMimetype(const QString &mimetype, const QString &formFactor)
00147 {
00148     QString constraint = QString("'%1' in [X-Plasma-DropMimeTypes]").arg(mimetype);
00149     if (!formFactor.isEmpty()) {
00150         constraint.append("[X-Plasma-FormFactors] ~~ '").append(formFactor).append("'");
00151     }
00152 
00153     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Wallpaper", constraint);
00154     kDebug() << offers.count() << constraint;
00155     return KPluginInfo::fromServices(offers);
00156 }
00157 
00158 Wallpaper *Wallpaper::load(const QString &wallpaperName, const QVariantList &args)
00159 {
00160     if (wallpaperName.isEmpty()) {
00161         return 0;
00162     }
00163 
00164     QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(wallpaperName);
00165     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Wallpaper", constraint);
00166 
00167     if (offers.isEmpty()) {
00168         kDebug() << "offers is empty for " << wallpaperName;
00169         return 0;
00170     }
00171 
00172     KService::Ptr offer = offers.first();
00173     QVariantList allArgs;
00174     allArgs << offer->storageId() << args;
00175 
00176     if (!offer->property("X-Plasma-API").toString().isEmpty()) {
00177         kDebug() << "we have a script using the"
00178                  << offer->property("X-Plasma-API").toString() << "API";
00179         return new WallpaperWithPaint(0, allArgs);
00180     }
00181 
00182     KPluginLoader plugin(*offer);
00183 
00184     if (!Plasma::isPluginVersionCompatible(plugin.pluginVersion())) {
00185         return 0;
00186     }
00187 
00188     QString error;
00189     Wallpaper *wallpaper = offer->createInstance<Plasma::Wallpaper>(0, allArgs, &error);
00190 
00191     if (!wallpaper) {
00192         kDebug() << "Couldn't load wallpaper \"" << wallpaperName << "\"! reason given: " << error;
00193     }
00194 
00195     return wallpaper;
00196 }
00197 
00198 Wallpaper *Wallpaper::load(const KPluginInfo &info, const QVariantList &args)
00199 {
00200     if (!info.isValid()) {
00201         return 0;
00202     }
00203     return load(info.pluginName(), args);
00204 }
00205 
00206 PackageStructure::Ptr Wallpaper::packageStructure(Wallpaper *paper)
00207 {
00208     if (paper) {
00209         PackageStructure::Ptr package(new WallpaperPackage(paper));
00210         return package;
00211     }
00212 
00213     if (!WallpaperPrivate::s_packageStructure) {
00214         WallpaperPrivate::s_packageStructure = new WallpaperPackage();
00215     }
00216 
00217     return WallpaperPrivate::s_packageStructure;
00218 }
00219 
00220 QString Wallpaper::name() const
00221 {
00222     if (!d->wallpaperDescription.isValid()) {
00223         return i18n("Unknown Wallpaper");
00224     }
00225 
00226     return d->wallpaperDescription.name();
00227 }
00228 
00229 QString Wallpaper::icon() const
00230 {
00231     if (!d->wallpaperDescription.isValid()) {
00232         return QString();
00233     }
00234 
00235     return d->wallpaperDescription.icon();
00236 }
00237 
00238 QString Wallpaper::pluginName() const
00239 {
00240     if (!d->wallpaperDescription.isValid()) {
00241         return QString();
00242     }
00243 
00244     return d->wallpaperDescription.pluginName();
00245 }
00246 
00247 KServiceAction Wallpaper::renderingMode() const
00248 {
00249     return d->mode;
00250 }
00251 
00252 QList<KServiceAction> Wallpaper::listRenderingModes() const
00253 {
00254     if (!d->wallpaperDescription.isValid()) {
00255         return QList<KServiceAction>();
00256     }
00257 
00258     return d->wallpaperDescription.service()->actions();
00259 }
00260 
00261 QRectF Wallpaper::boundingRect() const
00262 {
00263     return d->boundingRect;
00264 }
00265 
00266 bool Wallpaper::isInitialized() const
00267 {
00268     return d->initialized;
00269 }
00270 
00271 void Wallpaper::setBoundingRect(const QRectF &boundingRect)
00272 {
00273     QSizeF oldBoundingRectSize = d->boundingRect.size();
00274     d->boundingRect = boundingRect;
00275 
00276     if (!d->targetSize.isValid() || d->targetSize == oldBoundingRectSize)  {
00277         d->targetSize = boundingRect.size();
00278         emit renderHintsChanged();
00279     }
00280 }
00281 
00282 void Wallpaper::setRenderingMode(const QString &mode)
00283 {
00284     if (d->mode.name() == mode) {
00285         return;
00286     }
00287 
00288     d->mode = KServiceAction();
00289     if (!mode.isEmpty()) {
00290         QList<KServiceAction> modes = listRenderingModes();
00291 
00292         foreach (const KServiceAction &action, modes) {
00293             if (action.name() == mode) {
00294                 d->mode = action;
00295                 break;
00296             }
00297         }
00298     }
00299 }
00300 
00301 void Wallpaper::restore(const KConfigGroup &config)
00302 {
00303     init(config);
00304     d->initialized = true;
00305 }
00306 
00307 void Wallpaper::init(const KConfigGroup &config)
00308 {
00309     if (d->script) {
00310         d->initScript();
00311         d->script->initWallpaper(config);
00312     }
00313 }
00314 
00315 void Wallpaper::save(KConfigGroup &config)
00316 {
00317     if (d->script) {
00318         d->script->save(config);
00319     }
00320 }
00321 
00322 QWidget *Wallpaper::createConfigurationInterface(QWidget *parent)
00323 {
00324     if (d->script) {
00325         return d->script->createConfigurationInterface(parent);
00326     } else {
00327         return 0;
00328     }
00329 }
00330 
00331 void Wallpaper::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
00332 {
00333     if (d->script) {
00334         return d->script->mouseMoveEvent(event);
00335     }
00336 }
00337 
00338 void Wallpaper::mousePressEvent(QGraphicsSceneMouseEvent *event)
00339 {
00340     if (d->script) {
00341         return d->script->mousePressEvent(event);
00342     }
00343 }
00344 
00345 void Wallpaper::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
00346 {
00347     if (d->script) {
00348         return d->script->mouseReleaseEvent(event);
00349     }
00350 }
00351 
00352 void Wallpaper::wheelEvent(QGraphicsSceneWheelEvent *event)
00353 {
00354     if (d->script) {
00355         return d->script->wheelEvent(event);
00356     }
00357 }
00358 
00359 DataEngine *Wallpaper::dataEngine(const QString &name) const
00360 {
00361     return d->dataEngine(name);
00362 }
00363 
00364 bool Wallpaper::configurationRequired() const
00365 {
00366     return d->needsConfig;
00367 }
00368 
00369 void Wallpaper::setConfigurationRequired(bool needsConfig, const QString &reason)
00370 {
00371     //TODO: implement something for reason. first, we need to decide where/how
00372     //      to communicate it to the user
00373     Q_UNUSED(reason)
00374 
00375     if (d->needsConfig == needsConfig) {
00376         return;
00377     }
00378 
00379     d->needsConfig = needsConfig;
00380     emit configurationRequired(needsConfig);
00381 }
00382 
00383 bool Wallpaper::isUsingRenderingCache() const
00384 {
00385     return d->cacheRendering;
00386 }
00387 
00388 void Wallpaper::setUsingRenderingCache(bool useCache)
00389 {
00390     d->cacheRendering = useCache;
00391 }
00392 
00393 void Wallpaper::setResizeMethodHint(Wallpaper::ResizeMethod resizeMethod)
00394 {
00395     d->lastResizeMethod = resizeMethod;
00396     emit renderHintsChanged();
00397 }
00398 
00399 void Wallpaper::setTargetSizeHint(const QSizeF &targetSize)
00400 {
00401     d->targetSize = targetSize;
00402     emit renderHintsChanged();
00403 }
00404 
00405 void Wallpaper::render(const QString &sourceImagePath, const QSize &size,
00406                        Wallpaper::ResizeMethod resizeMethod, const QColor &color)
00407 {
00408     if (sourceImagePath.isEmpty() || !QFile::exists(sourceImagePath)) {
00409         //kDebug() << "failed on:" << sourceImagePath;
00410         return;
00411     }
00412 
00413     if (d->lastResizeMethod != resizeMethod) {
00414         d->lastResizeMethod = resizeMethod;
00415         emit renderHintsChanged();
00416     }
00417 
00418     if (d->cacheRendering) {
00419         QFileInfo info(sourceImagePath);
00420         QString cache = d->cacheKey(sourceImagePath, size, resizeMethod, color);
00421         if (d->findInCache(cache, info.lastModified().toTime_t())) {
00422             return;
00423         }
00424     }
00425 
00426     for (int i = 0; i < WallpaperPrivate::s_renderers.size(); i++) {
00427         if (d->renderToken == WallpaperPrivate::s_renderers[i]->currentToken()) {
00428             d->renderToken = WallpaperPrivate::s_renderers[i]->render(sourceImagePath, size, resizeMethod, color);
00429             return;
00430         }
00431     }
00432 
00433 #ifndef PLASMA_NO_SOLID
00434     const int numProcs = Solid::Device::listFromType(Solid::DeviceInterface::Processor).count();
00435 #else
00436     const int numProcs = 1;
00437 #endif
00438     if (WallpaperPrivate::s_renderers.size() < qMax(numProcs, 1)) {
00439         WallpaperRenderThread *renderThread = new WallpaperRenderThread();
00440         WallpaperPrivate::s_renderers.append(renderThread);
00441         d->renderToken = renderThread->render(sourceImagePath, size, resizeMethod, color);
00442         connect(renderThread, SIGNAL(done(WallpaperRenderThread*,int,QImage,QString,QSize,int,QColor)),
00443                 this, SLOT(newRenderCompleted(WallpaperRenderThread*,int,QImage,QString,QSize,int,QColor)), Qt::UniqueConnection);
00444     } else {
00445          WallpaperPrivate::RenderRequest request;
00446          request.parent = this;
00447          request.file = sourceImagePath;
00448          request.size = size;
00449          request.resizeMethod = resizeMethod;
00450          request.color = color;
00451 
00452          for (int i = 0; i < WallpaperPrivate::s_renderQueue.size(); i++) {
00453             if (WallpaperPrivate::s_renderQueue[i].parent.data() == this){
00454                 WallpaperPrivate::s_renderQueue[i] = request;
00455                 return;
00456             }
00457          }
00458          WallpaperPrivate::s_renderQueue.append(request);
00459     }
00460     //kDebug() << "rendering" << sourceImagePath << ", token is" << d->renderToken;
00461 }
00462 
00463 WallpaperPrivate::WallpaperPrivate(KService::Ptr service, Wallpaper *wallpaper) :
00464     q(wallpaper),
00465     wallpaperDescription(service),
00466     package(0),
00467     renderToken(-1),
00468     lastResizeMethod(Wallpaper::ScaledResize),
00469     script(0),
00470     cacheRendering(false),
00471     initialized(false),
00472     needsConfig(false),
00473     scriptInitialized(false),
00474     previewing(false),
00475     needsPreviewDuringConfiguration(false)
00476 {
00477     if (wallpaperDescription.isValid()) {
00478         QString api = wallpaperDescription.property("X-Plasma-API").toString();
00479 
00480         if (!api.isEmpty()) {
00481             const QString path = KStandardDirs::locate("data",
00482                     "plasma/wallpapers/" + wallpaperDescription.pluginName() + '/');
00483             PackageStructure::Ptr structure =
00484                 Plasma::packageStructure(api, Plasma::WallpaperComponent);
00485             structure->setPath(path);
00486             package = new Package(path, structure);
00487 
00488             script = Plasma::loadScriptEngine(api, q);
00489             if (!script) {
00490                 kDebug() << "Could not create a" << api << "ScriptEngine for the"
00491                         << wallpaperDescription.name() << "Wallpaper.";
00492                 delete package;
00493                 package = 0;
00494             }
00495        }
00496     }
00497 }
00498 
00499 QString WallpaperPrivate::cacheKey(const QString &sourceImagePath, const QSize &size,
00500                                    int resizeMethod, const QColor &color) const
00501 {
00502     const QString id = QString("%5_%3_%4_%1x%2")
00503                               .arg(size.width()).arg(size.height()).arg(color.name())
00504                               .arg(resizeMethod).arg(sourceImagePath);
00505     return id;
00506 }
00507 
00508 QString WallpaperPrivate::cachePath(const QString &key) const
00509 {
00510     return KGlobal::dirs()->locateLocal("cache", "plasma-wallpapers/" + key + ".png");
00511 }
00512 
00513 void WallpaperPrivate::newRenderCompleted(WallpaperRenderThread *currentRenderer, int token, const QImage &image,
00514                                           const QString &sourceImagePath, const QSize &size,
00515                                           int resizeMethod, const QColor &color)
00516 {
00517     q->disconnect(currentRenderer, SIGNAL(done(WallpaperRenderThread*,int,QImage,QString,QSize,int,QColor)),
00518                     q, SLOT(newRenderCompleted(WallpaperRenderThread*,int,QImage,QString,QSize,int,QColor)));
00519 
00520     if (!s_renderQueue.isEmpty()) {
00521         while (!s_renderQueue.isEmpty()) {
00522             WallpaperPrivate::RenderRequest request = s_renderQueue.dequeue();
00523 
00524             if (!request.parent) {
00525                 continue;
00526             }
00527 
00528             currentRenderer->wait();
00529             request.parent.data()->d->renderToken = currentRenderer->render(request.file, request.size, request.resizeMethod, request.color);
00530             QObject::connect(currentRenderer, SIGNAL(done(WallpaperRenderThread*,int,QImage,QString,QSize,int,QColor)),
00531                     request.parent.data(), SLOT(newRenderCompleted(WallpaperRenderThread*,int,QImage,QString,QSize,int,QColor)), Qt::UniqueConnection);
00532 
00533             break;
00534         }
00535     } else {
00536          for (int i = 0; i < s_renderers.size(); i++) {
00537             if (s_renderers[i] == currentRenderer){
00538                 s_renderers.removeAt(i);
00539             }
00540          }
00541          currentRenderer->deleteLater();
00542          currentRenderer = 0;
00543     }
00544 
00545     if (token != renderToken) {
00546         //kDebug() << "render token mismatch" << token << renderToken;
00547         return;
00548     }
00549 
00550     if (cacheRendering) {
00551         q->insertIntoCache(cacheKey(sourceImagePath, size, resizeMethod, color), image);
00552     }
00553 
00554     //kDebug() << "rendering complete!";
00555     emit q->renderCompleted(image);
00556 }
00557 
00558 // put all setup routines for script here. at this point we can assume that
00559 // package exists and that we have a script engine
00560 void WallpaperPrivate::setupScriptSupport()
00561 {
00562     Q_ASSERT(package);
00563     kDebug() << "setting up script support, package is in" << package->path()
00564              << "which is a" << package->structure()->type() << "package"
00565              << ", main script is" << package->filePath("mainscript");
00566 
00567     QString translationsPath = package->filePath("translations");
00568     if (!translationsPath.isEmpty()) {
00569         //FIXME: we should _probably_ use a KComponentData to segregate the applets
00570         //       from each other; but I want to get the basics working first :)
00571         KGlobal::dirs()->addResourceDir("locale", translationsPath);
00572         KGlobal::locale()->insertCatalog(package->metadata().pluginName());
00573     }
00574 }
00575 
00576 void WallpaperPrivate::initScript()
00577 {
00578     if (script && !scriptInitialized) {
00579         setupScriptSupport();
00580         script->init();
00581         scriptInitialized = true;
00582     }
00583 }
00584 
00585 bool WallpaperPrivate::findInCache(const QString &key, unsigned int lastModified)
00586 {
00587     if (cacheRendering) {
00588         QString cache = cachePath(key);
00589         if (QFile::exists(cache)) {
00590             if (lastModified > 0) {
00591                 QFileInfo info(cache);
00592                 if (info.lastModified().toTime_t() < lastModified) {
00593                     return false;
00594                 }
00595             }
00596 
00597             LoadImageThread *loadImageT = new LoadImageThread(cache);
00598             q->connect(loadImageT, SIGNAL(done(const QImage&)), q, SIGNAL(renderCompleted(const QImage&)));
00599             QThreadPool::globalInstance()->start(loadImageT);
00600 
00601             return true;
00602         }
00603     }
00604 
00605     return false;
00606 }
00607 
00608 bool Wallpaper::findInCache(const QString &key, QImage &image, unsigned int lastModified)
00609 {
00610     if (d->cacheRendering) {
00611         QString cache = d->cachePath(key);
00612         if (QFile::exists(cache)) {
00613             if (lastModified > 0) {
00614                 QFileInfo info(cache);
00615                 if (info.lastModified().toTime_t() < lastModified) {
00616                     return false;
00617                 }
00618             }
00619 
00620             image.load(cache);
00621             return true;
00622         }
00623     }
00624 
00625     return false;
00626 }
00627 
00628 void Wallpaper::insertIntoCache(const QString& key, const QImage &image)
00629 {
00630     //TODO: cache limits?
00631     if (key.isEmpty()) {
00632         return;
00633     }
00634 
00635     if (d->cacheRendering) {
00636         if (image.isNull()) {
00637 #ifndef PLASMA_NO_KIO
00638             KIO::file_delete(d->cachePath(key));
00639 #else
00640             QFile f(d->cachePath(key));
00641             f.remove();
00642 #endif
00643         } else {
00644             QThreadPool::globalInstance()->start(new SaveImageThread(image, d->cachePath(key)));
00645         }
00646     }
00647 }
00648 
00649 QList<QAction*> Wallpaper::contextualActions() const
00650 {
00651     return contextActions;
00652 }
00653 
00654 void Wallpaper::setContextualActions(const QList<QAction*> &actions)
00655 {
00656     contextActions = actions;
00657 }
00658 
00659 bool Wallpaper::isPreviewing() const
00660 {
00661     return d->previewing;
00662 }
00663 
00664 void Wallpaper::setPreviewing(bool previewing)
00665 {
00666     d->previewing = previewing;
00667 }
00668 
00669 bool Wallpaper::needsPreviewDuringConfiguration() const
00670 {
00671     return d->needsPreviewDuringConfiguration;
00672 }
00673 
00674 void Wallpaper::setPreviewDuringConfiguration(const bool preview)
00675 {
00676     d->needsPreviewDuringConfiguration = preview;
00677 }
00678 
00679 const Package *Wallpaper::package() const
00680 {
00681     return d->package;
00682 }
00683 
00684 } // Plasma namespace
00685 
00686 #include "wallpaper.moc"
00687 #include "private/wallpaper_p.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