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

Plasma

applet.cpp

Go to the documentation of this file.
00001 #ifndef APPLET_CPP
00002 #define APPLET_CPP
00003 /*
00004  *   Copyright 2005 by Aaron Seigo <aseigo@kde.org>
00005  *   Copyright 2007 by Riccardo Iaconelli <riccardo@kde.org>
00006  *   Copyright 2008 by Ménard Alexis <darktears31@gmail.com>
00007  *
00008  *   This program is free software; you can redistribute it and/or modify
00009  *   it under the terms of the GNU Library General Public License as
00010  *   published by the Free Software Foundation; either version 2, or
00011  *   (at your option) any later version.
00012  *
00013  *   This program is distributed in the hope that it will be useful,
00014  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  *   GNU General Public License for more details
00017  *
00018  *   You should have received a copy of the GNU Library General Public
00019  *   License along with this program; if not, write to the
00020  *   Free Software Foundation, Inc.,
00021  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00022  */
00023 
00024 #include "applet.h"
00025 #include "private/applet_p.h"
00026 
00027 #include <cmath>
00028 #include <limits>
00029 
00030 #include <QApplication>
00031 #include <QEvent>
00032 #include <QFile>
00033 #include <QGraphicsGridLayout>
00034 #include <QGraphicsSceneMouseEvent>
00035 #include <QGraphicsView>
00036 #include <QLabel>
00037 #include <QList>
00038 #include <QGraphicsLinearLayout>
00039 #include <QPainter>
00040 #include <QSize>
00041 #include <QStyleOptionGraphicsItem>
00042 #include <QTextDocument>
00043 #include <QUiLoader>
00044 #include <QVBoxLayout>
00045 #include <QWidget>
00046 
00047 #include <kaction.h>
00048 #include <kactioncollection.h>
00049 #include <kauthorized.h>
00050 #include <kcolorscheme.h>
00051 #include <kconfigdialog.h>
00052 #include <kdialog.h>
00053 #include <kicon.h>
00054 #include <kiconloader.h>
00055 #include <kkeysequencewidget.h>
00056 #include <kplugininfo.h>
00057 #include <kstandarddirs.h>
00058 #include <kservice.h>
00059 #include <kservicetypetrader.h>
00060 #include <kshortcut.h>
00061 #include <kwindowsystem.h>
00062 #include <kpushbutton.h>
00063 
00064 #include <solid/powermanagement.h>
00065 
00066 #include "configloader.h"
00067 #include "containment.h"
00068 #include "corona.h"
00069 #include "dataenginemanager.h"
00070 #include "extender.h"
00071 #include "extenderitem.h"
00072 #include "package.h"
00073 #include "plasma.h"
00074 #include "scripting/appletscript.h"
00075 #include "svg.h"
00076 #include "framesvg.h"
00077 #include "private/framesvg_p.h"
00078 #include "popupapplet.h"
00079 #include "theme.h"
00080 #include "view.h"
00081 #include "widgets/iconwidget.h"
00082 #include "widgets/label.h"
00083 #include "widgets/pushbutton.h"
00084 #include "widgets/busywidget.h"
00085 #include "tooltipmanager.h"
00086 #include "wallpaper.h"
00087 #include "paintutils.h"
00088 
00089 #include "private/containment_p.h"
00090 #include "private/extenderapplet_p.h"
00091 #include "private/packages_p.h"
00092 #include "private/popupapplet_p.h"
00093 #include "private/toolbox_p.h"
00094 
00095 //#define DYNAMIC_SHADOWS
00096 namespace Plasma
00097 {
00098 
00099 Applet::Applet(QGraphicsItem *parent, const QString &serviceID, uint appletId)
00100     :  QGraphicsWidget(parent),
00101        d(new AppletPrivate(KService::serviceByStorageId(serviceID), appletId, this))
00102 {
00103     // WARNING: do not access config() OR globalConfig() in this method!
00104     //          that requires a scene, which is not available at this point
00105     d->init();
00106 }
00107 
00108 Applet::Applet(QGraphicsItem *parent,
00109                const QString &serviceID,
00110                uint appletId,
00111                const QVariantList &args)
00112     :  QGraphicsWidget(parent),
00113        d(new AppletPrivate(KService::serviceByStorageId(serviceID), appletId, this))
00114 {
00115     // WARNING: do not access config() OR globalConfig() in this method!
00116     //          that requires a scene, which is not available at this point
00117 
00118     QVariantList &mutableArgs = const_cast<QVariantList &>(args);
00119     if (!mutableArgs.isEmpty()) {
00120         mutableArgs.removeFirst();
00121 
00122         if (!mutableArgs.isEmpty()) {
00123             mutableArgs.removeFirst();
00124         }
00125     }
00126 
00127     d->args = mutableArgs;
00128 
00129     d->init();
00130 }
00131 
00132 Applet::Applet(QObject *parentObject, const QVariantList &args)
00133     :  QGraphicsWidget(0),
00134        d(new AppletPrivate(
00135              KService::serviceByStorageId(args.count() > 0 ? args[0].toString() : QString()),
00136              args.count() > 1 ? args[1].toInt() : 0, this))
00137 {
00138     // now remove those first two items since those are managed by Applet and subclasses shouldn't
00139     // need to worry about them. yes, it violates the constness of this var, but it lets us add
00140     // or remove items later while applets can just pretend that their args always start at 0
00141     QVariantList &mutableArgs = const_cast<QVariantList &>(args);
00142     if (!mutableArgs.isEmpty()) {
00143         mutableArgs.removeFirst();
00144 
00145         if (!mutableArgs.isEmpty()) {
00146             mutableArgs.removeFirst();
00147         }
00148     }
00149 
00150     d->args = mutableArgs;
00151 
00152     setParent(parentObject);
00153 
00154     // WARNING: do not access config() OR globalConfig() in this method!
00155     //          that requires a scene, which is not available at this point
00156     d->init();
00157 
00158     // the brain damage seen in the initialization list is due to the
00159     // inflexibility of KService::createInstance
00160 }
00161 
00162 Applet::~Applet()
00163 {
00164     //let people know that i will die
00165     emit appletDestroyed(this);
00166 
00167     if (!d->transient && d->extender) {
00168         //This would probably be nicer if it was located in extender. But in it's dtor, this won't
00169         //work since when that get's called, the applet's config() isn't accessible anymore. (same
00170         //problem with calling saveState(). Doing this in saveState() might be a possibility, but
00171         //that would require every extender savestate implementation to call it's parent function,
00172         //which isn't very nice.
00173         d->extender->saveState();
00174 
00175         foreach (ExtenderItem *item, d->extender->attachedItems()) {
00176             if (item->autoExpireDelay()) {
00177                 //destroy temporary extender items, or items that aren't detached, so their
00178                 //configuration won't linger after a plasma restart.
00179                 item->destroy();
00180             }
00181         }
00182     }
00183 
00184     // clean up our config dialog, if any
00185     delete KConfigDialog::exists(d->configDialogId());
00186     delete d;
00187 }
00188 
00189 PackageStructure::Ptr Applet::packageStructure()
00190 {
00191     if (!AppletPrivate::packageStructure) {
00192         AppletPrivate::packageStructure = new PlasmoidPackage();
00193     }
00194 
00195     return AppletPrivate::packageStructure;
00196 }
00197 
00198 void Applet::init()
00199 {
00200     if (d->script) {
00201         if (d->package) {
00202             d->setupScriptSupport();
00203         }
00204 
00205         if (!d->script->init()) {
00206             setFailedToLaunch(true, i18n("Script initialization failed"));
00207             delete d->script;
00208             d->script = 0;
00209             delete d->package;
00210             d->package = 0;
00211         }
00212     }
00213 }
00214 
00215 uint Applet::id() const
00216 {
00217     return d->appletId;
00218 }
00219 
00220 void Applet::save(KConfigGroup &g) const
00221 {
00222     if (d->transient) {
00223         return;
00224     }
00225 
00226     KConfigGroup group = g;
00227     if (!group.isValid()) {
00228         group = *d->mainConfigGroup();
00229     }
00230 
00231     //kDebug() << "saving to" << group.name();
00232     // we call the dptr member directly for locked since isImmutable()
00233     // also checks kiosk and parent containers
00234     group.writeEntry("immutability", (int)d->immutability);
00235     group.writeEntry("plugin", pluginName());
00236 
00237     group.writeEntry("geometry", geometry());
00238     group.writeEntry("zvalue", zValue());
00239 
00240     if (!d->started) {
00241         return;
00242     }
00243 
00244     //FIXME: for containments, we need to have some special values here w/regards to
00245     //       screen affinity (e.g. "bottom of screen 0")
00246     //kDebug() << pluginName() << "geometry is" << geometry()
00247     //         << "pos is" << pos() << "bounding rect is" << boundingRect();
00248     if (transform() == QTransform()) {
00249         group.deleteEntry("transform");
00250     } else {
00251         QList<qreal> m;
00252         QTransform t = transform();
00253         m << t.m11() << t.m12() << t.m13() << t.m21() << t.m22() << t.m23() << t.m31() << t.m32() << t.m33();
00254         group.writeEntry("transform", m);
00255         //group.writeEntry("transform", transformToString(transform()));
00256     }
00257 
00258     KConfigGroup appletConfigGroup(&group, "Configuration");
00259     saveState(appletConfigGroup);
00260 
00261     if (d->configLoader) {
00262         // we're saving so we know its changed, we don't need or want the configChanged
00263         // signal bubbling up at this point due to that
00264         disconnect(d->configLoader, SIGNAL(configChanged()), this, SLOT(configChanged()));
00265         d->configLoader->writeConfig();
00266         connect(d->configLoader, SIGNAL(configChanged()), this, SLOT(configChanged()));
00267     }
00268 }
00269 
00270 void Applet::restore(KConfigGroup &group)
00271 {
00272     QList<qreal> m = group.readEntry("transform", QList<qreal>());
00273     if (m.count() == 9) {
00274         QTransform t(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
00275         setTransform(t);
00276     }
00277 
00278     qreal z = group.readEntry("zvalue", 0);
00279 
00280     if (z >= AppletPrivate::s_maxZValue) {
00281         AppletPrivate::s_maxZValue = z;
00282     }
00283 
00284     if (z > 0) {
00285         setZValue(z);
00286     }
00287 
00288     setImmutability((ImmutabilityType)group.readEntry("immutability", (int)Mutable));
00289 
00290     QRectF geom = group.readEntry("geometry", QRectF());
00291     if (geom.isValid()) {
00292         setGeometry(geom);
00293     }
00294 
00295     KConfigGroup shortcutConfig(&group, "Shortcuts");
00296     QString shortcutText = shortcutConfig.readEntryUntranslated("global", QString());
00297     if (!shortcutText.isEmpty()) {
00298         setGlobalShortcut(KShortcut(shortcutText));
00299         /*
00300         kDebug() << "got global shortcut for" << name() << "of" << QKeySequence(shortcutText);
00301         kDebug() << "set to" << d->activationAction->objectName()
00302                  << d->activationAction->globalShortcut().primary();
00303                  */
00304     }
00305 
00306     // local shortcut, if any
00307     //TODO: implement; the shortcut will need to be registered with the containment
00308     /*
00309     shortcutText = shortcutConfig.readEntryUntranslated("local", QString());
00310     if (!shortcutText.isEmpty()) {
00311         //TODO: implement; the shortcut
00312     }
00313     */
00314 }
00315 
00316 void AppletPrivate::setFocus()
00317 {
00318     //kDebug() << "setting focus";
00319     q->setFocus(Qt::ShortcutFocusReason);
00320 }
00321 
00322 void Applet::setFailedToLaunch(bool failed, const QString &reason)
00323 {
00324     if (d->failed == failed) {
00325         if (failed && !reason.isEmpty()) {
00326             foreach (QGraphicsItem *item, QGraphicsItem::children()) {
00327                 Label *l = dynamic_cast<Label *>(item);
00328                 if (l) {
00329                     l->setText(d->visibleFailureText(reason));
00330                 }
00331             }
00332         }
00333         return;
00334     }
00335 
00336     d->failed = failed;
00337     prepareGeometryChange();
00338 
00339     qDeleteAll(QGraphicsItem::children());
00340     setLayout(0);
00341 
00342     if (failed) {
00343         setBackgroundHints(d->backgroundHints|StandardBackground);
00344 
00345         QGraphicsLinearLayout *failureLayout = new QGraphicsLinearLayout(this);
00346         failureLayout->setContentsMargins(0, 0, 0, 0);
00347 
00348         IconWidget *failureIcon = new IconWidget(this);
00349         failureIcon->setIcon(KIcon("dialog-error"));
00350         failureLayout->addItem(failureIcon);
00351 
00352         Label *failureWidget = new Plasma::Label(this);
00353         failureWidget->setText(d->visibleFailureText(reason));
00354         QLabel *label = failureWidget->nativeWidget();
00355         label->setWordWrap(true);
00356         failureLayout->addItem(failureWidget);
00357 
00358         Plasma::ToolTipManager::self()->registerWidget(failureIcon);
00359         Plasma::ToolTipContent data(i18n("Unable to load the widget"), reason,
00360                                     KIcon("dialog-error"));
00361         Plasma::ToolTipManager::self()->setContent(failureIcon, data);
00362 
00363         setLayout(failureLayout);
00364         resize(300, 250);
00365         setMinimumSize(failureLayout->minimumSize());
00366         d->background->resizeFrame(geometry().size());
00367     }
00368 
00369     update();
00370 }
00371 
00372 void Applet::saveState(KConfigGroup &group) const
00373 {
00374     if (group.config()->name() != config().config()->name()) {
00375         // we're being saved to a different file!
00376         // let's just copy the current values in our configuration over
00377         KConfigGroup c = config();
00378         c.copyTo(&group);
00379     }
00380 }
00381 
00382 KConfigGroup Applet::config(const QString &group) const
00383 {
00384     if (d->transient) {
00385         return KConfigGroup(KGlobal::config(), "PlasmaTransientsConfig");
00386     }
00387 
00388     KConfigGroup cg = config();
00389     return KConfigGroup(&cg, group);
00390 }
00391 
00392 KConfigGroup Applet::config() const
00393 {
00394     if (d->transient) {
00395         return KConfigGroup(KGlobal::config(), "PlasmaTransientsConfig");
00396     }
00397 
00398     if (d->isContainment) {
00399         return *(d->mainConfigGroup());
00400     }
00401 
00402     return KConfigGroup(d->mainConfigGroup(), "Configuration");
00403 }
00404 
00405 KConfigGroup Applet::globalConfig() const
00406 {
00407     KConfigGroup globalAppletConfig;
00408     QString group = isContainment() ? "ContainmentGlobals" : "AppletGlobals";
00409 
00410     Corona *corona = qobject_cast<Corona*>(scene());
00411     if (corona) {
00412         KSharedConfig::Ptr coronaConfig = corona->config();
00413         globalAppletConfig = KConfigGroup(coronaConfig, group);
00414     } else {
00415         globalAppletConfig = KConfigGroup(KGlobal::config(), group);
00416     }
00417 
00418     return KConfigGroup(&globalAppletConfig, d->globalName());
00419 }
00420 
00421 void Applet::destroy()
00422 {
00423     if (immutability() != Mutable || d->transient || !d->started) {
00424         return; //don't double delete
00425     }
00426 
00427     d->transient = true;
00428 
00429     if (isContainment()) {
00430         d->cleanUpAndDelete();
00431     } else {
00432         connect(Animator::self(), SIGNAL(animationFinished(QGraphicsItem*,Plasma::Animator::Animation)),
00433                 this, SLOT(appletAnimationComplete(QGraphicsItem*,Plasma::Animator::Animation)));
00434         Animator::self()->animateItem(this, Animator::DisappearAnimation);
00435     }
00436 }
00437 
00438 bool Applet::destroyed() const
00439 {
00440     return d->transient;
00441 }
00442 
00443 void AppletPrivate::appletAnimationComplete(QGraphicsItem *item, Plasma::Animator::Animation anim)
00444 {
00445     if (anim != Animator::DisappearAnimation || item != q) {
00446         return; //it's not our time yet
00447     }
00448 
00449     cleanUpAndDelete();
00450 }
00451 
00452 void AppletPrivate::selectItemToDestroy()
00453 {
00454     //FIXME: this will not work nicely with multiple screens and being zoomed out!
00455     if (isContainment) {
00456         QGraphicsView *view = q->view();
00457         if (view && view->transform().isScaling() &&
00458             q->scene()->focusItem() != q) {
00459             QGraphicsItem *focus = q->scene()->focusItem();
00460 
00461             if (focus) {
00462                 Containment *toDestroy = dynamic_cast<Containment*>(focus->topLevelItem());
00463 
00464                 if (toDestroy) {
00465                     toDestroy->destroy();
00466                     return;
00467                 }
00468             }
00469         }
00470     }
00471 
00472     q->destroy();
00473 }
00474 
00475 void AppletPrivate::updateRect(const QRectF &rect)
00476 {
00477     q->update(rect);
00478 }
00479 
00480 void AppletPrivate::cleanUpAndDelete()
00481 {
00482     //kDebug() << "???????????????? DESTROYING APPLET" << q->name() << q->scene() << " ???????????????????????????";
00483     QGraphicsWidget *parent = dynamic_cast<QGraphicsWidget *>(q->parentItem());
00484     //it probably won't matter, but right now if there are applethandles, *they* are the parent.
00485     //not the containment.
00486 
00487     //is the applet in a containment and does the containment have a layout?
00488     //if yes, we remove the applet in the layout
00489     if (parent && parent->layout()) {
00490         QGraphicsLayout *l = parent->layout();
00491         for (int i = 0; i < l->count(); ++i) {
00492             if (q == l->itemAt(i)) {
00493                 l->removeAt(i);
00494                 break;
00495             }
00496         }
00497     }
00498 
00499     if (configLoader) {
00500         configLoader->setDefaults();
00501     }
00502 
00503     resetConfigurationObject();
00504 
00505     q->scene()->removeItem(q);
00506     q->deleteLater();
00507 }
00508 
00509 void AppletPrivate::createMessageOverlay(bool usePopup)
00510 {
00511     if (messageOverlay) {
00512         qDeleteAll(messageOverlay->children());
00513         messageOverlay->setLayout(0);
00514     }
00515 
00516     PopupApplet *popup = qobject_cast<Plasma::PopupApplet*>(q);
00517 
00518     if (!messageOverlay) {
00519         if (usePopup && popup && popup->widget()) {
00520             messageOverlayProxy = new QGraphicsProxyWidget(q);
00521             messageOverlayProxy->setWidget(popup->widget());
00522             messageOverlay = new AppletOverlayWidget(messageOverlayProxy);
00523         } else if (usePopup && popup && popup->graphicsWidget() &&
00524                    popup->graphicsWidget() != extender) {
00525             messageOverlay = new AppletOverlayWidget(popup->graphicsWidget());
00526         } else {
00527             messageOverlay = new AppletOverlayWidget(q);
00528         }
00529     }
00530 
00531     if (usePopup && popup && popup->widget()) {
00532         // popupapplet with widget()
00533         messageOverlay->setGeometry(popup->widget()->contentsRect());
00534     } else if (usePopup && popup && popup->graphicsWidget() &&
00535                popup->graphicsWidget() != extender) {
00536         // popupapplet with graphicsWidget()
00537         messageOverlay->setGeometry(popup->graphicsWidget()->boundingRect());
00538     } else {
00539         // normal applet
00540         messageOverlay->setGeometry(q->contentsRect());
00541 
00542         // raise the overlay above all the other children!
00543         int zValue = 100;
00544         foreach (QGraphicsItem *child, q->QGraphicsItem::children()) {
00545             if (child->zValue() > zValue) {
00546                 zValue = child->zValue() + 1;
00547             }
00548         }
00549         messageOverlay->setZValue(zValue);
00550     }
00551 }
00552 
00553 void AppletPrivate::destroyMessageOverlay()
00554 {
00555     //TODO: fade out? =)
00556     if (!messageOverlay) {
00557         return;
00558     }
00559 
00560     messageOverlay->destroy();
00561     messageOverlay = 0;
00562 
00563     if (messageOverlayProxy) {
00564         messageOverlayProxy->setWidget(0);
00565         delete messageOverlayProxy;
00566         messageOverlayProxy = 0;
00567     }
00568 
00569     MessageButton buttonCode = ButtonNo;
00570     //find out if we're disappearing because of a button press
00571     PushButton *button = qobject_cast<PushButton *>(q->sender());
00572     if (button) {
00573         if (button->text() == i18n("Ok")) {
00574             buttonCode = ButtonOk;
00575         }
00576         if (button->text() == i18n("Yes")) {
00577             buttonCode = ButtonYes;
00578         }
00579         if (button->text() == i18n("No")) {
00580             buttonCode = ButtonNo;
00581         }
00582         if (button->text() == i18n("Cancel")) {
00583             buttonCode = ButtonCancel;
00584         }
00585 
00586         emit q->messageButtonPressed(buttonCode);
00587     }
00588 }
00589 
00590 ConfigLoader *Applet::configScheme() const
00591 {
00592     return d->configLoader;
00593 }
00594 
00595 DataEngine *Applet::dataEngine(const QString &name) const
00596 {
00597     return d->dataEngine(name);
00598 }
00599 
00600 const Package *Applet::package() const
00601 {
00602     return d->package;
00603 }
00604 
00605 QGraphicsView *Applet::view() const
00606 {
00607     // It's assumed that we won't be visible on more than one view here.
00608     // Anything that actually needs view() should only really care about
00609     // one of them anyway though.
00610     if (!scene()) {
00611         return 0;
00612     }
00613 
00614     QGraphicsView *found = 0;
00615     QGraphicsView *possibleFind = 0;
00616     //kDebug() << "looking through" << scene()->views().count() << "views";
00617     foreach (QGraphicsView *view, scene()->views()) {
00618         //kDebug() << "     checking" << view << view->sceneRect()
00619         //         << "against" << sceneBoundingRect() << scenePos();
00620         if (view->sceneRect().intersects(sceneBoundingRect()) ||
00621             view->sceneRect().contains(scenePos())) {
00622             //kDebug() << "     found something!" << view->isActiveWindow();
00623             if (view->isActiveWindow()) {
00624                 found = view;
00625             } else {
00626                 possibleFind = view;
00627             }
00628         }
00629     }
00630 
00631     return found ? found : possibleFind;
00632 }
00633 
00634 QRectF Applet::mapFromView(const QGraphicsView *view, const QRect &rect) const
00635 {
00636     // Why is this adjustment needed? Qt calculation error?
00637     return mapFromScene(view->mapToScene(rect)).boundingRect().adjusted(0, 0, 1, 1);
00638 }
00639 
00640 QRect Applet::mapToView(const QGraphicsView *view, const QRectF &rect) const
00641 {
00642     // Why is this adjustment needed? Qt calculation error?
00643     return view->mapFromScene(mapToScene(rect)).boundingRect().adjusted(0, 0, -1, -1);
00644 }
00645 
00646 QPoint Applet::popupPosition(const QSize &s) const
00647 {
00648     Corona * corona = qobject_cast<Corona*>(scene());
00649     Q_ASSERT(corona);
00650     return corona->popupPosition(this, s);
00651 }
00652 
00653 void Applet::updateConstraints(Plasma::Constraints constraints)
00654 {
00655     d->scheduleConstraintsUpdate(constraints);
00656 }
00657 
00658 void Applet::constraintsEvent(Plasma::Constraints constraints)
00659 {
00660     //NOTE: do NOT put any code in here that reacts to constraints updates
00661     //      as it will not get called for any applet that reimplements constraintsEvent
00662     //      without calling the Applet:: version as well, which it shouldn't need to.
00663     //      INSTEAD put such code into flushPendingConstraintsEvents
00664     Q_UNUSED(constraints)
00665     //kDebug() << constraints << "constraints are FormFactor: " << formFactor()
00666     //         << ", Location: " << location();
00667     if (d->script) {
00668         d->script->constraintsEvent(constraints);
00669     }
00670 }
00671 
00672 void Applet::initExtenderItem(ExtenderItem *item)
00673 {
00674     if (d->script) {
00675         emit extenderItemRestored(item);
00676     } else {
00677         kWarning() << "Missing implementation of initExtenderItem in the applet "
00678                    << item->config().readEntry("SourceAppletPluginName", "")
00679                    << "!\n Any applet that uses extenders should implement initExtenderItem to "
00680                    << "instantiate a widget. Destroying the item...";
00681         item->destroy();
00682     }
00683 }
00684 
00685 Extender *Applet::extender() const
00686 {
00687     if (!d->extender) {
00688         new Extender(const_cast<Applet*>(this));
00689     }
00690 
00691     return d->extender;
00692 }
00693 
00694 void Applet::setBusy(bool busy)
00695 {
00696     if (busy) {
00697         if (!d->busyWidget) {
00698             d->createMessageOverlay(false);
00699             d->messageOverlay->opacity = 0;
00700 
00701             QGraphicsLinearLayout *mainLayout = new QGraphicsLinearLayout(d->messageOverlay);
00702             d->busyWidget = new Plasma::BusyWidget(d->messageOverlay);
00703             d->busyWidget->setAcceptHoverEvents(false);
00704             d->busyWidget->setAcceptedMouseButtons(Qt::NoButton);
00705             d->messageOverlay->setAcceptHoverEvents(false);
00706 
00707             mainLayout->addStretch();
00708             mainLayout->addItem(d->busyWidget);
00709             mainLayout->addStretch();
00710         }
00711     } else if (d->busyWidget) {
00712         //will be deleted by its parent
00713         d->busyWidget = 0;
00714         d->destroyMessageOverlay();
00715     }
00716 }
00717 
00718 bool Applet::isBusy() const
00719 {
00720     return d->busyWidget && d->busyWidget->isVisible();
00721 }
00722 
00723 QString Applet::name() const
00724 {
00725     if (d->isContainment) {
00726         if (!d->appletDescription.isValid()) {
00727             return i18n("Unknown Activity");
00728         }
00729 
00730         const Containment *c = qobject_cast<const Containment*>(this);
00731         if (c && !c->activity().isNull()) {
00732             return i18n("%1 Activity", c->activity());
00733         }
00734     } else if (!d->appletDescription.isValid()) {
00735         return i18n("Unknown Widget");
00736     }
00737 
00738     return d->appletDescription.name();
00739 }
00740 
00741 QFont Applet::font() const
00742 {
00743     return QApplication::font();
00744 }
00745 
00746 QString Applet::icon() const
00747 {
00748     if (!d->appletDescription.isValid()) {
00749         return QString();
00750     }
00751 
00752     return d->appletDescription.icon();
00753 }
00754 
00755 QString Applet::pluginName() const
00756 {
00757     if (!d->appletDescription.isValid()) {
00758         return QString();
00759     }
00760 
00761     return d->appletDescription.pluginName();
00762 }
00763 
00764 bool Applet::shouldConserveResources() const
00765 {
00766     return Solid::PowerManagement::appShouldConserveResources();
00767 }
00768 
00769 QString Applet::category() const
00770 {
00771     if (!d->appletDescription.isValid()) {
00772         return i18nc("misc category", "Miscellaneous");
00773     }
00774 
00775     return d->appletDescription.category();
00776 }
00777 
00778 QString Applet::category(const KPluginInfo &applet)
00779 {
00780     return applet.property("X-KDE-PluginInfo-Category").toString();
00781 }
00782 
00783 QString Applet::category(const QString &appletName)
00784 {
00785     if (appletName.isEmpty()) {
00786         return QString();
00787     }
00788 
00789     QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(appletName);
00790     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
00791     
00792     if (offers.isEmpty()) {
00793         offers = KServiceTypeTrader::self()->query("Plasma/PopupApplet", constraint);
00794     }
00795     
00796     if (offers.isEmpty()) {
00797         return QString();
00798     }
00799 
00800     return offers.first()->property("X-KDE-PluginInfo-Category").toString();
00801 }
00802 
00803 ImmutabilityType Applet::immutability() const
00804 {
00805     //Returning the more strict immutability between the applet immutability and Corona one
00806     ImmutabilityType coronaImmutability = Mutable;
00807 
00808     if (qobject_cast<Corona*>(scene())) {
00809         coronaImmutability = static_cast<Corona*>(scene())->immutability();
00810     }
00811 
00812     if (coronaImmutability == SystemImmutable) {
00813         return SystemImmutable;
00814     } else if (coronaImmutability == UserImmutable && d->immutability != SystemImmutable) {
00815         return UserImmutable;
00816     } else {
00817         return d->immutability;
00818     }
00819 }
00820 
00821 void Applet::setImmutability(const ImmutabilityType immutable)
00822 {
00823     if (d->immutability == immutable) {
00824         return;
00825     }
00826 
00827     d->immutability = immutable;
00828     updateConstraints(ImmutableConstraint);
00829 }
00830 
00831 Applet::BackgroundHints Applet::backgroundHints() const
00832 {
00833     return d->backgroundHints;
00834 }
00835 
00836 void Applet::setBackgroundHints(const BackgroundHints hints)
00837 {
00838     if (d->backgroundHints == hints) {
00839         return;
00840     }
00841 
00842     d->backgroundHints = hints;
00843     d->preferredBackgroundHints = hints;
00844 
00845     //Draw the standard background?
00846     if ((hints & StandardBackground) || (hints & TranslucentBackground)) {
00847         if (!d->background) {
00848             d->background = new Plasma::FrameSvg(this);
00849         }
00850 
00851         if ((hints & TranslucentBackground) &&
00852             Plasma::Theme::defaultTheme()->currentThemeHasImage("widgets/translucentbackground")) {
00853             d->background->setImagePath("widgets/translucentbackground");
00854         } else {
00855             d->background->setImagePath("widgets/background");
00856         }
00857 
00858         d->background->setEnabledBorders(Plasma::FrameSvg::AllBorders);
00859         qreal left, top, right, bottom;
00860         d->background->getMargins(left, top, right, bottom);
00861         setContentsMargins(left, right, top, bottom);
00862         QSizeF fitSize(left + right, top + bottom);
00863         if (minimumSize().expandedTo(fitSize) != minimumSize()) {
00864             setMinimumSize(minimumSize().expandedTo(fitSize));
00865         }
00866         d->background->resizeFrame(boundingRect().size());
00867 
00868         //if the background has an "overlay" element decide a random position for it and then save it so it's consistent across plasma starts
00869 
00870         if (d->background->hasElement("overlay")) {
00871             QSize overlaySize = d->background->elementSize("overlay");
00872 
00873             //position is in the boundaries overlaySize.width()*2, overlaySize.height()
00874             qsrand(id());
00875             d->background->d->overlayPos.rx() = - (overlaySize.width() /2) + (overlaySize.width() /4) * (qrand() % (4 + 1));
00876             d->background->d->overlayPos.ry() = (- (overlaySize.height() /2) + (overlaySize.height() /4) * (qrand() % (4 + 1)))/2;
00877         }
00878 
00879     } else if (d->background) {
00880         qreal left, top, right, bottom;
00881         d->background->getMargins(left, top, right, bottom);
00882         //Setting a minimum size of 0,0 would result in the panel to be only
00883         //on the first virtual desktop
00884         setMinimumSize(qMax(minimumSize().width() - left - right, qreal(1.0)),
00885                        qMax(minimumSize().height() - top - bottom, qreal(1.0)));
00886 
00887         delete d->background;
00888         d->background = 0;
00889         setContentsMargins(0, 0, 0, 0);
00890     }
00891 
00892     update();
00893 }
00894 
00895 bool Applet::hasFailedToLaunch() const
00896 {
00897     return d->failed;
00898 }
00899 
00900 void Applet::paintWindowFrame(QPainter *painter,
00901                               const QStyleOptionGraphicsItem *option, QWidget *widget)
00902 {
00903     Q_UNUSED(painter)
00904     Q_UNUSED(option)
00905     Q_UNUSED(widget)
00906     //Here come the code for the window frame
00907     //kDebug() << windowFrameGeometry();
00908     //painter->drawRoundedRect(windowFrameGeometry(), 5, 5);
00909 }
00910 
00911 bool Applet::configurationRequired() const
00912 {
00913     return d->needsConfig;
00914 }
00915 
00916 void Applet::setConfigurationRequired(bool needsConfig, const QString &reason)
00917 {
00918     if (d->needsConfig == needsConfig) {
00919         return;
00920     }
00921 
00922     d->needsConfig = needsConfig;
00923 
00924     if (!needsConfig) {
00925         d->destroyMessageOverlay();
00926         return;
00927     }
00928 
00929     d->createMessageOverlay(true);
00930     d->messageOverlay->opacity = 0.4;
00931 
00932     QGraphicsGridLayout *configLayout = new QGraphicsGridLayout(d->messageOverlay);
00933     configLayout->setContentsMargins(0, 0, 0, 0);
00934 
00935   //  configLayout->addStretch();
00936     configLayout->setColumnStretchFactor(0, 5);
00937     configLayout->setColumnStretchFactor(2, 5);
00938     configLayout->setRowStretchFactor(0, 5);
00939     configLayout->setRowStretchFactor(3, 5);
00940 
00941     int row = 1;
00942     if (!reason.isEmpty()) {
00943         Label *explanation = new Label(d->messageOverlay);
00944         explanation->setText(reason);
00945         configLayout->addItem(explanation, row, 1);
00946         configLayout->setColumnStretchFactor(1, 5);
00947         ++row;
00948         configLayout->setAlignment(explanation, Qt::AlignBottom | Qt::AlignCenter);
00949     }
00950 
00951     PushButton *configWidget = new PushButton(d->messageOverlay);
00952     configWidget->setText(i18n("Configure..."));
00953     connect(configWidget, SIGNAL(clicked()), this, SLOT(showConfigurationInterface()));
00954     configLayout->addItem(configWidget, row, 1);
00955 
00956     //configLayout->setAlignment(configWidget, Qt::AlignTop | Qt::AlignCenter);
00957     //configLayout->addStretch();
00958 
00959     d->messageOverlay->show();
00960 }
00961 
00962 void Applet::showMessage(const QIcon &icon, const QString &message, const MessageButtons buttons)
00963 {
00964     if (message.isEmpty()) {
00965         d->destroyMessageOverlay();
00966         return;
00967     }
00968 
00969     d->createMessageOverlay();
00970     d->messageOverlay->opacity = 0.8;
00971     QGraphicsLinearLayout *mainLayout = new QGraphicsLinearLayout(d->messageOverlay);
00972     mainLayout->setOrientation(Qt::Vertical);
00973     mainLayout->addStretch();
00974 
00975     QGraphicsLinearLayout *messageLayout = new QGraphicsLinearLayout();
00976     messageLayout->setOrientation(Qt::Horizontal);
00977 
00978     QGraphicsLinearLayout *buttonLayout = new QGraphicsLinearLayout();
00979     buttonLayout->setOrientation(Qt::Horizontal);
00980 
00981     mainLayout->addItem(messageLayout);
00982     mainLayout->addItem(buttonLayout);
00983     mainLayout->addStretch();
00984 
00985     IconWidget *messageIcon = new IconWidget(this);
00986     Label *messageText = new Label(this);
00987     messageText->nativeWidget()->setWordWrap(true);
00988 
00989     messageLayout->addStretch();
00990     messageLayout->addItem(messageIcon);
00991     messageLayout->addItem(messageText);
00992     messageLayout->addStretch();
00993 
00994     messageIcon->setIcon(icon);
00995     messageText->setText(message);
00996 
00997 
00998     buttonLayout->addStretch();
00999 
01000     if (buttons & ButtonOk) {
01001         PushButton *ok = new PushButton(this);
01002         ok->setText(i18n("Ok"));
01003         buttonLayout->addItem(ok);
01004         connect(ok, SIGNAL(clicked()), this, SLOT(destroyMessageOverlay()));
01005     }
01006     if (buttons & ButtonYes) {
01007         PushButton *yes = new PushButton(this);
01008         yes->setText(i18n("Yes"));
01009         buttonLayout->addItem(yes);
01010         connect(yes, SIGNAL(clicked()), this, SLOT(destroyMessageOverlay()));
01011     }
01012     if (buttons & ButtonNo) {
01013         PushButton *no = new PushButton(this);
01014         no->setText(i18n("No"));
01015         buttonLayout->addItem(no);
01016         connect(no, SIGNAL(clicked()), this, SLOT(destroyMessageOverlay()));
01017     }
01018     if (buttons & ButtonCancel) {
01019         PushButton *cancel = new PushButton(this);
01020         cancel->setText(i18n("Cancel"));
01021         buttonLayout->addItem(cancel);
01022         connect(cancel, SIGNAL(clicked()), this, SLOT(destroyMessageOverlay()));
01023     }
01024 
01025     buttonLayout->addStretch();
01026 
01027     d->messageOverlay->show();
01028 
01029 }
01030 
01031 QVariantList Applet::startupArguments() const
01032 {
01033     return d->args;
01034 }
01035 
01036 void Applet::flushPendingConstraintsEvents()
01037 {
01038     if (d->pendingConstraints == NoConstraint) {
01039         return;
01040     }
01041 
01042     if (d->constraintsTimerId) {
01043         killTimer(d->constraintsTimerId);
01044         d->constraintsTimerId = 0;
01045     }
01046 
01047     //kDebug() << "fushing constraints: " << d->pendingConstraints << "!!!!!!!!!!!!!!!!!!!!!!!!!!!";
01048     Plasma::Constraints c = d->pendingConstraints;
01049     d->pendingConstraints = NoConstraint;
01050 
01051     if (c & Plasma::StartupCompletedConstraint) {
01052         //common actions
01053         bool unlocked = immutability() == Mutable;
01054         //FIXME desktop containments can't be removed while in use.
01055         //it's kinda silly to have a keyboard shortcut for something that can only be used when the
01056         //shortcut isn't active.
01057         QAction *closeApplet = d->actions->action("remove");
01058         if (closeApplet) {
01059             closeApplet->setEnabled(unlocked);
01060             closeApplet->setVisible(unlocked);
01061             connect(closeApplet, SIGNAL(triggered(bool)), this, SLOT(selectItemToDestroy()));
01062         }
01063 
01064         QAction *configAction = d->actions->action("configure");
01065         if (configAction) {
01066             //XXX assumption: isContainment won't change after this
01067             if (d->isContainment) {
01068                 connect(configAction, SIGNAL(triggered()), this, SLOT(requestConfiguration()));
01069             } else {
01070                 connect(configAction, SIGNAL(triggered(bool)), this, SLOT(showConfigurationInterface()));
01071             }
01072             bool canConfig = unlocked || KAuthorized::authorize("PlasmaAllowConfigureWhenLocked");
01073             canConfig = canConfig && (d->hasConfigurationInterface || d->isContainment);
01074             configAction->setVisible(canConfig);
01075             configAction->setEnabled(canConfig);
01076         }
01077 
01078         d->updateShortcuts();
01079         Corona * corona = qobject_cast<Corona*>(scene());
01080         if (corona) {
01081             connect(corona, SIGNAL(shortcutsChanged()), this, SLOT(updateShortcuts()));
01082         }
01083     }
01084 
01085     if (c & Plasma::ImmutableConstraint) {
01086         bool unlocked = immutability() == Mutable;
01087         QAction *action = d->actions->action("remove");
01088         if (action) {
01089             action->setVisible(unlocked);
01090             action->setEnabled(unlocked);
01091         }
01092 
01093         bool canConfig = unlocked || KAuthorized::authorize("PlasmaAllowConfigureWhenLocked");
01094         canConfig = canConfig && (d->hasConfigurationInterface || d->isContainment);
01095         action = d->actions->action("configure");
01096         if (action) {
01097             action->setVisible(canConfig);
01098             action->setEnabled(canConfig);
01099         }
01100     }
01101 
01102     if (c & Plasma::SizeConstraint) {
01103         if (d->messageOverlay) {
01104             d->messageOverlay->setGeometry(QRectF(QPointF(0, 0), geometry().size()));
01105 /*
01106             QGraphicsItem *button = 0;
01107             QList<QGraphicsItem*> children = d->messageOverlay->QGraphicsItem::children();
01108 
01109             if (!children.isEmpty()) {
01110                 button = children.first();
01111             }
01112 
01113             if (button) {
01114                 QSizeF s = button->boundingRect().size();
01115                 button->setPos(d->messageOverlay->boundingRect().width() / 2 - s.width() / 2,
01116                         d->messageOverlay->boundingRect().height() / 2 - s.height() / 2);
01117             }*/
01118         }
01119 
01120         if (d->started && layout()) {
01121             layout()->updateGeometry();
01122         }
01123     }
01124 
01125     if (c & Plasma::FormFactorConstraint) {
01126         FormFactor f = formFactor();
01127         if (!d->isContainment && f != Vertical && f != Horizontal) {
01128             setBackgroundHints(d->preferredBackgroundHints);
01129         } else {
01130             BackgroundHints hints = d->preferredBackgroundHints;
01131             setBackgroundHints(NoBackground);
01132             d->preferredBackgroundHints = hints;
01133         }
01134 
01135         if (d->failed) {
01136             if (f == Vertical || f == Horizontal) {
01137                 setMinimumSize(0, 0);
01138                 QGraphicsLayoutItem *item = layout()->itemAt(1);
01139                 layout()->removeAt(1);
01140                 delete item;
01141             }
01142         }
01143     }
01144     if (c & Plasma::SizeConstraint || c & Plasma::FormFactorConstraint) {
01145         if (aspectRatioMode() == Plasma::Square || aspectRatioMode() == Plasma::ConstrainedSquare) {
01146             // enforce square size in panels
01147             if (formFactor() == Horizontal) {
01148                 setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding));
01149             } else if (formFactor() == Vertical) {
01150                 setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
01151             }
01152         }
01153         updateGeometry();
01154     }
01155 
01156     // now take care of constraints in special subclasses: Contaiment and PopupApplet
01157     Containment* containment = qobject_cast<Plasma::Containment*>(this);
01158     if (d->isContainment && containment) {
01159         containment->d->containmentConstraintsEvent(c);
01160     }
01161 
01162     PopupApplet* popup = qobject_cast<Plasma::PopupApplet*>(this);
01163     if (popup) {
01164         popup->d->popupConstraintsEvent(c);
01165     }
01166 
01167     // pass the constraint on to the actual subclass
01168     constraintsEvent(c);
01169 
01170     if (c & StartupCompletedConstraint) {
01171         // start up is done, we can now go do a mod timer
01172         if (d->modificationsTimerId > 0) {
01173             killTimer(d->modificationsTimerId);
01174         }
01175 
01176         d->modificationsTimerId = 0;
01177     }
01178 }
01179 
01180 int Applet::type() const
01181 {
01182     return Type;
01183 }
01184 
01185 QList<QAction*> Applet::contextualActions()
01186 {
01187     //kDebug() << "empty context actions";
01188     return d->script ? d->script->contextualActions() : QList<QAction*>();
01189 }
01190 
01191 QAction *Applet::action(QString name) const
01192 {
01193     return d->actions->action(name);
01194 }
01195 
01196 void Applet::addAction(QString name, QAction *action)
01197 {
01198     d->actions->addAction(name, action);
01199 }
01200 
01201 void Applet::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
01202 {
01203     if (!d->started) {
01204         //kDebug() << "not started";
01205         return;
01206     }
01207 
01208     if (transform().isRotating()) {
01209         painter->setRenderHint(QPainter::SmoothPixmapTransform);
01210         painter->setRenderHint(QPainter::Antialiasing);
01211     }
01212 
01213     if (d->background &&
01214         formFactor() != Plasma::Vertical &&
01215         formFactor() != Plasma::Horizontal) {
01216         //kDebug() << "option rect is" << option->rect;
01217         d->background->paintFrame(painter);
01218     }
01219 
01220     if (d->failed) {
01221         //kDebug() << "failed!";
01222         return;
01223     }
01224 
01225     qreal left, top, right, bottom;
01226     getContentsMargins(&left, &top, &right, &bottom);
01227     QRect contentsRect = QRectF(QPointF(0, 0),
01228                                 boundingRect().size()).adjusted(left, top, -right, -bottom).toRect();
01229 
01230     if (widget && d->isContainment) {
01231         // note that the widget we get is actually the viewport of the view, not the view itself
01232         View* v = qobject_cast<Plasma::View*>(widget->parent());
01233         Containment* c = qobject_cast<Plasma::Containment*>(this);
01234 
01235         //update the view transform of the toolbox, since it ignores transforms
01236         if (v && c && c->d->toolBox) {
01237             if (c->d->toolBox->viewTransform().isScaling() && !v->transform().isScaling()) {
01238                 c->d->positionToolBox();
01239             }
01240             if (v) {
01241                 c->d->toolBox->setViewTransform(v->transform());
01242             }
01243         }
01244 
01245         if (!v || v->isWallpaperEnabled()) {
01246 
01247             // paint the wallpaper
01248             if (c && c->drawWallpaper() && c->wallpaper()) {
01249                 Wallpaper *w = c->wallpaper();
01250                 if (!w->isInitialized()) {
01251                     // delayed paper initialization
01252                     KConfigGroup wallpaperConfig = c->config();
01253                     wallpaperConfig = KConfigGroup(&wallpaperConfig, "Wallpaper");
01254                     wallpaperConfig = KConfigGroup(&wallpaperConfig, w->pluginName());
01255                     w->restore(wallpaperConfig);
01256                 }
01257 
01258                 painter->save();
01259                 c->wallpaper()->paint(painter, option->exposedRect);
01260                 painter->restore();
01261             }
01262 
01263             // .. and now paint the actual containment interface, but with
01264             //  a Containment style option based on the one we get
01265             Containment::StyleOption coption(*option);
01266             coption.view = v;
01267             paintInterface(painter, &coption, contentsRect);
01268         }
01269     } else {
01270         //kDebug() << "paint interface of" << (QObject*) this;
01271         // paint the applet's interface
01272         paintInterface(painter, option, contentsRect);
01273     }
01274 }
01275 
01276 void Applet::paintInterface(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRect &contentsRect)
01277 {
01278     if (d->script) {
01279         d->script->paintInterface(painter, option, contentsRect);
01280     } else {
01281         //kDebug() << "Applet::paintInterface() default impl";
01282     }
01283 }
01284 
01285 FormFactor Applet::formFactor() const
01286 {
01287     Containment *c = containment();
01288     return c ? c->d->formFactor : Plasma::Planar;
01289 }
01290 
01291 Containment *Applet::containment() const
01292 {
01293     if (d->isContainment) {
01294         Containment *c = qobject_cast<Containment*>(const_cast<Applet*>(this));
01295         if (c) {
01296             return c;
01297         }
01298     }
01299 
01300     QGraphicsItem *parent = parentItem();
01301     Containment *c = 0;
01302 
01303     while (parent) {
01304         Containment *possibleC = dynamic_cast<Containment*>(parent);
01305         if (possibleC && possibleC->Applet::d->isContainment) {
01306             c = possibleC;
01307             break;
01308         }
01309         parent = parent->parentItem();
01310     }
01311 
01312     return c;
01313 }
01314 
01315 void Applet::setGlobalShortcut(const KShortcut &shortcut)
01316 {
01317     if (!d->activationAction) {
01318         d->activationAction = new KAction(this);
01319         d->activationAction->setText(i18n("Activate %1 Widget", name()));
01320         d->activationAction->setObjectName(QString("activate widget %1").arg(id())); // NO I18N
01321         connect(d->activationAction, SIGNAL(triggered()), this, SIGNAL(activate()));
01322 
01323         QList<QWidget *> widgets = d->actions->associatedWidgets();
01324         foreach (QWidget *w, widgets) {
01325             w->addAction(d->activationAction);
01326         }
01327     }
01328 
01329     //kDebug() << "before" << shortcut.primary() << d->activationAction->globalShortcut().primary();
01330     d->activationAction->setGlobalShortcut(
01331         shortcut,
01332         KAction::ShortcutTypes(KAction::ActiveShortcut | KAction::DefaultShortcut),
01333         KAction::NoAutoloading);
01334 
01335     KConfigGroup shortcutConfig(d->mainConfigGroup(), "Shortcuts");
01336     shortcutConfig.writeEntry("global", d->activationAction->globalShortcut().toString());
01337     d->scheduleModificationNotification();
01338     //kDebug() << "after" << shortcut.primary() << d->activationAction->globalShortcut().primary();
01339 }
01340 
01341 KShortcut Applet::globalShortcut() const
01342 {
01343     if (d->activationAction) {
01344         return d->activationAction->globalShortcut();
01345     }
01346 
01347     return KShortcut();
01348 }
01349 
01350 bool Applet::isPopupShowing() const
01351 {
01352     return false;
01353 }
01354 
01355 void Applet::addAssociatedWidget(QWidget *widget)
01356 {
01357     d->actions->addAssociatedWidget(widget);
01358 }
01359 
01360 void Applet::removeAssociatedWidget(QWidget *widget)
01361 {
01362     d->actions->removeAssociatedWidget(widget);
01363 }
01364 
01365 Location Applet::location() const
01366 {
01367     Containment *c = containment();
01368     return c ? c->d->location : Plasma::Desktop;
01369 }
01370 
01371 Context *Applet::context() const
01372 {
01373     Containment *c = containment();
01374     Q_ASSERT(c);
01375     return c->d->context();
01376 }
01377 
01378 Plasma::AspectRatioMode Applet::aspectRatioMode() const
01379 {
01380     return d->aspectRatioMode;
01381 }
01382 
01383 void Applet::setAspectRatioMode(Plasma::AspectRatioMode mode)
01384 {
01385     d->aspectRatioMode = mode;
01386 }
01387 
01388 void Applet::registerAsDragHandle(QGraphicsItem *item)
01389 {
01390     if (!item || d->registeredAsDragHandle.contains(item)) {
01391         return;
01392     }
01393 
01394     d->registeredAsDragHandle.insert(item);
01395     item->installSceneEventFilter(this);
01396 }
01397 
01398 void Applet::unregisterAsDragHandle(QGraphicsItem *item)
01399 {
01400     if (!item) {
01401         return;
01402     }
01403 
01404     if (d->registeredAsDragHandle.remove(item)) {
01405         item->removeSceneEventFilter(this);
01406     }
01407 }
01408 
01409 bool Applet::isRegisteredAsDragHandle(QGraphicsItem *item)
01410 {
01411     return d->registeredAsDragHandle.contains(item);
01412 }
01413 
01414 bool Applet::hasConfigurationInterface() const
01415 {
01416     return d->hasConfigurationInterface;
01417 }
01418 
01419 //it bugs me that this can get turned on and off at will. I don't see it being useful and it just
01420 //makes more work for me and more code duplication.
01421 void Applet::setHasConfigurationInterface(bool hasInterface)
01422 {
01423     if (d->hasConfigurationInterface == hasInterface) {
01424         return;
01425     }
01426 
01427     d->hasConfigurationInterface = hasInterface;
01428 
01429     //FIXME I'm pretty sure this line has issues but I'm preserving current behaviour for now
01430     if (!hasInterface && (d->isContainment || qobject_cast<Plasma::Containment*>(this))) {
01431         return;
01432     }
01433 
01434     //config action
01435     KAction *configAction = qobject_cast<KAction*>(d->actions->action("configure"));
01436     if (configAction) {
01437         bool canConfig = false;
01438         if (hasInterface) {
01439             bool unlocked = immutability() == Mutable;
01440             canConfig = unlocked || KAuthorized::authorize("PlasmaAllowConfigureWhenLocked");
01441         }
01442         configAction->setVisible(canConfig);
01443         configAction->setEnabled(canConfig);
01444     }
01445 }
01446 
01447 KActionCollection* AppletPrivate::defaultActions(QObject *parent)
01448 {
01449     KActionCollection *actions = new KActionCollection(parent);
01450     actions->setConfigGroup("Shortcuts-Applet");
01451 
01452     KAction *configAction = actions->addAction("configure");
01453     configAction->setAutoRepeat(false);
01454     configAction->setText(i18n("Widget Settings"));
01455     configAction->setIcon(KIcon("configure"));
01456     configAction->setShortcut(KShortcut("alt+d, s"));
01457 
01458     KAction *closeApplet = actions->addAction("remove");
01459     closeApplet->setAutoRepeat(false);
01460     closeApplet->setText("Remove this Widget");
01461     closeApplet->setIcon(KIcon("edit-delete"));
01462     closeApplet->setShortcut(KShortcut("alt+d, r"));
01463 
01464     return actions;
01465 }
01466 
01467 bool Applet::eventFilter(QObject *o, QEvent *e)
01468 {
01469     return QObject::eventFilter(o, e);
01470 }
01471 
01472 bool Applet::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
01473 {
01474     switch (event->type()) {
01475     case QEvent::GraphicsSceneMouseMove:
01476     {
01477         // don't move when the containment is not mutable,
01478         // in the rare case the containment doesn't exists consider it as mutable
01479         if (d->registeredAsDragHandle.contains(watched)) {
01480             Containment *c = containment();
01481             if (!c || c->immutability() == Mutable) {
01482                 mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent*>(event));
01483                 return true;
01484             }
01485         }
01486         break;
01487     }
01488 
01489     default:
01490         break;
01491     }
01492 
01493     return QGraphicsItem::sceneEventFilter(watched, event);
01494 }
01495 
01496 void Applet::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
01497 {
01498     if (immutability() == Mutable && formFactor() == Plasma::Planar) {
01499         QGraphicsItem *parent = parentItem();
01500         Plasma::Applet *applet = qgraphicsitem_cast<Plasma::Applet*>(parent);
01501 
01502         if (applet && applet->isContainment()) {
01503             // our direct parent is a containment. just move ourselves.
01504             QPointF curPos = event->pos();
01505             QPointF lastPos = event->lastPos();
01506             QPointF delta = curPos - lastPos;
01507 
01508             moveBy(delta.x(), delta.y());
01509         } else if (parent) {
01510             //don't move the icon as well because our parent
01511             //(usually an appletHandle) will do it for us
01512             //parent->moveBy(delta.x(),delta.y());
01513             QPointF curPos = parent->transform().map(event->pos());
01514             QPointF lastPos = parent->transform().map(event->lastPos());
01515             QPointF delta = curPos - lastPos;
01516 
01517             parent->setPos(parent->pos() + delta);
01518         }
01519     }
01520 }
01521 
01522 void Applet::focusInEvent(QFocusEvent *event)
01523 {
01524     if (!isContainment() && containment()) {
01525         //focusing an applet may trigger this event again, but we won't be here more than twice
01526         containment()->d->focusApplet(this);
01527     }
01528 
01529     QGraphicsWidget::focusInEvent(event);
01530 }
01531 
01532 void Applet::resizeEvent(QGraphicsSceneResizeEvent *event)
01533 {
01534     QGraphicsWidget::resizeEvent(event);
01535 
01536     if (d->background) {
01537         d->background->resizeFrame(boundingRect().size());
01538     }
01539 
01540     updateConstraints(Plasma::SizeConstraint);
01541 
01542     d->scheduleModificationNotification();
01543     emit geometryChanged();
01544 }
01545 
01546 void Applet::showConfigurationInterface()
01547 {
01548     if (!hasConfigurationInterface()) {
01549         return;
01550     }
01551 
01552     if (immutability() != Mutable && !KAuthorized::authorize("PlasmaAllowConfigureWhenLocked")) {
01553         //FIXME: in 4.3 add an explanatory dialog
01554         return;
01555     }
01556 
01557     KConfigDialog *dlg = KConfigDialog::exists(d->configDialogId());
01558 
01559     if (dlg) {
01560         KWindowSystem::setOnDesktop(dlg->winId(), KWindowSystem::currentDesktop());
01561         dlg->show();
01562         KWindowSystem::activateWindow(dlg->winId());
01563         return;
01564     }
01565 
01566     if (d->package && d->configLoader) {
01567         KConfigDialog *dialog = new KConfigDialog(0, d->configDialogId(), d->configLoader);
01568 
01569         QString uiFile = d->package->filePath("mainconfigui");
01570         if (!uiFile.isEmpty()) {
01571             dialog->setWindowTitle(d->configWindowTitle());
01572             dialog->setAttribute(Qt::WA_DeleteOnClose, true);
01573 
01574             QUiLoader loader;
01575             QFile f(uiFile);
01576             if (!f.open(QIODevice::ReadOnly)) {
01577                 delete dialog;
01578 
01579                 if (d->script) {
01580                     d->script->showConfigurationInterface();
01581                 }
01582                 return;
01583             }
01584 
01585             QWidget *w = loader.load(&f);
01586             f.close();
01587 
01588             dialog->addPage(w, i18n("Settings"), icon(), i18n("%1 Settings", name()));
01589         }
01590 
01591         d->addGlobalShortcutsPage(dialog);
01592         dialog->show();
01593     } else if (d->script) {
01594         d->script->showConfigurationInterface();
01595     } else {
01596         KConfigDialog *dialog = d->generateGenericConfigDialog();
01597         createConfigurationInterface(dialog);
01598         d->addGlobalShortcutsPage(dialog);
01599         dialog->show();
01600     }
01601 
01602     emit releaseVisualFocus();
01603 }
01604 
01605 QString AppletPrivate::configDialogId() const
01606 {
01607     return QString("%1settings%2").arg(appletId).arg(q->name());
01608 }
01609 
01610 QString AppletPrivate::configWindowTitle() const
01611 {
01612     return i18nc("@title:window", "%1 Settings", q->name());
01613 }
01614 
01615 QSet<QString> AppletPrivate::knownCategories()
01616 {
01617     // this is to trick the tranlsation tools into making the correct
01618     // strings for translation
01619     QSet<QString> categories = s_customCategories;
01620     categories << QString(I18N_NOOP("Accessibility")).toLower()
01621                << QString(I18N_NOOP("Application Launchers")).toLower()
01622                << QString(I18N_NOOP("Astronomy")).toLower()
01623                << QString(I18N_NOOP("Date and Time")).toLower()
01624                << QString(I18N_NOOP("Development Tools")).toLower()
01625                << QString(I18N_NOOP("Education")).toLower()
01626                << QString(I18N_NOOP("Environment and Weather")).toLower()
01627                << QString(I18N_NOOP("Examples")).toLower()
01628                << QString(I18N_NOOP("File System")).toLower()
01629                << QString(I18N_NOOP("Fun and Games")).toLower()
01630                << QString(I18N_NOOP("Graphics")).toLower()
01631                << QString(I18N_NOOP("Language")).toLower()
01632                << QString(I18N_NOOP("Mapping")).toLower()
01633                << QString(I18N_NOOP("Miscellaneous")).toLower()
01634                << QString(I18N_NOOP("Multimedia")).toLower()
01635                << QString(I18N_NOOP("Online Services")).toLower()
01636                << QString(I18N_NOOP("System Information")).toLower()
01637                << QString(I18N_NOOP("Utilities")).toLower()
01638                << QString(I18N_NOOP("Windows and Tasks")).toLower();
01639     return categories;
01640 }
01641 
01642 KConfigDialog *AppletPrivate::generateGenericConfigDialog()
01643 {
01644     KConfigSkeleton *nullManager = new KConfigSkeleton(0);
01645     KConfigDialog *dialog = new KConfigDialog(0, configDialogId(), nullManager);
01646     dialog->setFaceType(KPageDialog::Auto);
01647     dialog->setWindowTitle(configWindowTitle());
01648     dialog->setAttribute(Qt::WA_DeleteOnClose, true);
01649     QObject::connect(dialog, SIGNAL(finished()), nullManager, SLOT(deleteLater()));
01650     return dialog;
01651 }
01652 
01653 void AppletPrivate::addGlobalShortcutsPage(KConfigDialog *dialog)
01654 {
01655     if (isContainment) {
01656         return;
01657     }
01658 
01659     QWidget *page = new QWidget;
01660     QVBoxLayout *layout = new QVBoxLayout(page);
01661 
01662     if (!shortcutEditor) {
01663         shortcutEditor = new KKeySequenceWidget(page);
01664         QObject::connect(shortcutEditor, SIGNAL(destroyed(QObject*)), q, SLOT(clearShortcutEditorPtr()));
01665     }
01666 
01667     shortcutEditor->setKeySequence(q->globalShortcut().primary());
01668     layout->addWidget(shortcutEditor);
01669     layout->addStretch();
01670     dialog->addPage(page, i18n("Keyboard Shortcut"), "preferences-desktop-keyboard");
01671 
01672     //TODO: Apply button does not correctly work for now, so do not show it
01673     dialog->showButton(KDialog::Apply, false);
01674     QObject::connect(dialog, SIGNAL(applyClicked()), q, SLOT(configDialogFinished()));
01675     QObject::connect(dialog, SIGNAL(okClicked()), q, SLOT(configDialogFinished()));
01676 }
01677 
01678 void AppletPrivate::clearShortcutEditorPtr()
01679 {
01680     shortcutEditor = 0;
01681 }
01682 
01683 void AppletPrivate::configDialogFinished()
01684 {
01685     if (shortcutEditor) {
01686         QKeySequence sequence = shortcutEditor->keySequence();
01687         if (sequence != q->globalShortcut().primary()) {
01688             q->setGlobalShortcut(KShortcut(sequence));
01689             emit q->configNeedsSaving();
01690         }
01691     }
01692 
01693     if (!configLoader) {
01694         // the config loader will trigger this for us, so we don't need to.
01695         q->configChanged();
01696     }
01697 }
01698 
01699 void AppletPrivate::updateShortcuts()
01700 {
01701     if (isContainment) {
01702         //a horrible hack to avoid clobbering corona settings
01703         //we pull them out, then read, then put them back
01704         QList<QString> names;
01705         QList<QAction*> qactions;
01706         names << "zoom out" << "add sibling containment" << "configure shortcuts" << "lock widgets";
01707         foreach (const QString &name, names) {
01708             QAction *a = actions->action(name);
01709             actions->takeAction(a); //FIXME this is stupid, KActionCollection needs a takeAction(QString) method
01710             qactions << a;
01711         }
01712 
01713         actions->readSettings();
01714 
01715         for (int i=0; i<names.size(); ++i) {
01716             QAction *a = qactions.at(i);
01717             if (a) {
01718                 actions->addAction(names.at(i), a);
01719             }
01720         }
01721     } else {
01722         actions->readSettings();
01723     }
01724 }
01725 
01726 void Applet::configChanged()
01727 {
01728     if (d->script) {
01729         d->script->configChanged();
01730     }
01731 }
01732 
01733 void Applet::createConfigurationInterface(KConfigDialog *parent)
01734 {
01735     Q_UNUSED(parent)
01736     // virtual method reimplemented by subclasses.
01737     // do not put anything here ...
01738 }
01739 
01740 bool Applet::hasAuthorization(const QString &constraint) const
01741 {
01742     KConfigGroup constraintGroup(KGlobal::config(), "Constraints");
01743     return constraintGroup.readEntry(constraint, true);
01744 }
01745 
01746 KPluginInfo::List Applet::listAppletInfo(const QString &category,
01747                                          const QString &parentApp)
01748 {
01749     QString constraint;
01750 
01751     if (parentApp.isEmpty()) {
01752         constraint.append("(not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '')");
01753     } else {
01754         constraint.append("[X-KDE-ParentApp] == '").append(parentApp).append("'");
01755     }
01756     //note: constraint guaranteed non-empty from here down
01757 
01758     if (category.isEmpty()) { //use all but the excluded categories
01759         KConfigGroup group(KGlobal::config(), "General");
01760         QStringList excluded = group.readEntry("ExcludeCategories", QStringList());
01761         foreach (const QString &category, excluded) {
01762             constraint.append(" and [X-KDE-PluginInfo-Category] != '").append(category).append("'");
01763         }
01764     } else { //specific category (this could be an excluded one - is that bad?)
01765         constraint.append(" and ");
01766 
01767         constraint.append("[X-KDE-PluginInfo-Category] == '").append(category).append("'");
01768         if (category == "Miscellaneous") {
01769             constraint.append(" or (not exist [X-KDE-PluginInfo-Category] or [X-KDE-PluginInfo-Category] == '')");
01770         }
01771     }
01772 
01773     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
01774     offers << KServiceTypeTrader::self()->query("Plasma/PopupApplet", constraint);
01775 
01776     //now we have to do some manual filtering because the constraint can't handle everything
01777     KConfigGroup constraintGroup(KGlobal::config(), "Constraints");
01778     foreach (const QString &key, constraintGroup.keyList()) {
01779         //kDebug() << "security constraint" << key;
01780         if (constraintGroup.readEntry(key, true)) {
01781             continue;
01782         }
01783         //ugh. a qlist of ksharedptr<kservice>
01784         QMutableListIterator<KService::Ptr> it(offers);
01785         while (it.hasNext()) {
01786             KService::Ptr p = it.next();
01787             QString prop = QString("X-Plasma-Requires-").append(key);
01788             QVariant req = p->property(prop, QVariant::String);
01789             //valid values: Required/Optional/Unused
01790             QString reqValue;
01791             if (req.isValid()) {
01792                 reqValue = req.toString();
01793             } else {
01794                 //TODO if it's a scripted language default to not-required because the bindings disable it
01795                 //this isn't actually implemented in any bindings yet but should be possible for
01796                 //anything but c++
01797             }
01798 
01799             if (!(reqValue == "Optional" || reqValue == "Unused")) {
01800             //if (reqValue == "Required") {
01801                 it.remove();
01802             }
01803         }
01804     }
01805 
01806     //kDebug() << "Applet::listAppletInfo constraint was '" << constraint
01807     //         << "' which got us " << offers.count() << " matches";
01808     return KPluginInfo::fromServices(offers);
01809 }
01810 
01811 KPluginInfo::List Applet::listAppletInfoForMimetype(const QString &mimetype)
01812 {
01813     QString constraint = QString("'%1' in [X-Plasma-DropMimeTypes]").arg(mimetype);
01814     //kDebug() << "listAppletInfoForMimetype with" << mimetype << constraint;
01815     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
01816     offers << KServiceTypeTrader::self()->query("Plasma/PopupApplet", constraint);
01817     return KPluginInfo::fromServices(offers);
01818 }
01819 
01820 QStringList Applet::listCategories(const QString &parentApp, bool visibleOnly)
01821 {
01822     QString constraint = "exist [X-KDE-PluginInfo-Category]";
01823 
01824     if (parentApp.isEmpty()) {
01825         constraint.append("and (not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '')");
01826     } else {
01827         constraint.append(" and [X-KDE-ParentApp] == '").append(parentApp).append("'");
01828     }
01829 
01830     KConfigGroup group(KGlobal::config(), "General");
01831     QStringList excluded = group.readEntry("ExcludeCategories", QStringList());
01832     foreach (const QString &category, excluded) {
01833         constraint.append(" and [X-KDE-PluginInfo-Category] != '").append(category).append("'");
01834     }
01835 
01836     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
01837     offers << KServiceTypeTrader::self()->query("Plasma/PopupApplet", constraint);
01838     QStringList categories;
01839     QSet<QString> known = AppletPrivate::knownCategories();
01840     foreach (const KService::Ptr &applet, offers) {
01841         QString appletCategory = applet->property("X-KDE-PluginInfo-Category").toString();
01842         if (visibleOnly && applet->noDisplay()) {
01843             // we don't want to show the hidden category
01844             continue;
01845         }
01846 
01847         //kDebug() << "   and we have " << appletCategory;
01848         if (!appletCategory.isEmpty() && !known.contains(appletCategory.toLower())) {
01849             kDebug() << "Unknown category: " << applet->name() << "says it is in the"
01850                      << appletCategory << "category which is unknown to us";
01851             appletCategory.clear();
01852         }
01853 
01854         if (appletCategory.isEmpty()) {
01855             if (!categories.contains(i18nc("misc category", "Miscellaneous"))) {
01856                 categories << i18nc("misc category", "Miscellaneous");
01857             }
01858         } else  if (!categories.contains(appletCategory)) {
01859             categories << appletCategory;
01860         }
01861     }
01862 
01863     categories.sort();
01864     return categories;
01865 }
01866 
01867 void Applet::setCustomCategories(const QStringList &categories)
01868 {
01869     AppletPrivate::s_customCategories = QSet<QString>::fromList(categories);
01870 }
01871 
01872 QStringList Applet::customCategories()
01873 {
01874     return AppletPrivate::s_customCategories.toList();
01875 }
01876 
01877 Applet::Applet(const QString &packagePath, uint appletId, const QVariantList &args)
01878     : QGraphicsWidget(0),
01879       d(new AppletPrivate(KService::Ptr(new KService(packagePath + "/metadata.desktop")), appletId, this))
01880 {
01881     Q_UNUSED(args) // FIXME?
01882     d->init(packagePath);
01883 }
01884 
01885 Applet *Applet::loadPlasmoid(const QString &path, uint appletId, const QVariantList &args)
01886 {
01887     if (QFile::exists(path + "/metadata.desktop")) {
01888         return new Applet(path, appletId, args);
01889     }
01890 
01891     return 0;
01892 }
01893 
01894 Applet *Applet::load(const QString &appletName, uint appletId, const QVariantList &args)
01895 {
01896     if (appletName.isEmpty()) {
01897         return 0;
01898     }
01899 
01900     QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(appletName);
01901     KService::List offers = KServiceTypeTrader::self()->query("Plasma/Applet", constraint);
01902 
01903     bool isContainment = false;
01904     if (offers.isEmpty()) {
01905         //TODO: what would be -really- cool is offer to try and download the applet
01906         //      from the network at this point
01907         offers = KServiceTypeTrader::self()->query("Plasma/Containment", constraint);
01908         if (offers.count() > 0) {
01909             isContainment = true;
01910         }
01911     }
01912 
01913     bool isPopupApplet = false;
01914     if (offers.isEmpty()) {
01915         offers = KServiceTypeTrader::self()->query("Plasma/PopupApplet", constraint);
01916         if (offers.count() > 0) {
01917             isPopupApplet = true;
01918         }
01919     }
01920 
01921     /* if (offers.count() > 1) {
01922         kDebug() << "hey! we got more than one! let's blindly take the first one";
01923     } */
01924 
01925     if (offers.isEmpty()) {
01926         kDebug() << "offers is empty for " << appletName;
01927         return 0;
01928     }
01929 
01930     KService::Ptr offer = offers.first();
01931 
01932     if (appletId == 0) {
01933         appletId = ++AppletPrivate::s_maxAppletId;
01934     }
01935 
01936     QVariantList allArgs;
01937     allArgs << offer->storageId() << appletId << args;
01938 
01939     if (!offer->property("X-Plasma-API").toString().isEmpty()) {
01940         kDebug() << "we have a script using the"
01941                  << offer->property("X-Plasma-API").toString() << "API";
01942         if (isContainment) {
01943             return new Containment(0, allArgs);
01944         } else if (isPopupApplet) {
01945             return new PopupApplet(0, allArgs);
01946         } else {
01947             return new Applet(0, allArgs);
01948         }
01949     }
01950 
01951     KPluginLoader plugin(*offer);
01952 
01953     if (!Plasma::isPluginVersionCompatible(plugin.pluginVersion()) &&
01954         (appletName != "internal:extender")) {
01955         return 0;
01956     }
01957 
01958 
01959     QString error;
01960     Applet *applet;
01961 
01962     if (appletName == "internal:extender") {
01963         applet = new ExtenderApplet(0, allArgs);
01964     } else {
01965         applet = offer->createInstance<Plasma::Applet>(0, allArgs, &error);
01966     }
01967 
01968     if (!applet) {
01969         kDebug() << "Couldn't load applet \"" << appletName << "\"! reason given: " << error;
01970     }
01971 
01972     return applet;
01973 }
01974 
01975 Applet *Applet::load(const KPluginInfo &info, uint appletId, const QVariantList &args)
01976 {
01977     if (!info.isValid()) {
01978         return 0;
01979     }
01980 
01981     return load(info.pluginName(), appletId, args);
01982 }
01983 
01984 QVariant Applet::itemChange(GraphicsItemChange change, const QVariant &value)
01985 {
01986     QVariant ret = QGraphicsWidget::itemChange(change, value);
01987 
01988     //kDebug() << change;
01989     switch (change) {
01990     case ItemSceneHasChanged:
01991     {
01992         QGraphicsScene *newScene = qvariant_cast<QGraphicsScene*>(value);
01993         if (newScene) {
01994             d->checkImmutability();
01995         }
01996     }
01997         break;
01998     case ItemParentChange:
01999         if (d->mainConfig && !d->isContainment &&
02000             !containment() &&
02001             dynamic_cast<Containment*>(value.value<QGraphicsItem *>())) {
02002             // if this is an applet, and we've just been assigned to our first containment,
02003             // but the applet did something stupid like ask for the config() object prior to
02004             // this happening (e.g. inits ctor) then let's repair that situation for them.
02005             kWarning() << "Configuration object was requested prior to init(), which is too early. "
02006                           "Please fix this item:" << parentItem() << value.value<QGraphicsItem *>()
02007                           << name();
02008             KConfigGroup *old = d->mainConfig;
02009             KConfigGroup appletConfig = dynamic_cast<Containment*>(value.value<QGraphicsItem *>())->config();
02010             appletConfig = KConfigGroup(&appletConfig, "Applets");
02011             d->mainConfig = new KConfigGroup(&appletConfig, QString::number(d->appletId));
02012             old->copyTo(d->mainConfig);
02013             old->deleteGroup();
02014             delete old;
02015         } else if (!d->isContainment) {
02016             Plasma::PopupApplet *pa = qobject_cast<Plasma::PopupApplet *>(this);
02017             if (!pa) {
02018                 break;
02019             }
02020             //reconnect of popupapplets with new containment geometryChanged
02021             if (containment()) {
02022                 disconnect(containment(), SIGNAL(geometryChanged()), pa, SLOT(updateDialogPosition()));
02023             }
02024             Plasma::Containment *cont = dynamic_cast<Containment*>(value.value<QGraphicsItem *>());
02025             if (cont) {
02026                 connect(cont, SIGNAL(geometryChanged()), pa, SLOT(updateDialogPosition()));
02027             }
02028         }
02029     case ItemPositionChange:
02030         return (immutability() == Mutable || isContainment() || formFactor() == Horizontal || formFactor() == Vertical) ? value : pos();
02031         break;
02032     case ItemTransformChange:
02033         return immutability() == Mutable ? value : transform();
02034         break;
02035     case ItemPositionHasChanged:
02036         emit geometryChanged();
02037         // fall through!
02038     case ItemTransformHasChanged:
02039         d->scheduleModificationNotification();
02040         break;
02041     default:
02042         break;
02043     };
02044 
02045     return ret;
02046 }
02047 
02048 QPainterPath Applet::shape() const
02049 {
02050     if (d->script) {
02051         return d->script->shape();
02052     }
02053 
02054     return QGraphicsWidget::shape();
02055 }
02056 
02057 QSizeF Applet::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
02058 {
02059     QSizeF hint = QGraphicsWidget::sizeHint(which, constraint);
02060 
02061     //in panels make sure that the contents won't exit from the panel
02062     if (formFactor() == Horizontal && which == Qt::MinimumSize) {
02063         hint.setHeight(0);
02064     } else if (formFactor() == Vertical && which == Qt::MinimumSize) {
02065         hint.setWidth(0);
02066     }
02067 
02068     // enforce a square size in panels
02069     if (d->aspectRatioMode == Plasma::Square) {
02070         if (formFactor() == Horizontal) {
02071             hint.setWidth(size().height());
02072         } else if (formFactor() == Vertical) {
02073             hint.setHeight(size().width());
02074         }
02075     } else if (d->aspectRatioMode == Plasma::ConstrainedSquare) {
02076         //enforce a size not wider than tall
02077         if (formFactor() == Horizontal &&
02078             (which == Qt::MaximumSize || size().height() <= KIconLoader::SizeLarge)) {
02079             hint.setWidth(size().height());
02080         //enforce a size not taller than wide
02081         } else if (formFactor() == Vertical &&
02082                    (which == Qt::MaximumSize || size().width() <= KIconLoader::SizeLarge)) {
02083             hint.setHeight(size().width());
02084         }
02085     }
02086 
02087     return hint;
02088 }
02089 
02090 void Applet::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
02091 {
02092     Q_UNUSED(event)
02093 }
02094 
02095 void Applet::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
02096 {
02097     Q_UNUSED(event)
02098 }
02099 
02100 void Applet::timerEvent(QTimerEvent *event)
02101 {
02102     if (d->transient) {
02103         killTimer(d->constraintsTimerId);
02104         killTimer(d->modificationsTimerId);
02105         return;
02106     }
02107 
02108     if (event->timerId() == d->constraintsTimerId) {
02109         killTimer(d->constraintsTimerId);
02110         d->constraintsTimerId = 0;
02111 
02112         // Don't flushPendingConstraints if we're just starting up
02113         // flushPendingConstraints will be called by Corona
02114         if(!(d->pendingConstraints & Plasma::StartupCompletedConstraint)) {
02115             flushPendingConstraintsEvents();
02116         }
02117     } else if (event->timerId() == d->modificationsTimerId) {
02118         killTimer(d->modificationsTimerId);
02119         d->modificationsTimerId = 0;
02120         // invalid group, will result in save using the default group
02121         KConfigGroup cg;
02122         save(cg);
02123         emit configNeedsSaving();
02124     }
02125 }
02126 
02127 QRect Applet::screenRect() const
02128 {
02129     QGraphicsView *v = view();
02130 
02131     if (v) {
02132         QPointF bottomRight = pos();
02133         bottomRight.rx() += size().width();
02134         bottomRight.ry() += size().height();
02135 
02136         QPoint tL = v->mapToGlobal(v->mapFromScene(pos()));
02137         QPoint bR = v->mapToGlobal(v->mapFromScene(bottomRight));
02138         return QRect(QPoint(tL.x(), tL.y()), QSize(bR.x() - tL.x(), bR.y() - tL.y()));
02139     }
02140 
02141     //The applet doesn't have a view on it.
02142     //So a screenRect isn't relevant.
02143     return QRect(QPoint(0, 0), QSize(0, 0));
02144 }
02145 
02146 void Applet::raise()
02147 {
02148     setZValue(++AppletPrivate::s_maxZValue);
02149 }
02150 
02151 void Applet::lower()
02152 {
02153     setZValue(--AppletPrivate::s_minZValue);
02154 }
02155 
02156 void AppletPrivate::setIsContainment(bool nowIsContainment, bool forceUpdate)
02157 {
02158     if (isContainment == nowIsContainment && !forceUpdate) {
02159         return;
02160     }
02161 
02162     isContainment = nowIsContainment;
02163     //FIXME I do not like this function.
02164     //currently it's only called before ctmt/applet init, with (true,true), and I'm going to assume it stays that way.
02165     //if someone calls it at some other time it'll cause headaches. :P
02166 
02167     delete mainConfig;
02168     mainConfig = 0;
02169 }
02170 
02171 bool Applet::isContainment() const
02172 {
02173     return d->isContainment;
02174 }
02175 
02176 // PRIVATE CLASS IMPLEMENTATION
02177 
02178 AppletPrivate::AppletPrivate(KService::Ptr service, int uniqueID, Applet *applet)
02179         : appletId(uniqueID),
02180           q(applet),
02181           backgroundHints(Applet::NoBackground),
02182           preferredBackgroundHints(Applet::StandardBackground),
02183           aspectRatioMode(Plasma::KeepAspectRatio),
02184           immutability(Mutable),
02185           appletDescription(service),
02186           extender(0),
02187           background(0),
02188           mainConfig(0),
02189           pendingConstraints(NoConstraint),
02190           messageOverlay(0),
02191           messageOverlayProxy(0),
02192           busyWidget(0),
02193           script(0),
02194           package(0),
02195           configLoader(0),
02196           actions(AppletPrivate::defaultActions(applet)),
02197           activationAction(0),
02198           shortcutEditor(0),
02199           constraintsTimerId(0),
02200           modificationsTimerId(-1),
02201           hasConfigurationInterface(false),
02202           failed(false),
02203           isContainment(false),
02204           transient(false),
02205           needsConfig(false),
02206           started(false)
02207 {
02208     if (appletId == 0) {
02209         appletId = ++s_maxAppletId;
02210     } else if (appletId > s_maxAppletId) {
02211         s_maxAppletId = appletId;
02212     }
02213 }
02214 
02215 AppletPrivate::~AppletPrivate()
02216 {
02217     modificationsTimerId = -1;
02218 
02219     if (activationAction && activationAction->isGlobalShortcutEnabled()) {
02220         //kDebug() << "reseting global action for" << q->name() << activationAction->objectName();
02221         activationAction->forgetGlobalShortcut();
02222     }
02223 
02224     if (extender) {
02225         delete extender;
02226         extender = 0;
02227     }
02228 
02229     delete script;
02230     script = 0;
02231     delete package;
02232     package = 0;
02233     delete configLoader;
02234     configLoader = 0;
02235     delete mainConfig;
02236     mainConfig = 0;
02237 }
02238 
02239 void AppletPrivate::init(const QString &packagePath)
02240 {
02241     // WARNING: do not access config() OR globalConfig() in this method!
02242     //          that requires a scene, which is not available at this point
02243     q->setCacheMode(Applet::DeviceCoordinateCache);
02244     q->setAcceptsHoverEvents(true);
02245     q->setFlag(QGraphicsItem::ItemIsFocusable, true);
02246     q->setFocusPolicy(Qt::ClickFocus);
02247     // FIXME: adding here because nothing seems to be doing it in QGraphicsView,
02248     // but it doesn't actually work anyways =/
02249     q->setLayoutDirection(qApp->layoutDirection());
02250 
02251     if (!appletDescription.isValid()) {
02252         kDebug() << "Check your constructor! "
02253                  << "You probably want to be passing in a Service::Ptr "
02254                  << "or a QVariantList with a valid storageid as arg[0].";
02255         return;
02256     }
02257 
02258     QString api = appletDescription.property("X-Plasma-API").toString();
02259 
02260     // we have a scripted plasmoid
02261     if (!api.isEmpty()) {
02262         // find where the Package is
02263         QString path = packagePath;
02264         if (path.isEmpty()) {
02265             path = KStandardDirs::locate("data", "plasma/plasmoids/" + appletDescription.pluginName() + '/');
02266         } else if (!path.endsWith('/')) {
02267             path.append('/');
02268         }
02269 
02270         if (path.isEmpty()) {
02271             q->setFailedToLaunch(
02272                 true,
02273                 i18nc("Package file, name of the widget",
02274                       "Could not locate the %1 package required for the %2 widget.",
02275                       appletDescription.pluginName(), appletDescription.name()));
02276         } else {
02277             // create the package and see if we have something real
02278             //kDebug() << "trying for" << path;
02279             PackageStructure::Ptr structure = Plasma::packageStructure(api, Plasma::AppletComponent);
02280             structure->setPath(path);
02281             package = new Package(path, structure);
02282 
02283             if (package->isValid()) {
02284                 // now we try and set up the script engine.
02285                 // it will be parented to this applet and so will get
02286                 // deleted when the applet does
02287 
02288                 script = Plasma::loadScriptEngine(api, q);
02289                 if (!script) {
02290                     delete package;
02291                     package = 0;
02292                     q->setFailedToLaunch(true,
02293                                          i18nc("API or programming language the widget was written in, name of the widget",
02294                                                "Could not create a %1 ScriptEngine for the %2 widget.",
02295                                                api, appletDescription.name()));
02296                 }
02297             } else {
02298                 q->setFailedToLaunch(true, i18nc("Package file, name of the widget",
02299                                                  "Could not open the %1 package required for the %2 widget.",
02300                                                  appletDescription.pluginName(), appletDescription.name()));
02301                 delete package;
02302                 package = 0;
02303             }
02304         }
02305     }
02306 
02307     //set a default size before any saved settings are read
02308     QSize size = appletDescription.property("X-Plasma-DefaultSize").toSize();
02309     if (size.isEmpty()) {
02310         size = QSize(200, 200);
02311     }
02312     //kDebug() << "size" << size;
02313     q->resize(size);
02314 
02315     q->setBackgroundHints(Applet::DefaultBackground);
02316     q->setHasConfigurationInterface(true); //FIXME why not default it to true in the constructor?
02317 
02318     QAction *closeApplet = actions->action("remove");
02319     closeApplet->setText(i18nc("%1 is the name of the applet", "Remove this %1", q->name()));
02320     QAction *configAction = actions->action("configure");
02321     configAction->setText(i18nc("%1 is the name of the applet", "%1 Settings", q->name()));
02322 
02323     QObject::connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), q, SLOT(themeChanged()));
02324     QObject::connect(q, SIGNAL(activate()), q, SLOT(setFocus()));
02325 }
02326 
02327 // put all setup routines for script here. at this point we can assume that
02328 // package exists and that we have a script engine
02329 void AppletPrivate::setupScriptSupport()
02330 {
02331     Q_ASSERT(package);
02332     kDebug() << "setting up script support, package is in" << package->path()
02333              << "which is a" << package->structure()->type() << "package"
02334              << ", main script is" << package->filePath("mainscript");
02335 
02336     QString translationsPath = package->filePath("translations");
02337     if (!translationsPath.isEmpty()) {
02338         //FIXME: we should _probably_ use a KComponentData to segregate the applets
02339         //       from each other; but I want to get the basics working first :)
02340         KGlobal::dirs()->addResourceDir("locale", translationsPath);
02341         KGlobal::locale()->insertCatalog(package->metadata().pluginName());
02342     }
02343 
02344     QString xmlPath = package->filePath("mainconfigxml");
02345     if (!xmlPath.isEmpty()) {
02346         QFile file(xmlPath);
02347         KConfigGroup config = q->config();
02348         configLoader = new ConfigLoader(&config, &file);
02349         QObject::connect(configLoader, SIGNAL(configChanged()), q, SLOT(configChanged()));
02350     }
02351 
02352     if (!package->filePath("mainconfigui").isEmpty()) {
02353         q->setHasConfigurationInterface(true);
02354     }
02355 }
02356 
02357 QString AppletPrivate::globalName() const
02358 {
02359     if (!appletDescription.isValid()) {
02360         return QString();
02361     }
02362 
02363     return appletDescription.service()->library();
02364 }
02365 
02366 QString AppletPrivate::instanceName()
02367 {
02368     if (!appletDescription.isValid()) {
02369         return QString();
02370     }
02371 
02372     return appletDescription.service()->library() + QString::number(appletId);
02373 }
02374 
02375 void AppletPrivate::scheduleConstraintsUpdate(Plasma::Constraints c)
02376 {
02377     // Don't start up a timer if we're just starting up
02378     // flushPendingConstraints will be called by Corona
02379     if (started && !constraintsTimerId && !(c & Plasma::StartupCompletedConstraint)) {
02380         constraintsTimerId = q->startTimer(0);
02381     }
02382 
02383     if (c & Plasma::StartupCompletedConstraint) {
02384         started = true;
02385     }
02386 
02387     pendingConstraints |= c;
02388 }
02389 
02390 void AppletPrivate::scheduleModificationNotification()
02391 {
02392     // modificationsTimerId is -1 until we get our notice of being started
02393     if (modificationsTimerId != -1) {
02394         // schedule a save
02395         if (modificationsTimerId) {
02396             q->killTimer(modificationsTimerId);
02397         }
02398 
02399         modificationsTimerId = q->startTimer(1000);
02400     }
02401 }
02402 
02403 KConfigGroup *AppletPrivate::mainConfigGroup()
02404 {
02405     if (mainConfig) {
02406         return mainConfig;
02407     }
02408 
02409     if (isContainment) {
02410         Corona *corona = qobject_cast<Corona*>(q->scene());
02411         KConfigGroup containmentConfig;
02412         //kDebug() << "got a corona, baby?" << (QObject*)corona << (QObject*)q;
02413 
02414         if (corona) {
02415             containmentConfig = KConfigGroup(corona->config(), "Containments");
02416         } else {
02417             containmentConfig =  KConfigGroup(KGlobal::config(), "Containments");
02418         }
02419 
02420         mainConfig = new KConfigGroup(&containmentConfig, QString::number(appletId));
02421     } else {
02422         KConfigGroup appletConfig;
02423         if (q->containment()) {
02424             appletConfig = q->containment()->config();
02425             appletConfig = KConfigGroup(&appletConfig, "Applets");
02426         } else {
02427             kWarning() << "requesting config for" << q->name() << "without a containment!";
02428             appletConfig = KConfigGroup(KGlobal::config(), "Applets");
02429         }
02430 
02431         mainConfig = new KConfigGroup(&appletConfig, QString::number(appletId));
02432     }
02433 
02434     return mainConfig;
02435 }
02436 
02437 QString AppletPrivate::visibleFailureText(const QString &reason)
02438 {
02439     QString text;
02440 
02441     if (reason.isEmpty()) {
02442         text = i18n("This object could not be created.");
02443     } else {
02444         text = i18n("This object could not be created for the following reason:<p><b>%1</b></p>", reason);
02445     }
02446 
02447     return text;
02448 }
02449 
02450 void AppletPrivate::checkImmutability()
02451 {
02452     const bool systemImmutable = q->globalConfig().isImmutable() || q->config().isImmutable() ||
02453                                 ((!isContainment && q->containment()) &&
02454                                     q->containment()->immutability() == SystemImmutable) ||
02455                                 (qobject_cast<Corona*>(q->scene()) && static_cast<Corona*>(q->scene())->immutability() == SystemImmutable);
02456 
02457     if (systemImmutable) {
02458         q->updateConstraints(ImmutableConstraint);
02459     }
02460 }
02461 
02462 void AppletPrivate::themeChanged()
02463 {
02464     if (background) {
02465         //do again the translucent background fallback
02466         q->setBackgroundHints(backgroundHints);
02467 
02468         qreal left;
02469         qreal right;
02470         qreal top;
02471         qreal bottom;
02472         background->getMargins(left, top, right, bottom);
02473         q->setContentsMargins(left, right, top, bottom);
02474     }
02475     q->update();
02476 }
02477 
02478 void AppletPrivate::resetConfigurationObject()
02479 {
02480     // make sure mainConfigGroup exists in all cases
02481     mainConfigGroup();
02482 
02483     mainConfig->deleteGroup();
02484     delete mainConfig;
02485     mainConfig = 0;
02486 
02487     Corona * corona = qobject_cast<Corona*>(q->scene());
02488     if (corona) {
02489         corona->requireConfigSync();
02490     }
02491 }
02492 
02493 uint AppletPrivate::s_maxAppletId = 0;
02494 int AppletPrivate::s_maxZValue = 0;
02495 int AppletPrivate::s_minZValue = 0;
02496 PackageStructure::Ptr AppletPrivate::packageStructure(0);
02497 QSet<QString> AppletPrivate::s_customCategories;
02498 
02499 AppletOverlayWidget::AppletOverlayWidget(QGraphicsWidget *parent)
02500     : QGraphicsWidget(parent),
02501       opacity(0.4)
02502 {
02503     resize(parent->size());
02504     Animator::self()->animateItem(this, Animator::AppearAnimation);
02505 }
02506 
02507 void AppletOverlayWidget::destroy()
02508 {
02509     connect(Animator::self(),
02510             SIGNAL(animationFinished(QGraphicsItem*,Plasma::Animator::Animation)),
02511             this,
02512             SLOT(overlayAnimationComplete(QGraphicsItem*,Plasma::Animator::Animation)));
02513     Animator::self()->animateItem(this, Animator::DisappearAnimation);
02514 }
02515 
02516 void AppletOverlayWidget::overlayAnimationComplete(QGraphicsItem *item, Plasma::Animator::Animation)
02517 {
02518     if (item == this) {
02519         deleteLater();
02520     }
02521 }
02522 
02523 void AppletOverlayWidget::paint(QPainter *painter,
02524                                 const QStyleOptionGraphicsItem *option,
02525                                 QWidget *widget)
02526 {
02527     Q_UNUSED(option)
02528     Q_UNUSED(widget)
02529 
02530     if (qFuzzyCompare(1, 1+opacity)) {
02531         return;
02532     }
02533 
02534     QColor wash = Plasma::Theme::defaultTheme()->color(Theme::BackgroundColor);
02535     wash.setAlphaF(opacity);
02536 
02537     Applet *applet = qobject_cast<Applet *>(parentWidget());
02538 
02539 
02540     QPainterPath backgroundShape;
02541     if (!applet || applet->backgroundHints() & Applet::StandardBackground) {
02542         //FIXME: a resize here is nasty, but perhaps still better than an eventfilter just for that..
02543         if (parentWidget()->contentsRect().size() != size()) {
02544             resize(parentWidget()->contentsRect().size());
02545         }
02546         backgroundShape = PaintUtils::roundedRectangle(contentsRect(), 5);
02547     } else {
02548         backgroundShape = shape();
02549     }
02550 
02551     painter->save();
02552     painter->setRenderHints(QPainter::Antialiasing);
02553     painter->fillPath(backgroundShape, wash);
02554     painter->restore();
02555 }
02556 
02557 } // Plasma namespace
02558 
02559 #include "applet.moc"
02560 #include "private/applet_p.moc"
02561 #endif // APPLET_CPP

Plasma

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal