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