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.
26 #include <QApplication>
29 #include <QGraphicsLinearLayout>
33 #include <QStyleOptionGraphicsItem>
34 #include <QTextDocument>
37 #include <QGraphicsSceneMouseEvent>
40 #include <KColorScheme>
41 #include <KConfigDialog>
43 #include <KPluginInfo>
44 #include <KStandardDirs>
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
76 Private(KService::Ptr service
, int uniqueID
)
78 appletDescription(service
),
87 pendingConstraints(NoConstraint
),
88 aspectRatioMode(Qt::KeepAspectRatio
),
89 kioskImmutable(false),
91 hasConfigurationInterface(false),
98 appletId
= ++s_maxAppletId
;
99 } else if (appletId
> s_maxAppletId
) {
100 s_maxAppletId
= appletId
;
106 foreach ( const QString
& engine
, loadedEngines
) {
107 DataEngineManager::self()->unload( engine
);
113 delete cachedBackground
;
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"));
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() +
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()));
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()));
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
);
162 applet
->setFailedToLaunch(true, i18n("Could not create a %1 ScriptEngine for the %2 widget.",
163 language
, appletDescription
.name()));
166 applet
->setFailedToLaunch(true, i18n("Could not open the %1 package required for the %2 widget.",
167 appletDescription
.pluginName(), appletDescription
.name()));
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
)
187 QString xmlPath
= package
->filePath("mainconfigxml");
188 if (!xmlPath
.isEmpty()) {
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
)
203 return failureText
->geometry().size();
206 return q
->contentSize();
209 QString
instanceName()
211 if (!appletDescription
.isValid()) {
215 return appletDescription
.service()->library() + QString::number(appletId
);
218 void getBorderSize(int& left
, int& top
, int &right
, int& bottom
)
221 top
= background
->marginSize(Plasma::TopMargin
);
222 left
= background
->marginSize(Plasma::LeftMargin
);
223 right
= background
->marginSize(Plasma::RightMargin
);
224 bottom
= background
->marginSize(Plasma::BottomMargin
);
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
)
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");
253 containmentConfig
= KConfigGroup(KGlobal::config(), "Containments");
256 mainConfig
= new KConfigGroup(&containmentConfig
, QString::number(appletId
));
258 KConfigGroup appletConfig
;
259 if (q
->containment()) {
260 appletConfig
= q
->containment()->config();
261 appletConfig
= KConfigGroup(&appletConfig
, "Applets");
263 kWarning() << "requesting config for" << q
->name() << "without a containment!";
264 appletConfig
= KConfigGroup(KGlobal::config(), "Applets");
267 mainConfig
= new KConfigGroup(&appletConfig
, QString::number(appletId
));
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()) {
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
;
295 KPluginInfo appletDescription
;
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
;
305 QPixmap
* cachedBackground
;
306 KConfigGroup
*mainConfig
;
307 Plasma::Constraints pendingConstraints
;
308 Qt::AspectRatioMode aspectRatioMode
;
309 bool kioskImmutable
: 1;
311 bool hasConfigurationInterface
: 1;
313 bool needsConfig
: 1;
314 bool isContainment
: 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
,
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
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
341 // the brain damage seen in the initialization list is due to the
342 // inflexibility of KService::createInstance
351 PackageStructure::Ptr
Applet::packageStructure()
353 if (!Private::packageStructure
) {
354 Private::packageStructure
= new PlasmoidPackage();
357 return Private::packageStructure
;
362 if (d
->script
&& !d
->script
->init()) {
363 setFailedToLaunch(true, i18n("Script initialization failed"));
367 uint
Applet::id() const
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");
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]);
407 qreal z
= c
->readEntry("zvalue", -1);
410 z
= ++Private::s_maxZValue
;
411 } else if (z
>= Private::s_maxZValue
) {
412 Private::s_maxZValue
= z
;
417 if (c
->readEntry("locked", false)) {
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
);
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
);
459 globalAppletConfig
= KConfigGroup(KGlobal::config(), group
);
462 return KConfigGroup(&globalAppletConfig
, globalName());
465 void Applet::destroy()
467 //kDebug() << "???????????????? DESTROYING APPLET" << name() << " ???????????????????????????";
469 d
->configXml
->setDefaults();
472 resetConfigurationObject();
476 void Applet::resetConfigurationObject()
478 d
->mainConfigGroup(this)->deleteGroup();
479 delete d
->mainConfig
;
483 ConfigXml
* Applet::configXml() const
488 DataEngine
* Applet::dataEngine(const QString
& name
) const
490 int index
= d
->loadedEngines
.indexOf(name
);
492 return DataEngineManager::self()->get(name
);
495 DataEngine
* engine
= DataEngineManager::self()->load(name
);
496 if (engine
->isValid()) {
497 d
->loadedEngines
.append(name
);
503 const Package
* Applet::package() const
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()) {
538 return d
->appletDescription
.icon();
541 QString
Applet::pluginName() const
543 if (!d
->appletDescription
.isValid()) {
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()) {
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()) {
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
)) {
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
);
625 } else if (d
->background
) {
626 delete d
->background
;
628 setContentsMargins(0, 0, 0, 0);
633 bool Applet::failedToLaunch() const
638 QString
visibleFailureText(const QString
& reason
)
642 if (reason
.isEmpty()) {
643 text
= i18n("This object could not be created.");
645 text
= i18n("This object could not be created for the following reason:<p><b>%1</b></p>", reason
);
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
)));
662 prepareGeometryChange();
665 qDeleteAll(QGraphicsItem::children());
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
)));
689 bool Applet::needsConfiguring() const
691 return d
->needsConfig
;
694 void Applet::setNeedsConfiguring(bool needsConfig
)
696 if (d
->needsConfig
== needsConfig
) {
700 d
->needsConfig
= needsConfig
;
701 prepareGeometryChange();
702 qDeleteAll(QGraphicsItem::children());
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());
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
) {
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
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) {
767 if (newMax.isValid()) {
768 setMaximumContentSize(newMax);
769 if (newMax.width() < contentSize().width() ||
770 newMax.height() < contentSize().height()) {
777 Containment
* containment
= qobject_cast
<Plasma::Containment
*>(this);
778 if (isContainment() && containment
) {
779 containment
->containmentConstraintsUpdated(c
);
782 constraintsUpdated(c
);
785 layout()->updateGeometry();
789 void Applet::launchActivated()
792 containment()->emitLaunchActivated();
796 int Applet::type() const
801 QRectF
Applet::boundingRect() const
803 QRectF rect
= QRectF(QPointF(0,0), d
->contentSize(this));
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
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();
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
);
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();
885 if (transform().isRotating()) {
886 painter
->setRenderHint(QPainter::SmoothPixmapTransform
);
887 painter
->setRenderHint(QPainter::Antialiasing
);
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()));
911 //kDebug() << "paint interface of" << (QObject*) this;
912 paintInterface(painter
, option
, QRect(QPoint(0,0), d
->contentSize(this).toSize()));
918 void Applet::paintInterface(QPainter
*painter
, const QStyleOptionGraphicsItem
*option
,
919 const QRect
& contentsRect
)
921 Q_UNUSED(contentsRect
)
924 d
->script
->paintInterface(painter
, option
, contentsRect
);
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();
950 Containment
*possibleC
= dynamic_cast<Containment
*>(parent
);
951 if (possibleC
&& possibleC
->isContainment()) {
955 parent
= parent
->parentLayoutItem();
961 Location
Applet::location() const
963 Containment
* c
= containment();
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;
1013 size
= layout()->effectiveSizeHint(Qt::PreferredSize
);
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);
1022 // //kDebug() << "SizeHintIn: " << (QObject*)this << size;
1023 // switch (formFactor()) {
1024 // case Plasma::Vertical:
1025 // if (size.width() > max.height()) {
1026 // size.setWidth(max.height());
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());
1037 // size.setWidth(size.height());
1042 //kDebug() << "SizeHintOut: " << 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
1106 void Applet::setRemainSquare(bool square
)
1111 QString
Applet::globalName() const
1113 if (!d
->appletDescription
.isValid()) {
1117 return d
->appletDescription
.service()->library();
1120 QString
Applet::instanceName() const
1122 return d
->instanceName();
1125 void Applet::watchForFocus(QObject
*widget
, bool watch
)
1131 int index
= d
->watchedForFocus
.indexOf(widget
);
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
)
1149 int index
= d
->watchedForMouseMove
.indexOf(watched
);
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()) {
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
) {
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
));
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()) {
1248 KConfigDialog
*dialog
= new KConfigDialog(0, "", d
->configXml
);
1249 dialog
->setWindowTitle(i18n("%1 Settings", name()));
1250 dialog
->setAttribute(Qt::WA_DeleteOnClose
, true);
1254 if (!f
.open(QIODevice::ReadOnly
)) {
1259 QWidget
*w
= loader
.load(&f
);
1262 dialog
->addPage(w
, i18n("Settings"), icon(), i18n("%1 Settings", name()));
1267 KPluginInfo::List
Applet::knownApplets(const QString
&category
,
1268 const QString
&parentApp
)
1272 if (parentApp
.isEmpty()) {
1273 constraint
.append("not exist [X-KDE-ParentApp]");
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]");
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
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
;
1335 Applet
* Applet::load(const QString
& appletName
, uint appletId
, const QVariantList
& args
)
1337 if (appletName
.isEmpty()) {
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
;
1349 } /* else if (offers.count() > 1) {
1350 kDebug() << "hey! we got more than one! let's blindly take the first one";
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
);
1365 QVariantList allArgs
;
1366 allArgs
<< offer
->storageId() << appletId
<< args
;
1368 Applet
* applet
= offer
->createInstance
<Plasma::Applet
>(0, allArgs
, &error
);
1371 kDebug() << "Couldn't load applet \"" << appletName
<< "\"! reason given: " << error
;
1377 Applet
* Applet::load(const KPluginInfo
& info
, uint appletId
, const QVariantList
& args
)
1379 if (!info
.isValid()) {
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
1392 //2) the shape of the item odesn't conform to the shape of the standard background, e.g. with
1394 #ifdef DYNAMIC_SHADOWS
1397 d
->shadow
->setVisible(true);
1399 d
->shadow
= new ShadowItem(this);
1401 scene()->addItem(d
->shadow
);
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
)
1427 case ItemPositionChange
:
1429 d
->shadow
->adjustPosition();
1432 case ItemSceneChange
: {
1433 QGraphicsScene
*newScene
= qvariant_cast
<QGraphicsScene
*>(value
);
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()));
1443 if (d
->shadow
->scene()) {
1444 d
->shadow
->scene()->removeItem(d
->shadow
);
1448 newScene
->addItem(d
->shadow
);
1449 d
->shadow
->generate();
1450 d
->shadow
->adjustPosition();
1456 case ItemVisibleChange
:
1458 d
->shadow
->setVisible(isVisible());
1465 return Widget::itemChange(change
, value
);
1468 void Applet::setGeometry(const QRectF
& geometry
)
1470 Plasma::Constraints
updatedConstraints(0);
1474 Widget::setGeometry(geometry
);
1475 //kDebug() << s << size();
1477 if (d
->background
) {
1478 //kDebug() << "setting background to" << size();
1479 d
->background
->resize(size());
1482 updatedConstraints
|= Plasma::SizeConstraint
;
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()
1515 } // Plasma namespace
1517 #include "applet.moc"