1/*
2 * Copyright 2005 by Aaron Seigo <aseigo@kde.org>
3 * Copyright 2007 by Riccardo Iaconelli <riccardo@kde.org>
4 * Copyright 2008 by Ménard Alexis <darktears31@gmail.com>
5 * Copyright (c) 2009 Chani Armitage <chani@kde.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Library General Public License as
9 * published by the Free Software Foundation; either version 2, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this program; if not, write to the
19 * Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22
23#include "applet.h"
24#include "private/applet_p.h"
25
26#include "config-plasma.h"
27
28#include <plasma/animations/animation.h>
29
30#include <cmath>
31#include <limits>
32
33#include <QApplication>
34#include <QEvent>
35#include <QFile>
36#include <QGraphicsGridLayout>
37#include <QGraphicsSceneMouseEvent>
38#include <QGraphicsView>
39#include <QHostInfo>
40#include <QLabel>
41#include <QList>
42#include <QGraphicsLinearLayout>
43#include <QPainter>
44#include <QRegExp>
45#include <QSize>
46#include <QStyleOptionGraphicsItem>
47#include <QTextDocument>
48#include <QUiLoader>
49#include <QVBoxLayout>
50#include <QWidget>
51
52#include <kaction.h>
53#include <kactioncollection.h>
54#include <kauthorized.h>
55#include <kcolorscheme.h>
56#include <kdialog.h>
57#include <kdesktopfile.h>
58#include <kicon.h>
59#include <kiconloader.h>
60#include <kkeysequencewidget.h>
61#include <kplugininfo.h>
62#include <kstandarddirs.h>
63#include <kservice.h>
64#include <kservicetypetrader.h>
65#include <kshortcut.h>
66#include <kwindowsystem.h>
67#include <kpushbutton.h>
68
69#ifndef PLASMA_NO_KUTILS
70#include <kcmoduleinfo.h>
71#include <kcmoduleproxy.h>
72#else
73#include <kcmodule.h>
74#endif
75
76#ifndef PLASMA_NO_SOLID
77#include <solid/powermanagement.h>
78#endif
79
80#include "abstracttoolbox.h"
81#include "authorizationmanager.h"
82#include "authorizationrule.h"
83#include "configloader.h"
84#include "containment.h"
85#include "corona.h"
86#include "dataenginemanager.h"
87#include "dialog.h"
88#include "extenders/extender.h"
89#include "extenders/extenderitem.h"
90#include "package.h"
91#include "plasma.h"
92#include "scripting/appletscript.h"
93#include "svg.h"
94#include "framesvg.h"
95#include "popupapplet.h"
96#include "private/applethandle_p.h"
97#include "private/extenderitem_p.h"
98#include "private/framesvg_p.h"
99#include "theme.h"
100#include "view.h"
101#include "widgets/iconwidget.h"
102#include "widgets/label.h"
103#include "widgets/pushbutton.h"
104#include "widgets/busywidget.h"
105#include "tooltipmanager.h"
106#include "wallpaper.h"
107#include "paintutils.h"
108#include "abstractdialogmanager.h"
109#include "pluginloader.h"
110
111#include "private/associatedapplicationmanager_p.h"
112#include "private/authorizationmanager_p.h"
113#include "private/containment_p.h"
114#include "private/extenderapplet_p.h"
115#include "private/package_p.h"
116#include "private/packages_p.h"
117#include "private/plasmoidservice_p.h"
118#include "private/popupapplet_p.h"
119#include "private/remotedataengine_p.h"
120#include "private/service_p.h"
121#include "ui_publish.h"
122
123
124namespace Plasma
125{
126
127Applet::Applet(const KPluginInfo &info, QGraphicsItem *parent, uint appletId)
128 : QGraphicsWidget(parent),
129 d(new AppletPrivate(KService::Ptr(), &info, appletId, this))
130{
131 // WARNING: do not access config() OR globalConfig() in this method!
132 // that requires a scene, which is not available at this point
133 d->init();
134}
135
136Applet::Applet(QGraphicsItem *parent, const QString &serviceID, uint appletId)
137 : QGraphicsWidget(parent),
138 d(new AppletPrivate(KService::serviceByStorageId(serviceID), 0, appletId, this))
139{
140 // WARNING: do not access config() OR globalConfig() in this method!
141 // that requires a scene, which is not available at this point
142 d->init();
143}
144
145Applet::Applet(QGraphicsItem *parent,
146 const QString &serviceID,
147 uint appletId,
148 const QVariantList &args)
149 : QGraphicsWidget(parent),
150 d(new AppletPrivate(KService::serviceByStorageId(serviceID), 0, appletId, this))
151{
152 // WARNING: do not access config() OR globalConfig() in this method!
153 // that requires a scene, which is not available at this point
154
155 QVariantList &mutableArgs = const_cast<QVariantList &>(args);
156 if (!mutableArgs.isEmpty()) {
157 mutableArgs.removeFirst();
158
159 if (!mutableArgs.isEmpty()) {
160 mutableArgs.removeFirst();
161 }
162 }
163
164 d->args = mutableArgs;
165
166 d->init();
167}
168
169Applet::Applet(QObject *parentObject, const QVariantList &args)
170 : QGraphicsWidget(0),
171 d(new AppletPrivate(
172 KService::serviceByStorageId(args.count() > 0 ? args[0].toString() : QString()), 0,
173 args.count() > 1 ? args[1].toInt() : 0, this))
174{
175 // now remove those first two items since those are managed by Applet and subclasses shouldn't
176 // need to worry about them. yes, it violates the constness of this var, but it lets us add
177 // or remove items later while applets can just pretend that their args always start at 0
178 QVariantList &mutableArgs = const_cast<QVariantList &>(args);
179 if (!mutableArgs.isEmpty()) {
180 mutableArgs.removeFirst();
181
182 if (!mutableArgs.isEmpty()) {
183 mutableArgs.removeFirst();
184 }
185 }
186
187 d->args = mutableArgs;
188
189 setParent(parentObject);
190
191 // WARNING: do not access config() OR globalConfig() in this method!
192 // that requires a scene, which is not available at this point
193 d->init();
194
195 // the brain damage seen in the initialization list is due to the
196 // inflexibility of KService::createInstance
197}
198
199Applet::Applet(const QString &packagePath, uint appletId, const QVariantList &args)
200 : QGraphicsWidget(0),
201 d(new AppletPrivate(KService::Ptr(new KService(packagePath + "/metadata.desktop")), 0, appletId, this))
202{
203 Q_UNUSED(args) // FIXME?
204 d->init(packagePath);
205}
206
207Applet::~Applet()
208{
209 //let people know that i will die
210 emit appletDestroyed(this);
211
212 if (!d->transient && d->extender) {
213 //This would probably be nicer if it was located in extender. But in it's dtor, this won't
214 //work since when that get's called, the applet's config() isn't accessible anymore. (same
215 //problem with calling saveState(). Doing this in saveState() might be a possibility, but
216 //that would require every extender savestate implementation to call it's parent function,
217 //which isn't very nice.
218 d->extender.data()->saveState();
219
220 foreach (ExtenderItem *item, d->extender.data()->attachedItems()) {
221 if (item->autoExpireDelay()) {
222 //destroy temporary extender items, or items that aren't detached, so their
223 //configuration won't linger after a plasma restart.
224 item->destroy();
225 }
226 }
227 }
228
229 // clean up our config dialog, if any
230 delete KConfigDialog::exists(d->configDialogId());
231 delete d;
232}
233
234PackageStructure::Ptr Applet::packageStructure()
235{
236 if (!AppletPrivate::packageStructure) {
237 AppletPrivate::packageStructure = new PlasmoidPackage();
238 }
239
240 return AppletPrivate::packageStructure;
241}
242
243void Applet::init()
244{
245 setFlag(ItemIsMovable, true);
246 if (d->script) {
247 d->setupScriptSupport();
248
249 if (!d->script->init() && !d->failed) {
250 setFailedToLaunch(true, i18n("Script initialization failed"));
251 }
252 }
253}
254
255uint Applet::id() const
256{
257 return d->appletId;
258}
259
260void Applet::save(KConfigGroup &g) const
261{
262 if (d->transient) {
263 return;
264 }
265
266 KConfigGroup group = g;
267 if (!group.isValid()) {
268 group = *d->mainConfigGroup();
269 }
270
271 //kDebug() << "saving" << pluginName() << "to" << group.name();
272 // we call the dptr member directly for locked since isImmutable()
273 // also checks kiosk and parent containers
274 group.writeEntry("immutability", (int)d->immutability);
275 group.writeEntry("plugin", pluginName());
276
277 group.writeEntry("geometry", geometry());
278 group.writeEntry("zvalue", zValue());
279
280 if (!d->started) {
281 return;
282 }
283
284 //FIXME: for containments, we need to have some special values here w/regards to
285 // screen affinity (e.g. "bottom of screen 0")
286 //kDebug() << pluginName() << "geometry is" << geometry()
287 // << "pos is" << pos() << "bounding rect is" << boundingRect();
288 if (transform() == QTransform()) {
289 group.deleteEntry("transform");
290 } else {
291 QList<qreal> m;
292 QTransform t = transform();
293 m << t.m11() << t.m12() << t.m13() << t.m21() << t.m22() << t.m23() << t.m31() << t.m32() << t.m33();
294 group.writeEntry("transform", m);
295 //group.writeEntry("transform", transformToString(transform()));
296 }
297
298 KConfigGroup appletConfigGroup(&group, "Configuration");
299 saveState(appletConfigGroup);
300
301 if (d->configLoader) {
302 // we're saving so we know its changed, we don't need or want the configChanged
303 // signal bubbling up at this point due to that
304 disconnect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged()));
305 d->configLoader->writeConfig();
306 connect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged()));
307 }
308}
309
310void Applet::restore(KConfigGroup &group)
311{
312 QList<qreal> m = group.readEntry("transform", QList<qreal>());
313 if (m.count() == 9) {
314 QTransform t(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
315 setTransform(t);
316 }
317
318 qreal z = group.readEntry("zvalue", 0);
319
320 if (z >= AppletPrivate::s_maxZValue) {
321 AppletPrivate::s_maxZValue = z;
322 }
323
324 if (z > 0) {
325 setZValue(z);
326 }
327
328 setImmutability((ImmutabilityType)group.readEntry("immutability", (int)Mutable));
329
330 QRectF geom = group.readEntry("geometry", QRectF());
331 if (geom.isValid()) {
332 setGeometry(geom);
333 }
334
335 KConfigGroup shortcutConfig(&group, "Shortcuts");
336 QString shortcutText = shortcutConfig.readEntryUntranslated("global", QString());
337 if (!shortcutText.isEmpty()) {
338 setGlobalShortcut(KShortcut(shortcutText));
339 /*
340 kDebug() << "got global shortcut for" << name() << "of" << QKeySequence(shortcutText);
341 kDebug() << "set to" << d->activationAction->objectName()
342 << d->activationAction->globalShortcut().primary();
343 */
344 }
345
346 // local shortcut, if any
347 //TODO: implement; the shortcut will need to be registered with the containment
348 /*
349#include "accessmanager.h"
350#include "private/plasmoidservice_p.h"
351#include "authorizationmanager.h"
352#include "authorizationmanager.h"
353 shortcutText = shortcutConfig.readEntryUntranslated("local", QString());
354 if (!shortcutText.isEmpty()) {
355 //TODO: implement; the shortcut
356 }
357 */
358}
359
360void AppletPrivate::setFocus()
361{
362 //kDebug() << "setting focus";
363 q->setFocus(Qt::ShortcutFocusReason);
364}
365
366void Applet::setFailedToLaunch(bool failed, const QString &reason)
367{
368 if (d->failed == failed) {
369 if (failed && !reason.isEmpty()) {
370 foreach (QGraphicsItem *item, QGraphicsItem::children()) {
371 Label *l = dynamic_cast<Label *>(item);
372 if (l) {
373 l->setText(d->visibleFailureText(reason));
374 }
375 }
376 }
377 return;
378 }
379
380 d->failed = failed;
381 prepareGeometryChange();
382
383 foreach (QGraphicsItem *item, childItems()) {
384 if (!dynamic_cast<AppletHandle *>(item)) {
385 delete item;
386 }
387 }
388
389 d->messageOverlay = 0;
390 if (d->messageDialog) {
391 d->messageDialog.data()->deleteLater();
392 d->messageDialog.clear();
393 }
394
395 setLayout(0);
396
397 if (failed) {
398 setBackgroundHints(d->backgroundHints|StandardBackground);
399
400 QGraphicsLinearLayout *failureLayout = new QGraphicsLinearLayout(this);
401 failureLayout->setContentsMargins(0, 0, 0, 0);
402
403 IconWidget *failureIcon = new IconWidget(this);
404 failureIcon->setIcon(KIcon("dialog-error"));
405 failureLayout->addItem(failureIcon);
406
407 Label *failureWidget = new Plasma::Label(this);
408 failureWidget->setText(d->visibleFailureText(reason));
409 QLabel *label = failureWidget->nativeWidget();
410 label->setWordWrap(true);
411 failureLayout->addItem(failureWidget);
412
413 Plasma::ToolTipManager::self()->registerWidget(failureIcon);
414 Plasma::ToolTipContent data(i18n("Unable to load the widget"), reason,
415 KIcon("dialog-error"));
416 Plasma::ToolTipManager::self()->setContent(failureIcon, data);
417
418 setLayout(failureLayout);
419 resize(300, 250);
420 d->background->resizeFrame(geometry().size());
421 }
422
423 update();
424}
425
426void Applet::saveState(KConfigGroup &group) const
427{
428 if (d->script) {
429 emit d->script->saveState(group);
430 }
431
432 if (group.config()->name() != config().config()->name()) {
433 // we're being saved to a different file!
434 // let's just copy the current values in our configuration over
435 KConfigGroup c = config();
436 c.copyTo(&group);
437 }
438}
439
440KConfigGroup Applet::config(const QString &group) const
441{
442 if (d->transient) {
443 return KConfigGroup(KGlobal::config(), "PlasmaTransientsConfig");
444 }
445
446 KConfigGroup cg = config();
447 return KConfigGroup(&cg, group);
448}
449
450KConfigGroup Applet::config() const
451{
452 if (d->transient) {
453 return KConfigGroup(KGlobal::config(), "PlasmaTransientsConfig");
454 }
455
456 if (d->isContainment) {
457 return *(d->mainConfigGroup());
458 }
459
460 return KConfigGroup(d->mainConfigGroup(), "Configuration");
461}
462
463KConfigGroup Applet::globalConfig() const
464{
465 KConfigGroup globalAppletConfig;
466 QString group = isContainment() ? "ContainmentGlobals" : "AppletGlobals";
467
468 Corona *corona = qobject_cast<Corona*>(scene());
469 if (corona) {
470 KSharedConfig::Ptr coronaConfig = corona->config();
471 globalAppletConfig = KConfigGroup(coronaConfig, group);
472 } else {
473 globalAppletConfig = KConfigGroup(KGlobal::config(), group);
474 }
475
476 return KConfigGroup(&globalAppletConfig, d->globalName());
477}
478
479void Applet::destroy()
480{
481 if (immutability() != Mutable || d->transient || !d->started) {
482 return; //don't double delete
483 }
484
485 d->transient = true;
486
487 if (isContainment()) {
488 d->cleanUpAndDelete();
489 } else {
490 Animation *zoomAnim = Plasma::Animator::create(Plasma::Animator::ZoomAnimation);
491 connect(zoomAnim, SIGNAL(finished()), this, SLOT(cleanUpAndDelete()));
492 zoomAnim->setTargetWidget(this);
493 zoomAnim->start();
494 }
495}
496
497bool Applet::destroyed() const
498{
499 return d->transient;
500}
501
502void AppletPrivate::selectItemToDestroy()
503{
504 //FIXME: this will not work nicely with multiple screens and being zoomed out!
505 if (isContainment) {
506 QGraphicsView *view = q->view();
507 if (view && view->transform().isScaling() &&
508 q->scene()->focusItem() != q) {
509 QGraphicsItem *focus = q->scene()->focusItem();
510
511 if (focus) {
512 Containment *toDestroy = dynamic_cast<Containment*>(focus->topLevelItem());
513
514 if (toDestroy) {
515 toDestroy->destroy();
516 return;
517 }
518 }
519 }
520 }
521
522 q->destroy();
523}
524
525void AppletPrivate::updateRect(const QRectF &rect)
526{
527 q->update(rect);
528}
529
530void AppletPrivate::cleanUpAndDelete()
531{
532 //kDebug() << "???????????????? DESTROYING APPLET" << q->name() << q->scene() << " ???????????????????????????";
533 QGraphicsWidget *parent = dynamic_cast<QGraphicsWidget *>(q->parentItem());
534 //it probably won't matter, but right now if there are applethandles, *they* are the parent.
535 //not the containment.
536
537 //is the applet in a containment and does the containment have a layout?
538 //if yes, we remove the applet in the layout
539 if (parent && parent->layout()) {
540 QGraphicsLayout *l = parent->layout();
541 for (int i = 0; i < l->count(); ++i) {
542 if (q == l->itemAt(i)) {
543 l->removeAt(i);
544 break;
545 }
546 }
547 }
548
549 if (configLoader) {
550 configLoader->setDefaults();
551 }
552
553 resetConfigurationObject();
554
555 if (q->scene()) {
556 if (isContainment) {
557 // prematurely emit our destruction if we are a Containment,
558 // giving Corona a chance to remove this Containment from its collection
559 emit q->QObject::destroyed(q);
560 }
561
562 q->scene()->removeItem(q);
563 }
564
565 q->deleteLater();
566}
567
568void AppletPrivate::createMessageOverlay(bool usePopup)
569{
570 if (messageOverlay) {
571 qDeleteAll(messageOverlay->children());
572 messageOverlay->setLayout(0);
573 }
574
575 PopupApplet *popup = qobject_cast<Plasma::PopupApplet*>(q);
576
577 if (!messageOverlay) {
578 if (usePopup && popup) {
579 if (popup->widget()) {
580 messageOverlayProxy = new QGraphicsProxyWidget(q);
581 messageOverlayProxy->setWidget(popup->widget());
582 messageOverlay = new AppletOverlayWidget(messageOverlayProxy);
583 } else if (popup->graphicsWidget() &&
584 popup->graphicsWidget() != extender.data()) {
585 messageOverlay = new AppletOverlayWidget(popup->graphicsWidget());
586 }
587 }
588
589 if (!messageOverlay) {
590 messageOverlay = new AppletOverlayWidget(q);
591 }
592 }
593
594 positionMessageOverlay();
595}
596
597void AppletPrivate::positionMessageOverlay()
598{
599 if (!messageOverlay) {
600 return;
601 }
602
603 PopupApplet *popup = qobject_cast<Plasma::PopupApplet*>(q);
604 const bool usePopup = popup && (messageOverlay->parentItem() != q);
605 QGraphicsItem *topItem = q;
606
607 if (usePopup && popup->widget()) {
608 // popupapplet with widget()
609 topItem = popup->d->proxy.data();
610 messageOverlay->setGeometry(popup->widget()->contentsRect());
611 } else if (usePopup && popup->graphicsWidget() && popup->graphicsWidget() != extender.data()) {
612 // popupapplet with graphicsWidget()
613 topItem = popup->graphicsWidget();
614 QGraphicsWidget *w = dynamic_cast<QGraphicsWidget *>(topItem);
615 messageOverlay->setGeometry(w ? w->contentsRect() : topItem->boundingRect());
616 } else {
617 // normal applet
618 messageOverlay->setGeometry(q->contentsRect());
619 }
620
621 // raise the overlay above all the other children!
622 int zValue = 100;
623 foreach (QGraphicsItem *child, topItem->children()) {
624 if (child->zValue() > zValue) {
625 zValue = child->zValue() + 1;
626 }
627 }
628 messageOverlay->setZValue(zValue);
629}
630
631void AppletPrivate::destroyMessageOverlay()
632{
633 if (messageDialog) {
634 messageDialog.data()->animatedHide(Plasma::locationToInverseDirection(q->location()));
635 //messageDialog.data()->deleteLater();
636 messageDialog.clear();
637 }
638
639 if (!messageOverlay) {
640 return;
641 }
642
643 messageOverlay->destroy();
644 messageOverlay = 0;
645
646 if (messageOverlayProxy) {
647 messageOverlayProxy->setWidget(0);
648 delete messageOverlayProxy;
649 messageOverlayProxy = 0;
650 }
651
652 MessageButton buttonCode = ButtonNo;
653 //find out if we're disappearing because of a button press
654 PushButton *button = qobject_cast<PushButton *>(q->sender());
655 if (button) {
656 if (button == messageOkButton.data()) {
657 buttonCode = ButtonOk;
658 }
659 if (button == messageYesButton.data()) {
660 buttonCode = ButtonYes;
661 }
662 if (button == messageNoButton.data()) {
663 buttonCode = ButtonNo;
664 }
665 if (button == messageCancelButton.data()) {
666 buttonCode = ButtonCancel;
667 }
668
669 emit q->messageButtonPressed(buttonCode);
670 } else if (q->sender() == messageOverlay) {
671 emit q->messageButtonPressed(ButtonCancel);
672 }
673}
674
675ConfigLoader *Applet::configScheme() const
676{
677 return d->configLoader;
678}
679
680DataEngine *Applet::dataEngine(const QString &name) const
681{
682 if (!d->remoteLocation.isEmpty()) {
683 return d->remoteDataEngine(KUrl(d->remoteLocation), name);
684 } else if (!package() || package()->metadata().remoteLocation().isEmpty()) {
685 return d->dataEngine(name);
686 } else {
687 return d->remoteDataEngine(KUrl(package()->metadata().remoteLocation()), name);
688 }
689}
690
691const Package *Applet::package() const
692{
693 return d->package;
694}
695
696QGraphicsView *Applet::view() const
697{
698 // It's assumed that we won't be visible on more than one view here.
699 // Anything that actually needs view() should only really care about
700 // one of them anyway though.
701 if (!scene()) {
702 return 0;
703 }
704
705 QGraphicsView *found = 0;
706 QGraphicsView *possibleFind = 0;
707 //kDebug() << "looking through" << scene()->views().count() << "views";
708 foreach (QGraphicsView *view, scene()->views()) {
709 //kDebug() << " checking" << view << view->sceneRect()
710 // << "against" << sceneBoundingRect() << scenePos();
711 if (view->sceneRect().intersects(sceneBoundingRect()) ||
712 view->sceneRect().contains(scenePos())) {
713 //kDebug() << " found something!" << view->isActiveWindow();
714 if (view->isActiveWindow()) {
715 found = view;
716 } else {
717 possibleFind = view;
718 }
719 }
720 }
721
722 return found ? found : possibleFind;
723}
724
725QRectF Applet::mapFromView(const QGraphicsView *view, const QRect &rect) const
726{
727 // Why is this adjustment needed? Qt calculation error?
728 return mapFromScene(view->mapToScene(rect)).boundingRect().adjusted(0, 0, 1, 1);
729}
730
731QRect Applet::mapToView(const QGraphicsView *view, const QRectF &rect) const
732{
733 // Why is this adjustment needed? Qt calculation error?
734 return view->mapFromScene(mapToScene(rect)).boundingRect().adjusted(0, 0, -1, -1);
735}
736
737QPoint Applet::popupPosition(const QSize &s) const
738{
739 return popupPosition(s, Qt::AlignLeft);
740}
741
742QPoint Applet::popupPosition(const QSize &s, Qt::AlignmentFlag alignment) const
743{
744 Corona * corona = qobject_cast<Corona*>(scene());
745 Q_ASSERT(corona);
746
747 return corona->popupPosition(this, s, alignment);
748}
749
750void Applet::updateConstraints(Plasma::Constraints constraints)
751{
752 d->scheduleConstraintsUpdate(constraints);
753}
754
755void Applet::constraintsEvent(Plasma::Constraints constraints)
756{
757 //NOTE: do NOT put any code in here that reacts to constraints updates
758 // as it will not get called for any applet that reimplements constraintsEvent
759 // without calling the Applet:: version as well, which it shouldn't need to.
760 // INSTEAD put such code into flushPendingConstraintsEvents
761 Q_UNUSED(constraints)
762 //kDebug() << constraints << "constraints are FormFactor: " << formFactor()
763 // << ", Location: " << location();
764 if (d->script) {
765 d->script->constraintsEvent(constraints);
766 }
767}
768
769void Applet::initExtenderItem(ExtenderItem *item)
770{
771 if (d->script) {
772 emit extenderItemRestored(item);
773 } else {
774 kWarning() << "Missing implementation of initExtenderItem in the applet "
775 << item->config().readEntry("SourceAppletPluginName", "")
776 << "!\n Any applet that uses extenders should implement initExtenderItem to "
777 << "instantiate a widget. Destroying the item...";
778 item->destroy();
779 }
780}
781
782Extender *Applet::extender() const
783{
784 if (!d->extender) {
785 new Extender(const_cast<Applet*>(this));
786 }
787
788 return d->extender.data();
789}
790
791void Applet::setBusy(bool busy)
792{
793 if (busy) {
794 if (!d->busyWidget && !d->busyWidgetTimer.isActive()) {
795 d->busyWidgetTimer.start(500, this);
796 }
797 } else {
798 d->busyWidgetTimer.stop();
799 if (d->busyWidget) {
800 d->busyWidget = 0;
801 d->destroyMessageOverlay();
802 }
803 }
804}
805
806bool Applet::isBusy() const
807{
808 return d->busyWidgetTimer.isActive() || (d->busyWidget && d->busyWidget->isVisible());
809}
810
811QString Applet::name() const
812{
813 if (d->isContainment) {
814 const Containment *c = qobject_cast<const Containment*>(this);
815 if (c && c->d->isPanelContainment()) {
816 return i18n("Panel");
817 } else if (!d->appletDescription.isValid()) {
818 return i18n("Unknown");
819 } else {
820 return d->appletDescription.name();
821 }
822 } else if (!d->appletDescription.isValid()) {
823 return i18n("Unknown Widget");
824 }
825
826 return d->appletDescription.name();
827}
828
829QFont Applet::font() const
830{
831 return QApplication::font();
832}
833
834QString Applet::icon() const
835{
836 if (!d->appletDescription.isValid()) {
837 return QString();
838 }
839
840 return d->appletDescription.icon();
841}
842
843QString Applet::pluginName() const
844{
845 if (!d->appletDescription.isValid()) {
846 return d->mainConfigGroup()->readEntry("plugin", QString());
847 }
848
849 return d->appletDescription.pluginName();
850}
851
852bool Applet::shouldConserveResources() const
853{
854#ifndef PLASMA_NO_SOLID
855 return Solid::PowerManagement::appShouldConserveResources();
856#else
857 return true;
858#endif
859}
860
861QString Applet::category() const
862{
863 if (!d->appletDescription.isValid()) {
864 return i18nc("misc category", "Miscellaneous");
865 }
866
867 return d->appletDescription.category();
868}
869
870QString Applet::category(const KPluginInfo &applet)
871{
872 return applet.property("X-KDE-PluginInfo-Category").toString();
873}
874
875QString Applet::category(const QString &appletName)
876{
877 if (appletName.isEmpty()) {
878 return QString();
879 }
880
881 const QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(appletName);
882 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
883
884 if (offers.isEmpty()) {
885 return QString();
886 }
887
888 return offers.first()->property("X-KDE-PluginInfo-Category").toString();
889}
890
891ImmutabilityType Applet::immutability() const
892{
893 // if this object is itself system immutable, then just return that; it's the most
894 // restrictive setting possible and will override anything that might be happening above it
895 // in the Corona->Containment->Applet hierarchy
896 if (d->transient || (d->mainConfig && d->mainConfig->isImmutable())) {
897 return SystemImmutable;
898 }
899
900 //Returning the more strict immutability between the applet immutability, Containment and Corona
901 ImmutabilityType upperImmutability = Mutable;
902 Containment *cont = d->isContainment ? 0 : containment();
903
904 if (cont) {
905 upperImmutability = cont->immutability();
906 } else if (Corona *corona = qobject_cast<Corona*>(scene())) {
907 upperImmutability = corona->immutability();
908 }
909
910 if (upperImmutability != Mutable) {
911 // it's either system or user immutable, and we already check for local system immutability,
912 // so upperImmutability is guaranteed to be as or more severe as this object's immutability
913 return upperImmutability;
914 } else {
915 return d->immutability;
916 }
917}
918
919void Applet::setImmutability(const ImmutabilityType immutable)
920{
921 if (d->immutability == immutable || immutable == Plasma::SystemImmutable) {
922 // we do not store system immutability in d->immutability since that gets saved
923 // out to the config file; instead, we check with
924 // the config group itself for this information at all times. this differs from
925 // corona, where SystemImmutability is stored in d->immutability.
926 return;
927 }
928
929 d->immutability = immutable;
930 updateConstraints(ImmutableConstraint);
931}
932
933Applet::BackgroundHints Applet::backgroundHints() const
934{
935 return d->backgroundHints;
936}
937
938void Applet::setBackgroundHints(const BackgroundHints hints)
939{
940 if (d->backgroundHints == hints) {
941 return;
942 }
943
944 d->backgroundHints = hints;
945 d->preferredBackgroundHints = hints;
946
947 //Draw the standard background?
948 if ((hints & StandardBackground) || (hints & TranslucentBackground)) {
949 if (!d->background) {
950 d->background = new Plasma::FrameSvg(this);
951 QObject::connect(d->background, SIGNAL(repaintNeeded()), this, SLOT(themeChanged()));
952 }
953
954 if ((hints & TranslucentBackground) &&
955 Plasma::Theme::defaultTheme()->currentThemeHasImage("widgets/translucentbackground")) {
956 d->background->setImagePath("widgets/translucentbackground");
957 } else {
958 d->background->setImagePath("widgets/background");
959 }
960
961 d->background->setEnabledBorders(Plasma::FrameSvg::AllBorders);
962 qreal left, top, right, bottom;
963 d->background->getMargins(left, top, right, bottom);
964 setContentsMargins(left, right, top, bottom);
965 QSizeF fitSize(left + right, top + bottom);
966 d->background->resizeFrame(boundingRect().size());
967
968 //if the background has an "overlay" element decide a random position for it and then save it so it's consistent across plasma starts
969 if (d->background->hasElement("overlay")) {
970 QSize overlaySize = d->background->elementSize("overlay");
971
972 //position is in the boundaries overlaySize.width()*2, overlaySize.height()
973 qsrand(id());
974 d->background->d->overlayPos.rx() = - (overlaySize.width() /2) + (overlaySize.width() /4) * (qrand() % (4 + 1));
975 d->background->d->overlayPos.ry() = (- (overlaySize.height() /2) + (overlaySize.height() /4) * (qrand() % (4 + 1)))/2;
976 }
977 } else if (d->background) {
978 qreal left, top, right, bottom;
979 d->background->getMargins(left, top, right, bottom);
980
981 delete d->background;
982 d->background = 0;
983 setContentsMargins(0, 0, 0, 0);
984 }
985
986 update();
987}
988
989bool Applet::hasFailedToLaunch() const
990{
991 return d->failed;
992}
993
994void Applet::paintWindowFrame(QPainter *painter,
995 const QStyleOptionGraphicsItem *option, QWidget *widget)
996{
997 Q_UNUSED(painter)
998 Q_UNUSED(option)
999 Q_UNUSED(widget)
1000 //Here come the code for the window frame
1001 //kDebug() << windowFrameGeometry();
1002 //painter->drawRoundedRect(windowFrameGeometry(), 5, 5);
1003}
1004
1005bool Applet::configurationRequired() const
1006{
1007 return d->needsConfig;
1008}
1009
1010void Applet::setConfigurationRequired(bool needsConfig, const QString &reason)
1011{
1012 if (d->needsConfig == needsConfig) {
1013 return;
1014 }
1015
1016 d->needsConfig = needsConfig;
1017
1018 if (!needsConfig) {
1019 d->destroyMessageOverlay();
1020 return;
1021 }
1022
1023 d->createMessageOverlay(true);
1024 d->messageOverlay->opacity = 0.4;
1025
1026 QGraphicsGridLayout *configLayout = new QGraphicsGridLayout(d->messageOverlay);
1027 configLayout->setContentsMargins(0, 0, 0, 0);
1028
1029 // configLayout->addStretch();
1030 configLayout->setColumnStretchFactor(0, 5);
1031 configLayout->setColumnStretchFactor(2, 5);
1032 configLayout->setRowStretchFactor(0, 5);
1033 configLayout->setRowStretchFactor(3, 5);
1034
1035 int row = 1;
1036 if (!reason.isEmpty()) {
1037 Label *explanation = new Label(d->messageOverlay);
1038 explanation->setText(reason);
1039 configLayout->addItem(explanation, row, 1);
1040 configLayout->setColumnStretchFactor(1, 5);
1041 ++row;
1042 configLayout->setAlignment(explanation, Qt::AlignBottom | Qt::AlignCenter);
1043 }
1044
1045 PushButton *configWidget = new PushButton(d->messageOverlay);
1046 if (!qobject_cast<Plasma::PopupApplet *>(this) && (formFactor() == Plasma::Horizontal || formFactor() == Plasma::Vertical)) {
1047 configWidget->setImage("widgets/configuration-icons", "configure");
1048 configWidget->setMaximumSize(24,24);
1049 configWidget->setMinimumSize(24,24);
1050 } else {
1051 configWidget->setText(i18n("Configure..."));
1052 }
1053 connect(configWidget, SIGNAL(clicked()), this, SLOT(showConfigurationInterface()));
1054 configLayout->addItem(configWidget, row, 1);
1055
1056 //configLayout->setAlignment(configWidget, Qt::AlignTop | Qt::AlignCenter);
1057 //configLayout->addStretch();
1058
1059 d->messageOverlay->show();
1060}
1061
1062void Applet::showMessage(const QIcon &icon, const QString &message, const MessageButtons buttons)
1063{
1064 if (message.isEmpty()) {
1065 d->destroyMessageOverlay();
1066 return;
1067 }
1068
1069 Corona *corona = qobject_cast<Corona *>(scene());
1070 QGraphicsWidget *mainWidget = new QGraphicsWidget;
1071
1072 QGraphicsLinearLayout *mainLayout = new QGraphicsLinearLayout(mainWidget);
1073 mainLayout->setOrientation(Qt::Vertical);
1074 mainLayout->addStretch();
1075
1076 QGraphicsLinearLayout *messageLayout = new QGraphicsLinearLayout();
1077 messageLayout->setOrientation(Qt::Horizontal);
1078
1079 QGraphicsLinearLayout *buttonLayout = new QGraphicsLinearLayout();
1080 buttonLayout->setOrientation(Qt::Horizontal);
1081
1082 mainLayout->addItem(messageLayout);
1083 mainLayout->addItem(buttonLayout);
1084 mainLayout->addStretch();
1085
1086 IconWidget *messageIcon = new IconWidget(mainWidget);
1087 Label *messageText = new Label(mainWidget);
1088 messageText->nativeWidget()->setWordWrap(true);
1089
1090 messageLayout->addStretch();
1091 messageLayout->addItem(messageIcon);
1092 messageLayout->addItem(messageText);
1093 messageLayout->addStretch();
1094
1095 messageIcon->setIcon(icon);
1096 messageText->setText(message);
1097
1098 buttonLayout->addStretch();
1099
1100 if (buttons & ButtonOk) {
1101 d->messageOkButton = new PushButton(mainWidget);
1102 d->messageOkButton.data()->setText(i18n("&OK"));
1103 d->messageOkButton.data()->setIcon(KIcon("dialog-ok"));
1104 buttonLayout->addItem(d->messageOkButton.data());
1105 connect(d->messageOkButton.data(), SIGNAL(clicked()), this, SLOT(destroyMessageOverlay()));
1106 }
1107
1108 if (buttons & ButtonYes) {
1109 d->messageYesButton = new PushButton(mainWidget);
1110 d->messageYesButton.data()->setText(i18n("&Yes"));
1111 buttonLayout->addItem(d->messageYesButton.data());
1112 connect(d->messageYesButton.data(), SIGNAL(clicked()), this, SLOT(destroyMessageOverlay()));
1113 }
1114
1115 if (buttons & ButtonNo) {
1116 d->messageNoButton = new PushButton(mainWidget);
1117 d->messageNoButton.data()->setText(i18n("&No"));
1118 buttonLayout->addItem(d->messageNoButton.data());
1119 connect(d->messageNoButton.data(), SIGNAL(clicked()), this, SLOT(destroyMessageOverlay()));
1120 }
1121
1122 if (buttons & ButtonCancel) {
1123 d->messageCancelButton = new PushButton(mainWidget);
1124 d->messageCancelButton.data()->setText(i18n("&Cancel"));
1125 d->messageCancelButton.data()->setIcon(KIcon("dialog-cancel"));
1126 buttonLayout->addItem(d->messageCancelButton.data());
1127 connect(d->messageCancelButton.data(), SIGNAL(clicked()), this, SLOT(destroyMessageOverlay()));
1128 }
1129
1130 d->messageCloseAction = new QAction(d->messageOverlay);
1131 d->messageCloseAction.data()->setShortcut(Qt::Key_Escape);
1132 mainWidget->addAction(d->messageCloseAction.data());
1133 connect(d->messageCloseAction.data(), SIGNAL(triggered()), this, SLOT(destroyMessageOverlay()));
1134
1135 buttonLayout->addStretch();
1136
1137 mainWidget->adjustSize();
1138 QSizeF hint = mainWidget->preferredSize();
1139 if (hint.height() > size().height() || hint.width() > size().width()) {
1140 // either a collapsed popup in h/v form factor or just too small,
1141 // so show it in a dialog associated with ourselves
1142 if (corona) {
1143 corona->addOffscreenWidget(mainWidget);
1144 }
1145
1146 if (d->messageDialog) {
1147 delete d->messageDialog.data()->graphicsWidget();
1148 } else {
1149 d->messageDialog = new Plasma::Dialog;
1150 }
1151
1152 ToolTipManager::self()->hide(this);
1153 KWindowSystem::setOnAllDesktops(d->messageDialog.data()->winId(), true);
1154 KWindowSystem::setState(d->messageDialog.data()->winId(), NET::SkipTaskbar | NET::SkipPager);
1155 d->messageDialog.data()->setGraphicsWidget(mainWidget);
1156 connect(d->messageDialog.data(), SIGNAL(destroyed(QObject*)), mainWidget, SLOT(deleteLater()));
1157
1158 // if we are going to show it in a popup, then at least make sure it can be dismissed
1159 if (buttonLayout->count() < 1) {
1160 PushButton *ok = new PushButton(mainWidget);
1161 ok->setText(i18n("OK"));
1162 ok->setIcon(KIcon("dialog-ok"));
1163 buttonLayout->addItem(ok);
1164 connect(ok, SIGNAL(clicked()), this, SLOT(destroyMessageOverlay()));
1165 }
1166 } else {
1167 delete d->messageDialog.data();
1168 d->createMessageOverlay();
1169 d->messageOverlay->opacity = 0.8;
1170 mainWidget->setParentItem(d->messageOverlay);
1171 QGraphicsLinearLayout *l = new QGraphicsLinearLayout(d->messageOverlay);
1172 l->addItem(mainWidget);
1173 }
1174
1175 if (d->messageDialog) {
1176 QPoint pos = geometry().topLeft().toPoint();
1177 if (corona) {
1178 pos = corona->popupPosition(this, d->messageDialog.data()->size());
1179 }
1180
1181 d->messageDialog.data()->move(pos);
1182 d->messageDialog.data()->animatedShow(locationToDirection(location()));
1183 } else {
1184 d->messageOverlay->show();
1185 }
1186}
1187
1188QVariantList Applet::startupArguments() const
1189{
1190 return d->args;
1191}
1192
1193ItemStatus Applet::status() const
1194{
1195 return d->itemStatus;
1196}
1197
1198void Applet::setStatus(const ItemStatus status)
1199{
1200 d->itemStatus = status;
1201 emit newStatus(status);
1202}
1203
1204void Applet::flushPendingConstraintsEvents()
1205{
1206 if (d->pendingConstraints == NoConstraint) {
1207 return;
1208 }
1209
1210 if (d->constraintsTimer.isActive()) {
1211 d->constraintsTimer.stop();
1212 }
1213
1214 //kDebug() << "fushing constraints: " << d->pendingConstraints << "!!!!!!!!!!!!!!!!!!!!!!!!!!!";
1215 Plasma::Constraints c = d->pendingConstraints;
1216 d->pendingConstraints = NoConstraint;
1217
1218 if (c & Plasma::StartupCompletedConstraint) {
1219 //common actions
1220 bool unlocked = immutability() == Mutable;
1221 QAction *closeApplet = d->actions->action("remove");
1222 if (closeApplet) {
1223 closeApplet->setEnabled(unlocked);
1224 closeApplet->setVisible(unlocked);
1225 connect(closeApplet, SIGNAL(triggered(bool)), this, SLOT(selectItemToDestroy()), Qt::UniqueConnection);
1226 }
1227
1228 QAction *configAction = d->actions->action("configure");
1229 if (configAction) {
1230 if (d->isContainment) {
1231 connect(configAction, SIGNAL(triggered(bool)), this, SLOT(requestConfiguration()), Qt::UniqueConnection);
1232 } else {
1233 connect(configAction, SIGNAL(triggered(bool)), this, SLOT(showConfigurationInterface()), Qt::UniqueConnection);
1234 }
1235
1236 if (d->hasConfigurationInterface) {
1237 bool canConfig = unlocked || KAuthorized::authorize("plasma/allow_configure_when_locked");
1238 configAction->setVisible(canConfig);
1239 configAction->setEnabled(canConfig);
1240 }
1241 }
1242
1243 QAction *runAssociatedApplication = d->actions->action("run associated application");
1244 if (runAssociatedApplication) {
1245 connect(runAssociatedApplication, SIGNAL(triggered(bool)), this, SLOT(runAssociatedApplication()), Qt::UniqueConnection);
1246 }
1247
1248 d->updateShortcuts();
1249 Corona * corona = qobject_cast<Corona*>(scene());
1250 if (corona) {
1251 connect(corona, SIGNAL(shortcutsChanged()), this, SLOT(updateShortcuts()), Qt::UniqueConnection);
1252 }
1253 }
1254
1255 if (c & Plasma::ImmutableConstraint) {
1256 bool unlocked = immutability() == Mutable;
1257 QAction *action = d->actions->action("remove");
1258 if (action) {
1259 action->setVisible(unlocked);
1260 action->setEnabled(unlocked);
1261 }
1262
1263 action = d->actions->action("configure");
1264 if (action && d->hasConfigurationInterface) {
1265 bool canConfig = unlocked || KAuthorized::authorize("plasma/allow_configure_when_locked");
1266 action->setVisible(canConfig);
1267 action->setEnabled(canConfig);
1268 }
1269
1270 if (d->extender) {
1271 foreach (ExtenderItem *item, d->extender.data()->attachedItems()) {
1272 item->d->setMovable(unlocked);
1273 }
1274 }
1275
1276 if (!unlocked && d->handle) {
1277 AppletHandle *h = d->handle.data();
1278 disconnect(this);
1279
1280 QGraphicsScene *s = scene();
1281 if (s && h->scene() == s) {
1282 s->removeItem(h);
1283 }
1284
1285 h->deleteLater();
1286 }
1287
1288 emit immutabilityChanged(immutability());
1289 }
1290
1291 if (c & Plasma::SizeConstraint) {
1292 d->positionMessageOverlay();
1293
1294 if (d->started && layout()) {
1295 layout()->updateGeometry();
1296 }
1297 }
1298
1299 if (c & Plasma::FormFactorConstraint) {
1300 FormFactor f = formFactor();
1301 if (!d->isContainment && f != Vertical && f != Horizontal) {
1302 setBackgroundHints(d->preferredBackgroundHints);
1303 } else {
1304 BackgroundHints hints = d->preferredBackgroundHints;
1305 setBackgroundHints(NoBackground);
1306 d->preferredBackgroundHints = hints;
1307 }
1308
1309 if (d->failed) {
1310 if (f == Vertical || f == Horizontal) {
1311 QGraphicsLayoutItem *item = layout()->itemAt(1);
1312 layout()->removeAt(1);
1313 delete item;
1314 }
1315 }
1316
1317 // avoid putting rotated applets in panels
1318 if (f == Vertical || f == Horizontal) {
1319 QTransform at;
1320 at.rotateRadians(0);
1321 setTransform(at);
1322 }
1323
1324 //was a size saved for a particular form factor?
1325 if (d->sizeForFormFactor.contains(f)) {
1326 resize(d->sizeForFormFactor.value(f));
1327 }
1328 }
1329
1330 if (!size().isEmpty() &&
1331 ((c & Plasma::StartupCompletedConstraint) || (c & Plasma::SizeConstraint && !(c & Plasma::FormFactorConstraint)))) {
1332 d->sizeForFormFactor[formFactor()] = size();
1333 }
1334
1335 if (c & Plasma::SizeConstraint || c & Plasma::FormFactorConstraint) {
1336 if (aspectRatioMode() == Plasma::Square || aspectRatioMode() == Plasma::ConstrainedSquare) {
1337 // enforce square size in panels
1338 //save the old size policy. since ignored doesn't (yet) have a valid use case in containments, use it as special unset value
1339 if (d->preferredSizePolicy == QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored)) {
1340 d->preferredSizePolicy = sizePolicy();
1341 }
1342 if (formFactor() == Horizontal) {
1343 setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding));
1344 } else if (formFactor() == Vertical) {
1345 setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
1346 } else if (d->preferredSizePolicy != QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored)) {
1347 setSizePolicy(d->preferredSizePolicy);
1348 }
1349 }
1350 updateGeometry();
1351 }
1352
1353 // now take care of constraints in special subclasses: Contaiment and PopupApplet
1354 Containment* containment = qobject_cast<Plasma::Containment*>(this);
1355 if (d->isContainment && containment) {
1356 containment->d->containmentConstraintsEvent(c);
1357 }
1358
1359 PopupApplet* popup = qobject_cast<Plasma::PopupApplet*>(this);
1360 if (popup) {
1361 popup->d->popupConstraintsEvent(c);
1362 }
1363
1364 // pass the constraint on to the actual subclass
1365 constraintsEvent(c);
1366
1367 if (c & StartupCompletedConstraint) {
1368 // start up is done, we can now go do a mod timer
1369 if (d->modificationsTimer) {
1370 if (d->modificationsTimer->isActive()) {
1371 d->modificationsTimer->stop();
1372 }
1373 } else {
1374 d->modificationsTimer = new QBasicTimer;
1375 }
1376 }
1377}
1378
1379int Applet::type() const
1380{
1381 return Type;
1382}
1383
1384QList<QAction*> Applet::contextualActions()
1385{
1386 //kDebug() << "empty context actions";
1387 return d->script ? d->script->contextualActions() : QList<QAction*>();
1388}
1389
1390QAction *Applet::action(QString name) const
1391{
1392 return d->actions->action(name);
1393}
1394
1395void Applet::addAction(QString name, QAction *action)
1396{
1397 d->actions->addAction(name, action);
1398}
1399
1400void Applet::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
1401{
1402 if (!d->started) {
1403 //kDebug() << "not started";
1404 return;
1405 }
1406
1407 if (transform().isRotating()) {
1408 painter->setRenderHint(QPainter::SmoothPixmapTransform);
1409 painter->setRenderHint(QPainter::Antialiasing);
1410 }
1411
1412 if (d->background &&
1413 formFactor() != Plasma::Vertical &&
1414 formFactor() != Plasma::Horizontal) {
1415 //kDebug() << "option rect is" << option->rect;
1416 d->background->paintFrame(painter);
1417 }
1418
1419 if (d->failed) {
1420 //kDebug() << "failed!";
1421 return;
1422 }
1423
1424 qreal left, top, right, bottom;
1425 getContentsMargins(&left, &top, &right, &bottom);
1426 QRect contentsRect = QRectF(QPointF(0, 0),
1427 boundingRect().size()).adjusted(left, top, -right, -bottom).toRect();
1428
1429 if (widget && d->isContainment) {
1430 // note that the widget we get is actually the viewport of the view, not the view itself
1431 View* v = qobject_cast<Plasma::View*>(widget->parent());
1432 Containment* c = qobject_cast<Plasma::Containment*>(this);
1433
1434 if (!v || v->isWallpaperEnabled()) {
1435
1436 // paint the wallpaper
1437 if (c && c->drawWallpaper() && c->wallpaper()) {
1438 Wallpaper *w = c->wallpaper();
1439 if (!w->isInitialized()) {
1440 // delayed paper initialization
1441 KConfigGroup wallpaperConfig = c->config();
1442 wallpaperConfig = KConfigGroup(&wallpaperConfig, "Wallpaper");
1443 wallpaperConfig = KConfigGroup(&wallpaperConfig, w->pluginName());
1444 w->restore(wallpaperConfig);
1445 connect(w, SIGNAL(update(QRectF)), this, SLOT(updateRect(QRectF)),
1446 Qt::UniqueConnection);
1447 }
1448
1449 painter->save();
1450 c->wallpaper()->paint(painter, option->exposedRect);
1451 painter->restore();
1452 }
1453
1454 // .. and now paint the actual containment interface, but with
1455 // a Containment style option based on the one we get
1456 // the view must be assigned only if its containment is actually our own
1457 Containment::StyleOption coption(*option);
1458 if (v && v->containment() == containment()) {
1459 coption.view = v;
1460 }
1461 paintInterface(painter, &coption, contentsRect);
1462 }
1463 } else {
1464 //kDebug() << "paint interface of" << (QObject*) this;
1465 // paint the applet's interface
1466 paintInterface(painter, option, contentsRect);
1467 }
1468}
1469
1470void Applet::paintInterface(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRect &contentsRect)
1471{
1472 if (d->script) {
1473 d->script->paintInterface(painter, option, contentsRect);
1474 } else {
1475 //kDebug() << "Applet::paintInterface() default impl";
1476 }
1477}
1478
1479FormFactor Applet::formFactor() const
1480{
1481 Containment *c = containment();
1482 QGraphicsWidget *pw = qobject_cast<QGraphicsWidget *>(parent());
1483 if (!pw) {
1484 pw = dynamic_cast<QGraphicsWidget *>(parentItem());
1485 }
1486 Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(pw);
1487 //assumption: this loop is usually is -really- short or doesn't run at all
1488 while (!parentApplet && pw && pw->parentWidget()) {
1489 QGraphicsWidget *parentWidget = qobject_cast<QGraphicsWidget *>(pw->parent());
1490 if (!parentWidget) {
1491 parentWidget = dynamic_cast<QGraphicsWidget *>(pw->parentItem());
1492 }
1493 pw = parentWidget;
1494 parentApplet = qobject_cast<Plasma::Applet *>(pw);
1495 }
1496
1497
1498 const PopupApplet *pa = dynamic_cast<const PopupApplet *>(this);
1499
1500 //if the applet is in a widget that isn't a containment
1501 //try to retrieve the formFactor from the parent size
1502 //we can't use our own sizeHint here because it needs formFactor, so endless recursion.
1503 // a popupapplet can always be constrained.
1504 // a normal applet should to but
1505 //FIXME: not always constrained to not break systemmonitor
1506 if (parentApplet && parentApplet != c && c != this && (pa || layout())) {
1507 if (pa || (parentApplet->size().height() < layout()->effectiveSizeHint(Qt::MinimumSize).height())) {
1508 return Plasma::Horizontal;
1509 } else if (pa || (parentApplet->size().width() < layout()->effectiveSizeHint(Qt::MinimumSize).width())) {
1510 return Plasma::Vertical;
1511 }
1512 return parentApplet->formFactor();
1513 }
1514
1515 return c ? c->d->formFactor : Plasma::Planar;
1516}
1517
1518Containment *Applet::containment() const
1519{
1520 if (d->isContainment) {
1521 Containment *c = qobject_cast<Containment*>(const_cast<Applet*>(this));
1522 if (c) {
1523 return c;
1524 }
1525 }
1526
1527 QGraphicsItem *parent = parentItem();
1528 Containment *c = 0;
1529
1530 while (parent) {
1531 Containment *possibleC = dynamic_cast<Containment*>(parent);
1532 if (possibleC && possibleC->Applet::d->isContainment) {
1533 c = possibleC;
1534 break;
1535 }
1536 parent = parent->parentItem();
1537 }
1538
1539 if (!c) {
1540 //if the applet is an offscreen widget its parentItem will be 0, while its parent
1541 //will be its parentWidget, so here we check the QObject hierarchy.
1542 QObject *objParent = this->parent();
1543 while (objParent) {
1544 Containment *possibleC = qobject_cast<Containment*>(objParent);
1545 if (possibleC && possibleC->Applet::d->isContainment) {
1546 c = possibleC;
1547 break;
1548 }
1549 objParent = objParent->parent();
1550 }
1551 }
1552
1553 return c;
1554}
1555
1556void Applet::setGlobalShortcut(const KShortcut &shortcut)
1557{
1558 if (!d->activationAction) {
1559 d->activationAction = new KAction(this);
1560 d->activationAction->setText(i18n("Activate %1 Widget", name()));
1561 d->activationAction->setObjectName(QString("activate widget %1").arg(id())); // NO I18N
1562 connect(d->activationAction, SIGNAL(triggered()), this, SIGNAL(activate()));
1563 connect(d->activationAction, SIGNAL(globalShortcutChanged(QKeySequence)),
1564 this, SLOT(globalShortcutChanged()));
1565
1566 QList<QWidget *> widgets = d->actions->associatedWidgets();
1567 foreach (QWidget *w, widgets) {
1568 w->addAction(d->activationAction);
1569 }
1570 } else if (d->activationAction->globalShortcut() == shortcut) {
1571 return;
1572 }
1573
1574 //kDebug() << "before" << shortcut.primary() << d->activationAction->globalShortcut().primary();
1575 d->activationAction->setGlobalShortcut(
1576 shortcut,
1577 KAction::ShortcutTypes(KAction::ActiveShortcut | KAction::DefaultShortcut),
1578 KAction::NoAutoloading);
1579 d->globalShortcutChanged();
1580}
1581
1582void AppletPrivate::globalShortcutChanged()
1583{
1584 if (!activationAction) {
1585 return;
1586 }
1587
1588 KConfigGroup shortcutConfig(mainConfigGroup(), "Shortcuts");
1589 shortcutConfig.writeEntry("global", activationAction->globalShortcut().toString());
1590 scheduleModificationNotification();
1591 //kDebug() << "after" << shortcut.primary() << d->activationAction->globalShortcut().primary();
1592}
1593
1594KShortcut Applet::globalShortcut() const
1595{
1596 if (d->activationAction) {
1597 return d->activationAction->globalShortcut();
1598 }
1599
1600 return KShortcut();
1601}
1602
1603bool Applet::isPopupShowing() const
1604{
1605 return false;
1606}
1607
1608void Applet::addAssociatedWidget(QWidget *widget)
1609{
1610 d->actions->addAssociatedWidget(widget);
1611}
1612
1613void Applet::removeAssociatedWidget(QWidget *widget)
1614{
1615 d->actions->removeAssociatedWidget(widget);
1616}
1617
1618Location Applet::location() const
1619{
1620 Containment *c = containment();
1621 return c ? c->d->location : Plasma::Desktop;
1622}
1623
1624Context *Applet::context() const
1625{
1626 Containment *c = containment();
1627 Q_ASSERT(c);
1628 return c->d->context();
1629}
1630
1631Plasma::AspectRatioMode Applet::aspectRatioMode() const
1632{
1633 return d->aspectRatioMode;
1634}
1635
1636void Applet::setAspectRatioMode(Plasma::AspectRatioMode mode)
1637{
1638 PopupApplet *popup = qobject_cast<PopupApplet *>(this);
1639 if (popup && popup->d->dialogPtr) {
1640 popup->d->dialogPtr.data()->setAspectRatioMode(mode);
1641 popup->d->savedAspectRatio = mode;
1642 }
1643
1644 d->aspectRatioMode = mode;
1645}
1646
1647void Applet::registerAsDragHandle(QGraphicsItem *item)
1648{
1649 if (!item || d->registeredAsDragHandle.contains(item)) {
1650 return;
1651 }
1652
1653 d->registeredAsDragHandle.insert(item);
1654 item->installSceneEventFilter(this);
1655}
1656
1657void Applet::unregisterAsDragHandle(QGraphicsItem *item)
1658{
1659 if (!item) {
1660 return;
1661 }
1662
1663 if (d->registeredAsDragHandle.remove(item)) {
1664 if (item != this) {
1665 item->removeSceneEventFilter(this);
1666 }
1667 }
1668}
1669
1670bool Applet::isRegisteredAsDragHandle(QGraphicsItem *item)
1671{
1672 return d->registeredAsDragHandle.contains(item);
1673}
1674
1675bool Applet::hasConfigurationInterface() const
1676{
1677 return d->hasConfigurationInterface;
1678}
1679
1680void Applet::publish(AnnouncementMethods methods, const QString &resourceName)
1681{
1682 if (d->package) {
1683 d->package->d->publish(methods);
1684 } else if (d->appletDescription.isValid()) {
1685 if (!d->service) {
1686 d->service = new PlasmoidService(this);
1687 }
1688
1689 kDebug() << "publishing package under name " << resourceName;
1690 PackageMetadata pm;
1691 pm.setName(d->appletDescription.name());
1692 pm.setDescription(d->appletDescription.comment());
1693 pm.setIcon(d->appletDescription.icon());
1694 d->service->d->publish(methods, resourceName, pm);
1695 } else {
1696 kDebug() << "Can not publish invalid applets.";
1697 }
1698}
1699
1700void Applet::unpublish()
1701{
1702 if (d->package) {
1703 d->package->d->unpublish();
1704 } else {
1705 if (d->service) {
1706 d->service->d->unpublish();
1707 }
1708 }
1709}
1710
1711bool Applet::isPublished() const
1712{
1713 if (d->package) {
1714 return d->package->d->isPublished();
1715 } else {
1716 if (d->service) {
1717 return d->service->d->isPublished();
1718 } else {
1719 return false;
1720 }
1721 }
1722}
1723
1724void Applet::setHasConfigurationInterface(bool hasInterface)
1725{
1726 if (hasInterface == d->hasConfigurationInterface) {
1727 return;
1728 }
1729
1730 QAction *configAction = d->actions->action("configure");
1731 if (configAction) {
1732 bool enable = hasInterface;
1733 if (enable) {
1734 const bool unlocked = immutability() == Mutable;
1735 enable = unlocked || KAuthorized::authorize("plasma/allow_configure_when_locked");
1736 }
1737 configAction->setEnabled(enable);
1738 }
1739
1740 d->hasConfigurationInterface = hasInterface;
1741}
1742
1743KActionCollection* AppletPrivate::defaultActions(QObject *parent)
1744{
1745 KActionCollection *actions = new KActionCollection(parent);
1746 actions->setConfigGroup("Shortcuts-Applet");
1747
1748 KAction *configAction = actions->addAction("configure");
1749 configAction->setAutoRepeat(false);
1750 configAction->setText(i18n("Widget Settings"));
1751 configAction->setIcon(KIcon("configure"));
1752 configAction->setShortcut(KShortcut("alt+d, s"));
1753 configAction->setData(AbstractToolBox::ConfigureTool);
1754
1755 KAction *closeApplet = actions->addAction("remove");
1756 closeApplet->setAutoRepeat(false);
1757 closeApplet->setText(i18n("Remove this Widget"));
1758 closeApplet->setIcon(KIcon("edit-delete"));
1759 closeApplet->setShortcut(KShortcut("alt+d, r"));
1760 closeApplet->setData(AbstractToolBox::DestructiveTool);
1761
1762 KAction *runAssociatedApplication = actions->addAction("run associated application");
1763 runAssociatedApplication->setAutoRepeat(false);
1764 runAssociatedApplication->setText(i18n("Run the Associated Application"));
1765 runAssociatedApplication->setIcon(KIcon("system-run"));
1766 runAssociatedApplication->setShortcut(KShortcut("alt+d, t"));
1767 runAssociatedApplication->setVisible(false);
1768 runAssociatedApplication->setEnabled(false);
1769 runAssociatedApplication->setData(AbstractToolBox::ControlTool);
1770
1771 return actions;
1772}
1773
1774bool Applet::eventFilter(QObject *o, QEvent *e)
1775{
1776 return QObject::eventFilter(o, e);
1777}
1778
1779bool Applet::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
1780{
1781 if (watched == this) {
1782 switch (event->type()) {
1783 case QEvent::GraphicsSceneHoverEnter:
1784 //kDebug() << "got hoverenterEvent" << immutability() << " " << immutability();
1785 if (immutability() == Mutable) {
1786 QGraphicsWidget *pw = this;
1787 //This is for the rare case of applet in applet (systray)
1788 //if the applet is in an applet that is not a containment, don't create the handle BUG:301648
1789 while ((pw = pw->parentWidget())) {
1790 if (qobject_cast<Containment *>(pw)) {
1791 break;
1792 } else if (qobject_cast<Applet *>(pw)) {
1793 return false;
1794 }
1795 }
1796
1797 QGraphicsSceneHoverEvent *he = static_cast<QGraphicsSceneHoverEvent*>(event);
1798 if (d->handle) {
1799 d->handle.data()->setHoverPos(he->pos());
1800 } else {
1801 //kDebug() << "generated applet handle";
1802 AppletHandle *handle = new AppletHandle(containment(), this, he->pos());
1803 connect(handle, SIGNAL(disappearDone(AppletHandle*)),
1804 this, SLOT(handleDisappeared(AppletHandle*)));
1805 connect(this, SIGNAL(geometryChanged()),
1806 handle, SLOT(appletResized()));
1807 d->handle = handle;
1808 }
1809 }
1810 break;
1811
1812 case QEvent::GraphicsSceneHoverMove:
1813 if (d->handle && !d->handle.data()->shown() && immutability() == Mutable) {
1814 QGraphicsSceneHoverEvent *he = static_cast<QGraphicsSceneHoverEvent*>(event);
1815 d->handle.data()->setHoverPos(he->pos());
1816 }
1817 break;
1818
1819 case QEvent::GraphicsSceneMousePress: {
1820 QGraphicsSceneMouseEvent *me = static_cast<QGraphicsSceneMouseEvent *>(event);
1821 if (!contentsRect().contains(me->pos())) {
1822 event->setAccepted(false);
1823 return true;
1824 }
1825 break;
1826 }
1827
1828 default:
1829 break;
1830 }
1831
1832 }
1833
1834 switch (event->type()) {
1835 case QEvent::GraphicsSceneMouseMove:
1836 case QEvent::GraphicsSceneMousePress:
1837 case QEvent::GraphicsSceneMouseRelease:
1838 {
1839 if (watched == this) {
1840 event->setAccepted(false);
1841 return false;
1842 }
1843 // don't move when the containment is not mutable,
1844 // in the rare case the containment doesn't exists consider it as mutable
1845 if ((flags() & ItemIsMovable) && d->registeredAsDragHandle.contains(watched)) {
1846 Containment *c = containment();
1847 if (!c || c->immutability() == Mutable) {
1848 scene()->sendEvent(this, event);
1849 return false;
1850 }
1851 }
1852 break;
1853 }
1854
1855 default:
1856 break;
1857 }
1858
1859 return QGraphicsItem::sceneEventFilter(watched, event);
1860}
1861
1862void Applet::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
1863{
1864 if (immutability() == Mutable && formFactor() == Plasma::Planar && (flags() & ItemIsMovable)) {
1865 QGraphicsWidget::mouseMoveEvent(event);
1866 }
1867}
1868
1869void Applet::focusInEvent(QFocusEvent *event)
1870{
1871 if (!isContainment() && containment()) {
1872 //focusing an applet may trigger this event again, but we won't be here more than twice
1873 containment()->d->focusApplet(this);
1874 }
1875
1876 QGraphicsWidget::focusInEvent(event);
1877}
1878
1879void Applet::resizeEvent(QGraphicsSceneResizeEvent *event)
1880{
1881 QGraphicsWidget::resizeEvent(event);
1882
1883 if (d->background) {
1884 d->background->resizeFrame(boundingRect().size());
1885 }
1886
1887 updateConstraints(Plasma::SizeConstraint);
1888
1889 d->scheduleModificationNotification();
1890 emit geometryChanged();
1891}
1892
1893bool Applet::isUserConfiguring() const
1894{
1895 return KConfigDialog::exists(d->configDialogId());
1896}
1897
1898void Applet::showConfigurationInterface()
1899{
1900 if (!hasConfigurationInterface()) {
1901 return;
1902 }
1903
1904 if (immutability() != Mutable && !KAuthorized::authorize("plasma/allow_configure_when_locked")) {
1905 return;
1906 }
1907
1908 KConfigDialog *dlg = KConfigDialog::exists(d->configDialogId());
1909
1910 if (dlg) {
1911 KWindowSystem::setOnDesktop(dlg->winId(), KWindowSystem::currentDesktop());
1912 dlg->show();
1913 KWindowSystem::activateWindow(dlg->winId());
1914 return;
1915 }
1916
1917 d->publishUI.publishCheckbox = 0;
1918 if (d->package) {
1919 KConfigDialog *dialog = 0;
1920
1921 const QString uiFile = d->package->filePath("mainconfigui");
1922 KDesktopFile df(d->package->path() + "/metadata.desktop");
1923 const QStringList kcmPlugins = df.desktopGroup().readEntry("X-Plasma-ConfigPlugins", QStringList());
1924 if (!uiFile.isEmpty() || !kcmPlugins.isEmpty()) {
1925 KConfigSkeleton *configLoader = d->configLoader ? d->configLoader : new KConfigSkeleton(0);
1926 dialog = new AppletConfigDialog(0, d->configDialogId(), configLoader);
1927
1928 if (!d->configLoader) {
1929 // delete the temporary when this dialog is done
1930 configLoader->setParent(dialog);
1931 }
1932
1933 dialog->setWindowTitle(d->configWindowTitle());
1934 dialog->setAttribute(Qt::WA_DeleteOnClose, true);
1935 bool hasPages = false;
1936
1937 QFile f(uiFile);
1938 QUiLoader loader;
1939 QWidget *w = loader.load(&f);
1940 if (w) {
1941 dialog->addPage(w, i18n("Settings"), icon(), i18n("%1 Settings", name()));
1942 hasPages = true;
1943 }
1944
1945 foreach (const QString &kcm, kcmPlugins) {
1946#ifndef PLASMA_NO_KUTILS
1947 KCModuleProxy *module = new KCModuleProxy(kcm);
1948 if (module->realModule()) {
1949 //preemptively load modules to prevent save() crashing on some kcms, like powerdevil ones
1950 module->load();
1951 connect(module, SIGNAL(changed(bool)), dialog, SLOT(settingsModified(bool)));
1952 connect(dialog, SIGNAL(okClicked()),
1953 module->realModule(), SLOT(save()));
1954 connect(dialog, SIGNAL(applyClicked()),
1955 module->realModule(), SLOT(save()));
1956 dialog->addPage(module, module->moduleInfo().moduleName(), module->moduleInfo().icon());
1957 hasPages = true;
1958 } else {
1959 delete module;
1960 }
1961#else
1962 KService::Ptr service = KService::serviceByStorageId(kcm);
1963 if (service) {
1964 QString error;
1965 KCModule *module = service->createInstance<KCModule>(dialog, QVariantList(), &error);
1966 if (module) {
1967 module->load();
1968 connect(module, SIGNAL(changed(bool)), dialog, SLOT(settingsModified(bool)));
1969 connect(dialog, SIGNAL(okClicked()),
1970 module, SLOT(save()));
1971 connect(dialog, SIGNAL(applyClicked()),
1972 module, SLOT(save()));
1973 dialog->addPage(module, service->name(), service->icon());
1974 hasPages = true;
1975 } else {
1976#ifndef NDEBUG
1977 kDebug() << "failed to load kcm" << kcm << "for" << name();
1978#endif
1979 }
1980 }
1981#endif
1982 }
1983
1984 if (hasPages) {
1985 d->addGlobalShortcutsPage(dialog);
1986 dialog->show();
1987 } else {
1988 delete dialog;
1989 dialog = 0;
1990 }
1991 }
1992
1993 if (!dialog && d->script) {
1994 d->script->showConfigurationInterface();
1995 }
1996 } else if (d->script) {
1997 d->script->showConfigurationInterface();
1998 } else {
1999 KConfigDialog *dialog = d->generateGenericConfigDialog();
2000 d->addStandardConfigurationPages(dialog);
2001 showConfigurationInterface(dialog);
2002 }
2003
2004 emit releaseVisualFocus();
2005}
2006
2007void Applet::showConfigurationInterface(QWidget *widget)
2008{
2009 if (!containment() || !containment()->corona() ||
2010 !containment()->corona()->dialogManager()) {
2011 widget->show();
2012 return;
2013 }
2014
2015 QMetaObject::invokeMethod(containment()->corona()->dialogManager(), "showDialog", Q_ARG(QWidget *, widget), Q_ARG(Plasma::Applet *, this));
2016}
2017
2018QString AppletPrivate::configDialogId() const
2019{
2020 return QString("%1settings%2").arg(appletId).arg(q->name());
2021}
2022
2023QString AppletPrivate::configWindowTitle() const
2024{
2025 return i18nc("@title:window", "%1 Settings", q->name());
2026}
2027
2028QSet<QString> AppletPrivate::knownCategories()
2029{
2030 // this is to trick the tranlsation tools into making the correct
2031 // strings for translation
2032 QSet<QString> categories = s_customCategories;
2033 categories << QString(I18N_NOOP("Accessibility")).toLower()
2034 << QString(I18N_NOOP("Application Launchers")).toLower()
2035 << QString(I18N_NOOP("Astronomy")).toLower()
2036 << QString(I18N_NOOP("Date and Time")).toLower()
2037 << QString(I18N_NOOP("Development Tools")).toLower()
2038 << QString(I18N_NOOP("Education")).toLower()
2039 << QString(I18N_NOOP("Environment and Weather")).toLower()
2040 << QString(I18N_NOOP("Examples")).toLower()
2041 << QString(I18N_NOOP("File System")).toLower()
2042 << QString(I18N_NOOP("Fun and Games")).toLower()
2043 << QString(I18N_NOOP("Graphics")).toLower()
2044 << QString(I18N_NOOP("Language")).toLower()
2045 << QString(I18N_NOOP("Mapping")).toLower()
2046 << QString(I18N_NOOP("Miscellaneous")).toLower()
2047 << QString(I18N_NOOP("Multimedia")).toLower()
2048 << QString(I18N_NOOP("Online Services")).toLower()
2049 << QString(I18N_NOOP("Productivity")).toLower()
2050 << QString(I18N_NOOP("System Information")).toLower()
2051 << QString(I18N_NOOP("Utilities")).toLower()
2052 << QString(I18N_NOOP("Windows and Tasks")).toLower();
2053 return categories;
2054}
2055
2056KConfigDialog *AppletPrivate::generateGenericConfigDialog()
2057{
2058 KConfigSkeleton *nullManager = new KConfigSkeleton(0);
2059 KConfigDialog *dialog = new AppletConfigDialog(0, configDialogId(), nullManager);
2060 nullManager->setParent(dialog);
2061 dialog->setFaceType(KPageDialog::Auto);
2062 dialog->setWindowTitle(configWindowTitle());
2063 dialog->setAttribute(Qt::WA_DeleteOnClose, true);
2064 q->createConfigurationInterface(dialog);
2065 dialog->showButton(KDialog::Default, false);
2066 dialog->showButton(KDialog::Help, false);
2067 QObject::connect(dialog, SIGNAL(applyClicked()), q, SLOT(configDialogFinished()));
2068 QObject::connect(dialog, SIGNAL(okClicked()), q, SLOT(configDialogFinished()));
2069 return dialog;
2070}
2071
2072void AppletPrivate::addStandardConfigurationPages(KConfigDialog *dialog)
2073{
2074 addGlobalShortcutsPage(dialog);
2075}
2076
2077void AppletPrivate::addGlobalShortcutsPage(KConfigDialog *dialog)
2078{
2079#ifndef PLASMA_NO_GLOBAL_SHORTCUTS
2080 if (isContainment) {
2081 return;
2082 }
2083
2084 QWidget *page = new QWidget;
2085 QVBoxLayout *layout = new QVBoxLayout(page);
2086
2087 if (!shortcutEditor) {
2088 shortcutEditor = new KKeySequenceWidget(page);
2089 QObject::connect(shortcutEditor.data(), SIGNAL(keySequenceChanged(QKeySequence)), dialog, SLOT(settingsModified()));
2090 }
2091
2092 shortcutEditor.data()->setKeySequence(q->globalShortcut().primary());
2093 layout->addWidget(shortcutEditor.data());
2094 layout->addStretch();
2095 dialog->addPage(page, i18n("Keyboard Shortcut"), "preferences-desktop-keyboard");
2096
2097 QObject::connect(dialog, SIGNAL(applyClicked()), q, SLOT(configDialogFinished()), Qt::UniqueConnection);
2098 QObject::connect(dialog, SIGNAL(okClicked()), q, SLOT(configDialogFinished()), Qt::UniqueConnection);
2099#endif
2100}
2101
2102void AppletPrivate::publishCheckboxStateChanged(int state)
2103{
2104 if (state == Qt::Checked) {
2105 publishUI.allUsersCheckbox->setEnabled(true);
2106 } else {
2107 publishUI.allUsersCheckbox->setEnabled(false);
2108 }
2109}
2110
2111void AppletPrivate::configDialogFinished()
2112{
2113 if (shortcutEditor) {
2114 QKeySequence sequence = shortcutEditor.data()->keySequence();
2115 if (sequence != q->globalShortcut().primary()) {
2116 q->setGlobalShortcut(KShortcut(sequence));
2117 emit q->configNeedsSaving();
2118 }
2119 }
2120
2121#ifdef ENABLE_REMOTE_WIDGETS
2122 if (KConfigDialog::exists(configDialogId()) && publishUI.publishCheckbox) {
2123 q->config().writeEntry("Share", publishUI.publishCheckbox->isChecked());
2124
2125 if (publishUI.publishCheckbox->isChecked()) {
2126 QString resourceName =
2127 i18nc("%1 is the name of a plasmoid, %2 the name of the machine that plasmoid is published on",
2128 "%1 on %2", q->name(), QHostInfo::localHostName());
2129 q->publish(Plasma::ZeroconfAnnouncement, resourceName);
2130 if (publishUI.allUsersCheckbox->isChecked()) {
2131 if (!AuthorizationManager::self()->d->matchingRule(resourceName, Credentials())) {
2132 AuthorizationRule *rule = new AuthorizationRule(resourceName, "");
2133 rule->setPolicy(AuthorizationRule::Allow);
2134 rule->setTargets(AuthorizationRule::AllUsers);
2135 AuthorizationManager::self()->d->rules.append(rule);
2136 }
2137 } else {
2138 AuthorizationRule *matchingRule =
2139 AuthorizationManager::self()->d->matchingRule(resourceName, Credentials());
2140 if (matchingRule) {
2141 AuthorizationManager::self()->d->rules.removeAll(matchingRule);
2142 }
2143 }
2144 } else {
2145 q->unpublish();
2146 }
2147 }
2148#endif
2149
2150 if (!configLoader) {
2151 // the config loader will trigger this for us, so we don't need to.
2152 propagateConfigChanged();
2153 if (KConfigDialog *dialog = qobject_cast<KConfigDialog *>(q->sender())) {
2154 dialog->enableButton(KDialog::Apply, false);
2155 }
2156 }
2157}
2158
2159void AppletPrivate::updateShortcuts()
2160{
2161 if (isContainment) {
2162 //a horrible hack to avoid clobbering corona settings
2163 //we pull them out, then read, then put them back
2164 QList<QString> names;
2165 QList<QAction*> qactions;
2166 names << "add sibling containment" << "configure shortcuts" << "lock widgets";
2167 foreach (const QString &name, names) {
2168 QAction *a = actions->action(name);
2169 actions->takeAction(a); //FIXME this is stupid, KActionCollection needs a takeAction(QString) method
2170 qactions << a;
2171 }
2172
2173 actions->readSettings();
2174
2175 for (int i = 0; i < names.size(); ++i) {
2176 QAction *a = qactions.at(i);
2177 if (a) {
2178 actions->addAction(names.at(i), a);
2179 }
2180 }
2181 } else {
2182 actions->readSettings();
2183 }
2184}
2185
2186void AppletPrivate::propagateConfigChanged()
2187{
2188 if (isContainment) {
2189 Containment *c = qobject_cast<Containment *>(q);
2190 if (c) {
2191 c->d->configChanged();
2192 }
2193 }
2194
2195 q->configChanged();
2196}
2197
2198void Applet::configChanged()
2199{
2200 if (d->script) {
2201 if (d->configLoader) {
2202 d->configLoader->readConfig();
2203 }
2204 d->script->configChanged();
2205 }
2206}
2207
2208void Applet::createConfigurationInterface(KConfigDialog *parent)
2209{
2210 Q_UNUSED(parent)
2211 // virtual method reimplemented by subclasses.
2212 // do not put anything here ...
2213}
2214
2215bool Applet::hasAuthorization(const QString &constraint) const
2216{
2217 KConfigGroup constraintGroup(KGlobal::config(), "Constraints");
2218 return constraintGroup.readEntry(constraint, true);
2219}
2220
2221void Applet::setAssociatedApplication(const QString &string)
2222{
2223 AssociatedApplicationManager::self()->setApplication(this, string);
2224
2225 QAction *runAssociatedApplication = d->actions->action("run associated application");
2226 if (runAssociatedApplication) {
2227 bool valid = AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this);
2228 valid = valid && hasAuthorization("LaunchApp"); //obey security!
2229 runAssociatedApplication->setVisible(valid);
2230 runAssociatedApplication->setEnabled(valid);
2231 }
2232}
2233
2234void Applet::setAssociatedApplicationUrls(const KUrl::List &urls)
2235{
2236 AssociatedApplicationManager::self()->setUrls(this, urls);
2237
2238 QAction *runAssociatedApplication = d->actions->action("run associated application");
2239 if (runAssociatedApplication) {
2240 bool valid = AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this);
2241 valid = valid && hasAuthorization("LaunchApp"); //obey security!
2242 runAssociatedApplication->setVisible(valid);
2243 runAssociatedApplication->setEnabled(valid);
2244 }
2245}
2246
2247QString Applet::associatedApplication() const
2248{
2249 return AssociatedApplicationManager::self()->application(this);
2250}
2251
2252KUrl::List Applet::associatedApplicationUrls() const
2253{
2254 return AssociatedApplicationManager::self()->urls(this);
2255}
2256
2257void Applet::runAssociatedApplication()
2258{
2259 if (hasAuthorization("LaunchApp")) {
2260 AssociatedApplicationManager::self()->run(this);
2261 }
2262}
2263
2264bool Applet::hasValidAssociatedApplication() const
2265{
2266 return AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this);
2267}
2268
2269void AppletPrivate::filterOffers(QList<KService::Ptr> &offers)
2270{
2271 KConfigGroup constraintGroup(KGlobal::config(), "Constraints");
2272 foreach (const QString &key, constraintGroup.keyList()) {
2273 //kDebug() << "security constraint" << key;
2274 if (constraintGroup.readEntry(key, true)) {
2275 continue;
2276 }
2277
2278 //ugh. a qlist of ksharedptr<kservice>
2279 QMutableListIterator<KService::Ptr> it(offers);
2280 while (it.hasNext()) {
2281 KService::Ptr p = it.next();
2282 QString prop = QString("X-Plasma-Requires-").append(key);
2283 QVariant req = p->property(prop, QVariant::String);
2284 //valid values: Required/Optional/Unused
2285 QString reqValue;
2286 if (req.isValid()) {
2287 reqValue = req.toString();
2288 } else if (p->property("X-Plasma-API").toString().toLower() == "javascript") {
2289 //TODO: be able to check whether or not a script engine provides "controled"
2290 //bindings; for now we just give a pass to the qscript ones
2291 reqValue = "Unused";
2292 }
2293
2294 if (!(reqValue == "Optional" || reqValue == "Unused")) {
2295 //if (reqValue == "Required") {
2296 it.remove();
2297 }
2298 }
2299 }
2300}
2301
2302QString AppletPrivate::parentAppConstraint(const QString &parentApp)
2303{
2304 if (parentApp.isEmpty()) {
2305 return QString("((not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '') or [X-KDE-ParentApp] == '%1')")
2306 .arg(KGlobal::mainComponent().aboutData()->appName());
2307 }
2308
2309 return QString("[X-KDE-ParentApp] == '%1'").arg(parentApp);
2310}
2311
2312KPluginInfo::List Applet::listAppletInfo(const QString &category, const QString &parentApp)
2313{
2314 return PluginLoader::pluginLoader()->listAppletInfo(category, parentApp);
2315}
2316
2317KPluginInfo::List Applet::listAppletInfoForMimetype(const QString &mimetype)
2318{
2319 QString constraint = AppletPrivate::parentAppConstraint();
2320 constraint.append(QString(" and '%1' in [X-Plasma-DropMimeTypes]").arg(mimetype));
2321 //kDebug() << "listAppletInfoForMimetype with" << mimetype << constraint;
2322 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
2323 AppletPrivate::filterOffers(offers);
2324 return KPluginInfo::fromServices(offers);
2325}
2326
2327KPluginInfo::List Applet::listAppletInfoForUrl(const QUrl &url)
2328{
2329 QString constraint = AppletPrivate::parentAppConstraint();
2330 constraint.append(" and exist [X-Plasma-DropUrlPatterns]");
2331 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
2332 AppletPrivate::filterOffers(offers);
2333
2334 KPluginInfo::List allApplets = KPluginInfo::fromServices(offers);
2335 KPluginInfo::List filtered;
2336 foreach (const KPluginInfo &info, allApplets) {
2337 QStringList urlPatterns = info.property("X-Plasma-DropUrlPatterns").toStringList();
2338 foreach (const QString &glob, urlPatterns) {
2339 QRegExp rx(glob);
2340 rx.setPatternSyntax(QRegExp::Wildcard);
2341 if (rx.exactMatch(url.toString())) {
2342 kDebug() << info.name() << "matches" << glob << url;
2343 filtered << info;
2344 }
2345 }
2346 }
2347
2348 return filtered;
2349}
2350
2351QStringList Applet::listCategories(const QString &parentApp, bool visibleOnly)
2352{
2353 QString constraint = AppletPrivate::parentAppConstraint(parentApp);
2354 constraint.append(" and exist [X-KDE-PluginInfo-Category]");
2355
2356 KConfigGroup group(KGlobal::config(), "General");
2357 const QStringList excluded = group.readEntry("ExcludeCategories", QStringList());
2358 foreach (const QString &category, excluded) {
2359 constraint.append(" and [X-KDE-PluginInfo-Category] != '").append(category).append("'");
2360 }
2361
2362 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
2363 AppletPrivate::filterOffers(offers);
2364
2365 QStringList categories;
2366 QSet<QString> known = AppletPrivate::knownCategories();
2367 foreach (const KService::Ptr &applet, offers) {
2368 QString appletCategory = applet->property("X-KDE-PluginInfo-Category").toString();
2369 if (visibleOnly && applet->noDisplay()) {
2370 // we don't want to show the hidden category
2371 continue;
2372 }
2373
2374 //kDebug() << " and we have " << appletCategory;
2375 if (!appletCategory.isEmpty() && !known.contains(appletCategory.toLower())) {
2376 kDebug() << "Unknown category: " << applet->name() << "says it is in the"
2377 << appletCategory << "category which is unknown to us";
2378 appletCategory.clear();
2379 }
2380
2381 if (appletCategory.isEmpty()) {
2382 if (!categories.contains(i18nc("misc category", "Miscellaneous"))) {
2383 categories << i18nc("misc category", "Miscellaneous");
2384 }
2385 } else if (!categories.contains(appletCategory)) {
2386 categories << appletCategory;
2387 }
2388 }
2389
2390 categories.sort();
2391 return categories;
2392}
2393
2394void Applet::setCustomCategories(const QStringList &categories)
2395{
2396 AppletPrivate::s_customCategories = QSet<QString>::fromList(categories);
2397}
2398
2399QStringList Applet::customCategories()
2400{
2401 return AppletPrivate::s_customCategories.toList();
2402}
2403
2404Applet *Applet::loadPlasmoid(const QString &path, uint appletId, const QVariantList &args)
2405{
2406 if (QFile::exists(path + "/metadata.desktop")) {
2407 KService service(path + "/metadata.desktop");
2408 const QStringList& types = service.serviceTypes();
2409
2410 if (types.contains("Plasma/Containment")) {
2411 return new Containment(path, appletId, args);
2412 } else if (types.contains("Plasma/PopupApplet")) {
2413 return new PopupApplet(path, appletId, args);
2414 } else {
2415 return new Applet(path, appletId, args);
2416 }
2417 }
2418
2419 return 0;
2420}
2421
2422Applet *Applet::load(const QString &appletName, uint appletId, const QVariantList &args)
2423{
2424 return PluginLoader::pluginLoader()->loadApplet(appletName, appletId, args);
2425}
2426
2427Applet *Applet::load(const KPluginInfo &info, uint appletId, const QVariantList &args)
2428{
2429 if (!info.isValid()) {
2430 return 0;
2431 }
2432
2433 return load(info.pluginName(), appletId, args);
2434}
2435
2436QVariant Applet::itemChange(GraphicsItemChange change, const QVariant &value)
2437{
2438 QVariant ret = QGraphicsWidget::itemChange(change, value);
2439
2440 //kDebug() << change;
2441 switch (change) {
2442 case ItemSceneHasChanged: {
2443 Corona *newCorona = qobject_cast<Corona *>(qvariant_cast<QGraphicsScene*>(value));
2444 if (newCorona && newCorona->immutability() != Mutable) {
2445 updateConstraints(ImmutableConstraint);
2446 }
2447 }
2448 break;
2449 case ItemParentChange:
2450 if (!d->isContainment) {
2451 Containment *c = containment();
2452 if (d->mainConfig && !c) {
2453 kWarning() << "Configuration object was requested prior to init(), which is too early. "
2454 "Please fix this item:" << parentItem() << value.value<QGraphicsItem *>()
2455 << name();
2456
2457 Applet *newC = dynamic_cast<Applet*>(value.value<QGraphicsItem *>());
2458 if (newC) {
2459 // if this is an applet, and we've just been assigned to our first containment,
2460 // but the applet did something stupid like ask for the config() object prior to
2461 // this happening (e.g. inits ctor) then let's repair that situation for them.
2462 KConfigGroup *old = d->mainConfig;
2463 KConfigGroup appletConfig = newC->config();
2464 appletConfig = KConfigGroup(&appletConfig, "Applets");
2465 d->mainConfig = new KConfigGroup(&appletConfig, QString::number(d->appletId));
2466 old->copyTo(d->mainConfig);
2467 old->deleteGroup();
2468 delete old;
2469 }
2470 }
2471 }
2472 break;
2473 case ItemParentHasChanged:
2474 {
2475 if (isContainment()) {
2476 removeSceneEventFilter(this);
2477 } else {
2478 Containment *c = containment();
2479 if (c && c->containmentType() == Containment::DesktopContainment) {
2480 installSceneEventFilter(this);
2481 } else {
2482 removeSceneEventFilter(this);
2483 }
2484 }
2485 }
2486 break;
2487 case ItemPositionHasChanged:
2488 emit geometryChanged();
2489 // fall through!
2490 case ItemTransformHasChanged:
2491 d->scheduleModificationNotification();
2492 break;
2493 default:
2494 break;
2495 };
2496
2497 return ret;
2498}
2499
2500QPainterPath Applet::shape() const
2501{
2502 if (d->script) {
2503 return d->script->shape();
2504 }
2505
2506 return QGraphicsWidget::shape();
2507}
2508
2509QSizeF Applet::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
2510{
2511 QSizeF hint = QGraphicsWidget::sizeHint(which, constraint);
2512 const FormFactor ff = formFactor();
2513
2514 // in panels make sure that the contents won't exit from the panel
2515 if (which == Qt::MinimumSize) {
2516 if (ff == Horizontal) {
2517 hint.setHeight(0);
2518 } else if (ff == Vertical) {
2519 hint.setWidth(0);
2520 }
2521 }
2522
2523 // enforce a square size in panels
2524 if (d->aspectRatioMode == Plasma::Square) {
2525 if (ff == Horizontal) {
2526 hint.setWidth(size().height());
2527 } else if (ff == Vertical) {
2528 hint.setHeight(size().width());
2529 }
2530 } else if (d->aspectRatioMode == Plasma::ConstrainedSquare) {
2531 //enforce a size not wider than tall
2532 if (ff == Horizontal) {
2533 hint.setWidth(size().height());
2534 //enforce a size not taller than wide
2535 } else if (ff == Vertical && (which == Qt::MaximumSize || size().width() <= KIconLoader::SizeLarge)) {
2536 hint.setHeight(size().width());
2537 }
2538 }
2539
2540 return hint;
2541}
2542
2543void Applet::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
2544{
2545 Q_UNUSED(event)
2546}
2547
2548void Applet::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
2549{
2550 Q_UNUSED(event)
2551}
2552
2553void Applet::timerEvent(QTimerEvent *event)
2554{
2555 if (d->transient) {
2556 d->constraintsTimer.stop();
2557 d->busyWidgetTimer.stop();
2558 if (d->modificationsTimer) {
2559 d->modificationsTimer->stop();
2560 }
2561 return;
2562 }
2563
2564 if (event->timerId() == d->constraintsTimer.timerId()) {
2565 d->constraintsTimer.stop();
2566
2567 // Don't flushPendingConstraints if we're just starting up
2568 // flushPendingConstraints will be called by Corona
2569 if(!(d->pendingConstraints & Plasma::StartupCompletedConstraint)) {
2570 flushPendingConstraintsEvents();
2571 }
2572 } else if (d->modificationsTimer && event->timerId() == d->modificationsTimer->timerId()) {
2573 d->modificationsTimer->stop();
2574 // invalid group, will result in save using the default group
2575 KConfigGroup cg;
2576
2577 save(cg);
2578 emit configNeedsSaving();
2579 } else if (event->timerId() == d->busyWidgetTimer.timerId()) {
2580 if (!d->busyWidget) {
2581 d->createMessageOverlay(false);
2582 d->messageOverlay->opacity = 0;
2583
2584 QGraphicsLinearLayout *mainLayout = new QGraphicsLinearLayout(d->messageOverlay);
2585 d->busyWidget = new Plasma::BusyWidget(d->messageOverlay);
2586 d->busyWidget->setAcceptHoverEvents(false);
2587 d->busyWidget->setAcceptedMouseButtons(Qt::NoButton);
2588 d->messageOverlay->setAcceptHoverEvents(false);
2589 d->messageOverlay->setAcceptedMouseButtons(Qt::NoButton);
2590
2591 mainLayout->addStretch();
2592 mainLayout->addItem(d->busyWidget);
2593 mainLayout->addStretch();
2594 }
2595 }
2596}
2597
2598QRect Applet::screenRect() const
2599{
2600 QGraphicsView *v = view();
2601
2602 if (v) {
2603 QPointF bottomRight = pos();
2604 bottomRight.rx() += size().width();
2605 bottomRight.ry() += size().height();
2606
2607 QPoint tL = v->mapToGlobal(v->mapFromScene(pos()));
2608 QPoint bR = v->mapToGlobal(v->mapFromScene(bottomRight));
2609 return QRect(QPoint(tL.x(), tL.y()), QSize(bR.x() - tL.x(), bR.y() - tL.y()));
2610 }
2611
2612 //The applet doesn't have a view on it.
2613 //So a screenRect isn't relevant.
2614 return QRect(QPoint(0, 0), QSize(0, 0));
2615}
2616
2617void Applet::raise()
2618{
2619 setZValue(++AppletPrivate::s_maxZValue);
2620}
2621
2622void Applet::lower()
2623{
2624 setZValue(--AppletPrivate::s_minZValue);
2625}
2626
2627void AppletPrivate::setIsContainment(bool nowIsContainment, bool forceUpdate)
2628{
2629 if (isContainment == nowIsContainment && !forceUpdate) {
2630 return;
2631 }
2632
2633 isContainment = nowIsContainment;
2634 //FIXME I do not like this function.
2635 //currently it's only called before ctmt/applet init, with (true,true), and I'm going to assume it stays that way.
2636 //if someone calls it at some other time it'll cause headaches. :P
2637
2638 delete mainConfig;
2639 mainConfig = 0;
2640
2641 Containment *c = q->containment();
2642 if (c) {
2643 c->d->checkContainmentFurniture();
2644 }
2645}
2646
2647bool Applet::isContainment() const
2648{
2649 return d->isContainment;
2650}
2651
2652// PRIVATE CLASS IMPLEMENTATION
2653
2654AppletPrivate::AppletPrivate(KService::Ptr service, const KPluginInfo *info, int uniqueID, Applet *applet)
2655 : appletId(uniqueID),
2656 q(applet),
2657 service(0),
2658 preferredBackgroundHints(Applet::StandardBackground),
2659 backgroundHints(Applet::NoBackground),
2660 aspectRatioMode(Plasma::KeepAspectRatio),
2661 immutability(Mutable),
2662 appletDescription(info ? *info : KPluginInfo(service)),
2663 background(0),
2664 mainConfig(0),
2665 pendingConstraints(NoConstraint),
2666 messageOverlay(0),
2667 messageOverlayProxy(0),
2668 busyWidget(0),
2669 script(0),
2670 package(0),
2671 configLoader(0),
2672 actions(AppletPrivate::defaultActions(applet)),
2673 activationAction(0),
2674 itemStatus(UnknownStatus),
2675 preferredSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored),
2676 modificationsTimer(0),
2677 hasConfigurationInterface(false),
2678 failed(false),
2679 isContainment(false),
2680 transient(false),
2681 needsConfig(false),
2682 started(false)
2683{
2684 if (appletId == 0) {
2685 appletId = ++s_maxAppletId;
2686 } else if (appletId > s_maxAppletId) {
2687 s_maxAppletId = appletId;
2688 }
2689
2690 publishUI.publishCheckbox = 0;
2691}
2692
2693AppletPrivate::~AppletPrivate()
2694{
2695 if (activationAction && activationAction->isGlobalShortcutEnabled()) {
2696 //kDebug() << "reseting global action for" << q->name() << activationAction->objectName();
2697 activationAction->forgetGlobalShortcut();
2698 }
2699
2700 delete extender.data();
2701
2702 delete script;
2703 script = 0;
2704 delete package;
2705 package = 0;
2706 delete configLoader;
2707 configLoader = 0;
2708 delete mainConfig;
2709 mainConfig = 0;
2710 delete modificationsTimer;
2711}
2712
2713void AppletPrivate::init(const QString &packagePath)
2714{
2715 // WARNING: do not access config() OR globalConfig() in this method!
2716 // that requires a scene, which is not available at this point
2717 q->setCacheMode(Applet::DeviceCoordinateCache);
2718 q->setAcceptsHoverEvents(true);
2719 q->setFlag(QGraphicsItem::ItemIsFocusable, true);
2720 q->setFocusPolicy(Qt::ClickFocus);
2721 // FIXME: adding here because nothing seems to be doing it in QGraphicsView,
2722 // but it doesn't actually work anyways =/
2723 q->setLayoutDirection(qApp->layoutDirection());
2724
2725 //set a default size before any saved settings are read
2726 QSize size(200, 200);
2727 q->setBackgroundHints(Applet::DefaultBackground);
2728 q->setHasConfigurationInterface(true); //FIXME why not default it to true in the constructor?
2729
2730 QAction *closeApplet = actions->action("remove");
2731 if (closeApplet) {
2732 closeApplet->setText(i18nc("%1 is the name of the applet", "Remove this %1", q->name()));
2733 }
2734
2735 QAction *configAction = actions->action("configure");
2736 if (configAction) {
2737 configAction->setText(i18nc("%1 is the name of the applet", "%1 Settings", q->name()));
2738 }
2739
2740 QObject::connect(q, SIGNAL(activate()), q, SLOT(setFocus()));
2741 if (!appletDescription.isValid()) {
2742 kDebug() << "Check your constructor! "
2743 << "You probably want to be passing in a Service::Ptr "
2744 << "or a QVariantList with a valid storageid as arg[0].";
2745 q->resize(size);
2746 return;
2747 }
2748
2749 QVariant s = appletDescription.property("X-Plasma-DefaultSize");
2750 if (s.isValid()) {
2751 size = s.toSize();
2752 }
2753 //kDebug() << "size" << size;
2754 q->resize(size);
2755
2756 QString api = appletDescription.property("X-Plasma-API").toString();
2757
2758 // we have a scripted plasmoid
2759 if (!api.isEmpty()) {
2760 // find where the Package is
2761 QString path = packagePath.isEmpty() ? appletDescription.pluginName() : packagePath;
2762 // create the package and see if we have something real
2763 PackageStructure::Ptr structure = Plasma::packageStructure(api, Plasma::AppletComponent);
2764 package = new Package(path, structure);
2765 //kDebug() << "***** package is" << package->path();
2766
2767 if (package->isValid()) {
2768 // now we try and set up the script engine.
2769 // it will be parented to this applet and so will get
2770 // deleted when the applet does
2771
2772 script = Plasma::loadScriptEngine(api, q);
2773 if (!script) {
2774 delete package;
2775 package = 0;
2776 q->setFailedToLaunch(true,
2777 i18nc("API or programming language the widget was written in, name of the widget",
2778 "Could not create a %1 ScriptEngine for the %2 widget.",
2779 api, appletDescription.name()));
2780 }
2781 } else {
2782 q->setFailedToLaunch(true, i18nc("Package file, name of the widget",
2783 "Could not open the %1 package required for the %2 widget.",
2784 appletDescription.pluginName(), appletDescription.name()));
2785 delete package;
2786 package = 0;
2787 }
2788 }
2789}
2790
2791// put all setup routines for script here. at this point we can assume that
2792// package exists and that we have a script engine
2793void AppletPrivate::setupScriptSupport()
2794{
2795 if (!package) {
2796 return;
2797 }
2798
2799 kDebug() << "setting up script support, package is in" << package->path()
2800 << "which is a" << package->structure()->type() << "package"
2801 << ", main script is" << package->filePath("mainscript");
2802
2803 QString translationsPath = package->filePath("translations");
2804 if (!translationsPath.isEmpty()) {
2805 //FIXME: we should _probably_ use a KComponentData to segregate the applets
2806 // from each other; but I want to get the basics working first :)
2807 KGlobal::dirs()->addResourceDir("locale", translationsPath);
2808 KGlobal::locale()->insertCatalog(package->metadata().pluginName());
2809 }
2810
2811 QString xmlPath = package->filePath("mainconfigxml");
2812 if (!xmlPath.isEmpty()) {
2813 QFile file(xmlPath);
2814 KConfigGroup config = q->config();
2815 configLoader = new ConfigLoader(&config, &file);
2816 QObject::connect(configLoader, SIGNAL(configChanged()), q, SLOT(propagateConfigChanged()));
2817 }
2818
2819 if (!package->filePath("mainconfigui").isEmpty()) {
2820 q->setHasConfigurationInterface(true);
2821 }
2822}
2823
2824QString AppletPrivate::globalName() const
2825{
2826 if (!appletDescription.isValid()) {
2827 return QString();
2828 }
2829
2830 return appletDescription.service()->library();
2831}
2832
2833QString AppletPrivate::instanceName()
2834{
2835 if (!appletDescription.isValid()) {
2836 return QString();
2837 }
2838
2839 return appletDescription.service()->library() + QString::number(appletId);
2840}
2841
2842void AppletPrivate::scheduleConstraintsUpdate(Plasma::Constraints c)
2843{
2844 // Don't start up a timer if we're just starting up
2845 // flushPendingConstraints will be called by Corona
2846 if (started && !constraintsTimer.isActive() && !(c & Plasma::StartupCompletedConstraint)) {
2847 constraintsTimer.start(0, q);
2848 }
2849
2850 if (c & Plasma::StartupCompletedConstraint) {
2851 started = true;
2852 }
2853
2854 pendingConstraints |= c;
2855}
2856
2857void AppletPrivate::scheduleModificationNotification()
2858{
2859 // modificationsTimer is not allocated until we get our notice of being started
2860 if (modificationsTimer) {
2861 // schedule a save
2862 if (modificationsTimer->isActive()) {
2863 modificationsTimer->stop();
2864 }
2865
2866 modificationsTimer->start(1000, q);
2867 }
2868}
2869
2870KConfigGroup *AppletPrivate::mainConfigGroup()
2871{
2872 if (mainConfig) {
2873 return mainConfig;
2874 }
2875
2876 bool newGroup = false;
2877 if (isContainment) {
2878 Corona *corona = qobject_cast<Corona*>(q->scene());
2879 KConfigGroup containmentConfig;
2880 //kDebug() << "got a corona, baby?" << (QObject*)corona << (QObject*)q;
2881
2882 if (corona) {
2883 containmentConfig = KConfigGroup(corona->config(), "Containments");
2884 } else {
2885 containmentConfig = KConfigGroup(KGlobal::config(), "Containments");
2886 }
2887
2888 if (package && !containmentConfig.hasGroup(QString::number(appletId))) {
2889 newGroup = true;
2890 }
2891
2892 mainConfig = new KConfigGroup(&containmentConfig, QString::number(appletId));
2893 } else {
2894 KConfigGroup appletConfig;
2895
2896 Containment *c = q->containment();
2897 Applet *parentApplet = qobject_cast<Applet *>(q->parent());
2898 if (parentApplet && parentApplet != static_cast<Applet *>(c)) {
2899 // this applet is nested inside another applet! use it's config
2900 // as the parent group in the config
2901 appletConfig = parentApplet->config();
2902 appletConfig = KConfigGroup(&appletConfig, "Applets");
2903 } else if (c) {
2904 // applet directly in a Containment, as usual
2905 appletConfig = c->config();
2906 appletConfig = KConfigGroup(&appletConfig, "Applets");
2907 } else {
2908 kWarning() << "requesting config for" << q->name() << "without a containment!";
2909 appletConfig = KConfigGroup(KGlobal::config(), "Applets");
2910 }
2911
2912 if (package && !appletConfig.hasGroup(QString::number(appletId))) {
2913 newGroup = true;
2914 }
2915
2916 mainConfig = new KConfigGroup(&appletConfig, QString::number(appletId));
2917 }
2918
2919 if (newGroup) {
2920 //see if we have a default configuration in our package
2921 const QString defaultConfigFile = q->package()->filePath("defaultconfig");
2922 if (!defaultConfigFile.isEmpty()) {
2923 kDebug() << "copying default config: " << q->package()->filePath("defaultconfig");
2924 KConfigGroup defaultConfig(KSharedConfig::openConfig(defaultConfigFile)->group("Configuration"));
2925 defaultConfig.copyTo(mainConfig);
2926 }
2927 }
2928
2929 return mainConfig;
2930}
2931
2932QString AppletPrivate::visibleFailureText(const QString &reason)
2933{
2934 QString text;
2935
2936 if (reason.isEmpty()) {
2937 text = i18n("This object could not be created.");
2938 } else {
2939 QString r = reason;
2940 r.replace('\n', "<br/>");
2941 text = i18n("This object could not be created for the following reason:<p><b>%1</b></p>", r);
2942 }
2943
2944 return text;
2945}
2946
2947void AppletPrivate::themeChanged()
2948{
2949 if (background) {
2950 //do again the translucent background fallback
2951 q->setBackgroundHints(backgroundHints);
2952
2953 qreal left;
2954 qreal right;
2955 qreal top;
2956 qreal bottom;
2957 background->getMargins(left, top, right, bottom);
2958 q->setContentsMargins(left, right, top, bottom);
2959 }
2960 q->update();
2961}
2962
2963void AppletPrivate::resetConfigurationObject()
2964{
2965 // make sure mainConfigGroup exists in all cases
2966 mainConfigGroup();
2967
2968 mainConfig->deleteGroup();
2969 delete mainConfig;
2970 mainConfig = 0;
2971
2972 Corona * corona = qobject_cast<Corona*>(q->scene());
2973 if (corona) {
2974 corona->requireConfigSync();
2975 }
2976}
2977
2978void AppletPrivate::handleDisappeared(AppletHandle *h)
2979{
2980 if (h == handle.data()) {
2981 h->detachApplet();
2982 QGraphicsScene *scene = q->scene();
2983 if (scene && h->scene() == scene) {
2984 scene->removeItem(h);
2985 }
2986 h->deleteLater();
2987 }
2988}
2989
2990void ContainmentPrivate::checkRemoveAction()
2991{
2992 q->enableAction("remove", q->immutability() == Mutable);
2993}
2994
2995
2996uint AppletPrivate::s_maxAppletId = 0;
2997int AppletPrivate::s_maxZValue = 0;
2998int AppletPrivate::s_minZValue = 0;
2999PackageStructure::Ptr AppletPrivate::packageStructure(0);
3000QSet<QString> AppletPrivate::s_customCategories;
3001
3002AppletOverlayWidget::AppletOverlayWidget(QGraphicsWidget *parent)
3003 : QGraphicsWidget(parent),
3004 opacity(0.4)
3005{
3006 resize(parent->size());
3007}
3008
3009void AppletOverlayWidget::destroy()
3010{
3011 Animation *anim = Plasma::Animator::create(Plasma::Animator::DisappearAnimation);
3012 if (anim) {
3013 connect(anim, SIGNAL(finished()), this, SLOT(overlayAnimationComplete()));
3014 anim->setTargetWidget(this);
3015 anim->start();
3016 } else {
3017 overlayAnimationComplete();
3018 }
3019}
3020
3021void AppletOverlayWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
3022{
3023 event->accept();
3024}
3025
3026void AppletOverlayWidget::overlayAnimationComplete()
3027{
3028 if (scene()) {
3029 scene()->removeItem(this);
3030 }
3031 deleteLater();
3032}
3033
3034void AppletOverlayWidget::paint(QPainter *painter,
3035 const QStyleOptionGraphicsItem *option,
3036 QWidget *widget)
3037{
3038 Q_UNUSED(option)
3039 Q_UNUSED(widget)
3040
3041 if (qFuzzyCompare(1, 1+opacity)) {
3042 return;
3043 }
3044
3045 QColor wash = Plasma::Theme::defaultTheme()->color(Theme::BackgroundColor);
3046 wash.setAlphaF(opacity);
3047
3048 Applet *applet = qobject_cast<Applet *>(parentWidget());
3049
3050
3051 QPainterPath backgroundShape;
3052 if (!applet || applet->backgroundHints() & Applet::StandardBackground) {
3053 //FIXME: a resize here is nasty, but perhaps still better than an eventfilter just for that..
3054 if (parentWidget()->contentsRect().size() != size()) {
3055 resize(parentWidget()->contentsRect().size());
3056 }
3057 backgroundShape = PaintUtils::roundedRectangle(contentsRect(), 5);
3058 } else {
3059 backgroundShape = shape();
3060 }
3061
3062 painter->setRenderHints(QPainter::Antialiasing);
3063 painter->fillPath(backgroundShape, wash);
3064}
3065
3066#if QT_VERSION >= 0x040700
3067// in QGraphicsWidget now; preserve BC by implementing it as a protected method
3068void Applet::geometryChanged()
3069{
3070 emit QGraphicsWidget::geometryChanged();
3071}
3072#endif
3073
3074} // Plasma namespace
3075
3076#include "applet.moc"
3077#include "private/applet_p.moc"
3078