Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / libs / plasma / applet.cpp
blobf0c2320d72fe51acc973de9e79594929c5bbff20
1 /*
2 * Copyright 2005 by Aaron Seigo <aseigo@kde.org>
3 * Copyright 2007 by Riccardo Iaconelli <riccardo@kde.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Library General Public License as
7 * published by the Free Software Foundation; either version 2, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details
15 * You should have received a copy of the GNU Library General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "applet.h"
23 #include <cmath>
24 #include <limits>
26 #include <QApplication>
27 #include <QEvent>
28 #include <QFile>
29 #include <QGraphicsLinearLayout>
30 #include <QList>
31 #include <QPainter>
32 #include <QSize>
33 #include <QStyleOptionGraphicsItem>
34 #include <QTextDocument>
35 #include <QTimer>
36 #include <QUiLoader>
37 #include <QGraphicsSceneMouseEvent>
39 #include <KIcon>
40 #include <KColorScheme>
41 #include <KConfigDialog>
42 #include <KDialog>
43 #include <KPluginInfo>
44 #include <KStandardDirs>
45 #include <KService>
46 #include <KServiceTypeTrader>
47 #include <KIconLoader>
49 #include <Solid/PowerManagement>
51 #include "plasma/configxml.h"
52 #include "plasma/containment.h"
53 #include "plasma/corona.h"
54 #include "plasma/dataenginemanager.h"
55 #include "plasma/package.h"
56 #include "plasma/packages_p.h"
57 #include "plasma/plasma.h"
58 #include "plasma/scripting/appletscript.h"
59 #include "plasma/shadowitem_p.h"
60 #include "plasma/svg.h"
61 #include "plasma/svgpanel.h"
62 #include "plasma/theme.h"
63 #include "plasma/view.h"
65 #include "plasma/widgets/widget.h"
66 #include "plasma/widgets/lineedit.h"
67 #include "plasma/widgets/pushbutton.h"
69 //#define DYNAMIC_SHADOWS
70 namespace Plasma
73 class Applet::Private
75 public:
76 Private(KService::Ptr service, int uniqueID)
77 : appletId(uniqueID),
78 appletDescription(service),
79 package(0),
80 background(0),
81 failureText(0),
82 script(0),
83 configXml(0),
84 shadow(0),
85 cachedBackground(0),
86 mainConfig(0),
87 pendingConstraints(NoConstraint),
88 aspectRatioMode(Qt::KeepAspectRatio),
89 kioskImmutable(false),
90 immutable(false),
91 hasConfigurationInterface(false),
92 failed(false),
93 needsConfig(false),
94 isContainment(false),
95 square(false)
97 if (appletId == 0) {
98 appletId = ++s_maxAppletId;
99 } else if (appletId > s_maxAppletId) {
100 s_maxAppletId = appletId;
104 ~Private()
106 foreach ( const QString& engine, loadedEngines ) {
107 DataEngineManager::self()->unload( engine );
109 delete background;
110 delete package;
111 delete configXml;
112 delete shadow;
113 delete cachedBackground;
114 delete mainConfig;
117 void init(Applet* applet)
119 // WARNING: do not access config() OR globalConfig() in this method!
120 // that requires a scene, which is not available at this point
121 applet->setAcceptsHoverEvents(true);
123 if (!appletDescription.isValid()) {
124 applet->setFailedToLaunch(true, i18n("Invalid applet description"));
125 return;
128 QString language = appletDescription.property("X-Plasma-Language").toString();
130 // we have a scripted plasmoid
131 if (!language.isEmpty()) {
132 // find where the Package is
133 QString path = KStandardDirs::locate("data",
134 "plasma/plasmoids/" + appletDescription.pluginName() +
135 "/");
137 if (path.isEmpty()) {
138 applet->setFailedToLaunch(true, i18n("Could not locate the %1 package required for the %2 widget.",
139 appletDescription.pluginName(), appletDescription.name()));
140 } else {
141 // create the package and see if we have something real
142 //kDebug() << "trying for" << path;
143 QString packageFormat = appletDescription.property("X-Plasma-Language").toString();
145 if (packageFormat.isEmpty()) {
146 package = new Package(path, PackageStructure::Ptr(new PlasmoidPackage()));
147 } else {
148 PackageStructure::Ptr structure = PackageStructure::load(packageFormat);
149 structure->setPath(path);
150 package = new Package(path, structure);
153 if (package->isValid()) {
154 // now we try and set up the script engine.
155 // it will be parented to this applet and so will get
156 // deleted when the applet does
158 script = Plasma::loadScriptEngine(language, applet);
159 if (!script) {
160 delete package;
161 package = 0;
162 applet->setFailedToLaunch(true, i18n("Could not create a %1 ScriptEngine for the %2 widget.",
163 language, appletDescription.name()));
165 } else {
166 applet->setFailedToLaunch(true, i18n("Could not open the %1 package required for the %2 widget.",
167 appletDescription.pluginName(), appletDescription.name()));
168 delete package;
169 package = 0;
172 if (package) {
173 setupScriptSupport(applet);
177 applet->setDrawStandardBackground(true);
179 connect(Plasma::Theme::self(), SIGNAL(changed()), applet, SLOT(themeChanged()));
182 // put all setup routines for script here. at this point we can assume that
183 // package exists and that we have a script engin
184 void setupScriptSupport(Applet* applet)
186 Q_ASSERT(package);
187 QString xmlPath = package->filePath("mainconfigxml");
188 if (!xmlPath.isEmpty()) {
189 QFile file(xmlPath);
190 // FIXME: KConfigSkeleton doesn't play well with KConfigGroup =/
191 KConfigGroup config = applet->config();
192 configXml = new ConfigXml(&config, &file);
195 if (!package->filePath("mainconfigui").isEmpty()) {
196 applet->setHasConfigurationInterface(true);
200 QSizeF contentSize(const Applet* q)
202 if (failureText) {
203 return failureText->geometry().size();
206 return q->contentSize();
209 QString instanceName()
211 if (!appletDescription.isValid()) {
212 return QString();
215 return appletDescription.service()->library() + QString::number(appletId);
218 void getBorderSize(int& left , int& top, int &right, int& bottom)
220 if (background) {
221 top = background->marginSize(Plasma::TopMargin);
222 left = background->marginSize(Plasma::LeftMargin);
223 right = background->marginSize(Plasma::RightMargin);
224 bottom = background->marginSize(Plasma::BottomMargin);
225 } else {
226 top = left = right = bottom = 0;
230 void scheduleConstraintsUpdate(Plasma::Constraints c, Applet* applet)
232 if (pendingConstraints == NoConstraint) {
233 QTimer::singleShot(0, applet, SLOT(flushUpdatedConstraints()));
235 pendingConstraints |= c;
238 KConfigGroup* mainConfigGroup(const Applet* q)
240 if (mainConfig) {
241 return mainConfig;
244 if (isContainment) {
245 const Containment *asContainment = qobject_cast<Containment*>(const_cast<Applet*>(q));
246 Q_ASSERT(asContainment);
248 KConfigGroup containmentConfig;
249 //kDebug() << "got a corona, baby?" << (QObject*)asContainment->corona();
250 if (asContainment->corona()) {
251 containmentConfig = KConfigGroup(asContainment->corona()->config(), "Containments");
252 } else {
253 containmentConfig = KConfigGroup(KGlobal::config(), "Containments");
256 mainConfig = new KConfigGroup(&containmentConfig, QString::number(appletId));
257 } else {
258 KConfigGroup appletConfig;
259 if (q->containment()) {
260 appletConfig = q->containment()->config();
261 appletConfig = KConfigGroup(&appletConfig, "Applets");
262 } else {
263 kWarning() << "requesting config for" << q->name() << "without a containment!";
264 appletConfig = KConfigGroup(KGlobal::config(), "Applets");
267 mainConfig = new KConfigGroup(&appletConfig, QString::number(appletId));
270 return mainConfig;
273 void copyEntries(KConfigGroup *source, KConfigGroup *destination)
275 foreach (const QString &group, source->groupList()) {
276 KConfigGroup subSource(source, group);
277 KConfigGroup subDest(destination, group);
278 copyEntries(&subSource, &subDest);
281 QMap<QString, QString> entries = source->entryMap();
282 QMapIterator<QString, QString> it(entries);
283 while (it.hasNext()) {
284 it.next();
285 destination->writeEntry(it.key(), it.value());
289 //TODO: examine the usage of memory here; there's a pretty large
290 // number of members at this point.
291 static uint s_maxAppletId;
292 static uint s_maxZValue;
293 static PackageStructure::Ptr packageStructure;
294 uint appletId;
295 KPluginInfo appletDescription;
296 Package* package;
297 QList<QObject*> watchedForFocus;
298 QList<QGraphicsItem*> watchedForMouseMove;
299 QStringList loadedEngines;
300 Plasma::SvgPanel *background;
301 Plasma::LineEdit *failureText;
302 AppletScript *script;
303 ConfigXml* configXml;
304 ShadowItem* shadow;
305 QPixmap* cachedBackground;
306 KConfigGroup *mainConfig;
307 Plasma::Constraints pendingConstraints;
308 Qt::AspectRatioMode aspectRatioMode;
309 bool kioskImmutable : 1;
310 bool immutable : 1;
311 bool hasConfigurationInterface : 1;
312 bool failed : 1;
313 bool needsConfig : 1;
314 bool isContainment : 1;
315 bool square : 1;
318 uint Applet::Private::s_maxAppletId = 0;
319 uint Applet::Private::s_maxZValue = 0;
320 PackageStructure::Ptr Applet::Private::packageStructure(0);
322 Applet::Applet(QGraphicsItem *parent,
323 const QString& serviceID,
324 uint appletId)
325 : Widget(parent),
326 d(new Private(KService::serviceByStorageId(serviceID), appletId))
328 // WARNING: do not access config() OR globalConfig() in this method!
329 // that requires a scene, which is not available at this point
330 d->init(this);
333 Applet::Applet(QObject* parentObject, const QVariantList& args)
334 : Widget(dynamic_cast<QGraphicsItem*>(parentObject)),
335 d(new Private(KService::serviceByStorageId(args.count() > 0 ? args[0].toString() : QString()),
336 args.count() > 1 ? args[1].toInt() : 0))
338 // WARNING: do not access config() OR globalConfig() in this method!
339 // that requires a scene, which is not available at this point
340 d->init(this);
341 // the brain damage seen in the initialization list is due to the
342 // inflexibility of KService::createInstance
345 Applet::~Applet()
347 needsFocus(false);
348 delete d;
351 PackageStructure::Ptr Applet::packageStructure()
353 if (!Private::packageStructure) {
354 Private::packageStructure = new PlasmoidPackage();
357 return Private::packageStructure;
360 void Applet::init()
362 if (d->script && !d->script->init()) {
363 setFailedToLaunch(true, i18n("Script initialization failed"));
367 uint Applet::id() const
369 return d->appletId;
372 void Applet::save(KConfigGroup* group) const
374 // we call the dptr member directly for locked since isImmutable()
375 // also checks kiosk and parent containers
376 group->writeEntry("locked", d->immutable);
377 group->writeEntry("plugin", pluginName());
378 //FIXME: for containments, we need to have some special values here w/regards to
379 // screen affinity (e.g. "bottom of screen 0")
380 //kDebug() << pluginName() << "geometry is" << geometry() << "pos is" << pos() << "bounding rect is" << boundingRect();
381 group->writeEntry("geometry", geometry());
382 group->writeEntry("zvalue", zValue());
384 if (transform() == QTransform()) {
385 group->deleteEntry("transform");
386 } else {
387 QList<qreal> m;
388 QTransform t = transform();
389 m << t.m11() << t.m12() << t.m13() << t.m21() << t.m22() << t.m23() << t.m31() << t.m32() << t.m33();
390 group->writeEntry("transform", m);
391 //group->writeEntry("transform", transformToString(transform()));
394 KConfigGroup appletConfigGroup(group, "Configuration");
395 //FIXME: we need a global save state too
396 saveState(&appletConfigGroup);
399 void Applet::restore(KConfigGroup *c)
401 QList<qreal> m = c->readEntry("transform", QList<qreal>());
402 if (m.count() == 9) {
403 QTransform t(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
404 setTransform(t);
407 qreal z = c->readEntry("zvalue", -1);
409 if (z < 0) {
410 z = ++Private::s_maxZValue;
411 } else if (z >= Private::s_maxZValue) {
412 Private::s_maxZValue = z;
415 setZValue(z);
417 if (c->readEntry("locked", false)) {
418 setImmutable(true);
422 void Applet::saveState(KConfigGroup* group) const
424 if (group->config()->name() != config().config()->name()) {
425 // we're being saved to a different file!
426 // let's just copy the current values in our configuration over
427 KConfigGroup c = config();
428 d->copyEntries(&c, group);
431 Q_UNUSED(group)
434 KConfigGroup Applet::config(const QString &group) const
436 KConfigGroup cg = config();
437 return KConfigGroup(&cg, group);
440 KConfigGroup Applet::config() const
442 if (d->isContainment) {
443 return *(d->mainConfigGroup(this));
446 return KConfigGroup(d->mainConfigGroup(this), "Configuration");
449 KConfigGroup Applet::globalConfig() const
451 KConfigGroup globalAppletConfig;
452 const Containment *c = isContainment() ? dynamic_cast<const Containment*>(this) : containment();
453 QString group = isContainment() ? "ContainmentGlobals" : "AppletGlobals";
455 if (c && c->corona()) {
456 KSharedConfig::Ptr coronaConfig = c->corona()->config();
457 globalAppletConfig = KConfigGroup(coronaConfig, group);
458 } else {
459 globalAppletConfig = KConfigGroup(KGlobal::config(), group);
462 return KConfigGroup(&globalAppletConfig, globalName());
465 void Applet::destroy()
467 //kDebug() << "???????????????? DESTROYING APPLET" << name() << " ???????????????????????????";
468 if (d->configXml) {
469 d->configXml->setDefaults();
472 resetConfigurationObject();
473 deleteLater();
476 void Applet::resetConfigurationObject()
478 d->mainConfigGroup(this)->deleteGroup();
479 delete d->mainConfig;
480 d->mainConfig = 0;
483 ConfigXml* Applet::configXml() const
485 return d->configXml;
488 DataEngine* Applet::dataEngine(const QString& name) const
490 int index = d->loadedEngines.indexOf(name);
491 if (index != -1) {
492 return DataEngineManager::self()->get(name);
495 DataEngine* engine = DataEngineManager::self()->load(name);
496 if (engine->isValid()) {
497 d->loadedEngines.append(name);
500 return engine;
503 const Package* Applet::package() const
505 return d->package;
508 void Applet::updateConstraints(Plasma::Constraints constraints)
510 d->scheduleConstraintsUpdate(constraints, this);
513 void Applet::constraintsUpdated(Plasma::Constraints constraints)
515 //NOTE: do NOT put any code in here that reacts to constraints updates
516 // as it will not get called for any applet that reimplements constraintsUpdated
517 // without calling the Applet:: version as well, which it shouldn't need to.
518 // INSTEAD put such code into flushUpdatedConstraints
519 Q_UNUSED(constraints)
520 //kDebug() << constraints << "constraints are FormFactor: " << formFactor() << ", Location: " << location();
523 QString Applet::name() const
525 if (!d->appletDescription.isValid()) {
526 return i18n("Unknown Applet");
529 return d->appletDescription.name();
532 QString Applet::icon() const
534 if (!d->appletDescription.isValid()) {
535 return QString();
538 return d->appletDescription.icon();
541 QString Applet::pluginName() const
543 if (!d->appletDescription.isValid()) {
544 return QString();
547 return d->appletDescription.pluginName();
550 bool Applet::shouldConserveResources() const
552 return Solid::PowerManagement::appShouldConserveResources();
555 QString Applet::category() const
557 if (!d->appletDescription.isValid()) {
558 return i18n("Miscellaneous");
561 return d->appletDescription.category();
564 QString Applet::category(const KPluginInfo& applet)
566 return applet.property("X-KDE-PluginInfo-Category").toString();
569 QString Applet::category(const QString& appletName)
571 if (appletName.isEmpty()) {
572 return QString();
575 QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(appletName);
576 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
578 if (offers.isEmpty()) {
579 return QString();
582 return offers.first()->property("X-KDE-PluginInfo-Category").toString();
585 bool Applet::isImmutable() const
587 return d->immutable || d->kioskImmutable ||
588 (containment() && containment()->isImmutable()) ||
589 (dynamic_cast<Corona*>( scene() ) && static_cast<Corona*>(scene())->isImmutable());
592 bool Applet::isKioskImmutable() const
594 return d->kioskImmutable;
597 void Applet::setImmutable(bool immutable)
599 if (d->immutable == immutable ||
600 (immutable && d->kioskImmutable)) {
601 return;
604 d->immutable = immutable;
605 // TODO: should we tell the applets too?
606 updateConstraints(ImmutableConstraint);
609 bool Applet::drawStandardBackground() const
611 return d->background != 0;
614 void Applet::setDrawStandardBackground(bool drawBackground)
616 if (drawBackground) {
617 if (!d->background) {
618 d->background = new Plasma::SvgPanel("widgets/background");
620 int left, top, right, bottom;
621 d->getBorderSize(left, top, right, bottom);
622 setContentsMargins(0, 0, right, bottom);
623 updateGeometry();
625 } else if (d->background) {
626 delete d->background;
627 d->background = 0;
628 setContentsMargins(0, 0, 0, 0);
629 updateGeometry();
633 bool Applet::failedToLaunch() const
635 return d->failed;
638 QString visibleFailureText(const QString& reason)
640 QString text;
642 if (reason.isEmpty()) {
643 text = i18n("This object could not be created.");
644 } else {
645 text = i18n("This object could not be created for the following reason:<p><b>%1</b></p>", reason);
648 return text;
651 void Applet::setFailedToLaunch(bool failed, const QString& reason)
653 if (d->failed == failed) {
654 if (d->failureText) {
655 d->failureText->setHtml(visibleFailureText(reason));
656 setGeometry(QRectF(geometry().topLeft(), d->failureText->sizeHint(Qt::PreferredSize)));
658 return;
661 d->failed = failed;
662 prepareGeometryChange();
664 d->failureText = 0;
665 qDeleteAll(QGraphicsItem::children());
666 setLayout(0);
668 if (failed) {
669 setDrawStandardBackground(true);
670 QGraphicsLinearLayout* failureLayout = new QGraphicsLinearLayout(Qt::Vertical, this);
671 failureLayout->setContentsMargins(0, 0, 0, 0);
672 d->failureText = new LineEdit(this);
673 d->failureText->setTextInteractionFlags( Qt::TextSelectableByMouse );
674 d->failureText->setStyled(false);
675 d->failureText->document()->setTextWidth(200);
676 d->failureText->setHtml(visibleFailureText(reason));
677 //FIXME: this needs to get the colour from the theme's colour scheme
678 d->failureText->setDefaultTextColor(KStatefulBrush(KColorScheme::Window,
679 KColorScheme::NormalText,
680 Theme::self()->colors())
681 .brush(QPalette::Normal).color());
682 failureLayout->addItem(d->failureText);
683 setGeometry(QRectF(geometry().topLeft(), d->failureText->sizeHint(Qt::PreferredSize)));
686 update();
689 bool Applet::needsConfiguring() const
691 return d->needsConfig;
694 void Applet::setNeedsConfiguring(bool needsConfig)
696 if (d->needsConfig == needsConfig) {
697 return;
700 d->needsConfig = needsConfig;
701 prepareGeometryChange();
702 qDeleteAll(QGraphicsItem::children());
703 setLayout(0);
705 if (needsConfig) {
706 setDrawStandardBackground(true);
707 QGraphicsLinearLayout* layout = new QGraphicsLinearLayout(Qt::Vertical,this);
708 PushButton* button = new PushButton(this);
709 button->setText(i18n("Configure..."));
710 connect(button, SIGNAL(clicked()), this, SLOT(performSetupConfig()));
711 layout->addItem(button);
715 void Applet::performSetupConfig()
717 qDeleteAll(QGraphicsItem::children());
718 setLayout(0);
719 showConfigurationInterface();
722 void Applet::checkImmutability()
724 d->kioskImmutable = globalConfig().isImmutable() || config().isImmutable() ||
725 (containment() && containment()->isKioskImmutable()) ||
726 (dynamic_cast<Corona*>(scene()) && static_cast<Corona*>(scene())->isKioskImmutable());
728 if (d->kioskImmutable) {
729 updateConstraints(ImmutableConstraint);
733 void Applet::flushUpdatedConstraints()
735 if (d->pendingConstraints == NoConstraint) {
736 return;
739 //kDebug() << "fushing constraints: " << d->pendingConstraints << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
740 Plasma::Constraints c = d->pendingConstraints;
741 d->pendingConstraints = NoConstraint;
743 if (c & Plasma::FormFactorConstraint) {
744 FormFactor f = formFactor();
745 setShadowShown(f == Planar);
746 setDrawStandardBackground(!isContainment() && f != Vertical && f != Horizontal);
749 FIXME: what follows was an attempt to constrain the size of applets. it is, however,
750 broken for the following reasons:
752 * it constrains to the size of an icon, when clearly this is not valid for
753 any non-single-icon applet
754 * it is far too pessimistic for horizontal constraints
756 QSizeF newMax;
757 const QSizeF infSize(std::numeric_limits<qreal>::infinity(),
758 std::numeric_limits<qreal>::infinity());
759 if (f == Plasma::Vertical && !(expandingDirections() & Qt::Vertical)) {
760 newMax = QSizeF(maximumContentSize().width(), IconSize(KIconLoader::Panel));
761 } else if (f == Plasma::Horizontal && !(expandingDirections() & Qt::Horizontal)) {
762 newMax = QSizeF(IconSize(KIconLoader::Panel), maximumContentSize().height());
763 } else if (maximumContentSize() != infSize) {
764 newMax = infSize;
767 if (newMax.isValid()) {
768 setMaximumContentSize(newMax);
769 if (newMax.width() < contentSize().width() ||
770 newMax.height() < contentSize().height()) {
771 updateGeometry();
777 Containment* containment = qobject_cast<Plasma::Containment*>(this);
778 if (isContainment() && containment) {
779 containment->containmentConstraintsUpdated(c);
782 constraintsUpdated(c);
784 if (layout()) {
785 layout()->updateGeometry();
789 void Applet::launchActivated()
791 if (containment()) {
792 containment()->emitLaunchActivated();
796 int Applet::type() const
798 return Type;
801 QRectF Applet::boundingRect() const
803 QRectF rect = QRectF(QPointF(0,0), d->contentSize(this));
805 int left;
806 int right;
807 int top;
808 int bottom;
810 d->getBorderSize(left, top, right, bottom);
811 //kDebug() << "Background , Border size" << d->background << left << top << right << bottom;
812 return rect.adjusted(-left, -top, right, bottom);
815 QPainterPath Applet::shape() const
817 if (isContainment()) {
818 return Plasma::Widget::shape();
821 return Plasma::roundedRectangle(boundingRect().adjusted(-2, -2, 2, 2), 10);
824 QSizeF Applet::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
826 int left = 0;
827 int right = 0;
828 int top = 0;
829 int bottom = 0;
831 d->getBorderSize(left, top, right, bottom);
832 QSizeF borderSize = QSizeF(left + right, top + bottom);
834 //kDebug() << "Applet content size hint: " << contentSizeHint() << "plus our borders" << left << right << top << bottom;
836 return contentSizeHint() + QSizeF(left + right, top + bottom);
839 QList<QAction*> Applet::contextActions()
841 //kDebug() << "empty context actions";
842 return QList<QAction*>();
845 QColor Applet::color() const
847 // TODO: add more colors for more categories and
848 // maybe read from config?
849 QString c = category();
850 int alpha = 200;
851 // Colors taken from Oxygen color palette
852 if (c == "Date and Time") {
853 return QColor(191, 94, 0, alpha);
854 } else if (c == "Environment & Weather") {
855 return QColor(191, 0, 0, alpha);
856 } else if (c == "Examples") {
857 return QColor(204, 0, 154, alpha);
858 } else if (c == "File System") {
859 return QColor(90, 0, 179, alpha);
860 } else if (c == "Graphics") {
861 return QColor(0, 0, 255, alpha);
862 } else if (c == "Language") {
863 return QColor(0, 191, 0, alpha);
864 } else if (c == "Mapping") {
865 return QColor(191, 245, 0, alpha);
866 } else if (c == "Online Services") {
867 return QColor(255, 213, 0, alpha);
868 } else if (c == "System Information") {
869 return QColor(0, 196, 204, alpha);
870 } else if (c == "Windows and Tasks") {
871 return QColor(255, 126, 0, alpha);
872 } else {
873 return QColor(136, 136, 136, alpha);
877 void Applet::paintWidget(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
879 if (d->shadow && d->shadow->shadowedSize() != boundingRect().size()) {
880 //kDebug() << "sizes are " << d->shadow->shadowedSize() << boundingRect().size();
881 d->shadow->generate();
884 painter->save();
885 if (transform().isRotating()) {
886 painter->setRenderHint(QPainter::SmoothPixmapTransform);
887 painter->setRenderHint(QPainter::Antialiasing);
890 if (d->background &&
891 formFactor() != Plasma::Vertical &&
892 formFactor() != Plasma::Horizontal) {
893 //kDebug() << "option rect is" << option->rect;
894 d->background->paint(painter, option->rect);
897 if (!d->failed && !d->needsConfig) {
898 if (widget && isContainment()) {
899 // note that the widget we get is actually the viewport of the view, not the view itself
900 View* v = qobject_cast<Plasma::View*>(widget->parent());
901 if (v && v->drawWallpaper()) {
902 Containment::StyleOption coption(*option);
903 coption.desktop = v->effectiveDesktop();
904 paintInterface(painter, &coption, QRect(QPoint(0,0), d->contentSize(this).toSize()));
907 painter->restore();
908 return;
911 //kDebug() << "paint interface of" << (QObject*) this;
912 paintInterface(painter, option, QRect(QPoint(0,0), d->contentSize(this).toSize()));
915 painter->restore();
918 void Applet::paintInterface(QPainter *painter, const QStyleOptionGraphicsItem *option,
919 const QRect & contentsRect)
921 Q_UNUSED(contentsRect)
923 if (d->script) {
924 d->script->paintInterface(painter, option, contentsRect);
925 } else {
926 //kDebug() << "Applet::paintInterface() default impl";
930 FormFactor Applet::formFactor() const
932 Containment* c = containment();
933 return c ? c->formFactor() : Plasma::Planar;
936 Containment* Applet::containment() const
939 * while this is probably "more correct", much of the code in applet assumes containment
940 * returns zero in the case that this is a containment itself.
941 * if (isContainment()) {
942 return dynamic_cast<Containment*>(const_cast<Applet*>(this));
946 QGraphicsLayoutItem *parent = parentLayoutItem();
947 Containment *c = 0;
949 while (parent) {
950 Containment *possibleC = dynamic_cast<Containment*>(parent);
951 if (possibleC && possibleC->isContainment()) {
952 c = possibleC;
953 break;
955 parent = parent->parentLayoutItem();
958 return c;
961 Location Applet::location() const
963 Containment* c = containment();
965 if (!c) {
966 return Plasma::Desktop;
969 return c->location();
972 QRectF Applet::contentRect() const
974 return QRectF(QPointF(0, 0), contentSize());
977 QSizeF Applet::contentSize() const
979 int top, left, right, bottom;
980 d->getBorderSize(left, top, right, bottom);
982 // kDebug() << "Geometry size: " << geometry().size();
983 // kDebug() << "Borders: " << left << top << right << bottom;
985 return (geometry().size() - QSizeF(left + right, top + bottom)).expandedTo(QSizeF(0, 0));
988 void Applet::setContentSize(const QSizeF &size)
990 int top, left, right, bottom;
991 d->getBorderSize(left, top, right, bottom);
993 resize(size + QSizeF(left + right, top + bottom));
996 void Applet::setContentSize(int width, int height)
998 setContentSize(QSizeF(width, height));
1001 QSizeF Applet::contentSizeHint() const
1003 static bool checkingScript = false;
1005 if (!checkingScript && d->script) {
1006 checkingScript = true;
1007 return d->script->contentSizeHint();
1010 checkingScript = false;
1011 QSizeF size;
1012 if (layout()) {
1013 size = layout()->effectiveSizeHint(Qt::PreferredSize);
1014 } else {
1015 size = contentSize();
1017 //FIXME: This causes infinite recursion in qt code.. maximumContentSize calls
1018 //sizeHint.. which eventually calls contentSizeHint again.
1019 // QSizeF max = maximumContentSize();
1020 // size = size.boundedTo(max);
1021 // if (d->square) {
1022 // //kDebug() << "SizeHintIn: " << (QObject*)this << size;
1023 // switch (formFactor()) {
1024 // case Plasma::Vertical:
1025 // if (size.width() > max.height()) {
1026 // size.setWidth(max.height());
1027 // }
1029 // size.setHeight(size.width());
1030 // case Plasma::Horizontal:
1031 // case Plasma::Planar:
1032 // case Plasma::MediaCenter:
1033 // if (size.height() > max.width()) {
1034 // size.setHeight(max.width());
1035 // }
1037 // size.setWidth(size.height());
1038 // default:
1039 // break;
1040 // }
1042 //kDebug() << "SizeHintOut: " << size;
1043 // return size;
1044 // }
1046 return size;
1049 void Applet::setMinimumContentSize(const QSizeF &minSize)
1051 int top, left, right, bottom;
1052 d->getBorderSize(left, top, right, bottom);
1054 setMinimumSize(minSize + QSizeF(left + right, top + bottom));
1057 void Applet::setMinimumContentSize(int minWidth, int minHeight)
1059 setMinimumContentSize(QSizeF(minWidth, minHeight));
1062 QSizeF Applet::minimumContentSize() const
1064 int top, left, right, bottom;
1065 d->getBorderSize(left, top, right, bottom);
1067 return minimumSize() - QSizeF(left + right, top + bottom);
1070 void Applet::setMaximumContentSize(const QSizeF &maxSize)
1072 int top, left, right, bottom;
1073 d->getBorderSize(left, top, right, bottom);
1075 setMaximumSize(maxSize + QSizeF(left + right, top + bottom));
1078 void Applet::setMaximumContentSize(int maxWidth, int maxHeight)
1080 setMaximumContentSize(QSizeF(maxWidth, maxHeight));
1083 QSizeF Applet::maximumContentSize() const
1085 int top, left, right, bottom;
1086 d->getBorderSize(left, top, right, bottom);
1088 return maximumSize() - QSizeF(left + right, top + bottom);
1091 Qt::AspectRatioMode Applet::aspectRatioMode() const
1093 return d->aspectRatioMode;
1096 void Applet::setAspectRatioMode(Qt::AspectRatioMode mode)
1098 d->aspectRatioMode = mode;
1101 bool Applet::remainSquare() const
1103 return d->square;
1106 void Applet::setRemainSquare(bool square)
1108 d->square = square;
1111 QString Applet::globalName() const
1113 if (!d->appletDescription.isValid()) {
1114 return QString();
1117 return d->appletDescription.service()->library();
1120 QString Applet::instanceName() const
1122 return d->instanceName();
1125 void Applet::watchForFocus(QObject *widget, bool watch)
1127 if (!widget) {
1128 return;
1131 int index = d->watchedForFocus.indexOf(widget);
1132 if (watch) {
1133 if (index == -1) {
1134 d->watchedForFocus.append(widget);
1135 widget->installEventFilter(this);
1137 } else if (index != -1) {
1138 d->watchedForFocus.removeAt(index);
1139 widget->removeEventFilter(this);
1143 void Applet::watchForMouseMove( QGraphicsItem * watched, bool watch )
1145 if (!watched) {
1146 return;
1149 int index = d->watchedForMouseMove.indexOf(watched);
1150 if (watch) {
1151 if (index == -1) {
1152 d->watchedForMouseMove.append(watched);
1153 watched->installSceneEventFilter(this);
1155 } else if (index != -1) {
1156 d->watchedForMouseMove.removeAt(index);
1157 watched->removeSceneEventFilter(this);
1161 void Applet::needsFocus(bool focus)
1163 if (focus == QGraphicsItem::hasFocus()) {
1164 return;
1167 emit requestFocus(focus);
1170 bool Applet::hasConfigurationInterface()
1172 return d->hasConfigurationInterface;
1175 void Applet::setHasConfigurationInterface(bool hasInterface)
1177 d->hasConfigurationInterface = hasInterface;
1180 bool Applet::eventFilter( QObject *o, QEvent * e )
1182 if ( !d->watchedForFocus.contains( o ) )
1184 if ( e->type() == QEvent::MouseButtonRelease ||
1185 e->type() == QEvent::FocusIn ) {
1186 needsFocus( true );
1187 } else if ( e->type() == QEvent::FocusOut ) {
1188 needsFocus( false );
1192 return QObject::eventFilter(o, e);
1195 bool Applet::sceneEventFilter( QGraphicsItem * watched, QEvent * event )
1197 switch (event->type()) {
1198 case QEvent::GraphicsSceneMouseMove: {
1199 if (d->watchedForMouseMove.contains( watched )) {
1200 mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent*>(event));
1201 return true;
1203 break;
1206 default:
1207 break;
1210 return QGraphicsItem::sceneEventFilter(watched, event);
1213 void Applet::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
1215 if (!isImmutable() && formFactor() == Plasma::Planar) {
1216 QGraphicsItem *parent = parentItem();
1217 Plasma::Applet *applet = qgraphicsitem_cast<Plasma::Applet*>(parent);
1219 if (applet && applet->isContainment()) {
1220 // our direct parent is a containment. just move ourselves.
1221 QPointF curPos = event->pos();
1222 QPointF lastPos = event->lastPos();
1223 QPointF delta = curPos-lastPos;
1225 moveBy(delta.x(),delta.y());
1226 } else if (parent) {
1227 //don't move the icon as well because our parent (usually an appletHandle) will do it for us
1228 //parent->moveBy(delta.x(),delta.y());
1229 QPointF curPos = parent->transform().map(event->pos());
1230 QPointF lastPos = parent->transform().map(event->lastPos());
1231 QPointF delta = curPos-lastPos;
1233 parent->setPos(parent->pos() + delta);
1240 void Applet::showConfigurationInterface()
1242 if (d->package && d->configXml) {
1243 QString uiFile = d->package->filePath("mainconfigui");
1244 if (uiFile.isEmpty()) {
1245 return;
1248 KConfigDialog *dialog = new KConfigDialog(0, "", d->configXml);
1249 dialog->setWindowTitle(i18n("%1 Settings", name()));
1250 dialog->setAttribute(Qt::WA_DeleteOnClose, true);
1252 QUiLoader loader;
1253 QFile f(uiFile);
1254 if (!f.open(QIODevice::ReadOnly)) {
1255 delete dialog;
1256 return;
1259 QWidget *w = loader.load(&f);
1260 f.close();
1262 dialog->addPage(w, i18n("Settings"), icon(), i18n("%1 Settings", name()));
1263 dialog->show();
1267 KPluginInfo::List Applet::knownApplets(const QString &category,
1268 const QString &parentApp)
1270 QString constraint;
1272 if (parentApp.isEmpty()) {
1273 constraint.append("not exist [X-KDE-ParentApp]");
1274 } else {
1275 constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'");
1278 if (!category.isEmpty()) {
1279 if (!constraint.isEmpty()) {
1280 constraint.append(" and ");
1283 constraint.append("[X-KDE-PluginInfo-Category] == '").append(category).append("'");
1284 if (category == "Miscellaneous") {
1285 constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')");
1289 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
1290 //kDebug() << "Applet::knownApplets constraint was '" << constraint << "' which got us " << offers.count() << " matches";
1291 return KPluginInfo::fromServices(offers);
1294 KPluginInfo::List Applet::knownAppletsForMimetype(const QString &mimetype)
1296 QString constraint = QString("'%1' in MimeTypes").arg(mimetype);
1297 //kDebug() << "knownAppletsForMimetype with" << mimetype << constraint;
1298 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
1299 return KPluginInfo::fromServices(offers);
1302 QStringList Applet::knownCategories(const QString &parentApp, bool visibleOnly)
1304 QString constraint = "exist [X-KDE-PluginInfo-Category]";
1306 if (parentApp.isEmpty()) {
1307 constraint.append(" and not exist [X-KDE-ParentApp]");
1308 } else {
1309 constraint.append(" and [X-KDE-ParentApp] == '").append(parentApp).append("'");
1312 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
1313 QStringList categories;
1314 foreach (const KService::Ptr applet, offers) {
1315 QString appletCategory = applet->property("X-KDE-PluginInfo-Category").toString();
1316 if (visibleOnly && applet->noDisplay()) {
1317 // we don't want to show the hidden category
1318 continue;
1321 //kDebug() << " and we have " << appletCategory;
1322 if (appletCategory.isEmpty()) {
1323 if (!categories.contains(i18n("Miscellaneous"))) {
1324 categories << i18n("Miscellaneous");
1326 } else if (!categories.contains(appletCategory)) {
1327 categories << appletCategory;
1331 categories.sort();
1332 return categories;
1335 Applet* Applet::load(const QString& appletName, uint appletId, const QVariantList& args)
1337 if (appletName.isEmpty()) {
1338 return 0;
1341 QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(appletName);
1342 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
1344 if (offers.isEmpty()) {
1345 //TODO: what would be -really- cool is offer to try and download the applet
1346 // from the network at this point
1347 kDebug() << "offers is empty for " << appletName;
1348 return 0;
1349 } /* else if (offers.count() > 1) {
1350 kDebug() << "hey! we got more than one! let's blindly take the first one";
1351 } */
1353 KService::Ptr offer = offers.first();
1355 if (appletId == 0) {
1356 appletId = ++Private::s_maxAppletId;
1359 if (!offer->property("X-Plasma-Language").toString().isEmpty()) {
1360 kDebug() << "we have a script in the language of" << offer->property("X-Plasma-Language").toString();
1361 Applet *applet = new Applet(0, offer->storageId(), appletId);
1362 return applet;
1365 QVariantList allArgs;
1366 allArgs << offer->storageId() << appletId << args;
1367 QString error;
1368 Applet* applet = offer->createInstance<Plasma::Applet>(0, allArgs, &error);
1370 if (!applet) {
1371 kDebug() << "Couldn't load applet \"" << appletName << "\"! reason given: " << error;
1374 return applet;
1377 Applet* Applet::load(const KPluginInfo& info, uint appletId, const QVariantList& args)
1379 if (!info.isValid()) {
1380 return 0;
1383 return load(info.pluginName(), appletId, args);
1386 void Applet::setShadowShown(bool shown)
1388 //There are various problems with shadows right now:
1390 //1) shadows can be seen through translucent areas, which is probably technically correct ubt
1391 //looks odd
1392 //2) the shape of the item odesn't conform to the shape of the standard background, e.g. with
1393 //rounded corners
1394 #ifdef DYNAMIC_SHADOWS
1395 if (shown) {
1396 if (d->shadow) {
1397 d->shadow->setVisible(true);
1398 } else {
1399 d->shadow = new ShadowItem(this);
1400 if (scene()) {
1401 scene()->addItem(d->shadow);
1402 d->shadow->show();
1405 } else {
1406 delete d->shadow;
1407 d->shadow = 0;
1409 #else
1410 Q_UNUSED(shown);
1411 #endif
1414 bool Applet::isShadowShown() const
1416 return d->shadow && d->shadow->isVisible();
1419 QPointF Applet::topLeft() const
1421 return boundingRect().topLeft();
1424 QVariant Applet::itemChange(GraphicsItemChange change, const QVariant &value)
1426 switch (change) {
1427 case ItemPositionChange:
1428 if (d->shadow) {
1429 d->shadow->adjustPosition();
1431 break;
1432 case ItemSceneChange: {
1433 QGraphicsScene *newScene = qvariant_cast<QGraphicsScene*>(value);
1434 if (newScene) {
1435 // checking immutability requires having a working config object
1436 // Applet relies on having a Corona scene to be able to get the
1437 // correct config. so we have to wait until we have the scene,
1438 // otherwise we trigger premature creation of the config objects
1439 QTimer::singleShot(0, this, SLOT(checkImmutability()));
1442 if (d->shadow) {
1443 if (d->shadow->scene()) {
1444 d->shadow->scene()->removeItem(d->shadow);
1447 if (newScene) {
1448 newScene->addItem(d->shadow);
1449 d->shadow->generate();
1450 d->shadow->adjustPosition();
1451 d->shadow->show();
1455 break;
1456 case ItemVisibleChange:
1457 if (d->shadow) {
1458 d->shadow->setVisible(isVisible());
1460 break;
1461 default:
1462 break;
1465 return Widget::itemChange(change, value);
1468 void Applet::setGeometry(const QRectF& geometry)
1470 Plasma::Constraints updatedConstraints(0);
1471 QSizeF s = size();
1472 QPointF p = pos();
1474 Widget::setGeometry(geometry);
1475 //kDebug() << s << size();
1476 if (s != size()) {
1477 if (d->background) {
1478 //kDebug() << "setting background to" << size();
1479 d->background->resize(size());
1482 updatedConstraints |= Plasma::SizeConstraint;
1485 if (p != pos()) {
1486 updatedConstraints |= Plasma::LocationConstraint;
1489 if (updatedConstraints) {
1490 updateConstraints(updatedConstraints);
1491 emit geometryChanged();
1495 void Applet::raise()
1497 setZValue(++Private::s_maxZValue);
1500 void Applet::setIsContainment(bool isContainment)
1502 d->isContainment = isContainment;
1505 bool Applet::isContainment() const
1507 return d->isContainment;
1510 void Applet::themeChanged()
1512 update();
1515 } // Plasma namespace
1517 #include "applet.moc"