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

libplasma

applet.cpp

Go to the documentation of this file.
00001 /*
00002  *   Copyright 2005 by Aaron Seigo <aseigo@kde.org>
00003  *   Copyright 2007 by Riccardo Iaconelli <riccardo@kde.org>
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 "applet.h"
00022 
00023 #include <cmath>
00024 #include <limits>
00025 
00026 #include <QApplication>
00027 #include <QEvent>
00028 #include <QFile>
00029 #include <QList>
00030 #include <QPainter>
00031 #include <QSize>
00032 #include <QStyleOptionGraphicsItem>
00033 #include <QTextDocument>
00034 #include <QTimer>
00035 #include <QUiLoader>
00036 
00037 #include <KIcon>
00038 #include <KColorScheme>
00039 #include <KConfigDialog>
00040 #include <KDialog>
00041 #include <KPluginInfo>
00042 #include <KStandardDirs>
00043 #include <KService>
00044 #include <KServiceTypeTrader>
00045 #include <KIconLoader>
00046 
00047 #include <Solid/PowerManagement>
00048 
00049 #include "plasma/configxml.h"
00050 #include "plasma/containment.h"
00051 #include "plasma/corona.h"
00052 #include "plasma/dataenginemanager.h"
00053 #include "plasma/package.h"
00054 #include "plasma/packages_p.h"
00055 #include "plasma/plasma.h"
00056 #include "plasma/scriptengine.h"
00057 #include "plasma/shadowitem_p.h"
00058 #include "plasma/svg.h"
00059 #include "plasma/svgpanel.h"
00060 #include "plasma/theme.h"
00061 #include "plasma/view.h"
00062 
00063 #include "plasma/layouts/boxlayout.h"
00064 #include "plasma/widgets/widget.h"
00065 #include "plasma/widgets/lineedit.h"
00066 #include "plasma/widgets/pushbutton.h"
00067 
00068 //#define DYNAMIC_SHADOWS
00069 namespace Plasma
00070 {
00071 
00072 class Applet::Private
00073 {
00074 public:
00075     Private(KService::Ptr service, int uniqueID)
00076         : appletId(uniqueID),
00077           appletDescription(service),
00078           package(0),
00079           background(0),
00080           failureText(0),
00081           scriptEngine(0),
00082           configXml(0),
00083           shadow(0),
00084           cachedBackground(0),
00085           mainConfig(0),
00086           pendingConstraints(NoConstraint),
00087           kioskImmutable(false),
00088           immutable(false),
00089           hasConfigurationInterface(false),
00090           failed(false),
00091           needsConfig(false),
00092           isContainment(false)
00093     {
00094         if (appletId == 0) {
00095             appletId = nextId();
00096         }
00097 
00098         if (appletId > s_maxAppletId) {
00099             s_maxAppletId = appletId;
00100         }
00101     }
00102 
00103     ~Private()
00104     {
00105         foreach ( const QString& engine, loadedEngines ) {
00106             DataEngineManager::self()->unloadDataEngine( engine );
00107         }
00108         delete background;
00109         delete package;
00110         delete configXml;
00111         delete shadow;
00112         delete cachedBackground;
00113         delete mainConfig;
00114     }
00115 
00116     void init(Applet* applet)
00117     {
00118         // WARNING: do not access config() OR globalConfig() in this method!
00119         //          that requires a scene, which is not available at this point
00120         applet->setAcceptsHoverEvents(true);
00121         applet->setZValue(100);
00122 
00123         if (!appletDescription.isValid()) {
00124             applet->setFailedToLaunch(true);
00125             return;
00126         }
00127 
00128         QString language = appletDescription.property("X-Plasma-Language").toString();
00129 
00130         // we have a scripted plasmoid
00131         if (!language.isEmpty()) {
00132             // find where the Package is
00133             QString path = KStandardDirs::locate("appdata",
00134                                                  "plasmoids/" + appletDescription.pluginName() + '/');
00135 
00136             if (path.isEmpty()) {
00137                 applet->setFailedToLaunch(true, i18n("Could not locate the %1 package required for the %2 widget.",
00138                                                      appletDescription.pluginName(), appletDescription.name()));
00139             } else {
00140                 // create the package and see if we have something real
00141                 //kDebug() << "trying for" << path;
00142                 package = new Package(path, PlasmoidStructure());
00143                 if (package->isValid()) {
00144                     // now we try and set up the script engine.
00145                     // it will be parented to this applet and so will get
00146                     // deleted when the applet does
00147 
00148                     scriptEngine = ScriptEngine::load(language, applet);
00149                     if (!scriptEngine) {
00150                         delete package;
00151                         package = 0;
00152                         applet->setFailedToLaunch(true, i18n("Could not create a %1 ScriptEngine for the %2 widget.",
00153                                                              language, appletDescription.name()));
00154                     }
00155                 } else {
00156                     applet->setFailedToLaunch(true, i18n("Could not open the %1 package required for the %2 widget.",
00157                                                          appletDescription.pluginName(), appletDescription.name()));
00158                     delete package;
00159                     package = 0;
00160                 }
00161 
00162                 if (package) {
00163                     setupScriptSupport(applet);
00164                 }
00165             }
00166         }
00167         applet->setDrawStandardBackground(true);
00168     }
00169 
00170     // put all setup routines for script here. at this point we can assume that
00171     // package exists and that we have a script engin
00172     void setupScriptSupport(Applet* applet)
00173     {
00174         Q_ASSERT(package);
00175         QString xmlPath = package->filePath("mainconfigxml");
00176         if (!xmlPath.isEmpty()) {
00177             QFile file(xmlPath);
00178             // FIXME: KConfigSkeleton doesn't play well with KConfigGroup =/
00179             KConfigGroup config = applet->config();
00180             configXml = new ConfigXml(&config, &file);
00181         }
00182 
00183         if (!package->filePath("mainconfigui").isEmpty()) {
00184             applet->setHasConfigurationInterface(true);
00185         }
00186     }
00187 
00188     QSizeF contentSize(const Applet* q)
00189     {
00190         if (failureText) {
00191             return failureText->geometry().size();
00192         }
00193 
00194         return q->contentSize();
00195     }
00196 
00197     static uint nextId()
00198     {
00199         ++s_maxAppletId;
00200         return s_maxAppletId;
00201     }
00202 
00203     QString instanceName()
00204     {
00205         if (!appletDescription.isValid()) {
00206             return QString();
00207         }
00208 
00209         return appletDescription.service()->library() + QString::number(appletId);
00210     }
00211 
00212     void getBorderSize(int& left , int& top, int &right, int& bottom)
00213     {
00214         if (background) {
00215             top = background->marginSize(Plasma::TopMargin);
00216             left = background->marginSize(Plasma::LeftMargin);
00217             right = background->marginSize(Plasma::RightMargin);
00218             bottom = background->marginSize(Plasma::BottomMargin);
00219         } else {
00220             top = left = right = bottom = 0;
00221         }
00222     }
00223 
00224     void scheduleConstraintsUpdate(Plasma::Constraints c, Applet* applet)
00225     {
00226         if (pendingConstraints == NoConstraint) {
00227             QTimer::singleShot(0, applet, SLOT(flushUpdatedConstraints()));
00228         }
00229         pendingConstraints |= c;
00230     }
00231 
00232     KConfigGroup* mainConfigGroup(const Applet* q)
00233     {
00234         if (mainConfig) {
00235             return mainConfig;
00236         }
00237 
00238         if (isContainment) {
00239             const Containment *asContainment = qobject_cast<Containment*>(const_cast<Applet*>(q));
00240             Q_ASSERT(asContainment);
00241 
00242             KConfigGroup containmentConfig;
00243             //kDebug() << "got a corona, baby?" << (QObject*)asContainment->corona();
00244             if (asContainment->corona()) {
00245                 containmentConfig = KConfigGroup(asContainment->corona()->config(), "Containments");
00246             } else {
00247                 containmentConfig =  KConfigGroup(KGlobal::config(), "Containments");
00248             }
00249 
00250             mainConfig = new KConfigGroup(&containmentConfig, QString::number(appletId));
00251         } else {
00252             KConfigGroup appletConfig;
00253             if (q->containment()) {
00254                 appletConfig = q->containment()->config();
00255                 appletConfig = KConfigGroup(&appletConfig, "Applets");
00256             } else {
00257                 kWarning() << "requesting config for" << q->name() << "without a containment!";
00258                 appletConfig = KConfigGroup(KGlobal::config(), "Applets");
00259             }
00260 
00261             mainConfig = new KConfigGroup(&appletConfig, QString::number(appletId));
00262         }
00263 
00264         return mainConfig;
00265     }
00266 
00267     void copyEntries(KConfigGroup *source, KConfigGroup *destination)
00268     {
00269         foreach (const QString &group, source->groupList()) {
00270             KConfigGroup subSource(source, group);
00271             KConfigGroup subDest(destination, group);
00272             copyEntries(&subSource, &subDest);
00273         }
00274 
00275         QMap<QString, QString> entries = source->entryMap();
00276         QMapIterator<QString, QString> it(entries);
00277         while (it.hasNext()) {
00278             it.next();
00279             destination->writeEntry(it.key(), it.value());
00280         }
00281     }
00282 
00283     //TODO: examine the usage of memory here; there's a pretty large
00284     //      number of members at this point.
00285     uint appletId;
00286     KPluginInfo appletDescription;
00287     Package* package;
00288     QList<QObject*> watchedForFocus;
00289     QStringList loadedEngines;
00290     static uint s_maxAppletId;
00291     Plasma::SvgPanel *background;
00292     Plasma::LineEdit *failureText;
00293     ScriptEngine* scriptEngine;
00294     ConfigXml* configXml;
00295     ShadowItem* shadow;
00296     QPixmap* cachedBackground;
00297     KConfigGroup *mainConfig;
00298     Plasma::Constraints pendingConstraints;
00299     bool kioskImmutable : 1;
00300     bool immutable : 1;
00301     bool hasConfigurationInterface : 1;
00302     bool failed : 1;
00303     bool needsConfig : 1;
00304     bool isContainment : 1;
00305 };
00306 
00307 uint Applet::Private::s_maxAppletId = 0;
00308 
00309 Applet::Applet(QGraphicsItem *parent,
00310                const QString& serviceID,
00311                uint appletId)
00312     :  Widget(parent),
00313        d(new Private(KService::serviceByStorageId(serviceID), appletId))
00314 {
00315     // WARNING: do not access config() OR globalConfig() in this method!
00316     //          that requires a scene, which is not available at this point
00317     d->init(this);
00318 }
00319 
00320 Applet::Applet(QObject* parentObject, const QVariantList& args)
00321     :  Widget(0,parentObject),
00322        d(new Private(KService::serviceByStorageId(args.count() > 0 ? args[0].toString() : QString()),
00323                      args.count() > 1 ? args[1].toInt() : 0))
00324 {
00325     // WARNING: do not access config() OR globalConfig() in this method!
00326     //          that requires a scene, which is not available at this point
00327     d->init(this);
00328     // the brain damage seen in the initialization list is due to the
00329     // inflexibility of KService::createInstance
00330 }
00331 
00332 Applet::~Applet()
00333 {
00334     needsFocus(false);
00335     delete d;
00336 }
00337 
00338 void Applet::init()
00339 {
00340 }
00341 
00342 uint Applet::id() const
00343 {
00344     return d->appletId;
00345 }
00346 
00347 void Applet::save(KConfigGroup* group) const
00348 {
00349     // we call the dptr member directly for locked since isImmutable()
00350     // also checks kiosk and parent containers
00351     group->writeEntry("locked", d->immutable);
00352     group->writeEntry("plugin", pluginName());
00353     //FIXME: for containments, we need to have some special values here w/regards to
00354     //       screen affinity (e.g. "bottom of screen 0")
00355     //kDebug() << pluginName() << "geometry is" << geometry() << "pos is" << pos() << "bounding rect is" << boundingRect();
00356     group->writeEntry("geometry", geometry());
00357 
00358     if (transform() == QTransform()) {
00359         group->deleteEntry("transform");
00360     } else {
00361         QList<qreal> m;
00362         QTransform t = transform();
00363         m << t.m11() << t.m12() << t.m13() << t.m21() << t.m22() << t.m23() << t.m31() << t.m32() << t.m33();
00364         group->writeEntry("transform", m);
00365         //group->writeEntry("transform", transformToString(transform()));
00366     }
00367 
00368     KConfigGroup appletConfigGroup(group, "Configuration");
00369     //FIXME: we need a global save state too
00370     saveState(&appletConfigGroup);
00371 }
00372 
00373 void Applet::saveState(KConfigGroup* group) const
00374 {
00375     if (group->config()->name() != config().config()->name()) {
00376         // we're being saved to a different file!
00377         // let's just copy the current values in our configuration over
00378         KConfigGroup c = config();
00379         d->copyEntries(&c, group);
00380     }
00381 
00382     Q_UNUSED(group)
00383 }
00384 
00385 KConfigGroup Applet::config(const QString &group) const
00386 {
00387     KConfigGroup cg = config();
00388     return KConfigGroup(&cg, group);
00389 }
00390 
00391 KConfigGroup Applet::config() const
00392 {
00393     if (d->isContainment) {
00394         return *(d->mainConfigGroup(this));
00395     }
00396 
00397     return KConfigGroup(d->mainConfigGroup(this), "Configuration");
00398 }
00399 
00400 KConfigGroup Applet::globalConfig() const
00401 {
00402     KConfigGroup globalAppletConfig;
00403     const Containment *c = isContainment() ? dynamic_cast<const Containment*>(this) : containment();
00404     QString group = isContainment() ? "ContainmentGlobals" : "AppletGlobals";
00405 
00406     if (c && c->corona()) {
00407         KSharedConfig::Ptr coronaConfig = c->corona()->config();
00408         globalAppletConfig = KConfigGroup(coronaConfig, group);
00409     } else {
00410         globalAppletConfig = KConfigGroup(KGlobal::config(), group);
00411     }
00412 
00413     return KConfigGroup(&globalAppletConfig, globalName());
00414 }
00415 
00416 void Applet::destroy()
00417 {
00418     //kDebug() << "???????????????? DESTROYING APPLET" << name() << " ???????????????????????????";
00419     if (d->configXml) {
00420         d->configXml->setDefaults();
00421     }
00422 
00423     resetConfigurationObject();
00424     deleteLater();
00425 }
00426 
00427 void Applet::resetConfigurationObject()
00428 {
00429     d->mainConfigGroup(this)->deleteGroup();
00430     delete d->mainConfig;
00431     d->mainConfig = 0;
00432 }
00433 
00434 ConfigXml* Applet::configXml() const
00435 {
00436     return d->configXml;
00437 }
00438 
00439 DataEngine* Applet::dataEngine(const QString& name) const
00440 {
00441     int index = d->loadedEngines.indexOf(name);
00442     if (index != -1) {
00443         return DataEngineManager::self()->dataEngine(name);
00444     }
00445 
00446     DataEngine* engine = DataEngineManager::self()->loadDataEngine(name);
00447     if (engine->isValid()) {
00448         d->loadedEngines.append(name);
00449     }
00450 
00451     return engine;
00452 }
00453 
00454 const Package* Applet::package() const
00455 {
00456     return d->package;
00457 }
00458 
00459 void Applet::updateConstraints(Plasma::Constraints constraints)
00460 {
00461     d->scheduleConstraintsUpdate(constraints, this);
00462 }
00463 
00464 void Applet::constraintsUpdated(Plasma::Constraints constraints)
00465 {
00466     Q_UNUSED(constraints)
00467     //kDebug() << constraints << "constraints are FormFactor: " << formFactor() << ", Location: " << location();
00468 }
00469 
00470 QString Applet::name() const
00471 {
00472     if (!d->appletDescription.isValid()) {
00473         return i18n("Unknown Applet");
00474     }
00475 
00476     return d->appletDescription.name();
00477 }
00478 
00479 QString Applet::icon() const
00480 {
00481     if (!d->appletDescription.isValid()) {
00482         return QString();
00483     }
00484 
00485     return d->appletDescription.icon();
00486 }
00487 
00488 QString Applet::pluginName() const
00489 {
00490     if (!d->appletDescription.isValid()) {
00491         return QString();
00492     }
00493 
00494     return d->appletDescription.pluginName();
00495 }
00496 
00497 bool Applet::shouldConserveResources() const
00498 {
00499     return Solid::PowerManagement::appShouldConserveResources();
00500 }
00501 
00502 QString Applet::category() const
00503 {
00504     if (!d->appletDescription.isValid()) {
00505         return i18n("Miscellaneous");
00506     }
00507 
00508     return d->appletDescription.category();
00509 }
00510 
00511 QString Applet::category(const KPluginInfo& applet)
00512 {
00513     return applet.property("X-KDE-PluginInfo-Category").toString();
00514 }
00515 
00516 QString Applet::category(const QString& appletName)
00517 {
00518     if (appletName.isEmpty()) {
00519         return QString();
00520     }
00521 
00522     QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(appletName);
00523     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
00524 
00525     if (offers.isEmpty()) {
00526         return QString();
00527     }
00528 
00529     return offers.first()->property("X-KDE-PluginInfo-Category").toString();
00530 }
00531 
00532 bool Applet::isImmutable() const
00533 {
00534     return  d->immutable || d->kioskImmutable ||
00535             (containment() && containment()->isImmutable()) ||
00536             (dynamic_cast<Corona*>( scene() ) && static_cast<Corona*>(scene())->isImmutable());
00537 }
00538 
00539 bool Applet::isKioskImmutable() const
00540 {
00541     return d->kioskImmutable;
00542 }
00543 
00544 void Applet::setImmutable(bool immutable)
00545 {
00546     if (d->immutable == immutable ||
00547         (immutable && d->kioskImmutable)) {
00548         return;
00549     }
00550 
00551     d->immutable = immutable;
00552     // TODO: should we tell the applets too?
00553     updateConstraints(ImmutableConstraint);
00554 }
00555 
00556 bool Applet::drawStandardBackground()
00557 {
00558     return d->background != 0;
00559 }
00560 
00561 void Applet::setDrawStandardBackground(bool drawBackground)
00562 {
00563     if (drawBackground) {
00564         if (!d->background) {
00565             d->background = new Plasma::SvgPanel("widgets/background");
00566             updateGeometry();
00567         }
00568     } else if (d->background) {
00569         delete d->background;
00570         d->background = 0;
00571         updateGeometry();
00572     }
00573 }
00574 
00575 bool Applet::failedToLaunch() const
00576 {
00577     return d->failed;
00578 }
00579 
00580 QString visibleFailureText(const QString& reason)
00581 {
00582     QString text;
00583 
00584     if (reason.isEmpty()) {
00585         text = i18n("This object could not be created.");
00586     } else {
00587         text = i18n("This object could not be created for the following reason:<p><b>%1</b></p>", reason);
00588     }
00589 
00590     return text;
00591 }
00592 
00593 void Applet::setFailedToLaunch(bool failed, const QString& reason)
00594 {
00595     if (d->failed == failed) {
00596         if (d->failureText) {
00597             d->failureText->setHtml(visibleFailureText(reason));
00598             setGeometry(QRectF(geometry().topLeft(), d->failureText->sizeHint()));
00599         }
00600         return;
00601     }
00602 
00603     d->failed = failed;
00604     prepareGeometryChange();
00605 
00606     d->failureText = 0;
00607     qDeleteAll(QGraphicsItem::children());
00608     setLayout(0);
00609 
00610     if (failed) {
00611         setDrawStandardBackground(true);
00612         Layout* failureLayout = new BoxLayout(BoxLayout::TopToBottom, this);
00613         failureLayout->setMargin(0);
00614         d->failureText = new LineEdit(this);
00615         d->failureText->setTextInteractionFlags( Qt::TextSelectableByMouse );
00616         d->failureText->setStyled(false);
00617         d->failureText->document()->setTextWidth(200);
00618         d->failureText->setHtml(visibleFailureText(reason));
00619         //FIXME: this needs to get the colour from the theme's colour scheme
00620         d->failureText->setDefaultTextColor(KStatefulBrush(KColorScheme::Window,
00621                                                            KColorScheme::NormalText,
00622                                                            Theme::self()->colors())
00623                                                         .brush(QPalette::Normal).color());
00624         failureLayout->addItem(d->failureText);
00625         setGeometry(QRectF(geometry().topLeft(), d->failureText->sizeHint()));
00626     }
00627 
00628     update();
00629 }
00630 
00631 bool Applet::needsConfiguring() const
00632 {
00633     return d->needsConfig;
00634 }
00635 
00636 void Applet::setNeedsConfiguring(bool needsConfig)
00637 {
00638     if (d->needsConfig == needsConfig) {
00639         return;
00640     }
00641 
00642     d->needsConfig = needsConfig;
00643     prepareGeometryChange();
00644     qDeleteAll(QGraphicsItem::children());
00645     setLayout(0);
00646 
00647     if (needsConfig) {
00648         setDrawStandardBackground(true);
00649         Layout* layout = new BoxLayout(BoxLayout::TopToBottom,this);
00650         PushButton* button = new PushButton(this);
00651         button->setText(i18n("Configure..."));
00652         connect(button, SIGNAL(clicked()), this, SLOT(performSetupConfig()));
00653         layout->addItem(button);
00654     }
00655 }
00656 
00657 void Applet::performSetupConfig()
00658 {
00659     qDeleteAll(QGraphicsItem::children());
00660     setLayout(0);
00661     showConfigurationInterface();
00662 }
00663 
00664 void Applet::checkImmutability()
00665 {
00666     d->kioskImmutable = globalConfig().isImmutable() || config().isImmutable() ||
00667                         (containment() && containment()->isKioskImmutable()) ||
00668                         (dynamic_cast<Corona*>(scene()) && static_cast<Corona*>(scene())->isKioskImmutable());
00669 
00670     if (d->kioskImmutable) {
00671         updateConstraints(ImmutableConstraint);
00672     }
00673 }
00674 
00675 void Applet::flushUpdatedConstraints()
00676 {
00677     if (d->pendingConstraints == NoConstraint) {
00678         return;
00679     }
00680 
00681     //kDebug() << "fushing constraints: " << d->pendingConstraints << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
00682     Plasma::Constraints c = d->pendingConstraints;
00683     d->pendingConstraints = NoConstraint;
00684 
00685     Containment* containment = qobject_cast<Plasma::Containment*>(this);
00686     if (c & Plasma::FormFactorConstraint) {
00687         FormFactor f = formFactor();
00688         setShadowShown(f == Planar);
00689         setDrawStandardBackground(!containment && f != Vertical && f != Horizontal);
00690     }
00691 
00692     if (isContainment() && containment) {
00693         containment->containmentConstraintsUpdated(c);
00694     }
00695 
00696     constraintsUpdated(c);
00697 
00698     if (layout()) {
00699         layout()->updateGeometry();
00700     }
00701 }
00702 
00703 int Applet::type() const
00704 {
00705     return Type;
00706 }
00707 
00708 QRectF Applet::boundingRect() const
00709 {
00710     QRectF rect = QRectF(QPointF(0,0), d->contentSize(this));
00711 
00712     int left;
00713     int right;
00714     int top;
00715     int bottom;
00716 
00717     d->getBorderSize(left,top,right,bottom);
00718 
00719 
00720     //kDebug() << "Background , Border size" << d->background << left << top << right << bottom;
00721 
00722     return rect.adjusted(-left,-top,right,bottom);
00723 }
00724 
00725 QSizeF Applet::sizeHint() const
00726 {
00727     int left = 0;
00728     int right = 0;
00729     int top = 0;
00730     int bottom = 0;
00731 
00732     d->getBorderSize(left, top, right, bottom);
00733     QSizeF borderSize = QSizeF(left + right, top + bottom);
00734 
00735     //kDebug() << "Applet content size hint: " << contentSizeHint() << "plus our borders" << left << right << top << bottom;
00736 
00737     return contentSizeHint() + QSizeF(left + right, top + bottom);
00738 }
00739 
00740 QList<QAction*> Applet::contextActions()
00741 {
00742     kDebug() << "empty context actions";
00743     return QList<QAction*>();
00744 }
00745 
00746 QColor Applet::color() const
00747 {
00748     // TODO: add more colors for more categories and
00749     // maybe read from config?
00750     QString c = category();
00751     int alpha = 200;
00752     // Colors taken from Oxygen color palette
00753     if (c == "Date and Time") {
00754         return QColor(191, 94, 0, alpha);
00755     } else if (c == "Environment & Weather") {
00756         return QColor(191, 0, 0, alpha);
00757     } else if (c == "Examples") {
00758         return QColor(204, 0, 154, alpha);
00759     } else if (c == "File System") {
00760         return QColor(90, 0, 179, alpha);
00761     } else if (c == "Graphics") {
00762         return QColor(0, 0, 255, alpha);
00763     } else if (c == "Language") {
00764         return QColor(0, 191, 0, alpha);
00765     } else if (c == "Mapping") {
00766         return QColor(191, 245, 0, alpha);
00767     } else if (c == "Online Services") {
00768         return QColor(255, 213, 0, alpha);
00769     } else if (c == "System Information") {
00770         return QColor(0, 196, 204, alpha);
00771     } else if (c == "Windows and Tasks") {
00772         return QColor(255, 126, 0, alpha);
00773     } else {
00774         return QColor(136, 136, 136, alpha);
00775     }
00776 }
00777 
00778 void Applet::paintWidget(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
00779 {
00780     Q_UNUSED(widget)
00781     if (d->shadow && d->shadow->shadowedSize() != boundingRect().size()) {
00782         //kDebug() << "sizes are " << d->shadow->shadowedSize() << boundingRect().size();
00783         d->shadow->generate();
00784     }
00785 
00786     painter->save();
00787     if (transform().isRotating()) {
00788         painter->setRenderHint(QPainter::SmoothPixmapTransform);
00789         painter->setRenderHint(QPainter::Antialiasing);
00790     }
00791 
00792     if (d->background &&
00793         formFactor() != Plasma::Vertical &&
00794         formFactor() != Plasma::Horizontal) {
00795         //kDebug() << "option rect is" << option->rect;
00796         d->background->paint(painter, option->rect);
00797     }
00798 
00799     if (!d->failed && !d->needsConfig) {
00800         if (widget && isContainment()) {
00801             // note that the widget we get is actually the viewport of the view, not the view itself
00802             View* v = qobject_cast<Plasma::View*>(widget->parent());
00803             if (v && !v->drawWallpaper()) {
00804                 painter->restore();
00805                 return;
00806             }
00807         }
00808 
00809         //kDebug() << "paint interface of" << (QObject*) this;
00810         paintInterface(painter, option, QRect(QPoint(0,0), d->contentSize(this).toSize()));
00811     }
00812 
00813     painter->restore();
00814 }
00815 
00816 void Applet::paintInterface(QPainter *painter, const QStyleOptionGraphicsItem *option,
00817                             const QRect & contentsRect)
00818 {
00819     Q_UNUSED(contentsRect)
00820 
00821     if (d->scriptEngine) {
00822         d->scriptEngine->paintInterface(painter, option, contentsRect);
00823     } else {
00824         //kDebug() << "Applet::paintInterface() default impl";
00825     }
00826 }
00827 
00828 FormFactor Applet::formFactor() const
00829 {
00830     Containment* c = containment();
00831     return c ? c->formFactor() : Plasma::Planar;
00832 }
00833 
00834 Containment* Applet::containment() const
00835 {
00836 /*
00837  * while this is probably "more correct", much of the code in applet assumes containment
00838  * returns zero in the case that this is a containment itself.
00839  * if (isContainment()) {
00840         return dynamic_cast<Containment*>(const_cast<Applet*>(this));
00841     }
00842 */
00843 
00844     QGraphicsItem *parent = parentItem();
00845     Containment *c = 0;
00846 
00847     while (parent) {
00848         Containment *possibleC =  dynamic_cast<Containment*>(parent);
00849         if (possibleC && possibleC->isContainment()) {
00850             c = possibleC;
00851             break;
00852         }
00853         parent = parent->parentItem();
00854     }
00855 
00856     return c;
00857 }
00858 
00859 Location Applet::location() const
00860 {
00861     Containment* c = containment();
00862 
00863     if (!c) {
00864         return Plasma::Desktop;
00865     }
00866 
00867     return c->location();
00868 }
00869 
00870 QRectF Applet::contentRect() const
00871 {
00872     return QRectF(QPointF(0, 0), contentSize());
00873 }
00874 
00875 QSizeF Applet::contentSize() const
00876 {
00877     int top, left, right, bottom;
00878     d->getBorderSize(left, top, right, bottom);
00879 
00880     // kDebug() << "Geometry size: " << geometry().size();
00881     // kDebug() << "Borders: " << left << top << right << bottom;
00882 
00883     return (geometry().size() - QSizeF(left + right, top + bottom)).expandedTo(QSizeF(0, 0));
00884 }
00885 
00886 void Applet::setContentSize(const QSizeF &size)
00887 {
00888     int top, left, right, bottom;
00889     d->getBorderSize(left, top, right, bottom);
00890 
00891     resize(size + QSizeF(left + right, top + bottom));
00892 }
00893 
00894 void Applet::setContentSize(int width, int height)
00895 {
00896     setContentSize(QSizeF(width, height));
00897 }
00898 
00899 QSizeF Applet::contentSizeHint() const
00900 {
00901     if (layout()) {
00902         return layout()->sizeHint();
00903     }
00904 
00905     return contentSize();
00906 }
00907 
00908 void Applet::setMinimumContentSize(const QSizeF &minSize)
00909 {
00910     int top, left, right, bottom;
00911     d->getBorderSize(left, top, right, bottom);
00912 
00913     setMinimumSize(minSize + QSizeF(left + right, top + bottom));
00914 }
00915 
00916 void Applet::setMinimumContentSize(int minWidth, int minHeight)
00917 {
00918     setMinimumContentSize(QSizeF(minWidth, minHeight));
00919 }
00920 
00921 QSizeF Applet::minimumContentSize() const
00922 {
00923     int top, left, right, bottom;
00924     d->getBorderSize(left, top, right, bottom);
00925 
00926     return minimumSize() - QSizeF(left + right, top + bottom);
00927 }
00928 
00929 void Applet::setMaximumContentSize(const QSizeF &maxSize)
00930 {
00931     int top, left, right, bottom;
00932     d->getBorderSize(left, top, right, bottom);
00933 
00934     setMaximumSize(maxSize + QSizeF(left + right, top + bottom));
00935 }
00936 
00937 void Applet::setMaximumContentSize(int maxWidth, int maxHeight)
00938 {
00939     setMaximumContentSize(QSizeF(maxWidth, maxHeight));
00940 }
00941 
00942 QSizeF Applet::maximumContentSize() const
00943 {
00944     int top, left, right, bottom;
00945     d->getBorderSize(left, top, right, bottom);
00946 
00947     return maximumSize() - QSizeF(left + right, top + bottom);
00948 }
00949 
00950 QString Applet::globalName() const
00951 {
00952     if (!d->appletDescription.isValid()) {
00953         return QString();
00954     }
00955 
00956     return d->appletDescription.service()->library();
00957 }
00958 
00959 QString Applet::instanceName() const
00960 {
00961     return d->instanceName();
00962 }
00963 
00964 void Applet::watchForFocus(QObject *widget, bool watch)
00965 {
00966     if ( !widget ) {
00967         return;
00968     }
00969 
00970     int index = d->watchedForFocus.indexOf(widget);
00971     if ( watch ) {
00972         if ( index == -1 ) {
00973             d->watchedForFocus.append( widget );
00974             widget->installEventFilter( this );
00975         }
00976     } else if ( index != -1 ) {
00977         d->watchedForFocus.removeAt( index );
00978         widget->removeEventFilter( this );
00979     }
00980 }
00981 
00982 void Applet::needsFocus(bool focus)
00983 {
00984     if (focus == QGraphicsItem::hasFocus()) {
00985         return;
00986     }
00987 
00988     emit requestFocus(focus);
00989 }
00990 
00991 bool Applet::hasConfigurationInterface()
00992 {
00993     return d->hasConfigurationInterface;
00994 }
00995 
00996 void Applet::setHasConfigurationInterface(bool hasInterface)
00997 {
00998     d->hasConfigurationInterface = hasInterface;
00999 }
01000 
01001 bool Applet::eventFilter( QObject *o, QEvent * e )
01002 {
01003     if ( !d->watchedForFocus.contains( o ) )
01004     {
01005         if ( e->type() == QEvent::MouseButtonRelease ||
01006              e->type() == QEvent::FocusIn ) {
01007             needsFocus( true );
01008         } else if ( e->type() == QEvent::FocusOut ) {
01009             needsFocus( false );
01010         }
01011     }
01012 
01013     return QObject::eventFilter(o, e);
01014 }
01015 
01016 void Applet::showConfigurationInterface()
01017 {
01018     if (d->package && d->configXml) {
01019         QString uiFile = d->package->filePath("mainconfigui");
01020         if (uiFile.isEmpty()) {
01021             return;
01022         }
01023 
01024         KConfigDialog *dialog = new KConfigDialog(0, "", d->configXml);
01025         dialog->setWindowTitle(i18n("%1 Settings", name()));
01026         dialog->setAttribute(Qt::WA_DeleteOnClose, true);
01027 
01028         QUiLoader loader;
01029         QString filename = d->package->filePath("mainconfigui");
01030         QFile f(filename);
01031         if (!f.open(QIODevice::ReadOnly)) {
01032             delete dialog;
01033             return;
01034         }
01035 
01036         QWidget *w = loader.load(&f);
01037         f.close();
01038 
01039         dialog->addPage(w, i18n("Settings"), icon(), i18n("%1 Settings", name()));
01040         dialog->show();
01041     }
01042 }
01043 
01044 KPluginInfo::List Applet::knownApplets(const QString &category,
01045                                        const QString &parentApp)
01046 {
01047     QString constraint;
01048 
01049     if (parentApp.isEmpty()) {
01050         constraint.append("not exist [X-KDE-ParentApp]");
01051     } else {
01052         constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'");
01053     }
01054 
01055     if (!category.isEmpty()) {
01056         if (!constraint.isEmpty()) {
01057             constraint.append(" and ");
01058         }
01059 
01060         constraint.append("[X-KDE-PluginInfo-Category] == '").append(category).append("'");
01061         if (category == "Miscellaneous") {
01062             constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')");
01063         }
01064     }
01065 
01066     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
01067     //kDebug() << "Applet::knownApplets constraint was '" << constraint << "' which got us " << offers.count() << " matches";
01068     return KPluginInfo::fromServices(offers);
01069 }
01070 
01071 KPluginInfo::List Applet::knownAppletsForMimetype(const QString &mimetype)
01072 {
01073     QString constraint = QString("'%1' in MimeTypes").arg(mimetype);
01074     //kDebug() << "knownAppletsForMimetype with" << mimetype << constraint;
01075     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
01076     return KPluginInfo::fromServices(offers);
01077 }
01078 
01079 QStringList Applet::knownCategories(const QString &parentApp, bool visibleOnly)
01080 {
01081     QString constraint = "exist [X-KDE-PluginInfo-Category]";
01082 
01083     if (parentApp.isEmpty()) {
01084         constraint.append(" and not exist [X-KDE-ParentApp]");
01085     } else {
01086         constraint.append(" and [X-KDE-ParentApp] == '").append(parentApp).append("'");
01087     }
01088 
01089     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
01090     QStringList categories;
01091     foreach (const KService::Ptr applet, offers) {
01092         QString appletCategory = applet->property("X-KDE-PluginInfo-Category").toString();
01093         if (visibleOnly && applet->noDisplay()) {
01094             // we don't want to show the hidden category
01095             continue;
01096         }
01097 
01098         //kDebug() << "   and we have " << appletCategory;
01099         if (appletCategory.isEmpty()) {
01100             if (!categories.contains(i18n("Miscellaneous"))) {
01101                 categories << i18n("Miscellaneous");
01102             }
01103         } else  if (!categories.contains(appletCategory)) {
01104             categories << appletCategory;
01105         }
01106     }
01107 
01108     categories.sort();
01109     return categories;
01110 }
01111 
01112 Applet* Applet::loadApplet(const QString& appletName, uint appletId, const QVariantList& args)
01113 {
01114     if (appletName.isEmpty()) {
01115         return 0;
01116     }
01117 
01118     QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(appletName);
01119     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
01120 
01121     if (offers.isEmpty()) {
01122         //TODO: what would be -really- cool is offer to try and download the applet
01123         //      from the network at this point
01124         kDebug() << "Applet::loadApplet: offers is empty for \"" << appletName << "\"";
01125         return 0;
01126     } /* else if (offers.count() > 1) {
01127         kDebug() << "hey! we got more than one! let's blindly take the first one";
01128     } */
01129 
01130     KService::Ptr offer = offers.first();
01131 
01132     if (appletId == 0) {
01133         appletId = Private::nextId();
01134     }
01135 
01136     if (!offer->property("X-Plasma-Language").toString().isEmpty()) {
01137         kDebug() << "we have a script in the language of" << offer->property("X-Plasma-Language").toString();
01138         Applet *applet = new Applet(0, offer->storageId(), appletId);
01139         return applet;
01140     }
01141 
01142     QVariantList allArgs;
01143     allArgs << offer->storageId() << appletId << args;
01144     QString error;
01145     Applet* applet = offer->createInstance<Plasma::Applet>(0, allArgs, &error);
01146 
01147     if (!applet) {
01148         kDebug() << "Couldn't load applet \"" << appletName << "\"! reason given: " << error;
01149     }
01150 
01151     return applet;
01152 }
01153 
01154 Applet* Applet::loadApplet(const KPluginInfo& info, uint appletId, const QVariantList& args)
01155 {
01156     if (!info.isValid()) {
01157         return 0;
01158     }
01159 
01160     return loadApplet(info.pluginName(), appletId, args);
01161 }
01162 
01163 void Applet::setShadowShown(bool shown)
01164 {
01165     //There are various problems with shadows right now:
01166     //
01167     //1) shadows can be seen through translucent areas, which is probably technically correct ubt
01168     //looks odd
01169     //2) the shape of the item odesn't conform to the shape of the standard background, e.g. with
01170     //rounded corners
01171 #ifdef DYNAMIC_SHADOWS
01172     if (shown) {
01173         if (d->shadow) {
01174             d->shadow->setVisible(true);
01175         } else {
01176             d->shadow = new ShadowItem(this);
01177             if (scene()) {
01178                 scene()->addItem(d->shadow);
01179                 d->shadow->show();
01180             }
01181         }
01182     } else {
01183         delete d->shadow;
01184         d->shadow = 0;
01185     }
01186 #else
01187     Q_UNUSED(shown);
01188 #endif
01189 }
01190 
01191 bool Applet::isShadowShown() const
01192 {
01193     return d->shadow && d->shadow->isVisible();
01194 }
01195 
01196 
01197 
01198 QVariant Applet::itemChange(GraphicsItemChange change, const QVariant &value)
01199 {
01200     switch (change) {
01201     case ItemPositionChange:
01202         if (d->shadow) {
01203             d->shadow->adjustPosition();
01204         }
01205         break;
01206     case ItemSceneChange: {
01207         QGraphicsScene *newScene = qvariant_cast<QGraphicsScene*>(value);
01208         if (newScene) {
01209             // checking immutability requires having a working config object
01210             // Applet relies on having a Corona scene to be able to get the
01211             // correct config. so we have to wait until we have the scene,
01212             // otherwise we trigger premature creation of the config objects
01213             QTimer::singleShot(0, this, SLOT(checkImmutability()));
01214         }
01215 
01216         if (d->shadow) {
01217             if (d->shadow->scene()) {
01218                 d->shadow->scene()->removeItem(d->shadow);
01219             }
01220 
01221             if (newScene) {
01222                 newScene->addItem(d->shadow);
01223                 d->shadow->generate();
01224                 d->shadow->adjustPosition();
01225                 d->shadow->show();
01226             }
01227         }
01228     }
01229         break;
01230     case ItemVisibleChange:
01231         if (d->shadow) {
01232             d->shadow->setVisible(isVisible());
01233         }
01234         break;
01235     default:
01236         break;
01237     };
01238 
01239     return Widget::itemChange(change, value);
01240 }
01241 
01242 void Applet::setGeometry(const QRectF& geometry)
01243 {
01244     Plasma::Constraints updatedConstraints(0);
01245 
01246     if (geometry.size().width() > 0 && geometry.size().height() > 0 && size() != geometry.size()) {
01247         prepareGeometryChange();
01248         qreal width = qBound(minimumSize().width(), geometry.size().width(), maximumSize().width());
01249         qreal height = qBound(minimumSize().height(), geometry.size().height(), maximumSize().height());
01250 
01251         setSize(QSizeF(width, height));
01252 
01253         if (layout()) {
01254             layout()->setGeometry(QRectF(QPoint(0, 0), contentSize()));
01255         }
01256 
01257         if (managingLayout()) {
01258             managingLayout()->invalidate();
01259         }
01260 
01261         if (d->background) {
01262             d->background->resize(size());
01263         }
01264         updatedConstraints |= Plasma::SizeConstraint;
01265     }
01266 
01267     if (geometry.topLeft() != pos()) {
01268         setPos(geometry.topLeft());
01269         updatedConstraints |= Plasma::LocationConstraint;
01270     }
01271 
01272     if (updatedConstraints) {
01273         updateConstraints(updatedConstraints);
01274         emit geometryChanged();
01275         update();
01276     }
01277 }
01278 
01279 void Applet::setIsContainment(bool isContainment)
01280 {
01281     d->isContainment = isContainment;
01282 }
01283 
01284 bool Applet::isContainment() const
01285 {
01286     return d->isContainment;
01287 }
01288 
01289 } // Plasma namespace
01290 
01291 #include "applet.moc"

libplasma

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

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
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